using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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("SailRebalance")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyProduct("SailRebalance")] [assembly: AssemblyTitle("SailRebalance")] [assembly: AssemblyVersion("1.2.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 SailRebalance { internal static class JunkSquareReverseReefing { private static readonly HashSet JunkSquareIndices = new HashSet { 27, 28, 29, 100, 101, 102, 103 }; private static readonly HashSet Rigs = new HashSet(); private static readonly Dictionary OriginalControllerDirections = new Dictionary(); private static readonly HashSet Warnings = new HashSet(); internal static bool IsTarget(Sail sail) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sail != (Object)null && sail.squareSail && (int)sail.category == 0) { return JunkSquareIndices.Contains(sail.prefabIndex); } return false; } internal static void PrepareController(RopeControllerSailReef controller) { if (Plugin.ReverseJunkSquareFurlingEnabled && !((Object)(object)controller == (Object)null) && IsTarget(controller.sail)) { if (!OriginalControllerDirections.ContainsKey(controller)) { OriginalControllerDirections.Add(controller, controller.reverseReefing); } controller.reverseReefing = true; } } internal static bool TakeOriginalControllerDirection(RopeControllerSailReef controller) { if (OriginalControllerDirections.TryGetValue(controller, out var value)) { OriginalControllerDirections.Remove(controller); return value; } return controller.reverseReefing; } internal static void RestorePreparedController(RopeControllerSailReef controller) { if (!((Object)(object)controller == (Object)null) && OriginalControllerDirections.TryGetValue(controller, out var value)) { OriginalControllerDirections.Remove(controller); float num = (((Object)(object)controller.sail != (Object)null) ? Mathf.Clamp01(controller.sail.currentUnroll) : 1f); controller.reverseReefing = value; ((RopeController)controller).currentLength = (value ? (1f - num) : num); ((RopeController)controller).changed = true; } } internal static void Register(JunkSquareReverseRig rig) { Rigs.Add(rig); } internal static void Unregister(JunkSquareReverseRig rig) { Rigs.Remove(rig); } internal static void PrepareMast(Mast mast) { JunkSquareReverseRig[] array = CopyRigs(); foreach (JunkSquareReverseRig junkSquareReverseRig in array) { if ((Object)(object)junkSquareReverseRig != (Object)null && (Object)(object)junkSquareReverseRig.OwningMast == (Object)(object)mast) { junkSquareReverseRig.PrepareForMastUpdate(); } } } internal static void ApplyToMast(Mast mast) { if ((Object)(object)mast == (Object)null || mast.sails == null) { return; } for (int i = 0; i < mast.sails.Count; i++) { GameObject val = mast.sails[i]; if ((Object)(object)val == (Object)null) { continue; } Sail component = val.GetComponent(); JunkSquareReverseRig junkSquareReverseRig = val.GetComponent(); if (!Plugin.ReverseJunkSquareFurlingEnabled || !IsTarget(component)) { if ((Object)(object)junkSquareReverseRig != (Object)null) { junkSquareReverseRig.RestoreAndDestroy(); } continue; } SailConnections component2 = val.GetComponent(); if ((Object)(object)component2 == (Object)null) { WarnOnce(component, "has no SailConnections component"); continue; } if ((Object)(object)junkSquareReverseRig == (Object)null) { junkSquareReverseRig = val.AddComponent(); } if (junkSquareReverseRig.Initialize(mast, component, component2)) { junkSquareReverseRig.BindAssignedWinch(); Warnings.Remove(((Object)component).GetInstanceID()); } else { RopeController reefController = component2.reefController; RestorePreparedController((RopeControllerSailReef)(object)((reefController is RopeControllerSailReef) ? reefController : null)); junkSquareReverseRig.RestoreAndDestroy(); } } } internal static void ApplyToLoadedMasts() { Mast[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { ApplyToMast(array[i]); } } internal static void SetEnabled(bool enabled) { if (enabled) { ApplyToLoadedMasts(); } else { DisposeAllRigs(); } } internal static void FurlNewlyInstalledShipyardSail(GameObject sailObject) { if (!Plugin.ReverseJunkSquareFurlingEnabled || (Object)(object)GameState.currentShipyard == (Object)null || (Object)(object)sailObject == (Object)null) { return; } Sail component = sailObject.GetComponent(); if (IsTarget(component) && component.IsInstalled()) { SailConnections component2 = sailObject.GetComponent(); RopeControllerSailReef val = (RopeControllerSailReef)(((Object)(object)component2 != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { WarnOnce(component, "its newly installed reef controller was not found"); return; } component.currentUnroll = 0f; val.reverseReefing = true; ((RopeController)val).currentLength = 1f; ((RopeController)val).changed = true; Warnings.Remove(((Object)component).GetInstanceID()); } } internal static void DisposeAllRigs() { JunkSquareReverseRig[] array = CopyRigs(); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null) { array[i].RestoreAndDestroy(); } } Rigs.Clear(); RestoreAllPreparedControllers(); } internal static void Reset() { DisposeAllRigs(); OriginalControllerDirections.Clear(); Warnings.Clear(); } internal static void WarnOnce(Sail sail, string reason) { if (!((Object)(object)sail == (Object)null) && Warnings.Add(((Object)sail).GetInstanceID()) && Plugin.Log != null) { Plugin.Log.LogWarning((object)("Could not reverse Junk-square furling for " + ((Object)sail).name + ": " + reason + ".")); } } private static JunkSquareReverseRig[] CopyRigs() { JunkSquareReverseRig[] array = new JunkSquareReverseRig[Rigs.Count]; Rigs.CopyTo(array); return array; } private static void RestoreAllPreparedControllers() { RopeControllerSailReef[] array = (RopeControllerSailReef[])(object)new RopeControllerSailReef[OriginalControllerDirections.Count]; OriginalControllerDirections.Keys.CopyTo(array, 0); for (int i = 0; i < array.Length; i++) { RestorePreparedController(array[i]); } OriginalControllerDirections.Clear(); } } [HarmonyPatch(typeof(RopeControllerSailReef), "Awake")] internal static class JunkSquareReverseControllerAwakePatch { private static void Prefix(RopeControllerSailReef __instance) { JunkSquareReverseReefing.PrepareController(__instance); } } [HarmonyPatch(typeof(Mast), "UpdateControllerAttachments")] internal static class JunkSquareReverseMastAttachmentPatch { private static void Prefix(Mast __instance) { JunkSquareReverseReefing.PrepareMast(__instance); } private static void Postfix(Mast __instance) { JunkSquareReverseReefing.ApplyToMast(__instance); } } [HarmonyPatch(typeof(Mast), "AttachSailToMast")] internal static class JunkSquareReverseShipyardInstallPatch { private static void Prefix(GameObject sailObject, out bool __state) { Sail val = (((Object)(object)sailObject != (Object)null) ? sailObject.GetComponent() : null); __state = (Object)(object)GameState.currentShipyard != (Object)null && (Object)(object)val != (Object)null && !val.IsInstalled() && JunkSquareReverseReefing.IsTarget(val); } private static void Postfix(GameObject sailObject, bool __state) { if (__state) { JunkSquareReverseReefing.FurlNewlyInstalledShipyardSail(sailObject); } } } internal sealed class JunkSquareReverseRig : MonoBehaviour { private Mast mast; private Sail sail; private RopeControllerSailReef reefController; private ReefEffectAnimUniversal reefEffect; private Transform visualRoot; private Transform boomLow; private Vector3 unfurledVisualRootLocalPosition; private Vector3 unfurledBoomLowLocalPosition; private bool originalControllerDirection; private GPButtonRopeWinch boundWinch; private bool originalWinchReverseResistance; private bool initialized; private bool refreshClothPending; private bool restored; internal Mast OwningMast => mast; internal bool Initialize(Mast owningMast, Sail targetSail, SailConnections targetConnections) { mast = owningMast; if (initialized) { return true; } sail = targetSail; ref RopeControllerSailReef reference = ref reefController; RopeController obj = targetConnections.reefController; reference = (RopeControllerSailReef)(object)((obj is RopeControllerSailReef) ? obj : null); if ((Object)(object)reefController == (Object)null) { return Fail("its reef controller is not RopeControllerSailReef"); } reefEffect = ((Component)targetSail).GetComponentInChildren(true); Animator val = FindAnimator(); if ((Object)(object)reefEffect == (Object)null || (Object)(object)val == (Object)null) { return Fail("the Junk-square reef animator was not found"); } visualRoot = FindDescendant(((Component)val).transform, "SAIL_junk_square"); boomLow = FindDescendant(visualRoot, "boom_010"); Transform val2 = FindDescendant(visualRoot, "sail_rope_att__top_"); if ((Object)(object)visualRoot == (Object)null || (Object)(object)boomLow == (Object)null || (Object)(object)val2 == (Object)null) { return Fail("the Junk-square visual root, lower batten, or lifting-rope anchor was not found"); } if (!CaptureUnfurledGeometry(val)) { return false; } originalControllerDirection = JunkSquareReverseReefing.TakeOriginalControllerDirection(reefController); ApplyControllerDirection(reverse: true); initialized = true; refreshClothPending = true; JunkSquareReverseReefing.Register(this); return true; } internal void PrepareForMastUpdate() { RestoreWinch(); } internal void BindAssignedWinch() { if (!initialized || (Object)(object)mast == (Object)null) { return; } GPButtonRopeWinch[] componentsInChildren = ((Component)(((Object)(object)mast.shipRigidbody != (Object)null) ? ((Component)mast.shipRigidbody).transform : ((Component)mast).transform)).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null && (Object)(object)componentsInChildren[i].rope == (Object)(object)reefController) { BindWinch(componentsInChildren[i]); return; } } RestoreWinch(); } internal void RestoreAndDestroy() { Restore(); if ((Object)(object)this != (Object)null) { Object.Destroy((Object)(object)this); } } private void LateUpdate() { if (initialized) { ApplyBoomLowAnchorCorrection(); if (refreshClothPending) { refreshClothPending = false; RefreshCloth(); } } } private void OnDestroy() { Restore(); } private Animator FindAnimator() { if ((Object)(object)reefEffect == (Object)null) { return null; } if ((Object)(object)reefEffect.overrideAnimator != (Object)null) { return reefEffect.overrideAnimator; } Animator val = ((Component)reefEffect).GetComponent(); if ((Object)(object)val == (Object)null) { val = ((Component)reefEffect).GetComponentInParent(); } if ((Object)(object)val == (Object)null) { val = ((Component)sail).GetComponentInChildren(true); } return val; } private bool CaptureUnfurledGeometry(Animator animator) { //IL_0054: 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_006b: 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_0075: Unknown result type (might be due to invalid IL or missing references) string clipName = (string.IsNullOrEmpty(reefEffect.clipName) ? "ArmatureAction" : reefEffect.clipName); float normalizedTime = 1f - Mathf.Clamp01(sail.currentUnroll); Exception ex = null; Exception ex2 = null; try { SampleAnimator(animator, clipName, 0f); unfurledVisualRootLocalPosition = visualRoot.localPosition; unfurledBoomLowLocalPosition = visualRoot.InverseTransformPoint(boomLow.position); } catch (Exception ex3) { ex = ex3; } finally { try { SampleAnimator(animator, clipName, normalizedTime); } catch (Exception ex4) { ex2 = ex4; } } if (ex != null) { LogAnimationWarning("sample", ex); if (ex2 != null) { LogAnimationWarning("restore", ex2); } return Fail("the Junk-square reef animation could not be sampled"); } if (ex2 != null) { LogAnimationWarning("restore", ex2); return Fail("the Junk-square reef animation state could not be restored"); } return true; } private void SampleAnimator(Animator animator, string clipName, float normalizedTime) { animator.Play(clipName, reefEffect.layer, normalizedTime); animator.Update(0f); } private void LogAnimationWarning(string operation, Exception exception) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("Could not " + operation + " Junk-square reef animation for " + ((Object)sail).name + ": " + exception.Message)); } } private void ApplyBoomLowAnchorCorrection() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)visualRoot == (Object)null) && !((Object)(object)boomLow == (Object)null)) { visualRoot.localPosition = unfurledVisualRootLocalPosition; Vector3 val = visualRoot.TransformPoint(unfurledBoomLowLocalPosition); Vector3 val2 = ParentVector(visualRoot.parent, val - boomLow.position); visualRoot.localPosition = unfurledVisualRootLocalPosition + val2; } } private void ApplyControllerDirection(bool reverse) { if (!((Object)(object)reefController == (Object)null) && !((Object)(object)sail == (Object)null)) { float num = Mathf.Clamp01(sail.currentUnroll); reefController.reverseReefing = reverse; ((RopeController)reefController).currentLength = (reverse ? (1f - num) : num); ((RopeController)reefController).changed = true; } } private void BindWinch(GPButtonRopeWinch winch) { if ((Object)(object)boundWinch == (Object)(object)winch) { winch.reverseWindResistance = true; return; } RestoreWinch(); boundWinch = winch; originalWinchReverseResistance = winch.reverseWindResistance; winch.reverseWindResistance = true; } private void RestoreWinch() { if ((Object)(object)boundWinch != (Object)null) { boundWinch.reverseWindResistance = originalWinchReverseResistance; } boundWinch = null; } private void Restore() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (restored) { return; } restored = true; RestoreWinch(); if (initialized) { ApplyControllerDirection(originalControllerDirection); if ((Object)(object)visualRoot != (Object)null) { visualRoot.localPosition = unfurledVisualRootLocalPosition; } RefreshCloth(); } JunkSquareReverseReefing.Unregister(this); } private void RefreshCloth() { if ((Object)(object)reefEffect == (Object)null || !((Behaviour)reefEffect).isActiveAndEnabled) { return; } try { reefEffect.RefreshCloth(); } catch (Exception ex) { if (Plugin.Log != null) { Plugin.Log.LogWarning((object)("Could not refresh Junk-square cloth for " + ((Object)sail).name + ": " + ex.Message)); } } } private bool Fail(string reason) { JunkSquareReverseReefing.WarnOnce(sail, reason); return false; } private static Transform FindDescendant(Transform root, string name) { if ((Object)(object)root == (Object)null) { return null; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (((Object)componentsInChildren[i]).name == name) { return componentsInChildren[i]; } } return null; } private static Vector3 ParentVector(Transform parent, Vector3 worldVector) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)parent != (Object)null)) { return worldVector; } return parent.InverseTransformVector(worldVector); } } [HarmonyPatch(typeof(Sail), "ApplyForce")] internal static class LateenBadTackForcePatch { private struct ForceState { internal bool Applied; internal float ForwardForce; internal float SideForce; } private static void Prefix(Sail __instance, ref float ___unamplifiedForwardForce, ref float ___unamplifiedSidewayForce, out ForceState __state) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 __state = default(ForceState); if (Plugin.LateenBadTackPenaltyEnabled && (int)__instance.category == 1 && LateenControlRegistry.TryGetRig(__instance, out var rig) && rig.IsBadTack()) { __state.Applied = true; __state.ForwardForce = ___unamplifiedForwardForce; __state.SideForce = ___unamplifiedSidewayForce; ___unamplifiedForwardForce *= 0.9f; ___unamplifiedSidewayForce *= 0.9f; } } private static void Postfix(ref float ___unamplifiedForwardForce, ref float ___unamplifiedSidewayForce, ForceState __state) { if (__state.Applied) { ___unamplifiedForwardForce = __state.ForwardForce; ___unamplifiedSidewayForce = __state.SideForce; } } } [HarmonyPatch(typeof(Mast), "UpdateControllerAttachments")] internal static class LateenBraceAttachmentPatch { private sealed class LateenSailContext { internal Mast Mast; internal Sail Sail; internal SailConnections Connections; internal GameObject SailObject; } private const string BraceWinchName = "brace winch"; private static readonly Dictionary OriginalDescriptions = new Dictionary(); private static readonly HashSet MissingWinchWarnings = new HashSet(); private static void Postfix(Mast __instance) { Mast[] boatMasts = GetBoatMasts(__instance); ReleaseBraceAssignments(__instance); List lateens = GetLateens(boatMasts); for (int i = 0; i < lateens.Count; i++) { AttachBrace(lateens[i], boatMasts); } } private static void AttachBrace(LateenSailContext context, Mast[] boatMasts) { LateenBraceWinchChoice lateenBraceWinchChoice = LateenBraceWinchSelector.Find(context.Mast, context.Sail.mastOrder, boatMasts); if (lateenBraceWinchChoice == null) { WarnMissingWinch(context.Sail); return; } LateenYardRig lateenYardRig = context.SailObject.GetComponent(); if ((Object)(object)lateenYardRig == (Object)null) { lateenYardRig = context.SailObject.AddComponent(); } lateenYardRig.SetBraceRopeHolder(lateenBraceWinchChoice.RopeHolder); if (lateenYardRig.Initialize(context.Mast, context.Sail, context.Connections) && !((Object)(object)lateenYardRig.Brace == (Object)null)) { RememberDescription(lateenBraceWinchChoice.Winch); lateenBraceWinchChoice.Winch.AttachToController((RopeController)(object)lateenYardRig.Brace); ((GoPointerButton)lateenBraceWinchChoice.Winch).description = "brace winch"; lateenBraceWinchChoice.Winch.ShowWinch(true); MissingWinchWarnings.Remove(((Object)context.Sail).GetInstanceID()); } } private static Mast[] GetBoatMasts(Mast mast) { Mast[] componentsInChildren = ((Component)GetBoatRoot(mast)).GetComponentsInChildren(false); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] == (Object)(object)mast) { return componentsInChildren; } } return (Mast[])(object)new Mast[1] { mast }; } private static List GetLateens(Mast[] masts) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 List list = new List(); foreach (Mast val in masts) { if ((Object)(object)val == (Object)null || val.sails == null) { continue; } for (int j = 0; j < val.sails.Count; j++) { GameObject val2 = val.sails[j]; if (!((Object)(object)val2 == (Object)null)) { Sail component = val2.GetComponent(); SailConnections component2 = val2.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && (int)component.category == 1) { list.Add(new LateenSailContext { Mast = val, Sail = component, Connections = component2, SailObject = val2 }); } } } } list.Sort(CompareLateens); return list; } private static int CompareLateens(LateenSailContext left, LateenSailContext right) { int num = left.Mast.orderIndex.CompareTo(right.Mast.orderIndex); if (num != 0) { return num; } num = left.Sail.mastOrder.CompareTo(right.Sail.mastOrder); if (num == 0) { return ((Object)left.Sail).GetInstanceID().CompareTo(((Object)right.Sail).GetInstanceID()); } return num; } private static Transform GetBoatRoot(Mast mast) { if (!((Object)(object)mast.shipRigidbody != (Object)null)) { return ((Component)mast).transform; } return ((Component)mast.shipRigidbody).transform; } private static void ReleaseBraceAssignments(Mast mast) { GPButtonRopeWinch[] componentsInChildren = ((Component)GetBoatRoot(mast)).GetComponentsInChildren(true); foreach (GPButtonRopeWinch val in componentsInChildren) { if (OriginalDescriptions.TryGetValue(val, out var value)) { ((GoPointerButton)val).description = value; OriginalDescriptions.Remove(val); } if (val.rope is LateenBraceController) { val.rope = null; val.ShowWinch(false); } } } private static void RememberDescription(GPButtonRopeWinch winch) { if (!OriginalDescriptions.ContainsKey(winch)) { OriginalDescriptions.Add(winch, ((GoPointerButton)winch).description); } } private static void WarnMissingWinch(Sail sail) { if (MissingWinchWarnings.Add(((Object)sail).GetInstanceID())) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("No unused winch is available for the brace on " + ((Object)sail).name + ".")); } } } internal static void Reset() { foreach (KeyValuePair originalDescription in OriginalDescriptions) { GPButtonRopeWinch key = originalDescription.Key; if ((Object)(object)key != (Object)null) { ((GoPointerButton)key).description = originalDescription.Value; if (key.rope is LateenBraceController) { key.rope = null; key.ShowWinch(false); } } } OriginalDescriptions.Clear(); MissingWinchWarnings.Clear(); } } internal sealed class LateenBraceController : RopeController { internal LateenYardRig Rig { get; private set; } internal void Initialize(LateenYardRig rig) { Rig = rig; base.currentLength = 0f; base.currentResistance = 0f; base.changed = true; } public override bool CanPull() { if ((Object)(object)Rig != (Object)null) { return Rig.CanOperateBrace; } return false; } private void Update() { base.currentLength = Mathf.Clamp01(base.currentLength); base.changed = false; } } internal static class LateenBraceWinchSelector { private sealed class RankedReefChoice { internal LateenBraceWinchChoice Choice; internal int SlotPriority; internal float WinchDistance; internal float RopeHolderDistance; internal int SourceOrder; internal int SourceInstanceId; } private static readonly int[] PreferredReefSlots = new int[3] { 1, 2, 3 }; internal static LateenBraceWinchChoice Find(Mast owningMast, int sailOrder, Mast[] boatMasts) { LateenBraceWinchChoice lateenBraceWinchChoice = FindReefWinchOnMast(owningMast); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } lateenBraceWinchChoice = FindSpecialReefWinch(owningMast, boatMasts, bowsprit: false); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } lateenBraceWinchChoice = FindAssociatedStaysailAngleWinch(owningMast, owningMast, boatMasts); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } lateenBraceWinchChoice = FindAngleWinchOnMast(owningMast, sailOrder); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } lateenBraceWinchChoice = FindOtherStaysailAngleWinch(owningMast, boatMasts); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } return FindSpecialReefWinch(owningMast, boatMasts, bowsprit: true); } private static LateenBraceWinchChoice FindReefWinchOnMast(Mast mast) { if ((Object)(object)mast == (Object)null || mast.reefWinch == null) { return null; } for (int i = 0; i < PreferredReefSlots.Length; i++) { LateenBraceWinchChoice availableReefWinch = GetAvailableReefWinch(mast, PreferredReefSlots[i]); if (availableReefWinch != null) { return availableReefWinch; } } for (int j = 0; j < mast.reefWinch.Length; j++) { if (!IsPreferredReefSlot(j)) { LateenBraceWinchChoice availableReefWinch2 = GetAvailableReefWinch(mast, j); if (availableReefWinch2 != null) { return availableReefWinch2; } } } return null; } private static LateenBraceWinchChoice FindSpecialReefWinch(Mast owningMast, Mast[] boatMasts, bool bowsprit) { //IL_0087: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_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_00c7: Unknown result type (might be due to invalid IL or missing references) RankedReefChoice rankedReefChoice = null; foreach (Mast val in boatMasts) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)owningMast || IsBowsprit(val) != bowsprit || (!bowsprit && !val.onlyStaysails) || val.reefWinch == null) { continue; } for (int j = 0; j < val.reefWinch.Length; j++) { LateenBraceWinchChoice availableReefWinch = GetAvailableReefWinch(val, j); if (availableReefWinch != null) { RankedReefChoice obj = new RankedReefChoice { Choice = availableReefWinch, SlotPriority = GetSlotPriority(j) }; Vector3 val2 = ((Component)availableReefWinch.Winch).transform.position - ((Component)owningMast).transform.position; obj.WinchDistance = ((Vector3)(ref val2)).sqrMagnitude; val2 = availableReefWinch.RopeHolder.position - ((Component)owningMast).transform.position; obj.RopeHolderDistance = ((Vector3)(ref val2)).sqrMagnitude; obj.SourceOrder = val.orderIndex; obj.SourceInstanceId = ((Object)val).GetInstanceID(); RankedReefChoice rankedReefChoice2 = obj; if (IsBetterSpecialReefChoice(rankedReefChoice2, rankedReefChoice)) { rankedReefChoice = rankedReefChoice2; } } } } return rankedReefChoice?.Choice; } private static bool IsBetterSpecialReefChoice(RankedReefChoice candidate, RankedReefChoice current) { if (current == null || candidate.SlotPriority != current.SlotPriority) { if (current != null) { return candidate.SlotPriority < current.SlotPriority; } return true; } int num = candidate.WinchDistance.CompareTo(current.WinchDistance); if (num != 0) { return num < 0; } num = candidate.RopeHolderDistance.CompareTo(current.RopeHolderDistance); if (num != 0) { return num < 0; } num = candidate.SourceOrder.CompareTo(current.SourceOrder); if (num == 0) { return candidate.SourceInstanceId < current.SourceInstanceId; } return num < 0; } private static LateenBraceWinchChoice FindAssociatedStaysailAngleWinch(Mast owningMast, Mast associatedOrdinaryMast, Mast[] boatMasts) { Transform boatRoot = GetBoatRoot(owningMast); List list = new List(); foreach (Mast val in boatMasts) { if (IsStaysailMast(val) && !((Object)(object)FindNearestOrdinaryMast(val, boatMasts, boatRoot) != (Object)(object)associatedOrdinaryMast)) { list.Add(val); } } list.Sort(delegate(Mast left, Mast right) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) float horizontalDistanceSquared = GetHorizontalDistanceSquared(((Component)left).transform.position, ((Component)associatedOrdinaryMast).transform.position, boatRoot.up); float horizontalDistanceSquared2 = GetHorizontalDistanceSquared(((Component)right).transform.position, ((Component)associatedOrdinaryMast).transform.position, boatRoot.up); int num2 = horizontalDistanceSquared.CompareTo(horizontalDistanceSquared2); return (num2 == 0) ? CompareMastIdentity(left, right) : num2; }); for (int num = 0; num < list.Count; num++) { LateenBraceWinchChoice lateenBraceWinchChoice = FindAnyAngleWinchOnMast(list[num]); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } } return null; } private static LateenBraceWinchChoice FindOtherStaysailAngleWinch(Mast owningMast, Mast[] boatMasts) { Transform boatRoot = GetBoatRoot(owningMast); List list = new List(); foreach (Mast val in boatMasts) { if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)owningMast && IsOrdinaryMast(val)) { list.Add(val); } } list.Sort(delegate(Mast left, Mast right) { //IL_0006: 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_0016: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) float value = Vector3.Dot(((Component)left).transform.position - boatRoot.position, boatRoot.forward); int num2 = Vector3.Dot(((Component)right).transform.position - boatRoot.position, boatRoot.forward).CompareTo(value); return (num2 == 0) ? CompareMastIdentity(left, right) : num2; }); for (int num = 0; num < list.Count; num++) { LateenBraceWinchChoice lateenBraceWinchChoice = FindAssociatedStaysailAngleWinch(owningMast, list[num], boatMasts); if (lateenBraceWinchChoice != null) { return lateenBraceWinchChoice; } } return null; } private static Mast FindNearestOrdinaryMast(Mast staysailMast, Mast[] boatMasts, Transform boatRoot) { //IL_001e: 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_002f: Unknown result type (might be due to invalid IL or missing references) Mast val = null; float num = float.PositiveInfinity; foreach (Mast val2 in boatMasts) { if (IsOrdinaryMast(val2)) { float horizontalDistanceSquared = GetHorizontalDistanceSquared(((Component)staysailMast).transform.position, ((Component)val2).transform.position, boatRoot.up); if ((Object)(object)val == (Object)null || horizontalDistanceSquared < num || (Mathf.Approximately(horizontalDistanceSquared, num) && CompareMastIdentity(val2, val) < 0)) { val = val2; num = horizontalDistanceSquared; } } } return val; } private static float GetHorizontalDistanceSquared(Vector3 first, Vector3 second, Vector3 boatUp) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_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) Vector3 val = first - second; val -= Vector3.Project(val, boatUp); return ((Vector3)(ref val)).sqrMagnitude; } private static int CompareMastIdentity(Mast left, Mast right) { int num = left.orderIndex.CompareTo(right.orderIndex); if (num == 0) { return ((Object)left).GetInstanceID().CompareTo(((Object)right).GetInstanceID()); } return num; } private static LateenBraceWinchChoice FindAngleWinchOnMast(Mast mast, int index) { LateenBraceWinchChoice availableWinch = GetAvailableWinch(mast, mast.leftAngleWinch, index); if (availableWinch != null) { return availableWinch; } availableWinch = GetAvailableWinch(mast, mast.rightAngleWinch, index); return availableWinch ?? GetAvailableWinch(mast, mast.midAngleWinch, index); } private static LateenBraceWinchChoice FindAnyAngleWinchOnMast(Mast mast) { int maximumLength = GetMaximumLength(mast.leftAngleWinch, mast.rightAngleWinch, mast.midAngleWinch); for (int i = 0; i < maximumLength; i++) { LateenBraceWinchChoice availableWinch = GetAvailableWinch(mast, mast.leftAngleWinch, i); if (availableWinch != null) { return availableWinch; } availableWinch = GetAvailableWinch(mast, mast.rightAngleWinch, i); if (availableWinch != null) { return availableWinch; } availableWinch = GetAvailableWinch(mast, mast.midAngleWinch, i); if (availableWinch != null) { return availableWinch; } } return null; } private static LateenBraceWinchChoice GetAvailableReefWinch(Mast mast, int index) { if (!((Object)(object)mast == (Object)null)) { return GetAvailableWinch(mast, mast.reefWinch, index); } return null; } private static LateenBraceWinchChoice GetAvailableWinch(Mast ropeHolderMast, GPButtonRopeWinch[] winches, int index) { GPButtonRopeWinch winch = GetWinch(winches, index); Transform transform = GetTransform(ropeHolderMast.mastReefAtt, index); if ((Object)(object)winch == (Object)null || (Object)(object)winch.rope != (Object)null || (Object)(object)transform == (Object)null) { return null; } return new LateenBraceWinchChoice { Winch = winch, RopeHolder = transform }; } private static bool IsOrdinaryMast(Mast mast) { if ((Object)(object)mast != (Object)null && !mast.onlyStaysails) { return !IsBowsprit(mast); } return false; } private static bool IsStaysailMast(Mast mast) { if ((Object)(object)mast != (Object)null && mast.onlyStaysails) { return !IsBowsprit(mast); } return false; } private static bool IsBowsprit(Mast mast) { if (ContainsIgnoreCase(((Object)((Component)mast).gameObject).name, "bowsprit")) { return true; } if (mast.reefWinch == null) { return false; } for (int i = 0; i < mast.reefWinch.Length; i++) { GPButtonRopeWinch val = mast.reefWinch[i]; if ((Object)(object)val != (Object)null && ContainsIgnoreCase(((Object)((Component)val).gameObject).name, "bowsprit")) { return true; } } return false; } private static bool ContainsIgnoreCase(string value, string search) { if (value != null) { return value.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static int GetSlotPriority(int index) { return index switch { 1 => 0, 2 => 1, 3 => 2, _ => 3, }; } private static bool IsPreferredReefSlot(int index) { if (index != 1 && index != 2) { return index == 3; } return true; } private static int GetMaximumLength(params GPButtonRopeWinch[][] arrays) { int num = 0; for (int i = 0; i < arrays.Length; i++) { if (arrays[i] != null) { num = Mathf.Max(num, arrays[i].Length); } } return num; } private static GPButtonRopeWinch GetWinch(GPButtonRopeWinch[] winches, int index) { if (winches == null || index < 0 || index >= winches.Length) { return null; } return winches[index]; } private static Transform GetTransform(Transform[] transforms, int index) { if (transforms == null || index < 0 || index >= transforms.Length) { return null; } return transforms[index]; } private static Transform GetBoatRoot(Mast mast) { if (!((Object)(object)mast.shipRigidbody != (Object)null)) { return ((Component)mast).transform; } return ((Component)mast.shipRigidbody).transform; } } internal sealed class LateenBraceWinchChoice { internal GPButtonRopeWinch Winch; internal Transform RopeHolder; } internal sealed class LateenControlBinding { internal LateenYardRig Rig; internal LateenControlRole Role; } internal static class LateenControlRegistry { private static readonly Dictionary Controls = new Dictionary(); private static readonly Dictionary SailRigs = new Dictionary(); private static readonly List Rigs = new List(); internal static IEnumerable ActiveRigs => Rigs; internal static void Register(LateenYardRig rig) { if (!((Object)(object)rig == (Object)null)) { UnregisterBindings(rig); Sail component = ((Component)rig).GetComponent(); if ((Object)(object)component != (Object)null) { SailRigs[component] = rig; } if (!Rigs.Contains(rig)) { Rigs.Add(rig); } Bind((RopeController)(object)rig.Brace, rig, LateenControlRole.Brace); Bind(rig.Halyard, rig, LateenControlRole.Halyard); Bind(rig.SheetLeft, rig, LateenControlRole.Sheet); Bind(rig.SheetMid, rig, LateenControlRole.Sheet); Bind(rig.SheetRight, rig, LateenControlRole.Sheet); LateenYardPersistence.ApplyLoadedSide(rig); } } private static void Bind(RopeController controller, LateenYardRig rig, LateenControlRole role) { if (!((Object)(object)controller == (Object)null)) { Controls[controller] = new LateenControlBinding { Rig = rig, Role = role }; } } internal static bool TryGet(RopeController controller, out LateenControlBinding binding) { if ((Object)(object)controller != (Object)null && Controls.TryGetValue(controller, out binding) && (Object)(object)binding.Rig != (Object)null) { return true; } binding = null; return false; } internal static bool TryGetRig(Sail sail, out LateenYardRig rig) { if ((Object)(object)sail != (Object)null && SailRigs.TryGetValue(sail, out rig) && (Object)(object)rig != (Object)null) { return true; } rig = null; return false; } internal static void Unregister(LateenYardRig rig) { if (!((Object)(object)rig == (Object)null)) { UnregisterBindings(rig); Rigs.Remove(rig); Sail component = ((Component)rig).GetComponent(); if ((Object)(object)component != (Object)null) { SailRigs.Remove(component); } } } private static void UnregisterBindings(LateenYardRig rig) { List list = new List(); foreach (KeyValuePair control in Controls) { if ((Object)(object)control.Value.Rig == (Object)(object)rig) { list.Add(control.Key); } } for (int i = 0; i < list.Count; i++) { Controls.Remove(list[i]); } } internal static void DisposeAllRigs() { List list = new List(Rigs); for (int i = 0; i < list.Count; i++) { LateenYardRig lateenYardRig = list[i]; if ((Object)(object)lateenYardRig != (Object)null) { lateenYardRig.Dispose(); } } Controls.Clear(); SailRigs.Clear(); Rigs.Clear(); } } internal enum LateenControlRole { Brace, Halyard, Sheet } [HarmonyPatch(typeof(GPButtonRopeWinch), "Update")] internal static class LateenWinchLockPatch { private static bool Prefix(GPButtonRopeWinch __instance, ref float ___currentInput) { if (!LateenControlRegistry.TryGet(__instance.rope, out var binding)) { return true; } if ((binding.Role == LateenControlRole.Brace) ? binding.Rig.CanOperateBrace : binding.Rig.CanOperateHalyardOrSheet) { return true; } ___currentInput = 0f; return false; } } [HarmonyPatch(typeof(SaveLoadManager), "LoadGame")] internal static class LateenYardBeginLoadPatch { private static void Prefix() { LateenYardPersistence.BeginLoad(); } } [HarmonyPatch(typeof(SaveLoadManager), "LoadModData")] internal static class LateenYardLoadPatch { private static void Postfix() { LateenYardPersistence.Load(); } } internal static class LateenYardPersistence { private const string ModDataKey = "com.pro1p.sailwind.junkandgaffadjustment.lateen-yard-sides.v1"; private static readonly Dictionary LoadedSides = new Dictionary(); private static bool loaded; internal static void BeginLoad() { LoadedSides.Clear(); loaded = false; } internal static void Save() { if (GameState.modData == null) { GameState.modData = new Dictionary(); } List list = new List(); foreach (LateenYardRig activeRig in LateenControlRegistry.ActiveRigs) { if (!((Object)(object)activeRig == (Object)null)) { string persistenceKey = activeRig.GetPersistenceKey(); if (!string.IsNullOrEmpty(persistenceKey)) { list.Add(persistenceKey + "," + activeRig.YardSideSign); } } } GameState.modData["com.pro1p.sailwind.junkandgaffadjustment.lateen-yard-sides.v1"] = string.Join("|", list.ToArray()); } internal static void Load() { LoadedSides.Clear(); loaded = true; if (GameState.modData == null || !GameState.modData.TryGetValue("com.pro1p.sailwind.junkandgaffadjustment.lateen-yard-sides.v1", out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { int num = array[i].LastIndexOf(','); if (num > 0 && num < array[i].Length - 1) { string key = array[i].Substring(0, num); if (int.TryParse(array[i].Substring(num + 1), out var result)) { LoadedSides[key] = ((result >= 0) ? 1 : (-1)); } } } foreach (LateenYardRig activeRig in LateenControlRegistry.ActiveRigs) { ApplyLoadedSide(activeRig); } } internal static void ApplyLoadedSide(LateenYardRig rig) { if (loaded && !((Object)(object)rig == (Object)null)) { string persistenceKey = rig.GetPersistenceKey(); if (!string.IsNullOrEmpty(persistenceKey) && LoadedSides.TryGetValue(persistenceKey, out var value)) { rig.RestoreYardSide(value); } } } internal static void Reset() { LoadedSides.Clear(); loaded = false; } } internal sealed class LateenYardRig : MonoBehaviour { private readonly struct ClearancePoseSnapshot { internal readonly Quaternion YardRotation; internal readonly Vector2 CurrentRingPosition; internal readonly Vector2 MirroredRingPosition; internal readonly Matrix4x4 RigToMastLocal; internal readonly Vector3 MastLossyScale; internal readonly Vector3 ColliderCenter; internal readonly float ColliderRadius; internal readonly float ColliderHeight; internal readonly int ColliderDirection; internal ClearancePoseSnapshot(Quaternion yardRotation, Vector2 currentRingPosition, Vector2 mirroredRingPosition, Matrix4x4 rigToMastLocal, Vector3 mastLossyScale, Vector3 colliderCenter, float colliderRadius, float colliderHeight, int colliderDirection) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_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_0016: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) YardRotation = yardRotation; CurrentRingPosition = currentRingPosition; MirroredRingPosition = mirroredRingPosition; RigToMastLocal = rigToMastLocal; MastLossyScale = mastLossyScale; ColliderCenter = colliderCenter; ColliderRadius = colliderRadius; ColliderHeight = colliderHeight; ColliderDirection = colliderDirection; } internal bool ApproximatelyEquals(ClearancePoseSnapshot other) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0078: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) float num = 9.9999994E-11f; if (Mathf.Abs(Quaternion.Dot(YardRotation, other.YardRotation)) >= 0.999999f) { Vector2 val = CurrentRingPosition - other.CurrentRingPosition; if (((Vector2)(ref val)).sqrMagnitude <= num) { val = MirroredRingPosition - other.MirroredRingPosition; if (((Vector2)(ref val)).sqrMagnitude <= num && MatrixApproximatelyEquals(RigToMastLocal, other.RigToMastLocal)) { Vector3 val2 = MastLossyScale - other.MastLossyScale; if (((Vector3)(ref val2)).sqrMagnitude <= num) { val2 = ColliderCenter - other.ColliderCenter; if (((Vector3)(ref val2)).sqrMagnitude <= num && Mathf.Abs(ColliderRadius - other.ColliderRadius) <= 1E-05f && Mathf.Abs(ColliderHeight - other.ColliderHeight) <= 1E-05f) { return ColliderDirection == other.ColliderDirection; } } } } } return false; } } private readonly struct CapsuleWorldGeometry { internal readonly Vector3 Center; internal readonly Vector3 Axis; internal readonly float HalfSegment; internal readonly float Radius; internal CapsuleWorldGeometry(Vector3 center, Vector3 axis, float halfSegment, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Center = center; Axis = axis; HalfSegment = halfSegment; Radius = radius; } internal Vector3 GetCenterlinePoint(Vector3 point) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0031: 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) float num = Mathf.Clamp(Vector3.Dot(point - Center, Axis), 0f - HalfSegment, HalfSegment); return Center + Axis * num; } } internal const float ControlEpsilon = 0.0001f; internal const float FullyRaisedThreshold = 0.9999f; internal const float BadTackPowerMultiplier = 0.9f; private const float TackRearmPullThreshold = 0.7f; private const float WindMagnitudeEpsilon = 0.001f; private const float WindSideDeadZone = 0.05f; private Sail sail; private Mast mast; private SailConnections connections; private HingeJoint hinge; private int originalYardSide; private int yardSide; private float previousBracePull; private int transferStartSide; private int transferTargetSide; private bool sideTransferActive; private bool tackChangeArmed; private bool initialized; private bool cleanedUp; private const float LooseRopeTension = 0.86f; private const float TightRopeTension = 0.98f; private const float LooseRopeCurveLength = 0.45f; private const float TightRopeCurveLength = 0.18f; private Transform yardHeelAttachment; private Transform braceRopeHolder; private Transform braceRopeHolderRoute; private RopeEffect braceWinchRope; private readonly List braceRopes = new List(2); private const float MastPassMovementStart = 0.85f; private const float NonVanillaClearanceCompensation = 0.15f; private const float GeometryChangeEpsilon = 1E-06f; private const float PlaneIntersectionEpsilon = 0.0001f; private const float SurfaceClearanceTolerance = 0.0005f; private const float SurfaceSearchStep = 0.025f; private const float SurfaceSearchLimit = 5f; private const float SurfaceSearchMinimumRadius = 0.001f; private const float AxisMagnitudeEpsilon = 0.001f; private const float MinimumTipTolerance = 0.001f; private const float ClearanceCachePoseEpsilon = 1E-05f; private const float ClearanceCacheRotationDotEpsilon = 1E-06f; private const int SurfaceSearchIterations = 12; private const int PrincipalAxisIterations = 12; private const float TipSampleFraction = 0.01f; private MeshFilter yardMesh; private Transform visualRoot; private Transform proceduralPivot; private Transform originalVisualParent; private int originalVisualSiblingIndex; private bool hasOriginalVisualParent; private Quaternion verticalRotation; private Vector3 starboardAxisLocal; private Vector3 aftAxisLocal; private Vector3 mastCenterLocal; private Vector3 heelPositionLocal; private Vector3 mastPivotLocal; private Vector3 yardContactCenterLocal; private Vector3[] yardContactSurfacePointsLocal = Array.Empty(); private CapsuleCollider mastContactCollider; private float vanillaRingSide; private float vanillaRingAft; private float vanillaSurfaceClearance; private bool hasSurfaceMeasurement; private Mesh measuredYardMesh; private Vector3 measuredYardScale; private float measuredInstallHeight; private bool hasCachedClearancePosition; private ClearancePoseSnapshot cachedClearancePose; private Vector2 cachedClearancePosition; internal LateenBraceController Brace { get; private set; } internal RopeController Halyard { get { if (!((Object)(object)connections != (Object)null)) { return null; } return connections.reefController; } } internal RopeController SheetLeft { get { if (!((Object)(object)connections != (Object)null)) { return null; } return connections.angleControllerLeft; } } internal RopeController SheetMid { get { if (!((Object)(object)connections != (Object)null)) { return null; } return connections.angleControllerMid; } } internal RopeController SheetRight { get { if (!((Object)(object)connections != (Object)null)) { return null; } return connections.angleControllerRight; } } internal RopeController Sheet { get { if ((Object)(object)SheetMid != (Object)null) { return SheetMid; } if (!((Object)(object)SheetLeft != (Object)null)) { return SheetRight; } return SheetLeft; } } internal float BracePull { get { if (!((Object)(object)Brace != (Object)null)) { return 0f; } return 1f - Mathf.Clamp01(((RopeController)Brace).currentLength); } } internal int YardSideSign => yardSide; internal bool CanOperateBrace { get { if ((Object)(object)sail != (Object)null && sail.currentUnroll <= 0.0001f) { return AreAllSheetsFullyLoose(); } return false; } } internal bool CanOperateHalyardOrSheet => BracePull >= 0.9999f; private bool AreAllSheetsFullyLoose() { if ((Object)(object)Sheet != (Object)null && IsSheetFullyLoose(SheetLeft) && IsSheetFullyLoose(SheetMid)) { return IsSheetFullyLoose(SheetRight); } return false; } private static bool IsSheetFullyLoose(RopeController controller) { if (!((Object)(object)controller == (Object)null)) { return controller.currentLength >= 0.9999f; } return true; } internal bool Initialize(Mast owningMast, Sail attachedSail, SailConnections attachedConnections) { if (cleanedUp) { return false; } mast = owningMast; sail = attachedSail; connections = attachedConnections; hinge = ((Component)this).GetComponent(); if (initialized) { RefreshGeometryIfNeeded(); RefreshRopeHolderPosition(); LateenControlRegistry.Register(this); return true; } if ((Object)(object)mast == (Object)null || (Object)(object)sail == (Object)null || (Object)(object)connections == (Object)null || (Object)(object)hinge == (Object)null) { return false; } visualRoot = FindVisualRoot(); yardMesh = FindYardMesh(visualRoot); if ((Object)(object)visualRoot == (Object)null || (Object)(object)yardMesh == (Object)null || (Object)(object)yardMesh.sharedMesh == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not locate the lateen visual root or yard on " + ((Object)this).name + ".")); } return false; } ConfigureAxes(); ConfigureYardGeometry(initializeSide: true); CreateProceduralPivot(); CreateBraceRoute(); if ((Object)(object)Brace == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Could not create a brace controller for " + ((Object)this).name + ".")); } return false; } CaptureGeometrySignature(); initialized = true; previousBracePull = Mathf.Clamp01(BracePull); tackChangeArmed = previousBracePull >= 0.7f; LateenControlRegistry.Register(this); return true; } private void LateUpdate() { if (initialized && !((Object)(object)Brace == (Object)null) && !((Object)(object)proceduralPivot == (Object)null)) { RefreshGeometryIfNeeded(); float bracePull = Mathf.Clamp01(BracePull); UpdateAutomaticSideTransfer(bracePull); ApplyProceduralPose(bracePull); UpdateRopeTension(bracePull); } } private void UpdateAutomaticSideTransfer(float bracePull) { bool flag = bracePull < previousBracePull - 0.0001f; bool flag2 = bracePull > previousBracePull + 0.0001f; if (!sideTransferActive && bracePull >= 0.7f) { tackChangeArmed = true; } if (tackChangeArmed && flag) { transferStartSide = yardSide; transferTargetSide = -yardSide; sideTransferActive = true; tackChangeArmed = false; } if (sideTransferActive && bracePull <= 0.0001f) { yardSide = transferTargetSide; sideTransferActive = false; } else if (sideTransferActive && flag2 && bracePull >= 0.7f) { sideTransferActive = false; tackChangeArmed = true; } previousBracePull = bracePull; } internal bool IsBadTack() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = (((Object)(object)sail != (Object)null) ? sail.shipRigidbody : null); if ((Object)(object)val == (Object)null || ((Vector3)(ref sail.apparentWind)).sqrMagnitude < 0.001f) { return false; } float num = Vector3.Dot(-((Vector3)(ref sail.apparentWind)).normalized, ((Component)val).transform.right); if (Mathf.Abs(num) >= 0.05f) { return ((num > 0f) ? 1 : (-1)) == yardSide; } return false; } internal string GetPersistenceKey() { SaveableObject componentInParent = ((Component)this).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || (Object)(object)mast == (Object)null || (Object)(object)sail == (Object)null) { return null; } return $"{componentInParent.sceneIndex}:{mast.orderIndex}:{sail.mastOrder}:{sail.prefabIndex}"; } internal void RestoreYardSide(int savedSide) { yardSide = ((savedSide >= 0) ? 1 : (-1)); sideTransferActive = false; previousBracePull = Mathf.Clamp01(BracePull); tackChangeArmed = previousBracePull >= 0.7f; if (initialized && (Object)(object)proceduralPivot != (Object)null) { ApplyProceduralPose(previousBracePull); } } internal void Dispose() { CleanupGeneratedObjects(); Object.Destroy((Object)(object)this); } private void OnDestroy() { CleanupGeneratedObjects(); } private void CleanupGeneratedObjects() { if (!cleanedUp) { cleanedUp = true; initialized = false; LateenControlRegistry.Unregister(this); RestoreVisualHierarchy(); DestroyRopeObjects(); DestroyYardHeelAttachment(); DestroyProceduralPivot(); } } internal void SetBraceRopeHolder(Transform ropeHolder) { braceRopeHolder = ropeHolder; if ((Object)(object)braceWinchRope != (Object)null) { braceWinchRope.attachment = GetBraceRopeHolder(); } RefreshRopeHolderPosition(); } private void UpdateYardHeelAttachment() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //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) if ((Object)(object)yardHeelAttachment == (Object)null) { GameObject val = new GameObject("lateen yard heel attachment"); yardHeelAttachment = val.transform; } if ((Object)(object)yardHeelAttachment.parent != (Object)(object)((Component)yardMesh).transform) { yardHeelAttachment.SetParent(((Component)yardMesh).transform, false); } yardHeelAttachment.position = ((Component)this).transform.TransformPoint(heelPositionLocal); } private void CreateBraceRoute() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown Transform midRopeAttachment = connections.midRopeAttachment; RopeEffect source = (((Object)(object)Sheet != (Object)null) ? ((Component)Sheet).GetComponent() : null); RopeEffect source2 = (((Object)(object)midRopeAttachment != (Object)null) ? ((Component)midRopeAttachment).GetComponent() : null); GameObject val = new GameObject("lateen brace rope holder route"); val.SetActive(false); braceRopeHolderRoute = val.transform; RefreshRopeHolderPosition(); RopeEffect val2 = CreateRopeEffect(val, source2, yardHeelAttachment, sheetFallback: true); if ((Object)(object)val2 != (Object)null) { braceRopes.Add(val2); } val.SetActive(true); GameObject val3 = new GameObject(((Object)this).name + " brace controller"); val3.SetActive(false); RopeEffect val4 = CreateRopeEffect(val3, source, GetBraceRopeHolder(), sheetFallback: false); if ((Object)(object)val4 != (Object)null) { braceRopes.Add(val4); braceWinchRope = val4; } Brace = val3.AddComponent(); Brace.Initialize(this); val3.SetActive(true); } private RopeEffect CreateRopeEffect(GameObject owner, RopeEffect source, Transform attachment, bool sheetFallback) { if ((Object)(object)source == (Object)null || (Object)(object)attachment == (Object)null) { return null; } LineRenderer component = ((Component)source).GetComponent(); if ((Object)(object)component == (Object)null) { return null; } LineRenderer destination = owner.AddComponent(); CopyLineRenderer(component, destination); RopeEffect obj = owner.AddComponent(); obj.attachment = attachment; obj.sheet = source.sheet || sheetFallback; obj.jibSheet = false; obj.currentRopeLength = 0.45f; obj.ropeWidth = source.ropeWidth; obj.totalRopeLength = 0.86f; obj.curveRate = source.curveRate; obj.attachedSailArea = sail.sailArea; obj.allRopeSections = new List(); return obj; } private static void CopyLineRenderer(LineRenderer source, LineRenderer destination) { //IL_0032: 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) ((Renderer)destination).sharedMaterial = ((Renderer)source).sharedMaterial; destination.useWorldSpace = source.useWorldSpace; destination.startWidth = source.startWidth; destination.endWidth = source.endWidth; destination.startColor = source.startColor; destination.endColor = source.endColor; destination.positionCount = source.positionCount; ((Renderer)destination).enabled = ((Renderer)source).enabled; } private Transform GetBraceRopeHolder() { return braceRopeHolder; } private void RefreshRopeHolderPosition() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)braceRopeHolderRoute == (Object)null) { return; } Transform val = GetBraceRopeHolder(); Transform val2 = (((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)null) ? val.parent : ((Component)mast).transform); braceRopeHolderRoute.SetParent(val2, true); if (!((Object)(object)val == (Object)null)) { braceRopeHolderRoute.position = val.position; braceRopeHolderRoute.rotation = val.rotation; if ((Object)(object)braceWinchRope != (Object)null) { braceWinchRope.attachment = val; } } } private void UpdateRopeTension(float bracePull) { float currentRopeLength = Mathf.Lerp(0.45f, 0.18f, bracePull); float totalRopeLength = Mathf.Lerp(0.86f, 0.98f, bracePull); for (int i = 0; i < braceRopes.Count; i++) { RopeEffect val = braceRopes[i]; if (!((Object)(object)val == (Object)null)) { val.currentRopeLength = currentRopeLength; val.totalRopeLength = totalRopeLength; } } } private void DestroyRopeObjects() { if ((Object)(object)Brace != (Object)null) { Object.Destroy((Object)(object)((Component)Brace).gameObject); Brace = null; } if ((Object)(object)braceRopeHolderRoute != (Object)null) { Object.Destroy((Object)(object)((Component)braceRopeHolderRoute).gameObject); braceRopeHolderRoute = null; } braceWinchRope = null; braceRopeHolder = null; braceRopes.Clear(); } private void DestroyYardHeelAttachment() { if ((Object)(object)yardHeelAttachment != (Object)null) { Object.Destroy((Object)(object)((Component)yardHeelAttachment).gameObject); yardHeelAttachment = null; } } private void ConfigureAxes() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = (((Object)(object)sail.shipRigidbody != (Object)null) ? sail.shipRigidbody : mast.shipRigidbody); if ((Object)(object)val != (Object)null) { Vector3 val2 = ((Component)this).transform.InverseTransformDirection(((Component)val).transform.right); starboardAxisLocal = ((Vector3)(ref val2)).normalized; val2 = ((Component)this).transform.InverseTransformDirection(-((Component)val).transform.forward); aftAxisLocal = ((Vector3)(ref val2)).normalized; } else { starboardAxisLocal = Vector3.up; aftAxisLocal = -Vector3.right; } } private void ConfigureYardGeometry(bool initializeSide) { //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_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_0037: 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_003c: 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_0044: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00aa: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) InvalidateClearanceCache(); Vector3 val = ((Joint)hinge).axis; Vector3 val2; if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { val2 = Vector3.forward; } else { val = ((Joint)hinge).axis; val2 = ((Vector3)(ref val)).normalized; } Vector3 val3 = val2; val = ((Component)this).transform.TransformDirection(val3); Vector3 normalized = ((Vector3)(ref val)).normalized; mastPivotLocal = ((Joint)hinge).anchor; AnalyzeYardGeometry(val3, out var yardAxisLocal); if (Vector3.Dot(yardAxisLocal, val3) < 0f) { yardAxisLocal = -yardAxisLocal; } verticalRotation = Quaternion.FromToRotation(yardAxisLocal, val3); Vector3 contactWorld = ((Component)this).transform.TransformPoint(yardContactCenterLocal); Vector3 val4 = FindMastCenterlinePoint(contactWorld, normalized, out mastContactCollider); mastCenterLocal = ((Component)this).transform.InverseTransformPoint(val4); Vector3 val5 = yardContactCenterLocal - mastCenterLocal; vanillaRingSide = Vector3.Dot(val5, starboardAxisLocal); vanillaRingAft = Vector3.Dot(val5, aftAxisLocal); hasSurfaceMeasurement = (Object)(object)mastContactCollider != (Object)null && yardContactSurfacePointsLocal.Length != 0; vanillaSurfaceClearance = (hasSurfaceMeasurement ? MeasureSurfaceClearance(Quaternion.identity, Vector2.zero) : 0f); originalYardSide = ((vanillaRingSide >= 0f) ? 1 : (-1)); if (initializeSide) { yardSide = originalYardSide; } UpdateYardHeelAttachment(); } private void RefreshGeometryIfNeeded() { //IL_0040: 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) if (YardGeometryChanged()) { MeshFilter val = FindYardMesh(visualRoot); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null)) { yardMesh = val; proceduralPivot.localPosition = ((Joint)hinge).anchor; proceduralPivot.localRotation = Quaternion.identity; ConfigureAxes(); ConfigureYardGeometry(initializeSide: false); CaptureGeometrySignature(); ApplyProceduralPose(Mathf.Clamp01(BracePull)); } } } private bool YardGeometryChanged() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)yardMesh == (Object)null) && !((Object)(object)yardMesh.sharedMesh == (Object)null) && !((Object)(object)yardMesh.sharedMesh != (Object)(object)measuredYardMesh)) { Vector3 val = ((Component)yardMesh).transform.lossyScale - measuredYardScale; if (!(((Vector3)(ref val)).sqrMagnitude > 1E-06f)) { return !Mathf.Approximately(sail.installHeight, measuredInstallHeight); } } return true; } private void CaptureGeometrySignature() { //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) measuredYardMesh = yardMesh.sharedMesh; measuredYardScale = ((Component)yardMesh).transform.lossyScale; measuredInstallHeight = sail.installHeight; } private void CreateProceduralPivot() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) originalVisualParent = visualRoot.parent; originalVisualSiblingIndex = visualRoot.GetSiblingIndex(); hasOriginalVisualParent = true; GameObject val = new GameObject("lateen brace pivot"); proceduralPivot = val.transform; proceduralPivot.SetParent(((Component)this).transform, false); proceduralPivot.localPosition = mastPivotLocal; proceduralPivot.localRotation = Quaternion.identity; proceduralPivot.localScale = Vector3.one; visualRoot.SetParent(proceduralPivot, true); } private void RestoreVisualHierarchy() { //IL_002c: 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_004c: Unknown result type (might be due to invalid IL or missing references) if (hasOriginalVisualParent && !((Object)(object)visualRoot == (Object)null)) { if ((Object)(object)proceduralPivot != (Object)null) { proceduralPivot.localPosition = mastPivotLocal; proceduralPivot.localRotation = Quaternion.identity; proceduralPivot.localScale = Vector3.one; } Transform val = (((Object)(object)originalVisualParent != (Object)null) ? originalVisualParent : ((Component)this).transform); if ((Object)(object)val != (Object)null) { visualRoot.SetParent(val, true); visualRoot.SetSiblingIndex(Mathf.Clamp(originalVisualSiblingIndex, 0, val.childCount - 1)); } else if ((Object)(object)visualRoot.parent == (Object)(object)proceduralPivot) { visualRoot.SetParent((Transform)null, true); } hasOriginalVisualParent = false; } } private void DestroyProceduralPivot() { if ((Object)(object)proceduralPivot != (Object)null) { Object.Destroy((Object)(object)((Component)proceduralPivot).gameObject); proceduralPivot = null; } } private void ApplyProceduralPose(float bracePull) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_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_0039: 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_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: 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_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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) float num = 1f - bracePull; float num2 = Mathf.SmoothStep(0f, 1f, num); Quaternion val = Quaternion.Slerp(Quaternion.identity, verticalRotation, num2); proceduralPivot.localRotation = val; Vector2 currentRingPosition = GetCurrentRingPosition(val); float num3 = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.85f, 1f, num)); int side = (sideTransferActive ? transferStartSide : yardSide); int side2 = (sideTransferActive ? transferTargetSide : yardSide); Vector2 ringPosition3 = default(Vector2); if (sideTransferActive) { Vector2 ringPosition = GetRingPosition(side, val, currentRingPosition); Vector2 ringPosition2 = GetRingPosition(side2, val, currentRingPosition); float num4 = 0.5f - 0.5f * Mathf.Cos(num3 * MathF.PI); float num5 = Mathf.Atan2(ringPosition.x, ringPosition.y) * 57.29578f; float num6 = Mathf.Atan2(ringPosition2.x, ringPosition2.y) * 57.29578f; float num7 = Mathf.LerpAngle(num5, num6, num4) * (MathF.PI / 180f); float num8 = Mathf.Lerp(((Vector2)(ref ringPosition)).magnitude, ((Vector2)(ref ringPosition2)).magnitude, num4); ((Vector2)(ref ringPosition3))..ctor(Mathf.Sin(num7) * num8, Mathf.Cos(num7) * num8); } else { ringPosition3 = GetRingPosition(side2, val, currentRingPosition); } float num9 = ringPosition3.x - currentRingPosition.x; float num10 = ringPosition3.y - currentRingPosition.y; proceduralPivot.localPosition = mastPivotLocal + starboardAxisLocal * num9 + aftAxisLocal * num10; } private Vector2 GetCurrentRingPosition(Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 nativeRingOffset = GetNativeRingOffset(rotation); return new Vector2(Vector3.Dot(nativeRingOffset, starboardAxisLocal), Vector3.Dot(nativeRingOffset, aftAxisLocal)); } private Vector2 GetRingPosition(int side, Quaternion rotation, Vector2 currentRingPosition) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (side == originalYardSide) { return currentRingPosition; } Vector2 mirroredPosition = default(Vector2); ((Vector2)(ref mirroredPosition))..ctor((float)side * Mathf.Abs(vanillaRingSide), vanillaRingAft); return MatchVanillaSurfaceClearance(rotation, currentRingPosition, mirroredPosition); } private Vector2 MatchVanillaSurfaceClearance(Quaternion rotation, Vector2 currentRingPosition, Vector2 mirroredPosition) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (!hasSurfaceMeasurement) { return AddClearanceCompensation(mirroredPosition); } ClearancePoseSnapshot other = CaptureClearancePose(rotation, currentRingPosition, mirroredPosition); if (hasCachedClearancePosition && cachedClearancePose.ApproximatelyEquals(other)) { return cachedClearancePosition; } Vector2 result = CalculateSurfaceClearancePosition(rotation, currentRingPosition, mirroredPosition); cachedClearancePose = other; cachedClearancePosition = result; hasCachedClearancePosition = true; return result; } private ClearancePoseSnapshot CaptureClearancePose(Quaternion rotation, Vector2 currentRingPosition, Vector2 mirroredPosition) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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) Transform transform = ((Component)mastContactCollider).transform; return new ClearancePoseSnapshot(rotation, currentRingPosition, mirroredPosition, transform.worldToLocalMatrix * ((Component)this).transform.localToWorldMatrix, transform.lossyScale, mastContactCollider.center, mastContactCollider.radius, mastContactCollider.height, mastContactCollider.direction); } private static bool MatrixApproximatelyEquals(Matrix4x4 first, Matrix4x4 second) { for (int i = 0; i < 16; i++) { if (Mathf.Abs(((Matrix4x4)(ref first))[i] - ((Matrix4x4)(ref second))[i]) > 1E-05f) { return false; } } return true; } private void InvalidateClearanceCache() { hasCachedClearancePosition = false; } private Vector2 CalculateSurfaceClearancePosition(Quaternion rotation, Vector2 currentRingPosition, Vector2 mirroredPosition) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_003d: 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_0052: 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_010b: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_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_006d: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) Vector2 val = (Vector2)((((Vector2)(ref mirroredPosition)).sqrMagnitude > 0.0001f) ? ((Vector2)(ref mirroredPosition)).normalized : new Vector2((mirroredPosition.x >= 0f) ? 1f : (-1f), 0f)); float surfaceClearanceError = GetSurfaceClearanceError(rotation, currentRingPosition, mirroredPosition); if (Mathf.Abs(surfaceClearanceError) <= 0.0005f) { return mirroredPosition; } float num; float num2; if (surfaceClearanceError < 0f) { num = 0f; num2 = 0.025f; while (num2 < 5f && !(GetSurfaceClearanceError(rotation, currentRingPosition, mirroredPosition + val * num2) >= 0f)) { num2 = Mathf.Min(num2 * 2f, 5f); } } else { num2 = 0f; float num3 = Mathf.Max(0f, ((Vector2)(ref mirroredPosition)).magnitude - 0.001f); num = 0f - Mathf.Min(0.025f, num3); while (0f - num < num3 && GetSurfaceClearanceError(rotation, currentRingPosition, mirroredPosition + val * num) > 0f) { num = 0f - Mathf.Min((0f - num) * 2f, num3); } if (GetSurfaceClearanceError(rotation, currentRingPosition, mirroredPosition + val * num) > 0f) { return mirroredPosition; } } for (int i = 0; i < 12; i++) { float num4 = (num + num2) * 0.5f; Vector2 desiredRingPosition = mirroredPosition + val * num4; if (GetSurfaceClearanceError(rotation, currentRingPosition, desiredRingPosition) < 0f) { num = num4; } else { num2 = num4; } } return mirroredPosition + val * ((num + num2) * 0.5f); } private float GetSurfaceClearanceError(Quaternion rotation, Vector2 currentRingPosition, Vector2 desiredRingPosition) { //IL_000e: 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_0011: Unknown result type (might be due to invalid IL or missing references) float num = vanillaSurfaceClearance + 0.15f; return MeasureSurfaceClearance(rotation, desiredRingPosition - currentRingPosition) - num; } private static Vector2 AddClearanceCompensation(Vector2 mirroredPosition) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_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_000e: Unknown result type (might be due to invalid IL or missing references) if (((Vector2)(ref mirroredPosition)).sqrMagnitude <= 0.0001f) { return mirroredPosition; } return mirroredPosition + ((Vector2)(ref mirroredPosition)).normalized * 0.15f; } private float MeasureSurfaceClearance(Quaternion rotation, Vector2 ringTranslation) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00aa: 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) if ((Object)(object)mastContactCollider == (Object)null || yardContactSurfacePointsLocal.Length == 0) { return 0f; } Vector3 val = starboardAxisLocal * ringTranslation.x + aftAxisLocal * ringTranslation.y; CapsuleWorldGeometry capsuleWorldGeometry = CreateCapsuleWorldGeometry(mastContactCollider); float num = float.PositiveInfinity; for (int i = 0; i < yardContactSurfacePointsLocal.Length; i++) { Vector3 val2 = mastPivotLocal + rotation * (yardContactSurfacePointsLocal[i] - mastPivotLocal) + val; Vector3 val3 = ((Component)this).transform.TransformPoint(val2); Vector3 centerlinePoint = capsuleWorldGeometry.GetCenterlinePoint(val3); float num2 = num; Vector3 val4 = val3 - centerlinePoint; num = Mathf.Min(num2, ((Vector3)(ref val4)).magnitude - capsuleWorldGeometry.Radius); } return num; } private Vector3 GetNativeRingOffset(Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) return mastPivotLocal + rotation * (yardContactCenterLocal - mastPivotLocal) - mastCenterLocal; } private Transform FindVisualRoot() { if ((Object)(object)sail == (Object)null || (Object)(object)sail.cloth == (Object)null) { return null; } Transform parent = ((Component)sail.cloth).transform.parent; if (!((Object)(object)parent != (Object)null) || !((Object)(object)parent != (Object)(object)((Component)this).transform)) { return null; } return parent; } private static MeshFilter FindYardMesh(Transform root) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00ac: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { return null; } MeshFilter result = null; float num = 0f; MeshFilter[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (MeshFilter val in componentsInChildren) { if ((Object)(object)val.sharedMesh == (Object)null) { continue; } string text = ((Object)val).name.ToLowerInvariant(); if (text.Contains("boom") || text.Contains("cylinder")) { Bounds bounds = val.sharedMesh.bounds; Vector3 val2 = Vector3.Scale(((Bounds)(ref bounds)).size, Abs(((Component)val).transform.lossyScale)); float num2 = Mathf.Max(new float[3] { val2.x, val2.y, val2.z }); if (num2 > num) { result = val; num = num2; } } } return result; } private Vector3 FindMastCenterlinePoint(Vector3 contactWorld, Vector3 mastAxisWorld, out CapsuleCollider selectedCollider) { //IL_0029: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_0096: 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_00a8: Unknown result type (might be due to invalid IL or missing references) selectedCollider = null; Vector3 val = (((Object)(object)mast != (Object)null) ? ((Component)mast).transform.position : ((Component)this).transform.position); Vector3 result = val + mastAxisWorld * Vector3.Dot(contactWorld - val, mastAxisWorld); if (mast.mastCols == null) { return result; } float num = float.PositiveInfinity; for (int i = 0; i < mast.mastCols.Length; i++) { CapsuleCollider val2 = mast.mastCols[i]; if (!((Object)(object)val2 == (Object)null)) { Vector3 centerlinePoint = CreateCapsuleWorldGeometry(val2).GetCenterlinePoint(contactWorld); Vector3 val3 = centerlinePoint - contactWorld; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { result = centerlinePoint; selectedCollider = val2; num = sqrMagnitude; } } } return result; } private static CapsuleWorldGeometry CreateCapsuleWorldGeometry(CapsuleCollider collider) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_0107: 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_00de: 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_00c7: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (Vector3)(collider.direction switch { 2 => Vector3.forward, 0 => Vector3.right, _ => Vector3.up, }); Vector3 val2 = ((Component)collider).transform.TransformDirection(val); Vector3 normalized = ((Vector3)(ref val2)).normalized; Vector3 center = ((Component)collider).transform.TransformPoint(collider.center); Vector3 val3 = Abs(((Component)collider).transform.lossyScale); float num = collider.direction switch { 2 => val3.z, 0 => val3.x, _ => val3.y, }; float halfSegment = Mathf.Max(0f, collider.height * 0.5f - collider.radius) * num; float num2 = collider.direction switch { 2 => Mathf.Max(val3.x, val3.y), 0 => Mathf.Max(val3.y, val3.z), _ => Mathf.Max(val3.x, val3.z), }; return new CapsuleWorldGeometry(center, normalized, halfSegment, collider.radius * num2); } private void AnalyzeYardGeometry(Vector3 mastAxisLocal, out Vector3 yardAxisLocal) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) Mesh sharedMesh = yardMesh.sharedMesh; Vector3[] vertices = sharedMesh.vertices; Vector3[] array = (Vector3[])(object)new Vector3[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { Vector3 val = ((Component)yardMesh).transform.TransformPoint(vertices[i]); array[i] = ((Component)this).transform.InverseTransformPoint(val); } Vector3 val2 = Average(array); yardAxisLocal = FindPrincipalAxis(array, val2); float num = float.PositiveInfinity; float num2 = float.NegativeInfinity; for (int j = 0; j < array.Length; j++) { float num3 = Vector3.Dot(array[j] - val2, yardAxisLocal); num = Mathf.Min(num, num3); num2 = Mathf.Max(num2, num3); } Vector3 val3 = AverageTip(array, val2, yardAxisLocal, num, num2, first: true); Vector3 val4 = AverageTip(array, val2, yardAxisLocal, num, num2, first: false); heelPositionLocal = ((Vector3.Dot(val3, mastAxisLocal) <= Vector3.Dot(val4, mastAxisLocal)) ? val3 : val4); float num4 = Vector3.Dot(mastPivotLocal - val2, yardAxisLocal); yardContactCenterLocal = val2 + yardAxisLocal * Mathf.Clamp(num4, num, num2); yardContactSurfacePointsLocal = SliceMeshAtPlane(array, sharedMesh.triangles, yardContactCenterLocal, yardAxisLocal); } private static Vector3[] SliceMeshAtPlane(Vector3[] points, int[] triangles, Vector3 planePoint, Vector3 planeNormal) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00c8: Unknown result type (might be due to invalid IL or missing references) List list = new List(); for (int i = 0; i + 2 < triangles.Length; i += 3) { Vector3 val = points[triangles[i]]; Vector3 val2 = points[triangles[i + 1]]; Vector3 val3 = points[triangles[i + 2]]; AddPlaneEdgeIntersections(list, val, val2, planePoint, planeNormal); AddPlaneEdgeIntersections(list, val2, val3, planePoint, planeNormal); AddPlaneEdgeIntersections(list, val3, val, planePoint, planeNormal); } if (list.Count == 0) { float num = float.PositiveInfinity; for (int j = 0; j < points.Length; j++) { num = Mathf.Min(num, Mathf.Abs(Vector3.Dot(points[j] - planePoint, planeNormal))); } for (int k = 0; k < points.Length; k++) { if (Mathf.Abs(Vector3.Dot(points[k] - planePoint, planeNormal)) <= num + 0.0001f) { AddUniquePoint(list, points[k]); } } } return list.ToArray(); } private static void AddPlaneEdgeIntersections(List intersections, Vector3 first, Vector3 second, Vector3 planePoint, Vector3 planeNormal) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) float num = Vector3.Dot(first - planePoint, planeNormal); float num2 = Vector3.Dot(second - planePoint, planeNormal); if (Mathf.Abs(num) <= 0.0001f) { AddUniquePoint(intersections, first); } if (Mathf.Abs(num2) <= 0.0001f) { AddUniquePoint(intersections, second); } if ((!(num >= -0.0001f) && !(num2 <= 0.0001f)) || (!(num <= 0.0001f) && !(num2 >= -0.0001f))) { float num3 = num / (num - num2); AddUniquePoint(intersections, Vector3.Lerp(first, second, num3)); } } private static void AddUniquePoint(List points, Vector3 candidate) { //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_0017: 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) float num = 9.999999E-09f; for (int i = 0; i < points.Count; i++) { Vector3 val = points[i] - candidate; if (((Vector3)(ref val)).sqrMagnitude <= num) { return; } } points.Add(candidate); } private static Vector3 Average(Vector3[] points) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; for (int i = 0; i < points.Length; i++) { val += points[i]; } return val / (float)points.Length; } private static Vector3 FindPrincipalAxis(Vector3[] points, Vector3 center) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) float num = 0f; float num2 = 0f; float num3 = 0f; float num4 = 0f; float num5 = 0f; float num6 = 0f; for (int i = 0; i < points.Length; i++) { Vector3 val = points[i] - center; num += val.x * val.x; num2 += val.x * val.y; num3 += val.x * val.z; num4 += val.y * val.y; num5 += val.y * val.z; num6 += val.z * val.z; } Vector3 val2 = ((num >= num4 && num >= num6) ? Vector3.right : ((num4 >= num6) ? Vector3.up : Vector3.forward)); Vector3 val3 = default(Vector3); for (int j = 0; j < 12; j++) { ((Vector3)(ref val3))..ctor(num * val2.x + num2 * val2.y + num3 * val2.z, num2 * val2.x + num4 * val2.y + num5 * val2.z, num3 * val2.x + num5 * val2.y + num6 * val2.z); if (((Vector3)(ref val3)).sqrMagnitude <= 1E-06f) { break; } val2 = ((Vector3)(ref val3)).normalized; } return val2; } private static Vector3 AverageTip(Vector3[] points, Vector3 center, Vector3 axis, float minimum, float maximum, bool first) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) float num = (first ? minimum : maximum); float num2 = Mathf.Max((maximum - minimum) * 0.01f, 0.001f); Vector3 val = Vector3.zero; int num3 = 0; for (int i = 0; i < points.Length; i++) { float num4 = Vector3.Dot(points[i] - center, axis); if (first ? (num4 <= minimum + num2) : (num4 >= maximum - num2)) { val += points[i]; num3++; } } Vector3 val2 = ((num3 > 0) ? (val / (float)num3) : (center + axis * num)); float num5 = Vector3.Dot(val2 - center, axis); return val2 + axis * (num - num5); } private static Vector3 Abs(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) return new Vector3(Mathf.Abs(value.x), Mathf.Abs(value.y), Mathf.Abs(value.z)); } } [HarmonyPatch(typeof(SaveLoadManager), "SaveModData")] internal static class LateenYardSavePatch { private static void Postfix() { LateenYardPersistence.Save(); } } [BepInPlugin("com.pro1p.sailwind.junkandgaffadjustment", "Sail Rebalance", "1.2.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.pro1p.sailwind.junkandgaffadjustment"; public const string PluginName = "Sail Rebalance"; public const string PluginVersion = "1.2.0"; private Harmony harmony; private volatile bool reverseJunkSquareRefreshPending; internal static ConfigEntry EnableJunkCurve { get; private set; } internal static ConfigEntry JunkMultiplier { get; private set; } internal static ConfigEntry EnableGaffCurve { get; private set; } internal static ConfigEntry GaffMultiplier { get; private set; } internal static ConfigEntry EnableLateenBadTackPenalty { get; private set; } internal static ConfigEntry ReverseJunkSquareFurling { get; private set; } internal static ManualLogSource Log { get; private set; } internal static bool JunkCurveEnabled => EnableJunkCurve?.Value ?? true; internal static float ConfiguredJunkMultiplier => JunkMultiplier?.Value ?? 0.75f; internal static bool GaffCurveEnabled => EnableGaffCurve?.Value ?? true; internal static float ConfiguredGaffMultiplier => GaffMultiplier?.Value ?? 0.85f; internal static bool LateenBadTackPenaltyEnabled => EnableLateenBadTackPenalty?.Value ?? true; internal static bool ReverseJunkSquareFurlingEnabled => ReverseJunkSquareFurling?.Value ?? true; private void Awake() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; AcceptableValueRange val = new AcceptableValueRange(0.5f, 1f); EnableJunkCurve = ((BaseUnityPlugin)this).Config.Bind("Junk sails", "Enable junk curve", true, "Use the junk apparent-wind curve. When enabled, the junk multiplier slider is ignored."); JunkMultiplier = ((BaseUnityPlugin)this).Config.Bind("Junk sails", "Junk multiplier", 0.75f, new ConfigDescription("Replaces the original 0.75 multiplier when the junk curve is disabled.", (AcceptableValueBase)(object)val, Array.Empty())); EnableGaffCurve = ((BaseUnityPlugin)this).Config.Bind("Gaff sails", "Enable gaff curve", true, "Use the gaff apparent-wind curve. When enabled, the gaff multiplier slider is ignored."); GaffMultiplier = ((BaseUnityPlugin)this).Config.Bind("Gaff sails", "Gaff multiplier", 0.85f, new ConfigDescription("Replaces the original 0.85 multiplier when the gaff curve is disabled.", (AcceptableValueBase)(object)val, Array.Empty())); EnableLateenBadTackPenalty = ((BaseUnityPlugin)this).Config.Bind("Lateen sails", "Enable bad tack penalty", true, "Reduce lateen sail force by 10% when the yard is on the windward side of the mast."); ReverseJunkSquareFurling = ((BaseUnityPlugin)this).Config.Bind("Junk square sails", "Reverse furling", true, "Pulling the reef winch raises and unfurls the sail; releasing it lowers and furls the sail."); ReverseJunkSquareFurling.SettingChanged += OnReverseJunkSquareFurlingChanged; harmony = new Harmony("com.pro1p.sailwind.junkandgaffadjustment"); harmony.PatchAll(typeof(Plugin).Assembly); reverseJunkSquareRefreshPending = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Sail Rebalance 1.2.0 loaded."); } private void Update() { if (reverseJunkSquareRefreshPending) { reverseJunkSquareRefreshPending = false; JunkSquareReverseReefing.SetEnabled(ReverseJunkSquareFurlingEnabled); } } private void OnReverseJunkSquareFurlingChanged(object sender, EventArgs eventArgs) { reverseJunkSquareRefreshPending = true; } private void OnDestroy() { if (ReverseJunkSquareFurling != null) { ReverseJunkSquareFurling.SettingChanged -= OnReverseJunkSquareFurlingChanged; } JunkSquareReverseReefing.Reset(); LateenBraceAttachmentPatch.Reset(); LateenControlRegistry.DisposeAllRigs(); LateenYardPersistence.Reset(); Harmony val = harmony; if (val != null) { val.UnpatchSelf(); } } } [HarmonyPatch(typeof(Sail), "ApplyForce")] internal static class SailCategoryPowerPatch { private static readonly MethodInfo JunkMethod = AccessTools.Method(typeof(SailPower), "Junk", (Type[])null, (Type[])null); private static readonly MethodInfo GaffMethod = AccessTools.Method(typeof(SailPower), "Gaff", (Type[])null, (Type[])null); private static IEnumerable Transpiler(IEnumerable instructions) { bool replacedJunk = false; bool replacedGaff = false; foreach (CodeInstruction instruction in instructions) { MethodInfo replacement = null; if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num) { if (!replacedJunk && num == 0.75f) { replacement = JunkMethod; replacedJunk = true; } else if (!replacedGaff && num == 0.85f) { replacement = GaffMethod; replacedGaff = true; } } if (replacement == null) { yield return instruction; continue; } instruction.opcode = OpCodes.Ldarg_0; instruction.operand = null; yield return instruction; yield return new CodeInstruction(OpCodes.Call, (object)replacement); } if (!replacedJunk || !replacedGaff) { throw new InvalidOperationException("Could not locate Sail.ApplyForce's junk and gaff multipliers. The game may have updated."); } } } internal static class SailPower { internal const float JunkDefault = 0.75f; internal const float GaffDefault = 0.85f; internal const float StrongWindThreshold = 21f; internal static float Junk(Sail sail) { if (!Plugin.JunkCurveEnabled) { return Plugin.ConfiguredJunkMultiplier; } if ((Object)(object)sail == (Object)null || (Object)(object)sail.shipRigidbody == (Object)null) { return 0.75f; } float num = ApparentAngle(sail); bool flag = ((Vector3)(ref Wind.currentWind)).magnitude > 21f; float num2 = (flag ? 0.85f : 0.75f); if (num <= 90f) { return num2; } if (num <= 150f) { return Mathf.Lerp(num2, 1f, Mathf.InverseLerp(90f, 150f, num)); } return Mathf.Lerp(1f, flag ? 0.9f : 0.85f, Mathf.InverseLerp(150f, 180f, num)); } internal static float Gaff(Sail sail) { if (!Plugin.GaffCurveEnabled) { return Plugin.ConfiguredGaffMultiplier; } if ((Object)(object)sail == (Object)null || (Object)(object)sail.shipRigidbody == (Object)null) { return 0.85f; } float num = ApparentAngle(sail); if (num <= 80f || num >= 160f) { return 0.85f; } if (num <= 120f) { return Mathf.Lerp(0.85f, 1f, Mathf.InverseLerp(80f, 120f, num)); } return Mathf.Lerp(1f, 0.85f, Mathf.InverseLerp(120f, 160f, num)); } private static float ApparentAngle(Sail sail) { //IL_000b: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return Mathf.Abs(Vector3.SignedAngle(-((Component)sail.shipRigidbody).transform.forward, sail.apparentWind, Vector3.up)); } } }