using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using ShipyardExpansion; using ShipyardExpansion.Scripts; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("UnstayedJunkSailMast")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.6.0")] [assembly: AssemblyInformationalVersion("1.2.6")] [assembly: AssemblyProduct("UnstayedJunkSailMast")] [assembly: AssemblyTitle("UnstayedJunkSailMast")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.6.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace UnstayedJunkSailMast { [HarmonyPatch(typeof(ShipyardSailInstaller), "AddNewSail")] internal static class AddNewSailPatch { private static bool Prefix(ShipyardSailInstaller __instance, GameObject sailObject) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) Mast currentMast = __instance.GetCurrentMast(); if ((Object)(object)currentMast == (Object)null || (Object)(object)((Component)currentMast).GetComponent() == (Object)null || UnstayedSailRules.IsAllowed(sailObject)) { return true; } Sail val = (((Object)(object)sailObject != (Object)null) ? sailObject.GetComponent() : null); ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Rejected " + (((Object)(object)val != (Object)null) ? val.sailName : "unknown sail") + " on an unstayed mast.")); } if ((Object)(object)sailObject != (Object)null) { Scene scene = sailObject.scene; if (((Scene)(ref scene)).IsValid()) { Object.Destroy((Object)(object)sailObject); } } return false; } } [HarmonyPatch(typeof(SaveableBoatCustomization), "Awake")] [HarmonyAfter(new string[] { "com.nandbrew.shipyardexpansion" })] [HarmonyPriority(0)] internal static class BoatCustomizationAwakePatch { private static void Postfix(SaveableBoatCustomization __instance, BoatCustomParts ___parts, BoatRefs ___refs) { try { UnstayedMastBuilder.TryBuild(__instance, ___parts, ___refs); } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Could not build unstayed mast options on " + ((Object)__instance).name + ": " + ex)); } } } } [HarmonyPatch(typeof(SaveableBoatCustomization), "LoadData")] [HarmonyAfter(new string[] { "com.nandbrew.shipyardexpansion" })] internal static class BoatCustomizationLoadPatch { private static void Prefix(SaveableBoatCustomization __instance, SaveBoatCustomizationData data) { UnstayedSaveCompatibility.PrepareLoad(__instance, data); } private static void Postfix(SaveableBoatCustomization __instance) { BoatCustomParts component = ((Component)__instance).GetComponent(); if (UnstayedSelectionRules.NormalizeActive(component)) { component.RefreshParts(); } } } [HarmonyPatch(typeof(SaveableBoatCustomization), "GetData")] internal static class BoatCustomizationSavePatch { private static void Postfix(SaveableBoatCustomization __instance, SaveBoatCustomizationData __result) { UnstayedSaveCompatibility.RecordActiveOptions(__instance, __result); } } [HarmonyPatch(typeof(SaveLoadManager), "LoadNeeds")] [HarmonyBefore(new string[] { "com.nandbrew.shipyardexpansion" })] [HarmonyPriority(800)] internal static class EarlyModDataLoadPatch { private static void Postfix(SaveContainer __0) { GameState.modData = ((__0 != null && __0.modData != null) ? __0.modData : new Dictionary()); UnstayedMastIndexCoordinator.RebindFromLoadedModData(); } } internal sealed class EmptyRestrictedPartMarker : MonoBehaviour { } [HarmonyPatch(typeof(BoatRefs), "RegisterMast")] internal static class IdempotentUnstayedMastRegistrationPatch { private static bool Prefix(BoatRefs __instance, Mast mast) { if ((Object)(object)__instance == (Object)null || (Object)(object)mast == (Object)null || (Object)(object)((Component)mast).GetComponent() == (Object)null || __instance.masts == null) { return true; } int orderIndex = mast.orderIndex; if (orderIndex >= 0 && orderIndex < __instance.masts.Length) { return (Object)(object)__instance.masts[orderIndex] != (Object)(object)mast; } return true; } } [BepInPlugin("dogeggz.unstayedjunksailmast", "Unstayed Junk Sail Mast", "1.2.6")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "dogeggz.unstayedjunksailmast"; public const string PluginName = "Unstayed Junk Sail Mast"; public const string PluginVersion = "1.2.6"; public const string ShipyardExpansionGuid = "com.nandbrew.shipyardexpansion"; private Harmony harmony; internal static ManualLogSource LogSource { get; private set; } internal static string PluginDirectory { get; private set; } private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown LogSource = ((BaseUnityPlugin)this).Logger; PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? string.Empty; harmony = new Harmony("dogeggz.unstayedjunksailmast"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Unstayed Junk Sail Mast 1.2.6 loaded; required Shipyard Expansion dependency is active."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } RectangleJunkSails.Reset(); RectangleJunkAssets.Reset(); UnstayedBoatRegistry.Clear(); LogSource = null; PluginDirectory = null; } } [HarmonyPatch(typeof(Sail), "GetSailArea")] internal static class RectangleJunkAreaPatch { private static void Postfix(Sail __instance, ref float __result) { RectangleJunkSailRig component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { __result = component.GetTransformedSailArea(__result); } } } internal static class RectangleJunkAssets { private const string BundleFileName = "unstayedjunksailmast_assets"; private const string NarrowPrefabPath = "assets/ujsmfinal/narrow_rectangle_junk.prefab"; private const string WidePrefabPath = "assets/ujsmfinal/wide_rectangle_junk.prefab"; private static AssetBundle bundle; private static GameObject narrowPrefab; private static GameObject widePrefab; internal static GameObject LoadNarrowPrefab() { if ((Object)(object)narrowPrefab != (Object)null) { return narrowPrefab; } if (!EnsureBundleLoaded()) { return null; } narrowPrefab = bundle.LoadAsset("assets/ujsmfinal/narrow_rectangle_junk.prefab"); if ((Object)(object)narrowPrefab == (Object)null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)"Could not find the final narrow rectangle Junk prefab in the Rectangle Junk AssetBundle."); } } return narrowPrefab; } internal static GameObject LoadWidePrefab() { if ((Object)(object)widePrefab != (Object)null) { return widePrefab; } if (!EnsureBundleLoaded()) { return null; } widePrefab = bundle.LoadAsset("assets/ujsmfinal/wide_rectangle_junk.prefab"); if ((Object)(object)widePrefab == (Object)null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)"Could not find the final wide rectangle Junk prefab in the Rectangle Junk AssetBundle."); } } return widePrefab; } private static bool EnsureBundleLoaded() { if ((Object)(object)bundle != (Object)null) { return true; } string text = Path.Combine(Plugin.PluginDirectory, "assets", "unstayedjunksailmast_assets"); if (!File.Exists(text)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Could not add Rectangle Junk sails: AssetBundle not found at " + text + ".")); } return false; } try { bundle = AssetBundle.LoadFromFile(text); if ((Object)(object)bundle != (Object)null) { return true; } ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)("Could not load Rectangle Junk AssetBundle at " + text + ".")); } } catch (Exception ex) { ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogError((object)("Could not load final Rectangle Junk assets: " + ex)); } } bundle = null; return false; } internal static void Reset() { narrowPrefab = null; widePrefab = null; AssetBundle obj = bundle; if (obj != null) { obj.Unload(false); } bundle = null; } } [HarmonyPatch(typeof(ShipyardSailColChecker), "RunColCheck")] internal static class RectangleJunkCollisionCheckerScalePatch { private static void Postfix(Sail ___sail) { RectangleJunkSails.SynchronizeCollisionCheckerScale(___sail); } } [HarmonyPatch(typeof(Mast), "UpdateControllerAttachments")] internal static class RectangleJunkMastAttachmentPatch { private static void Prefix(Mast __instance) { RectangleJunkSails.PrepareMast(__instance); } private static void Postfix(Mast __instance) { RectangleJunkSails.ApplyToMast(__instance); } } [HarmonyPatch(typeof(PrefabsDirectory), "Start")] [HarmonyAfter(new string[] { "com.nandbrew.shipyardexpansion" })] internal static class RectangleJunkPrefabPatch { private static void Prefix(PrefabsDirectory __instance) { RectangleJunkSails.EnsureRegistered(__instance); } } internal static class RectangleJunkSailIndexMigration { private const int CurrentLayoutVersion = 1; internal static void PrepareLoad(SaveableBoatCustomization customization, SaveBoatCustomizationData data) { if (data == null || data.sails == null) { return; } int sceneIndex; bool flag = TryGetSceneIndex(customization, out sceneIndex); if (flag && IsCurrent(sceneIndex)) { return; } int num; int num2; if (flag) { num = (HasLegacyModFootprint(sceneIndex) ? 1 : 0); if (num != 0) { num2 = 1; goto IL_0045; } } else { num = 0; } num2 = (RectangleJunkSails.CanMigrateLegacyIndex(131) ? 1 : 0); goto IL_0045; IL_0045: bool migrateNarrow = (byte)num2 != 0; bool migrateWide = num != 0 || RectangleJunkSails.CanMigrateLegacyIndex(132); int num3 = RemapLegacyIndices(data.sails, migrateNarrow, migrateWide); if (flag) { MarkCurrent(sceneIndex); } if (num3 > 0) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)("Migrated " + num3 + " Rectangle Junk sail index record(s) from 131/132 to 200/201" + (flag ? (" for boat scene " + sceneIndex) : string.Empty) + ".")); } } } internal static void MarkCurrent(SaveableBoatCustomization customization) { if (TryGetSceneIndex(customization, out var sceneIndex)) { MarkCurrent(sceneIndex); } } internal static int RemapLegacyIndices(IList sails, bool migrateNarrow, bool migrateWide) { if (sails == null) { return 0; } int num = 0; for (int i = 0; i < sails.Count; i++) { SaveSailData val = sails[i]; if (val != null) { if (migrateNarrow && val.prefabIndex == 131) { val.prefabIndex = 200; num++; } else if (migrateWide && val.prefabIndex == 132) { val.prefabIndex = 201; num++; } } } return num; } private static bool TryGetSceneIndex(SaveableBoatCustomization customization, out int sceneIndex) { SaveableObject val = (((Object)(object)customization != (Object)null) ? ((Component)customization).GetComponent() : null); if ((Object)(object)val != (Object)null) { sceneIndex = val.sceneIndex; return true; } sceneIndex = -1; return false; } private static bool IsCurrent(int sceneIndex) { if (GameState.modData == null) { return false; } if (GameState.modData.TryGetValue(GetLayoutKey(sceneIndex), out var value) && int.TryParse(value, out var result)) { return result >= 1; } return false; } private static bool HasLegacyModFootprint(int sceneIndex) { if (GameState.modData == null) { return false; } string text = "dogeggz.unstayedjunksailmast." + sceneIndex; if (!GameState.modData.ContainsKey(text + ".activeMasts")) { return GameState.modData.ContainsKey(text + ".indexLayoutVersion"); } return true; } private static void MarkCurrent(int sceneIndex) { if (GameState.modData != null) { GameState.modData[GetLayoutKey(sceneIndex)] = 1.ToString(); } } private static string GetLayoutKey(int sceneIndex) { return "dogeggz.unstayedjunksailmast." + sceneIndex + ".rectangleSailIndexLayoutVersion"; } } internal sealed class RectangleJunkSailRig : MonoBehaviour { private Mast mast; private Sail sail; private RopeControllerSailReef reefController; private ReefEffectAnimUniversal reefEffect; private GPButtonRopeWinch boundWinch; private bool originalWinchReverseResistance; private bool initialized; private bool refreshClothPending; private bool warningLogged; internal bool Initialize(Mast owningMast) { mast = owningMast; if (initialized) { return true; } sail = ((Component)this).GetComponent(); SailConnections component = ((Component)this).GetComponent(); reefController = (RopeControllerSailReef)(((Object)(object)component != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); reefEffect = ((Component)this).GetComponentInChildren(true); if ((Object)(object)sail == (Object)null || (Object)(object)component == (Object)null || (Object)(object)reefController == (Object)null || (Object)(object)reefEffect == (Object)null) { return Fail("its reefing components are incomplete"); } ApplyControllerDirection(); initialized = true; refreshClothPending = true; return true; } internal float GetTransformedSailArea(float fallback) { //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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00d2: 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_00d6: 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_00dd: 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_00e4: 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) if ((Object)(object)sail == (Object)null) { sail = ((Component)this).GetComponent(); } SkinnedMeshRenderer val = (((Object)(object)sail != (Object)null && (Object)(object)sail.cloth != (Object)null) ? ((Component)sail.cloth).GetComponent() : null); Mesh val2 = (((Object)(object)val != (Object)null) ? val.sharedMesh : null); if ((Object)(object)val2 == (Object)null) { return fallback; } int[] triangles = val2.triangles; Vector3[] vertices = val2.vertices; float num = 0f; for (int i = 0; i + 2 < triangles.Length; i += 3) { Vector3 val3 = ((Component)val).transform.TransformVector(vertices[triangles[i]]); Vector3 val4 = ((Component)val).transform.TransformVector(vertices[triangles[i + 1]]); Vector3 val5 = ((Component)val).transform.TransformVector(vertices[triangles[i + 2]]); float num2 = num; Vector3 val6 = Vector3.Cross(val4 - val3, val5 - val3); num = num2 + ((Vector3)(ref val6)).magnitude * 0.5f; } if (!(num > 0f)) { return fallback; } return num; } 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(); } private void LateUpdate() { if (initialized && refreshClothPending) { refreshClothPending = false; RefreshCloth(); ((Behaviour)this).enabled = false; } } private void OnDestroy() { RestoreWinch(); } private void ApplyControllerDirection() { float num = Mathf.Clamp01(sail.currentUnroll); reefController.reverseReefing = true; ((RopeController)reefController).currentLength = 1f - 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 RefreshCloth() { if ((Object)(object)reefEffect == (Object)null || !((Behaviour)reefEffect).isActiveAndEnabled) { return; } try { reefEffect.RefreshCloth(); } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Could not refresh Rectangle Junk cloth for " + ((Object)this).name + ": " + ex.Message)); } } } private bool Fail(string reason) { if (!warningLogged) { warningLogged = true; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Could not initialize Rectangle Junk rig for " + ((Object)this).name + ": " + reason + ".")); } } return false; } } internal static class RectangleJunkSails { internal const int LegacyNarrowIndex = 131; internal const int LegacyWideIndex = 132; internal const int NarrowIndex = 200; internal const int WideIndex = 201; private const int ShipyardExpansionSailCapacity = 512; private const float WindAxisToleranceDegrees = 0.01f; private static readonly int[] RuntimeMaterialSourceIndices = new int[7] { 101, 27, 28, 29, 100, 102, 103 }; private static GameObject prefabContainer; private static bool legacyNarrowIndexWasAvailable; private static bool legacyWideIndexWasAvailable; internal static GameObject NarrowPrefab { get; private set; } internal static GameObject WidePrefab { get; private set; } internal static bool EnsureRegistered(PrefabsDirectory directory) { if ((Object)(object)directory == (Object)null || directory.sails == null) { return false; } NarrowPrefab = GetRegisteredRectangle(directory, 200); WidePrefab = GetRegisteredRectangle(directory, 201); if ((Object)(object)NarrowPrefab == (Object)null || (Object)(object)WidePrefab == (Object)null) { Build(directory); NarrowPrefab = GetRegisteredRectangle(directory, 200); WidePrefab = GetRegisteredRectangle(directory, 201); } if ((Object)(object)NarrowPrefab != (Object)null) { EnsureShipyardExpansionComponents(NarrowPrefab); } if ((Object)(object)WidePrefab != (Object)null) { EnsureShipyardExpansionComponents(WidePrefab); } if ((Object)(object)NarrowPrefab != (Object)null) { return (Object)(object)WidePrefab != (Object)null; } return false; } internal static bool CanMigrateLegacyIndex(int prefabIndex) { return prefabIndex switch { 131 => legacyNarrowIndexWasAvailable, 132 => legacyWideIndexWasAvailable, _ => false, }; } internal static void Build(PrefabsDirectory directory) { if ((Object)(object)directory == (Object)null || directory.sails == null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)"Could not add Rectangle Junk sails: PrefabsDirectory.sails is unavailable."); } return; } if (directory.sails.Length < 512) { legacyNarrowIndexWasAvailable = false; legacyWideIndexWasAvailable = false; ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)"Could not add Rectangle Junk sails: Shipyard Expansion did not provide its 512-slot sail registry."); } return; } legacyNarrowIndexWasAvailable = (Object)(object)directory.sails[131] == (Object)null; legacyWideIndexWasAvailable = (Object)(object)directory.sails[132] == (Object)null; GameObject val = FindRuntimeMaterialSource(directory); if ((Object)(object)val == (Object)null) { ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogError((object)"Could not add Rectangle Junk sails: no live Junk Square rendering materials were found."); } return; } EnsurePrefabContainer(); NarrowPrefab = EnsureFinalRectangle(directory, val, RectangleJunkAssets.LoadNarrowPrefab(), 200, "narrow rectangle junk"); WidePrefab = EnsureFinalRectangle(directory, val, RectangleJunkAssets.LoadWidePrefab(), 201, "wide rectangle junk"); ManualLogSource logSource4 = Plugin.LogSource; if (logSource4 != null) { logSource4.LogInfo((object)(((Object)(object)NarrowPrefab != (Object)null) ? ("Registered narrow rectangle junk at sail index " + 200 + ".") : "Narrow rectangle junk was not registered.")); } ManualLogSource logSource5 = Plugin.LogSource; if (logSource5 != null) { logSource5.LogInfo((object)(((Object)(object)WidePrefab != (Object)null) ? ("Registered wide rectangle junk at sail index " + 201 + ".") : "Wide rectangle junk was not registered.")); } } internal static void AddToShipyard(PrefabsDirectory directory, ref GameObject[] sailPrefabs) { NarrowPrefab = GetRegisteredRectangle(directory, 200); WidePrefab = GetRegisteredRectangle(directory, 201); if ((Object)(object)NarrowPrefab == (Object)null || (Object)(object)WidePrefab == (Object)null) { return; } List list = new List(); HashSet hashSet = new HashSet(); if (sailPrefabs != null) { for (int i = 0; i < sailPrefabs.Length; i++) { GameObject val = sailPrefabs[i]; Sail val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val != (Object)null && ((Object)(object)val2 == (Object)null || !IsRectangleIndex(val2.prefabIndex)) && hashSet.Add(val)) { list.Add(val); } } } AddUnique(list, hashSet, NarrowPrefab); AddUnique(list, hashSet, WidePrefab); sailPrefabs = list.ToArray(); } internal static void RemoveFromShipyard(ref GameObject[] sailPrefabs) { if (sailPrefabs == null) { return; } List list = new List(sailPrefabs.Length); for (int i = 0; i < sailPrefabs.Length; i++) { GameObject val = sailPrefabs[i]; Sail val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null || !IsRectangleIndex(val2.prefabIndex)) { list.Add(val); } } sailPrefabs = list.ToArray(); } internal static void PrepareMast(Mast mast) { if (!((Object)(object)mast == (Object)null) && mast.sails != null) { for (int i = 0; i < mast.sails.Count; i++) { GameObject val = mast.sails[i]; (((Object)(object)val != (Object)null) ? val.GetComponent() : null)?.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]; RectangleJunkSailRig rectangleJunkSailRig = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)rectangleJunkSailRig != (Object)null && rectangleJunkSailRig.Initialize(mast)) { rectangleJunkSailRig.BindAssignedWinch(); } } } internal static void Reset() { NarrowPrefab = null; WidePrefab = null; legacyNarrowIndexWasAvailable = false; legacyWideIndexWasAvailable = false; if ((Object)(object)prefabContainer != (Object)null) { Object.Destroy((Object)(object)prefabContainer); prefabContainer = null; } } private static void EnsurePrefabContainer() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (!((Object)(object)prefabContainer != (Object)null)) { prefabContainer = new GameObject("UJSM Rectangle Junk Prefabs"); Object.DontDestroyOnLoad((Object)(object)prefabContainer); prefabContainer.SetActive(false); } } private static GameObject EnsureFinalRectangle(PrefabsDirectory directory, GameObject runtimeMaterialSource, GameObject authoredSource, int prefabIndex, string sailName) { GameObject val = directory.sails[prefabIndex]; if ((Object)(object)val != (Object)null) { RectangleJunkSailRig component = val.GetComponent(); Sail component2 = val.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && component2.prefabIndex == prefabIndex) { EnsureShipyardExpansionComponents(val); return val; } ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Could not register " + sailName + " at index " + prefabIndex + ": that sail slot is already occupied by " + ((Object)val).name + ".")); } return null; } if ((Object)(object)authoredSource == (Object)null) { return null; } GameObject val2 = null; try { val2 = Object.Instantiate(authoredSource, prefabContainer.transform); ((Object)val2).name = prefabIndex + " SAIL " + sailName; ConfigureFinalRectangle(val2, runtimeMaterialSource, prefabIndex, sailName); directory.sails[prefabIndex] = val2; return val2; } catch (Exception ex) { if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)val2); } ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)("Could not build authored " + sailName + ": " + ex)); } return null; } } private static void ConfigureFinalRectangle(GameObject clone, GameObject runtimeMaterialSource, int prefabIndex, string sailName) { Sail sail = RequireComponent(clone, "Sail"); SailConnections connections = RequireComponent(clone, "SailConnections"); HingeJoint hinge = RequireComponent(clone, "HingeJoint"); Animator componentInChildren = clone.GetComponentInChildren(true); Transform val = FindDescendant(((Object)(object)componentInChildren != (Object)null) ? ((Component)componentInChildren).transform : null, "SAIL_junk_square"); Transform val2 = FindDescendant(val, "sail_rope_att__angle_mid_rectangle_"); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { throw new InvalidOperationException("the authored Rectangle Junk hierarchy is incomplete"); } ValidateFinalRuntimeRig(clone, sail, connections, hinge, val2, prefabIndex, sailName); AssignRuntimeMaterials(clone, runtimeMaterialSource); clone.AddComponent(); EnsureShipyardExpansionComponents(clone); } private static void EnsureShipyardExpansionComponents(GameObject prefab) { if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent().Setup(); } } private static GameObject GetRegisteredRectangle(PrefabsDirectory directory, int prefabIndex) { if ((Object)(object)directory == (Object)null || directory.sails == null || prefabIndex < 0 || prefabIndex >= directory.sails.Length) { return null; } GameObject val = directory.sails[prefabIndex]; Sail val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); RectangleJunkSailRig rectangleJunkSailRig = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (!((Object)(object)val2 != (Object)null) || !((Object)(object)rectangleJunkSailRig != (Object)null) || val2.prefabIndex != prefabIndex) { return null; } return val; } private static void ValidateFinalRuntimeRig(GameObject clone, Sail sail, SailConnections connections, HingeJoint hinge, Transform sheetAnchor, int prefabIndex, string sailName) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) RopeController reefController = connections.reefController; RopeControllerSailReef val = (RopeControllerSailReef)(object)((reefController is RopeControllerSailReef) ? reefController : null); RopeController angleControllerMid = connections.angleControllerMid; RopeControllerSailAngle val2 = (RopeControllerSailAngle)(object)((angleControllerMid is RopeControllerSailAngle) ? angleControllerMid : null); Transform midRopeAttachment = connections.midRopeAttachment; RopeEffect val3 = (((Object)(object)midRopeAttachment != (Object)null) ? ((Component)midRopeAttachment).GetComponent() : null); RopeEffect val4 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent() : null); if (sail.prefabIndex != prefabIndex || sail.sailName != sailName || sail.squareSail || sail.obsolete || (Object)(object)sail.windcenter == (Object)null || Quaternion.Angle(clone.transform.rotation, sail.windcenter.rotation) > 0.01f || (Object)(object)connections.sail != (Object)(object)sail || (Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)midRopeAttachment == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)connections.angleControllerLeft != (Object)null || (Object)(object)connections.angleControllerRight != (Object)null || (Object)(object)val2.sailHinge != (Object)(object)hinge || (Object)(object)((Component)val2).transform.parent != (Object)(object)clone.transform || (Object)(object)midRopeAttachment.parent != (Object)(object)clone.transform || (Object)(object)val4.attachment != (Object)(object)midRopeAttachment || (Object)(object)val3.attachment != (Object)(object)sheetAnchor || !val3.sheet || (Object)(object)val.sail != (Object)(object)sail || !val.reverseReefing || (Object)(object)clone.GetComponent() != (Object)null || (Object)(object)clone.GetComponent() != (Object)null) { throw new InvalidOperationException("the final Rectangle Junk prefab has invalid serialized vanilla Junk rig or wind-axis configuration"); } } private static GameObject FindRuntimeMaterialSource(PrefabsDirectory directory) { for (int i = 0; i < RuntimeMaterialSourceIndices.Length; i++) { int num = RuntimeMaterialSourceIndices[i]; if (num >= 0 && num < directory.sails.Length) { GameObject val = directory.sails[num]; if (HasRuntimeMaterials(val)) { return val; } } } return null; } private static bool HasRuntimeMaterials(GameObject candidate) { Sail val = (((Object)(object)candidate != (Object)null) ? candidate.GetComponent() : null); Renderer val2 = (((Object)(object)val != (Object)null && (Object)(object)val.cloth != (Object)null) ? ((Component)val.cloth).GetComponent() : null); ReefEffectAnimUniversal val3 = (((Object)(object)candidate != (Object)null) ? candidate.GetComponentInChildren(true) : null); if ((Object)(object)val2 != (Object)null && val2.sharedMaterials.Length != 0 && (Object)(object)val2.sharedMaterials[0] != (Object)null && (Object)(object)val3 != (Object)null && (Object)(object)val3.furledSail != (Object)null && val3.furledSail.sharedMaterials.Length != 0) { return (Object)(object)val3.furledSail.sharedMaterials[0] != (Object)null; } return false; } private static void AssignRuntimeMaterials(GameObject target, GameObject source) { Sail val = RequireComponent(target, "Sail"); Sail val2 = RequireComponent(source, "Junk Square Sail"); Renderer obj = (((Object)(object)val.cloth != (Object)null) ? ((Component)val.cloth).GetComponent() : null); Renderer val3 = (((Object)(object)val2.cloth != (Object)null) ? ((Component)val2.cloth).GetComponent() : null); ReefEffectAnimUniversal componentInChildren = target.GetComponentInChildren(true); ReefEffectAnimUniversal componentInChildren2 = source.GetComponentInChildren(true); if ((Object)(object)obj == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.furledSail == (Object)null || (Object)(object)componentInChildren2 == (Object)null || (Object)(object)componentInChildren2.furledSail == (Object)null || val3.sharedMaterials.Length == 0 || componentInChildren2.furledSail.sharedMaterials.Length == 0) { throw new InvalidOperationException("the live Junk Square rendering materials are incomplete"); } obj.sharedMaterials = val3.sharedMaterials; componentInChildren.furledSail.sharedMaterials = componentInChildren2.furledSail.sharedMaterials; } internal static bool IsRectangle(GameObject sailObject) { Sail val = (((Object)(object)sailObject != (Object)null) ? sailObject.GetComponent() : null); if ((Object)(object)val != (Object)null) { return IsRectangleIndex(val.prefabIndex); } return false; } private static bool IsRectangleIndex(int prefabIndex) { if (prefabIndex != 200) { return prefabIndex == 201; } return true; } internal static void SynchronizeCollisionCheckerScale(Sail sail) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sail == (Object)null) && IsRectangle(((Component)sail).gameObject) && !((Object)(object)sail.cloth == (Object)null) && !((Object)(object)((Component)sail.cloth).transform.parent == (Object)null)) { SailConnections component = ((Component)sail).GetComponent(); Transform val = (((Object)(object)component != (Object)null && (Object)(object)component.colChecker != (Object)null) ? ((Component)component.colChecker).transform : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.parent == (Object)null) && ((Component)val.parent).gameObject.layer == 8) { Vector3 localScale = ((Component)sail.cloth).transform.parent.localScale; val.localScale = new Vector3(localScale.x, localScale.z, localScale.y); } } } internal static void FurlNewlyInstalledSail(GameObject sailObject) { if (!IsRectangle(sailObject)) { return; } Sail component = sailObject.GetComponent(); 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) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"Could not furl newly installed Rectangle Junk: its reef controller is missing."); } } else { component.currentUnroll = 0f; val.reverseReefing = true; ((RopeController)val).currentLength = 1f; ((RopeController)val).changed = true; } } internal static void SynchronizeWinchVisibility(Mast mast) { if ((Object)(object)mast == (Object)null) { return; } bool hasRectangle = false; if (mast.sails != null) { for (int i = 0; i < mast.sails.Count; i++) { if (IsRectangle(mast.sails[i])) { hasRectangle = true; break; } } } HashSet seen = new HashSet(); SynchronizeWinchArray(mast.reefWinch, hasRectangle, seen); SynchronizeWinchArray(mast.midAngleWinch, hasRectangle, seen); SynchronizeWinchArray(mast.leftAngleWinch, hasRectangle, seen); SynchronizeWinchArray(mast.rightAngleWinch, hasRectangle, seen); } private static void SynchronizeWinchArray(GPButtonRopeWinch[] winches, bool hasRectangle, HashSet seen) { if (winches == null) { return; } foreach (GPButtonRopeWinch val in winches) { if (!((Object)(object)val == (Object)null) && seen.Add(val)) { RectangleJunkWinchVisibility rectangleJunkWinchVisibility = ((Component)val).GetComponent(); if ((Object)(object)rectangleJunkWinchVisibility == (Object)null && hasRectangle && (Object)(object)val.rope == (Object)null) { rectangleJunkWinchVisibility = ((Component)val).gameObject.AddComponent(); } rectangleJunkWinchVisibility?.SetHidden(hasRectangle && (Object)(object)val.rope == (Object)null); } } } private static T RequireComponent(GameObject target, string componentName) where T : Component { T component = target.GetComponent(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException(componentName + " is missing"); } return component; } 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 void AddUnique(List result, HashSet seen, GameObject prefab) { if ((Object)(object)prefab != (Object)null && seen.Add(prefab)) { result.Add(prefab); } } } [HarmonyPatch(typeof(Mast), "AttachSailToMast")] internal static class RectangleJunkShipyardInstallPatch { 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() && RectangleJunkSails.IsRectangle(sailObject); } private static void Postfix(GameObject sailObject, bool __state) { if (__state) { RectangleJunkSails.FurlNewlyInstalledSail(sailObject); } } } [HarmonyPatch(typeof(Shipyard), "ActivateDocuments")] [HarmonyAfter(new string[] { "com.nandbrew.shipyardexpansion" })] [HarmonyPriority(0)] internal static class RectangleJunkShipyardPatch { private static readonly HashSet SaleSceneNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "island 9 E Dragon Cliffs", "island 27 Lagoon SwampShipyard" }; private static void Prefix(Shipyard __instance, ref GameObject[] ___sailPrefabs) { PrefabsDirectory instance = PrefabsDirectory.instance; bool num = RectangleJunkSails.EnsureRegistered(instance); if (num && IsSaleLocation(__instance)) { RectangleJunkSails.AddToShipyard(instance, ref ___sailPrefabs); } else { RectangleJunkSails.RemoveFromShipyard(ref ___sailPrefabs); } if (!num && IsSaleLocation(__instance)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)"Rectangle Junk sails are unavailable: indices 200/201 could not be registered."); } } } internal static bool IsSaleLocation(Shipyard shipyard) { //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) if ((Object)(object)shipyard == (Object)null) { return false; } Scene scene = ((Component)shipyard).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { return IsSaleSceneName(((Scene)(ref scene)).name); } return false; } internal static bool IsSaleSceneName(string sceneName) { if (!string.IsNullOrEmpty(sceneName)) { return SaleSceneNames.Contains(sceneName); } return false; } } internal sealed class RectangleJunkWinchVisibility : MonoBehaviour { private readonly List hiddenRenderers = new List(); private readonly List hiddenColliders = new List(); internal void SetHidden(bool shouldHide) { Restore(); if (!shouldHide) { return; } Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val != (Object)null && val.enabled) { hiddenRenderers.Add(val); val.enabled = false; } } Collider[] componentsInChildren2 = ((Component)this).GetComponentsInChildren(true); foreach (Collider val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null && val2.enabled) { hiddenColliders.Add(val2); val2.enabled = false; } } } private void OnDestroy() { Restore(); } private void Restore() { for (int i = 0; i < hiddenRenderers.Count; i++) { if ((Object)(object)hiddenRenderers[i] != (Object)null) { hiddenRenderers[i].enabled = true; } } for (int j = 0; j < hiddenColliders.Count; j++) { if ((Object)(object)hiddenColliders[j] != (Object)null) { hiddenColliders[j].enabled = true; } } hiddenRenderers.Clear(); hiddenColliders.Clear(); } } [HarmonyPatch(typeof(Mast), "UpdateWinchesEnabled")] internal static class RectangleJunkWinchVisibilityPatch { private static void Postfix(Mast __instance) { RectangleJunkSails.SynchronizeWinchVisibility(__instance); } } internal enum RestrictedPartKind { Rigging, RiggingAccessory, CrowsNest } internal sealed class RestrictedPartSelection { internal BoatPart Part; internal List OwningMastParts; internal BoatPartOption EmptyOption; internal List NonEmptyOptions; internal RestrictedPartKind Kind; } [HarmonyPatch(typeof(ShipyardUI), "SailMastCompatible")] internal static class SailMastCompatibilityPatch { private static bool Prefix(GameObject sailPrefab, ref bool __result) { Shipyard currentShipyard = GameState.currentShipyard; Mast val = (((Object)(object)currentShipyard != (Object)null) ? currentShipyard.sailInstaller.GetCurrentMast() : null); if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponent() == (Object)null) { return true; } __result = UnstayedSailRules.IsAllowed(sailPrefab); return false; } } [HarmonyPatch(typeof(ShipyardPartsInstaller), "UpdateOrder")] internal static class ShipyardOrderNormalizationPatch { private static void Prefix(BoatCustomParts ___currentParts, BoatPartsOrder ___currentOrder) { List list = UnstayedSelectionRules.NormalizeOrder(___currentParts, ___currentOrder); if ((Object)(object)ShipyardUI.instance == (Object)null || (Object)(object)___currentParts == (Object)null || ___currentOrder == null) { return; } for (int i = 0; i < list.Count; i++) { int num = list[i]; if (num >= 0 && num < ___currentParts.availableParts.Count && num < ___currentOrder.orderedOptions.Length) { BoatPart val = ___currentParts.availableParts[num]; int num2 = ___currentOrder.orderedOptions[num]; if (num2 >= 0 && num2 < val.partOptions.Count) { ShipyardUI.instance.ChangePartsOptionText(num, val.partOptions[num2].optionName); } } } } } internal sealed class UnstayedBoatMarker : MonoBehaviour { private void OnDestroy() { BoatCustomParts component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null) { UnstayedBoatRegistry.Unregister(component); } } } internal sealed class UnstayedBoatProfile { internal BoatCustomParts Parts; internal BoatRefs Refs; internal int SceneIndex; internal List Masts; internal List RetiredMastIndices; } internal static class UnstayedBoatRegistry { private static readonly Dictionary Profiles = new Dictionary(); internal static void Register(UnstayedBoatProfile profile) { Profiles[profile.Parts] = profile; } internal static bool TryGet(BoatCustomParts parts, out UnstayedBoatProfile profile) { if ((Object)(object)parts == (Object)null) { profile = null; return false; } return Profiles.TryGetValue(parts, out profile); } internal static void Unregister(BoatCustomParts parts) { Profiles.Remove(parts); } internal static List GetProfiles() { return new List(Profiles.Values); } internal static void Clear() { Profiles.Clear(); } } internal static class UnstayedMastBuilder { private sealed class HierarchyTransformSnapshot { internal Transform Target; internal Transform Parent; internal Vector3 LocalPosition; internal Quaternion LocalRotation; internal Vector3 LocalScale; } private sealed class SurfaceFittingPlacement { internal Transform Target; internal Vector2 RootRadial; internal float SourceSurfaceRadius; internal Vector2 SourceSurfaceRootPoint; internal Vector3 OriginalWorldPosition; } private static readonly string[] SmallJunkAnchorPaths = new string[3] { "junk small/structure/mast", "junk small/structure/mast_center", "junk small/structure/mast_001" }; private static readonly string[] MediumJunkAnchorPaths = new string[5] { "junk medium (actual)/structure/mast_mid_0", "junk medium (actual)/structure/mast_mid_1", "junk medium (actual)/structure/mast_mizzen_0", "junk medium (actual)/structure/mast_mizzen_1", "junk medium (actual)/structure/mast_front_" }; private static readonly string[] LargeJunkAnchorPaths = new string[4] { "junk large (3)/junk large (3)/structure/masts_structure/mast_main_1", "junk large (3)/junk large (3)/structure/masts_structure/mast_main_2", "junk large (3)/junk large (3)/structure/masts_structure/mast_back", "junk large (3)/junk large (3)/structure/masts_structure/mast_front" }; private const float DiameterScale = 1.41f; private const float MaxSurfaceFittingOffset = 1f; private static readonly HashSet SmallJunkRemovedNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "mast_holder", "mast_holder_001", "mast_holder_002" }; private const float MaximumSurfaceCorrection = 1f; private const float MinimumMastSectionBand = 0.01f; private const float MastSectionBandFraction = 0.0025f; internal static void TryBuild(SaveableBoatCustomization customization, BoatCustomParts parts, BoatRefs refs) { if ((Object)(object)customization == (Object)null || (Object)(object)parts == (Object)null || (Object)(object)refs == (Object)null || (Object)(object)((Component)customization).GetComponent() != (Object)null) { return; } SaveableObject component = ((Component)customization).GetComponent(); if ((Object)(object)component == (Object)null || !IsSupportedBoat(component.sceneIndex) || !HasShipyardExpansionMastCapacity(refs)) { return; } List list = FindTargetMastParts(((Component)customization).transform, parts, component.sceneIndex); if (list.Count == 0) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("No eligible mast part groups were found on " + ((Object)customization).name + ".")); } return; } Dictionary> dictionary = new Dictionary>(); for (int i = 0; i < list.Count; i++) { dictionary[list[i]] = GetEligibleSources(list[i]); } List selections = FindRestrictedSelections(((Component)customization).transform, parts, list, dictionary); List list2 = new List(); UnstayedMastIndexAllocator unstayedMastIndexAllocator = new UnstayedMastIndexAllocator(((Component)customization).transform, component.sceneIndex); for (int j = 0; j < list.Count; j++) { BoatPart val = list[j]; List list3 = dictionary[val]; if (list3.Count == 0) { continue; } List restrictedSelectionsForMast = GetRestrictedSelectionsForMast(selections, val); for (int k = 0; k < list3.Count; k++) { BoatPartOption val2 = list3[k]; UnstayedMastSourceIdentity identity = UnstayedMastSourceIdentity.Create(val2, val, ((Component)customization).transform, component.sceneIndex); if (unstayedMastIndexAllocator.TryClaim(val2, identity, out var mastIndex, out var usesFixedVanillaIndex)) { BoatPartOption val3 = CloneUnstayedMast(val2, component.sceneIndex, mastIndex, identity, restrictedSelectionsForMast); if (!((Object)(object)val3 == (Object)null)) { val.partOptions.Add(val3); AddMutualRestrictions(val3, restrictedSelectionsForMast); list2.Add(new UnstayedMastProfile { MastPart = val, UnstayedOption = val3, Marker = ((Component)val3).GetComponent(), UsesFixedVanillaIndex = usesFixedVanillaIndex, RestrictedSelections = new List(restrictedSelectionsForMast) }); } } } } unstayedMastIndexAllocator.Commit(); if (list2.Count == 0) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)("No unstayed mast options were created on " + ((Object)customization).name + ".")); } return; } RestrictNoShroudsToUnstayedMasts(selections); ((Component)customization).gameObject.AddComponent(); UnstayedBoatRegistry.Register(new UnstayedBoatProfile { Parts = parts, Refs = refs, SceneIndex = component.sceneIndex, Masts = list2, RetiredMastIndices = unstayedMastIndexAllocator.GetRetiredIndices() }); int num = 0; for (int l = 0; l < list2.Count; l++) { if (list2[l].UsesFixedVanillaIndex) { num++; } } ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)("Added " + list2.Count + " unstayed mast option(s) to " + ((Object)customization).name + " (scene " + component.sceneIndex + "): " + num + " fixed vanilla, " + (list2.Count - num) + " extended.")); } } private static bool IsSupportedBoat(int sceneIndex) { if (sceneIndex != 90 && sceneIndex != 80) { return sceneIndex == 70; } return true; } private static bool HasShipyardExpansionMastCapacity(BoatRefs refs) { if (refs.masts == null || refs.masts.Length < 128) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)"Could not add unstayed masts: Shipyard Expansion did not provide its 128-slot mast registry."); } return false; } return true; } private static List FindTargetMastParts(Transform boat, BoatCustomParts parts, int sceneIndex) { List list = new List(); string[] array = sceneIndex switch { 80 => MediumJunkAnchorPaths, 90 => SmallJunkAnchorPaths, _ => LargeJunkAnchorPaths, }; for (int i = 0; i < array.Length; i++) { Transform val = boat.Find(array[i]); if ((Object)(object)val == (Object)null) { val = FindUniqueMastByName(boat, GetLastPathSegment(array[i])); } BoatPartOption option = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); BoatPart value = FindContainingPart(parts, option); AddUnique(list, value); } for (int j = 0; j < parts.availableParts.Count; j++) { BoatPart val2 = parts.availableParts[j]; if (val2 != null && val2.category == 0 && ContainsEligibleMast(val2)) { AddUnique(list, val2); } } list.Sort((BoatPart left, BoatPart right) => parts.availableParts.IndexOf(left).CompareTo(parts.availableParts.IndexOf(right))); return list; } private static Transform FindUniqueMastByName(Transform root, string name) { Mast[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); Transform val = null; for (int i = 0; i < componentsInChildren.Length; i++) { if (string.Equals(((Object)componentsInChildren[i]).name, name, StringComparison.OrdinalIgnoreCase) && !((Object)(object)((Component)componentsInChildren[i]).GetComponent() == (Object)null)) { if ((Object)(object)val != (Object)null) { return null; } val = ((Component)componentsInChildren[i]).transform; } } return val; } private static string GetLastPathSegment(string path) { int num = path.LastIndexOf('/'); if (num < 0) { return path; } return path.Substring(num + 1); } private static BoatPart FindContainingPart(BoatCustomParts parts, BoatPartOption option) { if ((Object)(object)option == (Object)null) { return null; } for (int i = 0; i < parts.availableParts.Count; i++) { BoatPart val = parts.availableParts[i]; if (val != null && val.partOptions != null && val.partOptions.Contains(option)) { return val; } } return null; } private static bool ContainsEligibleMast(BoatPart part) { if (part.partOptions == null) { return false; } for (int i = 0; i < part.partOptions.Count; i++) { if (ShouldCloneSource(part.partOptions[i])) { return true; } } return false; } private static List GetEligibleSources(BoatPart part) { List list = new List(); BoatPartOption[] array = part.partOptions.ToArray(); for (int i = 0; i < array.Length; i++) { if (ShouldCloneSource(array[i])) { list.Add(array[i]); } } return list; } private static bool ShouldCloneSource(BoatPartOption option) { return IsEligibleSource(option); } private static bool IsEligibleSource(BoatPartOption option) { if ((Object)(object)option == (Object)null || (Object)(object)((Component)option).GetComponent() == (Object)null || (Object)(object)((Component)option).GetComponent() != (Object)null) { return false; } Mast component = ((Component)option).GetComponent(); string text = UnstayedNameRules.Normalize(option.optionName + " " + ((Object)option).name + " " + GetHierarchyPath(((Component)option).transform)); if (text.Contains("bermuda") || text.Contains("bowsprit") || text.Contains("forestay") || text.Contains("midstay") || text.Contains("front stay") || text.Contains("back stay") || text.Contains("mast stay") || text.Contains("sprit mast") || text.StartsWith("unstayed ", StringComparison.Ordinal) || component.onlyStaysails) { return false; } return true; } private static string GetHierarchyPath(Transform transform, Transform stopAt = null) { if ((Object)(object)transform == (Object)null) { return string.Empty; } List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)stopAt) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list.ToArray()); } private static string GetRelativePath(Transform transform, Transform root) { if ((Object)(object)transform == (Object)(object)root) { return string.Empty; } List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(((Object)val).name); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return null; } list.Reverse(); return string.Join("/", list.ToArray()); } private static bool IsWithin(Transform transform, Transform root) { if ((Object)(object)transform == (Object)null || (Object)(object)root == (Object)null) { return false; } if (!((Object)(object)transform == (Object)(object)root)) { return transform.IsChildOf(root); } return true; } private static bool Contains(List options, BoatPartOption value) { return options?.Contains(value) ?? false; } private static void AddUnique(List list, T value) where T : class { if (value != null && !list.Contains(value)) { list.Add(value); } } private static BoatPartOption CloneUnstayedMast(BoatPartOption sourceOption, int sceneIndex, int mastIndex, UnstayedMastSourceIdentity identity, List restrictedSelections) { Mast component = ((Component)sourceOption).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.walkColMast == (Object)null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Skipped " + sourceOption.optionName + ": source mast or walk collider is missing.")); } return null; } string text = (string.IsNullOrWhiteSpace(sourceOption.optionName) ? ((Object)sourceOption).name : sourceOption.optionName); Transform val = null; Transform val2 = null; try { bool activeSelf = ((Component)sourceOption).gameObject.activeSelf; try { ((Component)sourceOption).gameObject.SetActive(false); val = Object.Instantiate(((Component)sourceOption).transform, ((Component)sourceOption).transform.parent); ((Component)val).gameObject.SetActive(false); val2 = Object.Instantiate(component.walkColMast, component.walkColMast.parent); ((Component)val2).gameObject.SetActive(false); } finally { ((Component)sourceOption).gameObject.SetActive(activeSelf); } InitializeCloneTransforms(sourceOption, component, val, val2); Mast component2 = ((Component)val).GetComponent(); BoatPartOption component3 = ((Component)val).GetComponent(); if ((Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null) { throw new InvalidOperationException("cloned components are missing"); } InitializeClonedMast(component2, val2, sourceOption, mastIndex); InitializeClonedOption(component3, val2, sourceOption, text, sceneIndex); DisableClonedRiggingVisuals(sourceOption, component.walkColMast, component3, val2, restrictedSelections); PruneHierarchy(val, sceneIndex); PruneHierarchy(val2, sceneIndex); Dictionary visualColliderMap = ExtractScaledRootBodyAndPlaceFittings(val, "UJSM_scaled_mast_body", forceTriggerColliders: true, isWalkBody: false); Dictionary walkColliderMap = ExtractScaledRootBodyAndPlaceFittings(val2, "UJSM_scaled_walk_body", forceTriggerColliders: false, isWalkBody: true); component2.mastCols = CloneMastColliders(component, component2, sceneIndex, visualColliderMap, walkColliderMap); component2.walkColMast = val2; component3.walkColObject = ((Component)val2).gameObject; UnstayedMastMarker unstayedMastMarker = ((Component)val).gameObject.AddComponent(); unstayedMastMarker.Identity = identity; unstayedMastMarker.DiameterScale = 1.41f; return component3; } catch (Exception ex) { if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val).gameObject); } if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val2).gameObject); } ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)("Skipped " + text + ": " + ex)); } return null; } } private static void InitializeCloneTransforms(BoatPartOption sourceOption, Mast sourceMast, Transform cloneTransform, Transform cloneWalk) { //IL_0038: 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_005a: 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_007c: 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) ((Object)cloneTransform).name = "Unstayed_" + ((Object)sourceOption).name; ((Object)cloneWalk).name = "Unstayed_" + ((Object)sourceMast.walkColMast).name; cloneTransform.localPosition = ((Component)sourceOption).transform.localPosition; cloneTransform.localEulerAngles = ((Component)sourceOption).transform.localEulerAngles; cloneTransform.localScale = ((Component)sourceOption).transform.localScale; cloneWalk.localPosition = sourceMast.walkColMast.localPosition; cloneWalk.localEulerAngles = sourceMast.walkColMast.localEulerAngles; cloneWalk.localScale = sourceMast.walkColMast.localScale; } private static void InitializeClonedMast(Mast cloneMast, Transform cloneWalk, BoatPartOption sourceOption, int mastIndex) { cloneMast.orderIndex = mastIndex; cloneMast.walkColMast = cloneWalk; cloneMast.shipRigidbody = ((Component)sourceOption).GetComponentInParent(); cloneMast.startSailPrefab = null; cloneMast.startSailPrefabs = (GameObject[])(object)new GameObject[0]; cloneMast.startSailsHeightOffsets = new float[0]; } private static void InitializeClonedOption(BoatPartOption cloneOption, Transform cloneWalk, BoatPartOption sourceOption, string sourceDisplayName, int sceneIndex) { cloneOption.optionName = "Unstayed " + sourceDisplayName; cloneOption.mass = sourceOption.mass * 2; cloneOption.walkColObject = ((Component)cloneWalk).gameObject; cloneOption.requires = cloneOption.requires ?? new List(); cloneOption.requiresDisabled = cloneOption.requiresDisabled ?? new List(); cloneOption.childOptions = FilterChildOptions(cloneOption.childOptions, sceneIndex); } private static void DisableClonedRiggingVisuals(BoatPartOption sourceOption, Transform sourceWalk, BoatPartOption cloneOption, Transform cloneWalk, List selections) { HashSet hashSet = new HashSet(); for (int i = 0; i < selections.Count; i++) { RestrictedPartSelection restrictedPartSelection = selections[i]; if (restrictedPartSelection.Kind != RestrictedPartKind.Rigging && restrictedPartSelection.Kind != RestrictedPartKind.RiggingAccessory) { continue; } for (int j = 0; j < restrictedPartSelection.NonEmptyOptions.Count; j++) { BoatPartOption val = restrictedPartSelection.NonEmptyOptions[j]; if ((Object)(object)val == (Object)null || val.childOptions == null) { continue; } for (int k = 0; k < val.childOptions.Length; k++) { GameObject val2 = val.childOptions[k]; if (!((Object)(object)val2 == (Object)null)) { AddMappedVisual(hashSet, val2.transform, ((Component)sourceOption).transform, ((Component)cloneOption).transform); AddMappedVisual(hashSet, val2.transform, sourceWalk, cloneWalk); } } } } AddOrphanedTelltales(hashSet, ((Component)cloneOption).transform); AddOrphanedTelltales(hashSet, cloneWalk); if (hashSet.Count == 0) { return; } foreach (GameObject item in hashSet) { if ((Object)(object)item != (Object)null) { item.SetActive(false); } } List list = new List(); if (cloneOption.childOptions != null) { for (int l = 0; l < cloneOption.childOptions.Length; l++) { GameObject val3 = cloneOption.childOptions[l]; if ((Object)(object)val3 != (Object)null && !hashSet.Contains(val3)) { list.Add(val3); } } } cloneOption.childOptions = list.ToArray(); } private static void AddMappedVisual(HashSet result, Transform sourceVisual, Transform sourceRoot, Transform cloneRoot) { Transform val = MapToClone(sourceVisual, sourceRoot, cloneRoot); if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)cloneRoot) { result.Add(((Component)val).gameObject); } } private static void AddOrphanedTelltales(HashSet result, Transform mastRoot) { if ((Object)(object)mastRoot == (Object)null) { return; } Transform[] componentsInChildren = ((Component)mastRoot).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)(object)mastRoot)) { string text = UnstayedNameRules.Normalize(((Object)val).name); bool flag = text.Contains("telltale"); bool flag2 = text == "wind flag" && (Object)(object)val.parent == (Object)(object)mastRoot; if (flag || flag2) { result.Add(((Component)val).gameObject); } } } } private static Dictionary ExtractScaledRootBodyAndPlaceFittings(Transform root, string bodyName, bool forceTriggerColliders, bool isWalkBody) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0089: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root == (Object)null) { throw new ArgumentNullException("root"); } Vector3 localScale = root.localScale; List hierarchy = CaptureHierarchy(root); List list = CaptureSurfaceFittings(root); GameObject val = new GameObject(bodyName); val.layer = ((Component)root).gameObject.layer; val.tag = ((Component)root).gameObject.tag; val.isStatic = ((Component)root).gameObject.isStatic; UnstayedScaledBodyMarker unstayedScaledBodyMarker = val.AddComponent(); unstayedScaledBodyMarker.LogicalRoot = root; unstayedScaledBodyMarker.IsWalkBody = isWalkBody; Transform transform = val.transform; transform.SetParent(root, false); transform.localPosition = Vector3.zero; transform.localRotation = Quaternion.identity; transform.localScale = new Vector3(1.41f, 1.41f, 1f); bool num = CopyRootRenderer(root, val); Dictionary dictionary = CopyRootColliders(root, val, forceTriggerColliders); if (!num && dictionary.Count == 0) { Object.DestroyImmediate((Object)(object)val); throw new InvalidOperationException("root mast body has no renderer or collider components"); } if (list.Count > 0) { PlaceSurfaceFittings(root, list); } ValidateExtractedRootBody(root, transform, localScale, hierarchy, list); return dictionary; } private static List CaptureHierarchy(Transform root) { //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_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_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) List list = new List(); Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)(object)root)) { list.Add(new HierarchyTransformSnapshot { Target = val, Parent = val.parent, LocalPosition = val.localPosition, LocalRotation = val.localRotation, LocalScale = val.localScale }); } } return list; } private static void ValidateExtractedRootBody(Transform root, Transform body, Vector3 sourceRootScale, List hierarchy, List surfaceFittings) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_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_0056: 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_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_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010e: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_013a: 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_014b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = root.localScale - sourceRootScale; if (!(((Vector3)(ref val)).sqrMagnitude > 1E-08f) && !((Object)(object)body.parent != (Object)(object)root)) { val = body.localPosition; if (!(((Vector3)(ref val)).sqrMagnitude > 1E-08f) && !(Quaternion.Angle(body.localRotation, Quaternion.identity) > 0.001f)) { val = body.localScale - new Vector3(1.41f, 1.41f, 1f); if (!(((Vector3)(ref val)).sqrMagnitude > 1E-08f)) { HashSet hashSet = new HashSet(); for (int i = 0; i < surfaceFittings.Count; i++) { hashSet.Add(surfaceFittings[i].Target); } for (int j = 0; j < hierarchy.Count; j++) { HierarchyTransformSnapshot hierarchyTransformSnapshot = hierarchy[j]; Transform target = hierarchyTransformSnapshot.Target; if (!((Object)(object)target == (Object)null) && !((Object)(object)target.parent != (Object)(object)hierarchyTransformSnapshot.Parent) && !(Quaternion.Angle(target.localRotation, hierarchyTransformSnapshot.LocalRotation) > 0.001f)) { val = target.localScale - hierarchyTransformSnapshot.LocalScale; if (!(((Vector3)(ref val)).sqrMagnitude > 1E-08f)) { if (hashSet.Contains(target)) { continue; } val = target.localPosition - hierarchyTransformSnapshot.LocalPosition; if (!(((Vector3)(ref val)).sqrMagnitude > 1E-08f)) { continue; } } } throw new InvalidOperationException("mast child hierarchy changed unexpectedly at " + (((Object)(object)target != (Object)null) ? ((Object)target).name : "destroyed child")); } return; } } } throw new InvalidOperationException("scaled mast body hierarchy is invalid"); } private static bool CopyRootRenderer(Transform sourceRoot, GameObject target) { //IL_008b: 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_00af: 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) MeshRenderer component = ((Component)sourceRoot).GetComponent(); MeshFilter component2 = ((Component)sourceRoot).GetComponent(); Renderer component3 = ((Component)sourceRoot).GetComponent(); if ((Object)(object)component == (Object)null) { if ((Object)(object)component3 != (Object)null) { throw new InvalidOperationException("unsupported root renderer type " + ((object)component3).GetType().Name); } return false; } if ((Object)(object)component2 == (Object)null || (Object)(object)component2.sharedMesh == (Object)null) { throw new InvalidOperationException("root MeshRenderer has no source mesh"); } target.AddComponent().sharedMesh = component2.sharedMesh; MeshRenderer obj = target.AddComponent(); ((Renderer)obj).sharedMaterials = ((Renderer)component).sharedMaterials; ((Renderer)obj).shadowCastingMode = ((Renderer)component).shadowCastingMode; ((Renderer)obj).receiveShadows = ((Renderer)component).receiveShadows; ((Renderer)obj).lightProbeUsage = ((Renderer)component).lightProbeUsage; ((Renderer)obj).reflectionProbeUsage = ((Renderer)component).reflectionProbeUsage; ((Renderer)obj).probeAnchor = ((Renderer)component).probeAnchor; ((Renderer)obj).motionVectorGenerationMode = ((Renderer)component).motionVectorGenerationMode; ((Renderer)obj).allowOcclusionWhenDynamic = ((Renderer)component).allowOcclusionWhenDynamic; ((Renderer)obj).sortingLayerID = ((Renderer)component).sortingLayerID; ((Renderer)obj).sortingOrder = ((Renderer)component).sortingOrder; ((Renderer)obj).enabled = ((Renderer)component).enabled; ((Renderer)component).enabled = false; return true; } private static Dictionary CopyRootColliders(Transform sourceRoot, GameObject target, bool forceTrigger) { Dictionary dictionary = new Dictionary(); Collider[] components = ((Component)sourceRoot).GetComponents(); foreach (Collider val in components) { Collider val2 = CopyCollider(val, target); val2.sharedMaterial = val.sharedMaterial; val2.isTrigger = forceTrigger || val.isTrigger; val2.contactOffset = val.contactOffset; val2.enabled = val.enabled; val.enabled = false; dictionary[val] = val2; } return dictionary; } private static Collider CopyCollider(Collider source, GameObject target) { //IL_0018: 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_009a: 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_00c9: Unknown result type (might be due to invalid IL or missing references) CapsuleCollider val = (CapsuleCollider)(object)((source is CapsuleCollider) ? source : null); if ((Object)(object)val != (Object)null) { CapsuleCollider obj = target.AddComponent(); obj.center = val.center; obj.radius = val.radius; obj.height = val.height; obj.direction = val.direction; return (Collider)(object)obj; } MeshCollider val2 = (MeshCollider)(object)((source is MeshCollider) ? source : null); if ((Object)(object)val2 != (Object)null) { MeshCollider obj2 = target.AddComponent(); obj2.sharedMesh = val2.sharedMesh; obj2.convex = val2.convex; obj2.cookingOptions = val2.cookingOptions; return (Collider)(object)obj2; } BoxCollider val3 = (BoxCollider)(object)((source is BoxCollider) ? source : null); if ((Object)(object)val3 != (Object)null) { BoxCollider obj3 = target.AddComponent(); obj3.center = val3.center; obj3.size = val3.size; return (Collider)(object)obj3; } SphereCollider val4 = (SphereCollider)(object)((source is SphereCollider) ? source : null); if ((Object)(object)val4 != (Object)null) { SphereCollider obj4 = target.AddComponent(); obj4.center = val4.center; obj4.radius = val4.radius; return (Collider)(object)obj4; } throw new InvalidOperationException("unsupported root collider type " + ((object)source).GetType().Name); } private static GameObject[] FilterChildOptions(GameObject[] childOptions, int sceneIndex) { if (childOptions == null || childOptions.Length == 0) { return (GameObject[])(object)new GameObject[0]; } List list = new List(); foreach (GameObject val in childOptions) { if ((Object)(object)val != (Object)null && !ShouldRemove(val.transform, sceneIndex)) { list.Add(val); } } return list.ToArray(); } private static void PruneHierarchy(Transform root, int sceneIndex) { Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); for (int num = componentsInChildren.Length - 1; num >= 0; num--) { Transform val = componentsInChildren[num]; if ((Object)(object)val != (Object)(object)root && ShouldRemove(val, sceneIndex)) { Object.DestroyImmediate((Object)(object)((Component)val).gameObject); } } } private static bool ShouldRemove(Transform transform, int sceneIndex) { if ((Object)(object)transform == (Object)null) { return false; } if (sceneIndex == 90 && SmallJunkRemovedNames.Contains(((Object)transform).name)) { return true; } string text = UnstayedNameRules.Normalize(((Object)transform).name + " " + GetHierarchyPath(transform)); if (!text.Contains("static rig") && !text.Contains("static rope atts") && !text.Contains("static rope attachment") && !text.Contains("shroud") && !text.Contains("crowsnest") && !text.Contains("crows nest") && !text.Contains("crownest")) { return text.Contains("crow nest"); } return true; } private static CapsuleCollider[] CloneMastColliders(Mast source, Mast clone, int sceneIndex, Dictionary visualColliderMap, Dictionary walkColliderMap) { if (source.mastCols == null) { return (CapsuleCollider[])(object)new CapsuleCollider[0]; } List list = new List(); for (int i = 0; i < source.mastCols.Length; i++) { CapsuleCollider val = source.mastCols[i]; if (!((Object)(object)val == (Object)null) && !ShouldRemove(((Component)val).transform, sceneIndex)) { CapsuleCollider val2 = MapCollider(val, ((Component)source).transform, ((Component)clone).transform); if ((Object)(object)val2 == (Object)null && (Object)(object)source.walkColMast != (Object)null && (Object)(object)clone.walkColMast != (Object)null) { val2 = MapCollider(val, source.walkColMast, clone.walkColMast); } val2 = ResolveScaledCapsule(val2, visualColliderMap, walkColliderMap); if ((Object)(object)val2 != (Object)null) { AddUnique(list, val2); } else if (!IsWithin(((Component)val).transform, ((Component)source).transform) && !IsWithin(((Component)val).transform, source.walkColMast)) { AddUnique(list, val); } } } return list.ToArray(); } private static CapsuleCollider ResolveScaledCapsule(CapsuleCollider mapped, Dictionary visualColliderMap, Dictionary walkColliderMap) { if ((Object)(object)mapped == (Object)null) { return null; } if (visualColliderMap != null && visualColliderMap.TryGetValue((Collider)(object)mapped, out var value)) { return (CapsuleCollider)(object)((value is CapsuleCollider) ? value : null); } if (walkColliderMap != null && walkColliderMap.TryGetValue((Collider)(object)mapped, out value)) { return (CapsuleCollider)(object)((value is CapsuleCollider) ? value : null); } return mapped; } private static CapsuleCollider MapCollider(CapsuleCollider sourceCollider, Transform sourceRoot, Transform cloneRoot) { Transform val = MapToClone(((Component)sourceCollider).transform, sourceRoot, cloneRoot); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).GetComponent(); } private static Transform MapToClone(Transform sourceTransform, Transform sourceRoot, Transform cloneRoot) { if (!IsWithin(sourceTransform, sourceRoot)) { return null; } string relativePath = GetRelativePath(sourceTransform, sourceRoot); if (!string.IsNullOrEmpty(relativePath)) { return cloneRoot.Find(relativePath); } return cloneRoot; } private static List CaptureSurfaceFittings(Transform mastRoot) { //IL_0075: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) List result = new List(); HashSet hashSet = new HashSet(); List list = new List(); Transform[] componentsInChildren = ((Component)mastRoot).GetComponentsInChildren(true); CapsuleCollider component = ((Component)mastRoot).GetComponent(); MeshFilter component2 = ((Component)mastRoot).GetComponent(); Mesh val = (((Object)(object)component2 != (Object)null) ? component2.sharedMesh : null); Vector3[] mastVertices = (((Object)(object)val != (Object)null && val.isReadable) ? val.vertices : null); Bounds mastBounds = (Bounds)(((Object)(object)val != (Object)null) ? val.bounds : default(Bounds)); Transform[] array = componentsInChildren; foreach (Transform val2 in array) { if (!((Object)(object)val2 == (Object)(object)mastRoot) && ((Object)(object)((Component)val2).GetComponent() != (Object)null || (IsNamedSurfaceFitting(val2) && HasOwnVisibleGeometry(val2)))) { list.Add(val2); } } list.Sort((Transform first, Transform second) => GetHierarchyDepth(first).CompareTo(GetHierarchyDepth(second))); for (int num = 0; num < list.Count; num++) { Transform candidate = list[num]; if (!HasSelectedAncestor(candidate, hashSet)) { AddSurfaceFitting(result, hashSet, candidate, mastRoot, component, mastVertices, mastBounds); } } return result; } private static void AddSurfaceFitting(List result, HashSet added, Transform candidate, Transform mastRoot, CapsuleCollider mastCapsule, Vector3[] mastVertices, Bounds mastBounds) { //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_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_0042: 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_0050: 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_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_006e: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (!TryGetVisibleCenterInRoot(candidate, mastRoot, out var center)) { center = mastRoot.InverseTransformPoint(candidate.position); } if (TryResolveMastSection(center, mastCapsule, mastVertices, mastBounds, out var center2, out var radial, out var radius)) { added.Add(candidate); result.Add(new SurfaceFittingPlacement { Target = candidate, RootRadial = radial, SourceSurfaceRadius = radius, SourceSurfaceRootPoint = new Vector2(center2.x + radial.x * radius, center2.y + radial.y * radius), OriginalWorldPosition = candidate.position }); } } private static bool TryResolveMastSection(Vector3 fittingCenter, CapsuleCollider mastCapsule, Vector3[] mastVertices, Bounds mastBounds, out Vector2 center, out Vector2 radial, out float radius) { //IL_0090: 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_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_0022: 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_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_0043: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_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) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mastCapsule != (Object)null && mastCapsule.direction == 2 && mastCapsule.radius > 0f) { center = new Vector2(mastCapsule.center.x, mastCapsule.center.y); radial = new Vector2(fittingCenter.x - center.x, fittingCenter.y - center.y); if (!NormalizeAndValidateFittingRadial(ref radial)) { radius = 0f; return false; } radius = mastCapsule.radius; return true; } if (mastVertices == null || mastVertices.Length == 0) { center = Vector2.zero; radial = Vector2.zero; radius = 0f; return false; } float num = float.PositiveInfinity; for (int i = 0; i < mastVertices.Length; i++) { num = Mathf.Min(num, Mathf.Abs(mastVertices[i].z - fittingCenter.z)); } float num2 = Mathf.Max(0.01f, ((Bounds)(ref mastBounds)).size.z * 0.0025f); float num3 = num + num2; float num4 = float.PositiveInfinity; float num5 = float.NegativeInfinity; float num6 = float.PositiveInfinity; float num7 = float.NegativeInfinity; int num8 = 0; foreach (Vector3 val in mastVertices) { if (!(Mathf.Abs(val.z - fittingCenter.z) > num3)) { num4 = Mathf.Min(num4, val.x); num5 = Mathf.Max(num5, val.x); num6 = Mathf.Min(num6, val.y); num7 = Mathf.Max(num7, val.y); num8++; } } if (num8 < 3) { center = Vector2.zero; radial = Vector2.zero; radius = 0f; return false; } center = new Vector2((num4 + num5) * 0.5f, (num6 + num7) * 0.5f); radial = new Vector2(fittingCenter.x - center.x, fittingCenter.y - center.y); if (!NormalizeAndValidateFittingRadial(ref radial)) { radius = 0f; return false; } radius = 0f; foreach (Vector3 val2 in mastVertices) { if (!(Mathf.Abs(val2.z - fittingCenter.z) > num3)) { float num9 = Vector2.Dot(new Vector2(val2.x, val2.y) - center, radial); radius = Mathf.Max(radius, num9); } } return radius > 0.001f; } internal static bool TryGetSailMountOffset(Mast mast, Sail sail, float diameterScale, out Vector3 localOffset) { //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_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_004d: 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_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_0062: 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_006f: 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_00f5: 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_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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_0132: 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_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014b: 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) //IL_015e: 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) localOffset = Vector3.zero; if ((Object)(object)mast == (Object)null || (Object)(object)sail == (Object)null || (Object)(object)sail.windcenter == (Object)null || diameterScale <= 1f) { return false; } Transform transform = ((Component)mast).transform; Vector3 val = transform.InverseTransformPoint(sail.windcenter.position); Vector3 val2 = transform.InverseTransformPoint(((Component)sail).transform.position); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(val.x - val2.x, val.y - val2.y); if (((Vector2)(ref val3)).sqrMagnitude <= 1E-06f) { return false; } ((Vector2)(ref val3)).Normalize(); CapsuleCollider component = ((Component)transform).GetComponent(); MeshFilter component2 = ((Component)transform).GetComponent(); Mesh val4 = (((Object)(object)component2 != (Object)null) ? component2.sharedMesh : null); Vector3[] mastVertices = (((Object)(object)val4 != (Object)null && val4.isReadable) ? val4.vertices : null); Bounds mastBounds = (Bounds)(((Object)(object)val4 != (Object)null) ? val4.bounds : default(Bounds)); if (!TryResolveMastSurfaceAtHeight(((Component)sail).transform.localPosition.z, val3, component, mastVertices, mastBounds, out var center, out var radius)) { return false; } Vector2 sourceSurfaceRootPoint = center + val3 * radius; if (!TryGetSurfaceCorrectionWorld(transform, sourceSurfaceRootPoint, diameterScale, out var correctionWorld)) { return false; } Vector3 val5 = transform.InverseTransformVector(correctionWorld); localOffset = new Vector3(val5.x, val5.y, 0f); return ((Vector3)(ref localOffset)).sqrMagnitude > 1E-06f; } private static bool TryResolveMastSurfaceAtHeight(float height, Vector2 radial, CapsuleCollider mastCapsule, Vector3[] mastVertices, Bounds mastBounds, out Vector2 center, out float radius) { //IL_0055: 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_0022: 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_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_00a3: 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_00ea: 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_010b: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mastCapsule != (Object)null && mastCapsule.direction == 2 && mastCapsule.radius > 0f) { center = new Vector2(mastCapsule.center.x, mastCapsule.center.y); radius = mastCapsule.radius; return true; } if (mastVertices == null || mastVertices.Length == 0) { center = Vector2.zero; radius = 0f; return false; } float num = float.PositiveInfinity; for (int i = 0; i < mastVertices.Length; i++) { num = Mathf.Min(num, Mathf.Abs(mastVertices[i].z - height)); } float num2 = Mathf.Max(0.01f, ((Bounds)(ref mastBounds)).size.z * 0.0025f); float num3 = num + num2; float num4 = float.PositiveInfinity; float num5 = float.NegativeInfinity; float num6 = float.PositiveInfinity; float num7 = float.NegativeInfinity; int num8 = 0; foreach (Vector3 val in mastVertices) { if (!(Mathf.Abs(val.z - height) > num3)) { num4 = Mathf.Min(num4, val.x); num5 = Mathf.Max(num5, val.x); num6 = Mathf.Min(num6, val.y); num7 = Mathf.Max(num7, val.y); num8++; } } if (num8 < 3) { center = Vector2.zero; radius = 0f; return false; } center = new Vector2((num4 + num5) * 0.5f, (num6 + num7) * 0.5f); radius = 0f; foreach (Vector3 val2 in mastVertices) { if (!(Mathf.Abs(val2.z - height) > num3)) { float num9 = Vector2.Dot(new Vector2(val2.x, val2.y) - center, radial); radius = Mathf.Max(radius, num9); } } return radius > 0.001f; } private static bool NormalizeAndValidateFittingRadial(ref Vector2 radial) { //IL_001b: 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_0026: Unknown result type (might be due to invalid IL or missing references) float magnitude = ((Vector2)(ref radial)).magnitude; if (magnitude <= 0.001f || magnitude > 1f) { return false; } radial /= magnitude; return true; } private static bool IsNamedSurfaceFitting(Transform candidate) { string text = UnstayedNameRules.Normalize(((Object)(object)candidate != (Object)null) ? ((Object)candidate).name : null); if (!text.Contains("rope holder") && !text.Contains("rope att") && !text.Contains("reef att") && !text.Contains("winch") && !text.Contains("windcloth")) { return text.Contains("flag"); } return true; } private static bool HasOwnVisibleGeometry(Transform candidate) { if (!((Object)(object)((Component)candidate).GetComponent() != (Object)null) && !((Object)(object)((Component)candidate).GetComponent() != (Object)null)) { return (Object)(object)((Component)candidate).GetComponent() != (Object)null; } return true; } private static bool HasSelectedAncestor(Transform candidate, HashSet selected) { Transform parent = candidate.parent; while ((Object)(object)parent != (Object)null) { if (selected.Contains(parent)) { return true; } parent = parent.parent; } return false; } private static int GetHierarchyDepth(Transform candidate) { int num = 0; Transform val = candidate; while ((Object)(object)val != (Object)null) { num++; val = val.parent; } return num; } private unsafe static void PlaceSurfaceFittings(Transform mastRoot, List fittings) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_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) for (int i = 0; i < fittings.Count; i++) { SurfaceFittingPlacement surfaceFittingPlacement = fittings[i]; if ((Object)(object)surfaceFittingPlacement.Target == (Object)null) { continue; } if (!TryGetSurfaceCorrectionWorld(mastRoot, surfaceFittingPlacement.SourceSurfaceRootPoint, 1.41f, out var correctionWorld)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { string[] array = new string[11] { "Skipped implausible mast-fitting correction for ", ((Object)surfaceFittingPlacement.Target).name, " on ", ((Object)mastRoot).name, ": sourceRadius=", surfaceFittingPlacement.SourceSurfaceRadius.ToString("0.###"), ", sourcePoint=", null, null, null, null }; Vector2 sourceSurfaceRootPoint = surfaceFittingPlacement.SourceSurfaceRootPoint; array[7] = ((object)(*(Vector2*)(&sourceSurfaceRootPoint))/*cast due to .constrained prefix*/).ToString(); array[8] = ", radial="; sourceSurfaceRootPoint = surfaceFittingPlacement.RootRadial; array[9] = ((object)(*(Vector2*)(&sourceSurfaceRootPoint))/*cast due to .constrained prefix*/).ToString(); array[10] = "."; logSource.LogWarning((object)string.Concat(array)); } } else { surfaceFittingPlacement.Target.position = surfaceFittingPlacement.OriginalWorldPosition + correctionWorld; ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogDebug((object)("Placed mast fitting " + ((Object)surfaceFittingPlacement.Target).name + " on the enlarged surface of " + ((Object)mastRoot).name + " (correction " + ((Vector3)(ref correctionWorld)).magnitude.ToString("0.###") + ").")); } } } } private static bool TryGetSurfaceCorrectionWorld(Transform mastRoot, Vector2 sourceSurfaceRootPoint, float diameterScale, out Vector3 correctionWorld) { //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_002e: 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_0056: 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_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) correctionWorld = Vector3.zero; if ((Object)(object)mastRoot == (Object)null || diameterScale <= 1f || ((Vector2)(ref sourceSurfaceRootPoint)).sqrMagnitude <= 1E-06f) { return false; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(sourceSurfaceRootPoint.x * (diameterScale - 1f), sourceSurfaceRootPoint.y * (diameterScale - 1f), 0f); correctionWorld = mastRoot.TransformVector(val); if (((Vector3)(ref correctionWorld)).magnitude > 1f) { correctionWorld = Vector3.zero; return false; } return true; } private static bool TryGetVisibleCenterInRoot(Transform fitting, Transform mastRoot, out Vector3 center) { //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_002a: 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_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_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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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) Vector3 val = Vector3.zero; int num = 0; MeshFilter[] componentsInChildren = ((Component)fitting).GetComponentsInChildren(true); Bounds val4; foreach (MeshFilter val2 in componentsInChildren) { if (!((Object)(object)val2.sharedMesh == (Object)null)) { Vector3 val3 = val; Transform transform = ((Component)val2).transform; val4 = val2.sharedMesh.bounds; val = val3 + mastRoot.InverseTransformPoint(transform.TransformPoint(((Bounds)(ref val4)).center)); num++; } } SkinnedMeshRenderer[] componentsInChildren2 = ((Component)fitting).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren2.Length; j++) { Vector3 val5 = val; Transform transform2 = ((Component)componentsInChildren2[j]).transform; val4 = componentsInChildren2[j].localBounds; val = val5 + mastRoot.InverseTransformPoint(transform2.TransformPoint(((Bounds)(ref val4)).center)); num++; } if (num == 0) { center = Vector3.zero; return false; } center = val / (float)num; return true; } private static List FindRestrictedSelections(Transform boat, BoatCustomParts parts, List mastParts, Dictionary> sourcesByMastPart) { List list = new List(); for (int i = 0; i < parts.availableParts.Count; i++) { BoatPart val = parts.availableParts[i]; if (val == null || mastParts.Contains(val) || val.partOptions == null || val.partOptions.Count == 0 || !TryGetRestrictedKind(val, boat, out var kind)) { continue; } List list2 = FindOwningMastParts(val, mastParts, sourcesByMastPart); if (list2.Count != 0) { RestrictedPartSelection restrictedPartSelection = CreateRestrictedSelection(val, list2, kind, boat); if (restrictedPartSelection != null) { list.Add(restrictedPartSelection); } } } return list; } private static List GetRestrictedSelectionsForMast(List selections, BoatPart mastPart) { List list = new List(); for (int i = 0; i < selections.Count; i++) { if (selections[i].OwningMastParts.Contains(mastPart)) { list.Add(selections[i]); } } return list; } private static bool TryGetRestrictedKind(BoatPart part, Transform boat, out RestrictedPartKind kind) { bool flag = false; bool flag2 = false; bool flag3 = false; for (int i = 0; i < part.partOptions.Count; i++) { string directOptionSignalText = GetDirectOptionSignalText(part.partOptions[i], boat); flag |= HasRiggingText(directOptionSignalText); flag2 |= HasRiggingAccessoryText(directOptionSignalText); flag3 |= HasCrowsNestText(directOptionSignalText); } if (flag && !flag3) { kind = RestrictedPartKind.Rigging; return true; } if (flag3 && !flag) { kind = RestrictedPartKind.CrowsNest; return true; } if (flag2 && !flag && !flag3) { kind = RestrictedPartKind.RiggingAccessory; return true; } bool flag4 = false; bool flag5 = false; bool flag6 = false; for (int j = 0; j < part.partOptions.Count; j++) { string optionSignalText = GetOptionSignalText(part.partOptions[j], boat); flag4 |= HasRiggingText(optionSignalText); flag5 |= HasRiggingAccessoryText(optionSignalText); flag6 |= HasCrowsNestText(optionSignalText); } if (flag4 && !flag6) { kind = RestrictedPartKind.Rigging; return true; } if (flag6 && !flag4) { kind = RestrictedPartKind.CrowsNest; return true; } if (flag5 && !flag4 && !flag6) { kind = RestrictedPartKind.RiggingAccessory; return true; } kind = RestrictedPartKind.Rigging; return false; } private static List FindOwningMastParts(BoatPart candidatePart, List mastParts, Dictionary> sourcesByMastPart) { List list = new List(); int num = 0; for (int i = 0; i < mastParts.Count; i++) { BoatPart val = mastParts[i]; int partRelationScore = GetPartRelationScore(candidatePart, sourcesByMastPart[val]); if (partRelationScore > num) { num = partRelationScore; list.Clear(); list.Add(val); } else if (partRelationScore > 0 && partRelationScore == num) { list.Add(val); } } if (num == 0) { list.Clear(); } else if (list.Count > 1 && num < 80) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Skipped a weak multi-mast Shipyard relationship (" + GetPartLabel(candidatePart) + ").")); } list.Clear(); } return list; } private static int GetPartRelationScore(BoatPart candidatePart, List sources) { int num = 0; for (int i = 0; i < candidatePart.partOptions.Count; i++) { BoatPartOption candidate = candidatePart.partOptions[i]; for (int j = 0; j < sources.Count; j++) { num = Math.Max(num, GetOptionRelationScore(candidate, sources[j])); } } return num; } private static int GetOptionRelationScore(BoatPartOption candidate, BoatPartOption mastOption) { if ((Object)(object)candidate == (Object)null || (Object)(object)mastOption == (Object)null) { return 0; } if (Contains(candidate.requires, mastOption) || Contains(mastOption.requires, candidate) || (Object)(object)candidate.childMast == (Object)(object)((Component)mastOption).GetComponent()) { return 100; } if (Contains(candidate.requiresDisabled, mastOption) || Contains(mastOption.requiresDisabled, candidate)) { return 90; } if (AnyChildWithin(candidate.childOptions, ((Component)mastOption).transform, ((Object)(object)mastOption.walkColObject != (Object)null) ? mastOption.walkColObject.transform : null)) { return 80; } if (AnyChildWithin(mastOption.childOptions, ((Component)candidate).transform, ((Object)(object)candidate.walkColObject != (Object)null) ? candidate.walkColObject.transform : null)) { return 70; } return 0; } private static bool AnyChildWithin(GameObject[] children, Transform firstRoot, Transform secondRoot) { if (children == null) { return false; } for (int i = 0; i < children.Length; i++) { Transform transform = (((Object)(object)children[i] != (Object)null) ? children[i].transform : null); if (IsWithin(transform, firstRoot) || IsWithin(transform, secondRoot)) { return true; } } return false; } private static string GetPartLabel(BoatPart part) { if (part == null || part.partOptions == null || part.partOptions.Count == 0 || (Object)(object)part.partOptions[0] == (Object)null) { return "unnamed"; } BoatPartOption val = part.partOptions[0]; if (!string.IsNullOrWhiteSpace(val.optionName)) { return val.optionName; } return ((Object)val).name; } private static RestrictedPartSelection CreateRestrictedSelection(BoatPart part, List owningMastParts, RestrictedPartKind kind, Transform boat) { BoatPartOption val = null; List list = new List(); for (int i = 0; i < part.partOptions.Count; i++) { BoatPartOption val2 = part.partOptions[i]; if ((Object)(object)val2 == (Object)null) { continue; } if (IsEmptyOption(val2, kind)) { if ((Object)(object)val == (Object)null) { val = val2; } } else { list.Add(val2); } } if (list.Count == 0) { return null; } if ((Object)(object)val == (Object)null && kind == RestrictedPartKind.Rigging) { val = CreateNoShroudsOption(part, boat); } return new RestrictedPartSelection { Part = part, OwningMastParts = new List(owningMastParts), EmptyOption = val, NonEmptyOptions = list, Kind = kind }; } private static bool IsEmptyOption(BoatPartOption option, RestrictedPartKind kind) { if (!((Object)(object)((Component)option).GetComponent() != (Object)null)) { return IsExplicitEmptyName(option, kind); } return true; } private static bool IsExplicitEmptyName(BoatPartOption option, RestrictedPartKind kind) { string text = UnstayedNameRules.Normalize(option.optionName); string text2 = UnstayedNameRules.Normalize(((Object)option).name); string text3 = text + " " + text2; if (text == "none" || text == "empty") { return true; } switch (kind) { case RestrictedPartKind.CrowsNest: if (HasCrowsNestText(text3)) { return UnstayedNameRules.HasAbsenceToken(text3); } return false; case RestrictedPartKind.RiggingAccessory: if (HasRiggingAccessoryText(text3)) { return UnstayedNameRules.HasAbsenceToken(text3); } return false; default: if (HasRiggingText(text3) || UnstayedNameRules.ContainsWord(text3, "rig")) { return UnstayedNameRules.HasAbsenceToken(text3); } return false; } } private static BoatPartOption CreateNoShroudsOption(BoatPart part, Transform boat) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown GameObject val = new GameObject("UJSM_empty_shrouds"); val.SetActive(false); val.transform.SetParent(boat, false); BoatPartOption val2 = val.AddComponent(); GameObject val3 = new GameObject("UJSM_empty_shrouds_walk"); val3.SetActive(false); val3.transform.SetParent(boat, false); val2.optionName = "(no shrouds)"; val2.basePrice = 0; val2.installCost = 0; val2.mass = 0; val2.requires = new List(); val2.requiresDisabled = new List(); val2.walkColObject = val3; val2.canInstall = true; val2.childOptions = (GameObject[])(object)new GameObject[0]; val2.childMast = null; val.AddComponent(); part.partOptions.Add(val2); ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)"Added missing (no shrouds) option to a rigging Shipyard group."); } return val2; } private static void AddMutualRestrictions(BoatPartOption unstayed, List selections) { for (int i = 0; i < selections.Count; i++) { List nonEmptyOptions = selections[i].NonEmptyOptions; for (int j = 0; j < nonEmptyOptions.Count; j++) { AddMutualExclusion(unstayed, nonEmptyOptions[j]); } } } private static void RestrictNoShroudsToUnstayedMasts(List selections) { for (int i = 0; i < selections.Count; i++) { RestrictedPartSelection restrictedPartSelection = selections[i]; if (restrictedPartSelection.Kind != RestrictedPartKind.Rigging || (Object)(object)restrictedPartSelection.EmptyOption == (Object)null) { continue; } for (int j = 0; j < restrictedPartSelection.OwningMastParts.Count; j++) { BoatPart val = restrictedPartSelection.OwningMastParts[j]; for (int k = 0; k < val.partOptions.Count; k++) { BoatPartOption val2 = val.partOptions[k]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).GetComponent() == (Object)null) && !((Object)(object)((Component)val2).GetComponent() != (Object)null)) { AddMutualExclusion(restrictedPartSelection.EmptyOption, val2); } } } } } private static void AddMutualExclusion(BoatPartOption first, BoatPartOption second) { if (!((Object)(object)first == (Object)null) && !((Object)(object)second == (Object)null)) { first.requiresDisabled = first.requiresDisabled ?? new List(); second.requiresDisabled = second.requiresDisabled ?? new List(); AddUnique(first.requiresDisabled, second); AddUnique(second.requiresDisabled, first); } } private static bool HasRiggingText(string text) { if (!text.Contains("shroud") && !text.Contains("static rig") && !text.Contains("static rope atts")) { return text.Contains("static rope attachment"); } return true; } private static bool HasRiggingAccessoryText(string text) { if (!text.Contains("telltale")) { return text.Contains("wind flag"); } return true; } private static bool HasCrowsNestText(string text) { if (!text.Contains("crowsnest") && !text.Contains("crows nest") && !text.Contains("crownest")) { return text.Contains("crow nest"); } return true; } private static string GetDirectOptionSignalText(BoatPartOption option, Transform boat) { if ((Object)(object)option == (Object)null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(option.optionName).Append(' ').Append(((Object)option).name) .Append(' ') .Append(GetHierarchyPath(((Component)option).transform, boat)); if ((Object)(object)option.walkColObject != (Object)null) { stringBuilder.Append(' ').Append(GetHierarchyPath(option.walkColObject.transform, boat)); } return UnstayedNameRules.Normalize(stringBuilder.ToString()); } private static string GetOptionSignalText(BoatPartOption option, Transform boat) { if ((Object)(object)option == (Object)null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(option.optionName).Append(' ').Append(((Object)option).name) .Append(' ') .Append(GetHierarchyPath(((Component)option).transform, boat)); if ((Object)(object)option.walkColObject != (Object)null) { stringBuilder.Append(' ').Append(GetHierarchyPath(option.walkColObject.transform, boat)); } if (option.childOptions != null) { for (int i = 0; i < option.childOptions.Length; i++) { if ((Object)(object)option.childOptions[i] != (Object)null) { stringBuilder.Append(' ').Append(GetHierarchyPath(option.childOptions[i].transform, boat)); } } } return UnstayedNameRules.Normalize(stringBuilder.ToString()); } } internal sealed class UnstayedMastIndexAllocator { private const char RecordSeparator = ';'; private const char FieldSeparator = '='; private readonly int sceneIndex; private readonly HashSet occupiedIndices = new HashSet(); private readonly Dictionary extendedMappings = new Dictionary(StringComparer.Ordinal); private readonly Dictionary extendedOwners = new Dictionary(); private readonly HashSet retiredIndices = new HashSet(); private bool mappingsChanged; internal UnstayedMastIndexAllocator(Transform boat, int sceneIndex) { this.sceneIndex = sceneIndex; CollectOccupiedIndices(boat); LoadMappings(); ValidateMappings(); } internal List GetRetiredIndices() { List list = new List(retiredIndices); list.Sort(); return list; } internal bool TryClaim(BoatPartOption source, UnstayedMastSourceIdentity identity, out int mastIndex, out bool usesFixedVanillaIndex) { if (UnstayedMastIndexRules.TryGetFixedVanillaIndex(sceneIndex, source, out mastIndex)) { usesFixedVanillaIndex = true; if (occupiedIndices.Contains(mastIndex) || extendedOwners.ContainsKey(mastIndex)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Skipped " + source.optionName + ": fixed vanilla mast index " + mastIndex + " is already occupied.")); } return false; } occupiedIndices.Add(mastIndex); return true; } usesFixedVanillaIndex = false; mastIndex = -1; return TryClaimExtended(identity, out mastIndex); } internal bool TryClaimExtended(UnstayedMastSourceIdentity identity, out int mastIndex) { mastIndex = -1; if (identity == null || string.IsNullOrEmpty(identity.StableId)) { return false; } string stableId = identity.StableId; if (extendedMappings.TryGetValue(stableId, out var value) && UnstayedMastIndexRules.IsExtendedIndex(value) && extendedOwners.TryGetValue(value, out var value2) && value2 == stableId) { if (occupiedIndices.Contains(value)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Skipped duplicate Expansion mast source identity " + stableId + ".")); } return false; } occupiedIndices.Add(value); mastIndex = value; return true; } if (TryFindLegacyMapping(identity, out var legacySourceId)) { value = extendedMappings[legacySourceId]; if (occupiedIndices.Contains(value)) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)("Skipped duplicate Expansion mast source identity " + stableId + ".")); } return false; } extendedMappings.Remove(legacySourceId); extendedMappings[stableId] = value; extendedOwners[value] = stableId; occupiedIndices.Add(value); mappingsChanged = true; mastIndex = value; ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)("Migrated an Expansion mast source key to v2 at index " + value + ".")); } return true; } for (int i = 96; i < 128; i++) { if (!occupiedIndices.Contains(i) && !extendedOwners.ContainsKey(i)) { extendedMappings[stableId] = i; extendedOwners[i] = stableId; occupiedIndices.Add(i); mappingsChanged = true; mastIndex = i; return true; } } ManualLogSource logSource4 = Plugin.LogSource; if (logSource4 != null) { logSource4.LogError((object)("No extended mast index remains in the 96-127 range for " + stableId + ".")); } return false; } private bool TryFindLegacyMapping(UnstayedMastSourceIdentity identity, out string legacySourceId) { legacySourceId = null; int num = 0; bool flag = false; foreach (KeyValuePair extendedMapping in extendedMappings) { int persistedMatchScore = identity.GetPersistedMatchScore(extendedMapping.Key); if (persistedMatchScore > 0 && !(extendedMapping.Key == identity.StableId)) { if (persistedMatchScore > num) { num = persistedMatchScore; legacySourceId = extendedMapping.Key; flag = false; } else if (persistedMatchScore == num) { flag = true; } } } if (!flag) { return legacySourceId != null; } ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Did not migrate an ambiguous legacy Expansion mast source key for " + identity.StableId + ".")); } legacySourceId = null; return false; } internal void Commit() { if (mappingsChanged) { if (GameState.modData == null) { GameState.modData = new Dictionary(); } List list = new List(extendedMappings.Keys); list.Sort(StringComparer.Ordinal); List list2 = new List(); for (int i = 0; i < list.Count; i++) { string text = list[i]; list2.Add(Uri.EscapeDataString(text) + "=" + extendedMappings[text]); } GameState.modData[UnstayedMastIndexRules.GetExtendedMappingKey(sceneIndex)] = string.Join(';'.ToString(), list2.ToArray()); } } private void CollectOccupiedIndices(Transform boat) { if ((Object)(object)boat == (Object)null) { return; } Mast[] componentsInChildren = ((Component)boat).GetComponentsInChildren(true); foreach (Mast val in componentsInChildren) { if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent() == (Object)null) { occupiedIndices.Add(val.orderIndex); } } } private void LoadMappings() { if (GameState.modData == null || !GameState.modData.TryGetValue(UnstayedMastIndexRules.GetExtendedMappingKey(sceneIndex), out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { int num = array[i].LastIndexOf('='); if (num <= 0 || !int.TryParse(array[i].Substring(num + 1), out var result)) { mappingsChanged = true; continue; } string text; try { text = Uri.UnescapeDataString(array[i].Substring(0, num)); } catch (UriFormatException) { mappingsChanged = true; continue; } if (string.IsNullOrEmpty(text)) { mappingsChanged = true; } else { extendedMappings[text] = result; } } } private void ValidateMappings() { List list = new List(extendedMappings.Keys); list.Sort(StringComparer.Ordinal); for (int i = 0; i < list.Count; i++) { string text = list[i]; int num = extendedMappings[text]; if (!UnstayedMastIndexRules.IsExtendedIndex(num) || occupiedIndices.Contains(num) || extendedOwners.ContainsKey(num)) { if (UnstayedMastIndexRules.IsExtendedIndex(num)) { retiredIndices.Add(num); } extendedMappings.Remove(text); mappingsChanged = true; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Released conflicting extended mast index " + num + " for source " + text + ".")); } } else { extendedOwners[num] = text; } } } } internal static class UnstayedMastIndexCoordinator { internal static void RebindFromLoadedModData() { List profiles = UnstayedBoatRegistry.GetProfiles(); for (int i = 0; i < profiles.Count; i++) { RebindProfile(profiles[i]); } } private static void RebindProfile(UnstayedBoatProfile profile) { if (profile == null || (Object)(object)profile.Parts == (Object)null || (Object)(object)profile.Refs == (Object)null || profile.Masts == null) { return; } UnstayedMastIndexAllocator unstayedMastIndexAllocator = new UnstayedMastIndexAllocator(((Component)profile.Parts).transform, profile.SceneIndex); Dictionary dictionary = new Dictionary(); for (int i = 0; i < profile.Masts.Count; i++) { UnstayedMastProfile unstayedMastProfile = profile.Masts[i]; if (!unstayedMastProfile.UsesFixedVanillaIndex) { Mast val = (((Object)(object)unstayedMastProfile.UnstayedOption != (Object)null) ? ((Component)unstayedMastProfile.UnstayedOption).GetComponent() : null); UnstayedMastSourceIdentity unstayedMastSourceIdentity = (((Object)(object)unstayedMastProfile.Marker != (Object)null) ? unstayedMastProfile.Marker.Identity : null); if (!((Object)(object)val == (Object)null) && unstayedMastSourceIdentity != null && unstayedMastIndexAllocator.TryClaimExtended(unstayedMastSourceIdentity, out var mastIndex)) { dictionary[val] = mastIndex; } } } foreach (KeyValuePair item in dictionary) { Mast key = item.Key; if (key.orderIndex >= 0 && key.orderIndex < profile.Refs.masts.Length && (Object)(object)profile.Refs.masts[key.orderIndex] == (Object)(object)key) { profile.Refs.masts[key.orderIndex] = null; } } foreach (KeyValuePair item2 in dictionary) { Mast key2 = item2.Key; int value = item2.Value; key2.orderIndex = value; } PrepareRegistryForSaveConversion(profile); unstayedMastIndexAllocator.Commit(); profile.RetiredMastIndices = unstayedMastIndexAllocator.GetRetiredIndices(); if (dictionary.Count > 0) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)("Applied " + dictionary.Count + " persisted extended mast index mapping(s) for scene " + profile.SceneIndex + ".")); } } } private static void PrepareRegistryForSaveConversion(UnstayedBoatProfile profile) { if (profile.Refs.masts == null) { return; } int num = 0; for (int i = 0; i < profile.Masts.Count; i++) { UnstayedMastProfile unstayedMastProfile = profile.Masts[i]; Mast val = (((Object)(object)unstayedMastProfile.UnstayedOption != (Object)null) ? ((Component)unstayedMastProfile.UnstayedOption).GetComponent() : null); if ((Object)(object)val == (Object)null) { continue; } int orderIndex = val.orderIndex; if (orderIndex < 0 || orderIndex >= profile.Refs.masts.Length) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)("Could not prepare unstayed mast registry index " + orderIndex + " for scene " + profile.SceneIndex + ".")); } continue; } Mast val2 = profile.Refs.masts[orderIndex]; if ((Object)(object)val2 == (Object)(object)val) { continue; } if ((Object)(object)val2 != (Object)null) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)("Could not prepare unstayed mast registry index " + orderIndex + " for scene " + profile.SceneIndex + ": the slot is occupied by " + ((Object)((Component)val2).gameObject).name + ".")); } } else { profile.Refs.masts[orderIndex] = val; num++; } } if (num > 0) { ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)("Prepared " + num + " inactive unstayed mast registry entry/entries for save conversion in scene " + profile.SceneIndex + ".")); } } } } internal static class UnstayedMastIndexRules { internal const int ExtendedIndexStart = 96; internal const int MastArrayCapacity = 128; internal static bool TryGetFixedVanillaIndex(int sceneIndex, BoatPartOption source, out int cloneIndex) { cloneIndex = -1; Mast val = (((Object)(object)source != (Object)null) ? ((Component)source).GetComponent() : null); if ((Object)(object)val == (Object)null) { return false; } string name = ((Object)source).name; switch (sceneIndex) { case 90: if (Matches(val, name, 5, "mast")) { cloneIndex = 27; } else if (Matches(val, name, 6, "mast_center")) { cloneIndex = 28; } else if (Matches(val, name, 7, "mast_001")) { cloneIndex = 29; } break; case 80: if (Matches(val, name, 9, "mast_front_")) { cloneIndex = 25; } else if (Matches(val, name, 10, "mast_mid_0")) { cloneIndex = 26; } else if (Matches(val, name, 11, "mast_mid_1")) { cloneIndex = 27; } else if (Matches(val, name, 12, "mast_mizzen_0")) { cloneIndex = 28; } else if (Matches(val, name, 13, "mast_mizzen_1")) { cloneIndex = 29; } break; case 70: if (Matches(val, name, 2, "mast_main_1")) { cloneIndex = 26; } else if (Matches(val, name, 3, "mast_main_2")) { cloneIndex = 27; } else if (Matches(val, name, 4, "mast_back")) { cloneIndex = 28; } else if (Matches(val, name, 1, "mast_front")) { cloneIndex = 29; } break; } return cloneIndex >= 0; } internal static bool IsExtendedIndex(int mastIndex) { if (mastIndex >= 96) { return mastIndex < 128; } return false; } internal static string GetExtendedMappingKey(int sceneIndex) { return "dogeggz.unstayedjunksailmast." + sceneIndex + ".extendedMastIndices"; } private static bool Matches(Mast mast, string objectName, int sourceIndex, string expectedName) { if (mast.orderIndex == sourceIndex) { return string.Equals(objectName, expectedName, StringComparison.OrdinalIgnoreCase); } return false; } } internal sealed class UnstayedMastMarker : MonoBehaviour { internal UnstayedMastSourceIdentity Identity; internal float DiameterScale; } internal sealed class UnstayedMastProfile { internal BoatPart MastPart; internal BoatPartOption UnstayedOption; internal UnstayedMastMarker Marker; internal bool UsesFixedVanillaIndex; internal List RestrictedSelections; } internal sealed class UnstayedMastSourceIdentity { private const string StablePrefix = "v2|"; internal string StableId { get; } internal string LegacyId { get; } private UnstayedMastSourceIdentity(string stableId, string legacyId) { StableId = stableId; LegacyId = legacyId; } internal static UnstayedMastSourceIdentity Create(BoatPartOption source, BoatPart owningPart, Transform boat, int sceneIndex) { if ((Object)(object)source == (Object)null) { throw new ArgumentNullException("source"); } Mast component = ((Component)source).GetComponent(); string legacyId = UnstayedNameRules.Normalize(source.optionName) + "|" + UnstayedNameRules.Normalize(((Object)source).name) + "|" + (((Object)(object)component != (Object)null) ? component.orderIndex.ToString() : "-"); if (UnstayedMastIndexRules.TryGetFixedVanillaIndex(sceneIndex, source, out var _)) { return new UnstayedMastSourceIdentity("v2|boat:" + sceneIndex + "|vanilla:" + NormalizeSegment(((Object)source).name), legacyId); } string groupId = GetGroupId(owningPart, source, boat, sceneIndex); string normalizedRelativePath = GetNormalizedRelativePath(((Component)source).transform, boat); return new UnstayedMastSourceIdentity("v2|boat:" + sceneIndex + "|group:" + groupId + "|path:" + normalizedRelativePath, legacyId); } internal int GetPersistedMatchScore(string persistedId) { if (string.Equals(persistedId, StableId, StringComparison.Ordinal)) { return 100; } if (string.Equals(persistedId, LegacyId, StringComparison.Ordinal)) { return 90; } if (string.IsNullOrEmpty(persistedId) || persistedId.StartsWith("v2|", StringComparison.Ordinal)) { return 0; } string[] array = persistedId.Split(new char[1] { '|' }); string[] array2 = LegacyId.Split(new char[1] { '|' }); if (array.Length != 3 || array2.Length != 3) { return 0; } bool num = string.Equals(array[0], array2[0], StringComparison.Ordinal); bool flag = !string.IsNullOrEmpty(array2[1]) && string.Equals(array[1], array2[1], StringComparison.Ordinal); if (num && flag) { return 80; } if (!flag) { return 0; } return 70; } private static string GetGroupId(BoatPart owningPart, BoatPartOption source, Transform boat, int sceneIndex) { if (owningPart != null && owningPart.partOptions != null) { List list = new List(); for (int i = 0; i < owningPart.partOptions.Count; i++) { BoatPartOption val = owningPart.partOptions[i]; if ((Object)(object)val != (Object)null && UnstayedMastIndexRules.TryGetFixedVanillaIndex(sceneIndex, val, out var _)) { string item = "vanilla:" + NormalizeSegment(((Object)val).name); if (!list.Contains(item)) { list.Add(item); } } } if (list.Count > 0) { list.Sort(StringComparer.Ordinal); return list[0]; } } Transform parent = ((Component)source).transform.parent; return "path:" + GetNormalizedRelativePath(parent, boat); } private static string GetNormalizedRelativePath(Transform transform, Transform root) { if ((Object)(object)transform == (Object)null) { return "missing"; } List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(NormalizeSegment(((Object)val).name)); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return NormalizeSegment(((Object)transform).name); } list.Reverse(); if (list.Count <= 0) { return "root"; } return string.Join("/", list.ToArray()); } private static string NormalizeSegment(string value) { string text = UnstayedNameRules.Normalize(value); if (!string.IsNullOrEmpty(text)) { return text; } return "unnamed"; } } internal static class UnstayedNameRules { internal static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length); bool flag = false; for (int i = 0; i < value.Length; i++) { char c = char.ToLowerInvariant(value[i]); if (char.IsLetterOrDigit(c)) { stringBuilder.Append(c); flag = false; } else if (!flag) { stringBuilder.Append(' '); flag = true; } } return stringBuilder.ToString().Trim(); } internal static bool IsNamedVariant(string normalizedName, string normalizedBaseName) { if (!(normalizedName == normalizedBaseName)) { return normalizedName.StartsWith(normalizedBaseName + " ", StringComparison.Ordinal); } return true; } internal static bool ContainsWord(string normalizedText, string word) { return (" " + normalizedText + " ").Contains(" " + word + " "); } internal static bool HasAbsenceToken(string normalizedText) { if (!ContainsWord(normalizedText, "no") && !ContainsWord(normalizedText, "none") && !ContainsWord(normalizedText, "without")) { return ContainsWord(normalizedText, "empty"); } return true; } } [HarmonyPatch(typeof(ShipyardSailColChecker), "OnTriggerEnter")] internal static class UnstayedOwnMastCollisionPatch { private static bool Prefix(Collider other, Sail ___sail) { if ((Object)(object)other == (Object)null || (Object)(object)___sail == (Object)null) { return true; } UnstayedScaledBodyMarker component = ((Component)other).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsWalkBody) { return true; } Transform parent = ((Component)___sail).transform.parent; Mast val = (((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponent() : null); if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponent() == (Object)null) { return true; } return (Object)(object)component.LogicalRoot != (Object)(object)val.walkColMast; } } [HarmonyPatch(typeof(Sail), "UpdateInstallPosition")] [HarmonyAfter(new string[] { "com.nandbrew.shipyardexpansion" })] internal static class UnstayedSailInstallPositionPatch { private static void Postfix(Sail __instance) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_00c8: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Component)__instance).transform.parent; Mast val = (((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponent() : null); UnstayedMastMarker unstayedMastMarker = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); if (!((Object)(object)unstayedMastMarker == (Object)null) && UnstayedSailRules.UsesDiameterCompensation(__instance) && UnstayedMastBuilder.TryGetSailMountOffset(val, __instance, unstayedMastMarker.DiameterScale, out var localOffset)) { Vector3 localPosition = ((Component)__instance).transform.localPosition; localPosition.x = localOffset.x; localPosition.y = localOffset.y; ((Component)__instance).transform.localPosition = localPosition; HingeJoint component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)((Joint)component).connectedBody != (Object)null) { ((Joint)component).connectedAnchor = ((Component)((Joint)component).connectedBody).transform.InverseTransformPoint(((Component)__instance).transform.position); } } } } internal static class UnstayedSailRules { internal static bool IsAllowed(GameObject sailObject) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) Sail val = (((Object)(object)sailObject != (Object)null) ? sailObject.GetComponent() : null); if ((Object)(object)val == (Object)null) { return false; } string text = UnstayedNameRules.Normalize(val.sailName); if ((int)val.category != 2 && ((int)val.category != 4 || !(text == "fin sail"))) { if ((int)val.category == 0) { return UnstayedNameRules.IsNamedVariant(text, "junk square"); } return false; } return true; } internal static bool UsesDiameterCompensation(Sail sail) { if ((Object)(object)sail != (Object)null && !RectangleJunkSails.IsRectangle(((Component)sail).gameObject)) { return IsAllowed(((Component)sail).gameObject); } return false; } } internal static class UnstayedSaveCompatibility { private const char RecordSeparator = ';'; private const char FieldSeparator = '='; private const int IndexLayoutVersion = 2; internal static void PrepareLoad(SaveableBoatCustomization customization, SaveBoatCustomizationData data) { RectangleJunkSailIndexMigration.PrepareLoad(customization, data); RemapSavedOptions(customization, data); MigrateMastIndices(customization, data); } internal static void RecordActiveOptions(SaveableBoatCustomization customization, SaveBoatCustomizationData data) { RectangleJunkSailIndexMigration.MarkCurrent(customization); BoatCustomParts component = ((Component)customization).GetComponent(); if (data == null || !UnstayedBoatRegistry.TryGet(component, out var profile) || GameState.modData == null || (GetIndexLayoutVersion(profile.SceneIndex) < 2 && HasActiveMastRecords(profile.SceneIndex))) { return; } List list = new List(); for (int i = 0; i < profile.Masts.Count; i++) { UnstayedMastProfile unstayedMastProfile = profile.Masts[i]; int num = component.availableParts.IndexOf(unstayedMastProfile.MastPart); int num2 = unstayedMastProfile.MastPart.partOptions.IndexOf(unstayedMastProfile.UnstayedOption); if (num >= 0 && num2 >= 0 && num < data.partActiveOptions.Count && data.partActiveOptions[num] == num2) { UnstayedMastSourceIdentity unstayedMastSourceIdentity = (((Object)(object)unstayedMastProfile.Marker != (Object)null) ? unstayedMastProfile.Marker.Identity : null); if (unstayedMastSourceIdentity != null && !string.IsNullOrEmpty(unstayedMastSourceIdentity.StableId)) { list.Add(num + "=" + Uri.EscapeDataString(unstayedMastSourceIdentity.StableId)); } } } string key = GetKey(profile.SceneIndex); if (list.Count == 0) { GameState.modData.Remove(key); } else { GameState.modData[key] = string.Join(';'.ToString(), list.ToArray()); } GameState.modData[GetIndexLayoutKey(profile.SceneIndex)] = 2.ToString(); } private static void RemapSavedOptions(SaveableBoatCustomization customization, SaveBoatCustomizationData data) { BoatCustomParts component = ((Component)customization).GetComponent(); if (data == null || data.partActiveOptions == null || !UnstayedBoatRegistry.TryGet(component, out var profile) || GameState.modData == null || !GameState.modData.TryGetValue(GetKey(profile.SceneIndex), out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { int num = array[i].IndexOf('='); if (num <= 0 || !int.TryParse(array[i].Substring(0, num), out var result) || result < 0 || result >= data.partActiveOptions.Count) { continue; } string persistedSourceId; try { persistedSourceId = Uri.UnescapeDataString(array[i].Substring(num + 1)); } catch (UriFormatException) { continue; } UnstayedMastProfile unstayedMastProfile = FindMatchingMastProfile(profile, component, persistedSourceId, result); if (unstayedMastProfile != null) { int num2 = component.availableParts.IndexOf(unstayedMastProfile.MastPart); int num3 = unstayedMastProfile.MastPart.partOptions.IndexOf(unstayedMastProfile.UnstayedOption); if (num2 >= 0 && num2 < data.partActiveOptions.Count && num3 >= 0) { data.partActiveOptions[num2] = num3; } } } } private static UnstayedMastProfile FindMatchingMastProfile(UnstayedBoatProfile profile, BoatCustomParts parts, string persistedSourceId, int savedPartIndex) { UnstayedMastProfile result = null; int num = 0; bool flag = false; for (int i = 0; i < profile.Masts.Count; i++) { UnstayedMastProfile unstayedMastProfile = profile.Masts[i]; UnstayedMastSourceIdentity unstayedMastSourceIdentity = (((Object)(object)unstayedMastProfile.Marker != (Object)null) ? unstayedMastProfile.Marker.Identity : null); if (unstayedMastSourceIdentity == null) { continue; } int num2 = unstayedMastSourceIdentity.GetPersistedMatchScore(persistedSourceId); if (num2 > 0) { if (parts.availableParts.IndexOf(unstayedMastProfile.MastPart) == savedPartIndex) { num2 += 5; } if (num2 > num) { num = num2; result = unstayedMastProfile; flag = false; } else if (num2 == num) { flag = true; } } } if (!flag) { return result; } ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Did not remap an ambiguous saved unstayed mast source identity: " + persistedSourceId + ".")); } return null; } private static void MigrateMastIndices(SaveableBoatCustomization customization, SaveBoatCustomizationData data) { BoatCustomParts component = ((Component)customization).GetComponent(); if (data == null || !UnstayedBoatRegistry.TryGet(component, out var profile) || GameState.modData == null) { return; } HashSet hashSet = new HashSet(); if (profile.RetiredMastIndices != null) { for (int i = 0; i < profile.RetiredMastIndices.Count; i++) { hashSet.Add(profile.RetiredMastIndices[i]); } } if (GetIndexLayoutVersion(profile.SceneIndex) < 2 && HasActiveMastRecords(profile.SceneIndex)) { HashSet hashSet2 = FindForeignExtendedMastIndices(((Component)customization).transform); for (int j = 96; j < 128; j++) { if (!hashSet2.Contains(j)) { hashSet.Add(j); } } } int num = RemoveSavedSails(data, hashSet); int num2 = RemoveShipyardExpansionSailRecords(profile.SceneIndex, hashSet); if (num > 0 || num2 > 0) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Migrated unstayed mast indices for scene " + profile.SceneIndex + "; discarded " + num + " legacy sail(s) and " + num2 + " Shipyard Expansion sail setting record(s). No refund is issued by this migration.")); } } GameState.modData[GetIndexLayoutKey(profile.SceneIndex)] = 2.ToString(); } private static int RemoveSavedSails(SaveBoatCustomizationData data, HashSet discardedIndices) { if (data.sails == null || discardedIndices.Count == 0) { return 0; } int num = 0; for (int num2 = data.sails.Count - 1; num2 >= 0; num2--) { SaveSailData val = data.sails[num2]; if (val != null && discardedIndices.Contains(val.mastIndex)) { data.sails.RemoveAt(num2); num++; } } return num; } private static HashSet FindForeignExtendedMastIndices(Transform boat) { HashSet hashSet = new HashSet(); if ((Object)(object)boat == (Object)null) { return hashSet; } Mast[] componentsInChildren = ((Component)boat).GetComponentsInChildren(true); foreach (Mast val in componentsInChildren) { if ((Object)(object)val != (Object)null && UnstayedMastIndexRules.IsExtendedIndex(val.orderIndex) && (Object)(object)((Component)val).GetComponent() == (Object)null) { hashSet.Add(val.orderIndex); } } return hashSet; } private static int RemoveShipyardExpansionSailRecords(int sceneIndex, HashSet discardedIndices) { if (discardedIndices.Count == 0 || GameState.modData == null) { return 0; } string key = "SEboatSails." + sceneIndex; if (!GameState.modData.TryGetValue(key, out var value) || string.IsNullOrEmpty(value)) { return 0; } int num = value.LastIndexOf('|'); string obj = ((num >= 0) ? value.Substring(0, num) : value); string text = ((num >= 0) ? value.Substring(num) : string.Empty); string[] array = obj.Split(new char[1] { ')' }, StringSplitOptions.RemoveEmptyEntries); List list = new List(); int num2 = 0; for (int i = 0; i < array.Length; i++) { int num3 = array[i].IndexOf('('); if (num3 <= 0 || !int.TryParse(array[i].Substring(0, num3), out var result) || !discardedIndices.Contains(result)) { list.Add(array[i] + ")"); } else { num2++; } } if (num2 > 0) { GameState.modData[key] = string.Concat(string.Concat(list.ToArray()), text); } return num2; } private static bool HasActiveMastRecords(int sceneIndex) { if (GameState.modData.TryGetValue(GetKey(sceneIndex), out var value)) { return !string.IsNullOrEmpty(value); } return false; } private static int GetIndexLayoutVersion(int sceneIndex) { if (!GameState.modData.TryGetValue(GetIndexLayoutKey(sceneIndex), out var value) || !int.TryParse(value, out var result)) { return 0; } return result; } private static string GetKey(int sceneIndex) { return "dogeggz.unstayedjunksailmast." + sceneIndex + ".activeMasts"; } private static string GetIndexLayoutKey(int sceneIndex) { return "dogeggz.unstayedjunksailmast." + sceneIndex + ".indexLayoutVersion"; } } internal sealed class UnstayedScaledBodyMarker : MonoBehaviour { internal Transform LogicalRoot; internal bool IsWalkBody; } internal static class UnstayedSelectionRules { internal static List NormalizeOrder(BoatCustomParts parts, BoatPartsOrder order) { List list = new List(); if ((Object)(object)parts == (Object)null || order == null || !UnstayedBoatRegistry.TryGet(parts, out var profile)) { return list; } for (int i = 0; i < profile.Masts.Count; i++) { UnstayedMastProfile unstayedMastProfile = profile.Masts[i]; int num = parts.availableParts.IndexOf(unstayedMastProfile.MastPart); int num2 = unstayedMastProfile.MastPart.partOptions.IndexOf(unstayedMastProfile.UnstayedOption); if (num >= 0 && num2 >= 0 && num < order.orderedOptions.Length && order.orderedOptions[num] == num2) { ForceEmptySelections(parts, order.orderedOptions, unstayedMastProfile.RestrictedSelections, list); } } return list; } internal static bool NormalizeActive(BoatCustomParts parts) { if ((Object)(object)parts == (Object)null || !UnstayedBoatRegistry.TryGet(parts, out var profile)) { return false; } bool result = false; for (int i = 0; i < profile.Masts.Count; i++) { UnstayedMastProfile unstayedMastProfile = profile.Masts[i]; int num = unstayedMastProfile.MastPart.partOptions.IndexOf(unstayedMastProfile.UnstayedOption); if (num < 0 || unstayedMastProfile.MastPart.activeOption != num) { continue; } for (int j = 0; j < unstayedMastProfile.RestrictedSelections.Count; j++) { RestrictedPartSelection restrictedPartSelection = unstayedMastProfile.RestrictedSelections[j]; if (restrictedPartSelection.Kind == RestrictedPartKind.Rigging && !((Object)(object)restrictedPartSelection.EmptyOption == (Object)null)) { int num2 = restrictedPartSelection.Part.partOptions.IndexOf(restrictedPartSelection.EmptyOption); if (num2 >= 0 && restrictedPartSelection.Part.activeOption != num2) { restrictedPartSelection.Part.activeOption = num2; result = true; } } } } return result; } private static void ForceEmptySelections(BoatCustomParts parts, int[] orderedOptions, List selections, List changedPartIndices) { for (int i = 0; i < selections.Count; i++) { RestrictedPartSelection restrictedPartSelection = selections[i]; if (restrictedPartSelection.Kind != RestrictedPartKind.Rigging || (Object)(object)restrictedPartSelection.EmptyOption == (Object)null) { continue; } int num = parts.availableParts.IndexOf(restrictedPartSelection.Part); int num2 = restrictedPartSelection.Part.partOptions.IndexOf(restrictedPartSelection.EmptyOption); if (num >= 0 && num < orderedOptions.Length && num2 >= 0 && orderedOptions[num] != num2) { orderedOptions[num] = num2; if (!changedPartIndices.Contains(num)) { changedPartIndices.Add(num); } } } } } }