Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Tommys Ship Speed v1.2.0
plugins\TommysShipSpeed\TommysShipSpeed.dll
Decompiled 2 months agousing System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Extensions; using Jotunn.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace TommyMods.TommysShipSpeed; [BepInPlugin("tommy.valheim.shipspeed", "Tommy's Ship Speed", "1.2.0")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class ShipSpeedPlugin : BaseUnityPlugin { public const string PluginGuid = "tommy.valheim.shipspeed"; public const string PluginName = "Tommy's Ship Speed"; public const string PluginVersion = "1.2.0"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DebugLogging; internal static ConfigEntry<bool> AlwaysWindAtBack; internal static ConfigEntry<bool> BoardShipAnywhere; internal static ConfigEntry<bool> UseGlobalValues; internal static ConfigEntry<float> GlobalRowSpeedMultiplier; internal static ConfigEntry<float> GlobalSailSpeedMultiplier; internal static ConfigEntry<float> GlobalTurningSpeedMultiplier; internal static ConfigEntry<float> GlobalPropulsionRampSeconds; internal static ConfigEntry<bool> GlobalRowersAffectSailing; internal static ConfigEntry<float> GlobalPerRowerBonus; internal static ShipConfig Raft; internal static ShipConfig Karve; internal static ShipConfig Longship; internal static ShipConfig Drakkar; private Harmony _harmony; internal static ManualLogSource Log; private readonly Dictionary<string, string> _existingConfigValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static readonly MethodInfo GetAllPlayersMethod = AccessTools.Method(typeof(Player), "GetAllPlayers", (Type[])null, (Type[])null); private static readonly MethodInfo GetControlledShipMethod = AccessTools.Method(typeof(Player), "GetControlledShip", (Type[])null, (Type[])null); private static readonly FieldInfo PlayerAttachPointField = AccessTools.Field(typeof(Player), "m_attachPoint"); private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Expected O, but got Unknown //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; LoadExistingConfigValues(); ConfigFile config = ((BaseUnityPlugin)this).Config; int? num = 100; ConfigurationManagerAttributes val = new ConfigurationManagerAttributes(); val.DispName = "Enabled"; Enabled = ConfigFileExtensions.BindConfig<bool>(config, "General", "Enabled", true, "Master switch for the mod.", true, num, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val); ConfigFile config2 = ((BaseUnityPlugin)this).Config; int? num2 = 90; ConfigurationManagerAttributes val2 = new ConfigurationManagerAttributes(); val2.DispName = "DebugLogging"; DebugLogging = ConfigFileExtensions.BindConfig<bool>(config2, "General", "DebugLogging", false, "Write ship mode, prefab and rower-count information to the BepInEx log.", false, num2, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val2); ConfigFile config3 = ((BaseUnityPlugin)this).Config; int? num3 = 80; ConfigurationManagerAttributes val3 = new ConfigurationManagerAttributes(); val3.DispName = "AlwaysWindatBack"; AlwaysWindAtBack = ConfigFileExtensions.BindConfig<bool>(config3, "General", "AlwaysWindAtBack", false, "If enabled, while the local player is actively sailing at half/full sail, Valheim's reported wind direction is forced behind the controlled ship.", true, num3, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val3); ConfigFile config4 = ((BaseUnityPlugin)this).Config; int? num4 = 75; ConfigurationManagerAttributes val4 = new ConfigurationManagerAttributes(); val4.DispName = "BoardShipAnywhere"; BoardShipAnywhere = ConfigFileExtensions.BindConfig<bool>(config4, "General", "BoardShipAnywhere", false, "If enabled, press Use (E by default) on a non-interactive part of a ship hull to board near its normal ladder landing.", true, num4, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val4); BoardShipAnywhere.SettingChanged += delegate { ShipBoardingInstaller.RefreshAllShips(); }; Enabled.SettingChanged += delegate { ShipBoardingInstaller.RefreshAllShips(); }; ConfigFile config5 = ((BaseUnityPlugin)this).Config; int? num5 = 70; ConfigurationManagerAttributes val5 = new ConfigurationManagerAttributes(); val5.DispName = "Use global values?"; UseGlobalValues = ConfigFileExtensions.BindConfig<bool>(config5, "General", "UseGlobalValues", false, "If enabled, the global values below override each ship unless that ship has PerShipValues enabled. Per-ship settings are never deleted.", true, num5, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val5); GlobalRowSpeedMultiplier = BindHalfStep("General", "RowSpeedMultiplier", 1f, "Global rowing propulsion multiplier for both forward and reverse rowing when UseGlobalValues is enabled.", 0.05f, 20f, 60, "Row Speed Multiplier"); GlobalSailSpeedMultiplier = BindHalfStep("General", "SailingSpeedMultiplier", 1f, "Global sailing propulsion multiplier for both half and full sail when UseGlobalValues is enabled.", 0.05f, 10f, 50, "Sailing Speed Multiplier"); GlobalTurningSpeedMultiplier = BindHalfStep("General", "TurningSpeedMultiplier", 1f, "Global steering/rudder response multiplier used when UseGlobalValues is enabled.", 0.05f, 20f, 40, "Turn Speed Multiplier"); GlobalPropulsionRampSeconds = BindHalfStep("General", "PropulsionRampSeconds", 0.5f, "Seconds used to smoothly ramp modded propulsion when starting or changing speed. 0 disables smoothing.", 0f, 10f, 30, "Ramp Up Speed"); ConfigFile config6 = ((BaseUnityPlugin)this).Config; int? num6 = 20; ConfigurationManagerAttributes val6 = new ConfigurationManagerAttributes(); val6.DispName = "Rower Affects Sailing?"; GlobalRowersAffectSailing = ConfigFileExtensions.BindConfig<bool>(config6, "General", "RowersAffectSailing", false, "If enabled, occupied rowers also increase sailing propulsion when global values apply.", true, num6, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val6); GlobalPerRowerBonus = BindHalfStep("General", "PerOccupiedRowerBonus", 0.5f, "Global extra propulsion per occupied rower when global values apply. 0.5 means +50% per rower.", 0f, 5f, 10, "Rower Bonus"); MigrateFloatIfNeeded("General", "RowSpeedMultiplier", GlobalRowSpeedMultiplier, "General", "GlobalForwardRowingMultiplier"); MigrateFloatIfNeeded("General", "SailingSpeedMultiplier", GlobalSailSpeedMultiplier, "General", "GlobalFullSailSpeedMultiplier"); MigrateFloatIfNeeded("General", "TurningSpeedMultiplier", GlobalTurningSpeedMultiplier, "General", "GlobalTurningSpeedMultiplier"); MigrateBoolIfNeeded("General", "RowersAffectSailing", GlobalRowersAffectSailing, "General", "GlobalRowersAffectSailing"); MigrateFloatIfNeeded("General", "PerOccupiedRowerBonus", GlobalPerRowerBonus, "General", "GlobalPerOccupiedRowerBonus"); Raft = CreateShipConfig("Raft", 0); Karve = CreateShipConfig("Karve", 2); Longship = CreateShipConfig("Longship", 4); Drakkar = CreateShipConfig("Drakkar", 8); _harmony = new Harmony("tommy.valheim.shipspeed"); _harmony.PatchAll(typeof(ShipSpeedPlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Tommy's Ship Speed 1.2.0 loaded for Valheim 1.0."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Supported vanilla prefabs: Raft, Karve, VikingShip, VikingShip_Ashlands."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } private void LoadExistingConfigValues() { _existingConfigValues.Clear(); string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath)) { return; } string text = string.Empty; string[] array = File.ReadAllLines(configFilePath); foreach (string text2 in array) { string text3 = text2.Trim(); if (text3.Length == 0 || text3.StartsWith("#", StringComparison.Ordinal)) { continue; } if (text3.StartsWith("[", StringComparison.Ordinal) && text3.EndsWith("]", StringComparison.Ordinal)) { text = text3.Substring(1, text3.Length - 2).Trim(); continue; } int num = text3.IndexOf('='); if (num > 0 && text.Length != 0) { string text4 = text3.Substring(0, num).Trim(); string value = text3.Substring(num + 1).Trim(); _existingConfigValues[text + "\n" + text4] = value; } } } private bool ExistingValue(string section, string key, out string value) { return _existingConfigValues.TryGetValue(section + "\n" + key, out value); } private void MigrateFloatIfNeeded(string newSection, string newKey, ConfigEntry<float> entry, string oldSection, string oldKey) { if (!ExistingValue(newSection, newKey, out var _) && ExistingValue(oldSection, oldKey, out var value2) && float.TryParse(value2, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { entry.Value = result; } } private void MigrateBoolIfNeeded(string newSection, string newKey, ConfigEntry<bool> entry, string oldSection, string oldKey) { if (!ExistingValue(newSection, newKey, out var _) && ExistingValue(oldSection, oldKey, out var value2) && bool.TryParse(value2, out var result)) { entry.Value = result; } } private ConfigEntry<float> BindHalfStep(string section, string key, float defaultValue, string description, float min, float max, int order, string displayName) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown ConfigFile config = ((BaseUnityPlugin)this).Config; int? num = order; AcceptableValueRange<float> obj = new AcceptableValueRange<float>(min, max); ConfigurationManagerAttributes val = new ConfigurationManagerAttributes(); val.DispName = displayName; ConfigEntry<float> entry = ConfigFileExtensions.BindConfig<float>(config, section, key, defaultValue, description, true, num, (AcceptableValueBase)(object)obj, (Action<ConfigEntryBase>)null, val); entry.SettingChanged += delegate { float value = entry.Value; value = Mathf.Round(value * 2f) * 0.5f; if (min > 0f && value < 0.5f) { value = 0.5f; } value = Mathf.Clamp(value, min, max); if (Math.Abs(entry.Value - value) > 0.0001f) { entry.Value = value; } }; return entry; } private ShipConfig CreateShipConfig(string section, int seatCount) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown ShipConfig shipConfig = new ShipConfig(); shipConfig.SeatCount = seatCount; ConfigFile config = ((BaseUnityPlugin)this).Config; int? num = 70; ConfigurationManagerAttributes val = new ConfigurationManagerAttributes(); val.DispName = "Per Ship Values?"; shipConfig.PerShipValues = ConfigFileExtensions.BindConfig<bool>(config, section, "PerShipValues", false, "When UseGlobalValues is enabled, use this ship's own values instead of the global values.", true, num, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val); shipConfig.RowSpeedMultiplier = BindHalfStep(section, "RowSpeedMultiplier", 1f, "Propulsion multiplier for both forward and reverse rowing on this ship.", 0.05f, 20f, 60, "Row Speed"); shipConfig.SailSpeedMultiplier = BindHalfStep(section, "SailingSpeedMultiplier", 1f, "Sailing propulsion multiplier for both half and full sail on this ship.", 0.05f, 10f, 50, "Sail Speed"); shipConfig.TurningSpeedMultiplier = BindHalfStep(section, "TurningSpeedMultiplier", 1f, "Multiplier for steering/rudder response and turning force on this ship. 1 = vanilla, 2 = twice as responsive, 0.5 = half speed.", 0.05f, 20f, 40, "Turn Speed"); shipConfig.PropulsionRampSeconds = BindHalfStep(section, "PropulsionRampSeconds", (GlobalPropulsionRampSeconds != null) ? GlobalPropulsionRampSeconds.Value : 0.5f, "Seconds used to smoothly ramp rowing and sailing propulsion for this ship. 0 disables smoothing.", 0f, 10f, 30, "Ramp Speed"); ConfigFile config2 = ((BaseUnityPlugin)this).Config; int? num2 = 20; ConfigurationManagerAttributes val2 = new ConfigurationManagerAttributes(); val2.DispName = "Rower Affects Sailing?"; shipConfig.RowersAffectSailing = ConfigFileExtensions.BindConfig<bool>(config2, section, "RowersAffectSailing", false, "If enabled, occupied rowers also multiply sailing propulsion for this ship.", true, num2, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, val2); shipConfig.PerRowerBonus = BindHalfStep(section, "PerOccupiedRowerBonus", 0.5f, "Extra propulsion per additional seated/attached rower. 0.5 means +50% per rower.", 0f, 5f, 10, "Rower Bonus"); MigrateFloatIfNeeded(section, "RowSpeedMultiplier", shipConfig.RowSpeedMultiplier, section, "ForwardRowingMultiplier"); MigrateFloatIfNeeded(section, "SailingSpeedMultiplier", shipConfig.SailSpeedMultiplier, section, "FullSailSpeedMultiplier"); return shipConfig; } internal static bool UsesGlobalValues(ShipConfig cfg) { if (cfg != null && UseGlobalValues != null && UseGlobalValues.Value) { if (cfg.PerShipValues != null) { return !cfg.PerShipValues.Value; } return true; } return false; } internal static float GetSailSpeedMultiplier(ShipConfig cfg) { if (!UsesGlobalValues(cfg)) { return cfg.SailSpeedMultiplier.Value; } return GlobalSailSpeedMultiplier.Value; } internal static float GetRowSpeedMultiplier(ShipConfig cfg) { if (!UsesGlobalValues(cfg)) { return cfg.RowSpeedMultiplier.Value; } return GlobalRowSpeedMultiplier.Value; } internal static float GetPerRowerBonus(ShipConfig cfg) { if (!UsesGlobalValues(cfg)) { return cfg.PerRowerBonus.Value; } return GlobalPerRowerBonus.Value; } internal static bool GetRowersAffectSailing(ShipConfig cfg) { if (!UsesGlobalValues(cfg)) { return cfg.RowersAffectSailing.Value; } return GlobalRowersAffectSailing.Value; } internal static float GetTurningSpeedMultiplier(ShipConfig cfg) { if (!UsesGlobalValues(cfg)) { return cfg.TurningSpeedMultiplier.Value; } return GlobalTurningSpeedMultiplier.Value; } internal static float GetPropulsionRampSeconds(ShipConfig cfg) { if (!UsesGlobalValues(cfg)) { return cfg.PropulsionRampSeconds.Value; } return GlobalPropulsionRampSeconds.Value; } internal static ShipConfig GetConfigForShip(Ship ship) { if ((Object)(object)ship == (Object)null || (Object)(object)((Component)ship).gameObject == (Object)null) { return null; } string text = ((Object)((Component)ship).gameObject).name ?? string.Empty; int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } text = text.Trim(); if (string.Equals(text, "Raft", StringComparison.OrdinalIgnoreCase)) { return Raft; } if (string.Equals(text, "Karve", StringComparison.OrdinalIgnoreCase)) { return Karve; } if (string.Equals(text, "VikingShip", StringComparison.OrdinalIgnoreCase)) { return Longship; } if (string.Equals(text, "VikingShip_Ashlands", StringComparison.OrdinalIgnoreCase)) { return Drakkar; } return null; } internal static int CountActiveRowers(Ship ship, int max) { if ((Object)(object)ship == (Object)null || max <= 0 || GetAllPlayersMethod == null || PlayerAttachPointField == null) { return 0; } IEnumerable enumerable; try { enumerable = GetAllPlayersMethod.Invoke(null, null) as IEnumerable; } catch { return 0; } if (enumerable == null) { return 0; } int num = 0; Transform transform = ((Component)ship).transform; foreach (object item in enumerable) { Player val = (Player)((item is Player) ? item : null); if ((Object)(object)val == (Object)null) { continue; } Transform val2; try { object? value = PlayerAttachPointField.GetValue(val); val2 = (Transform)((value is Transform) ? value : null); } catch { continue; } if ((Object)(object)val2 == (Object)null || !IsTransformUnderShip(val2, transform)) { continue; } if (GetControlledShipMethod != null) { try { object objA = GetControlledShipMethod.Invoke(val, null); if (object.ReferenceEquals(objA, ship)) { continue; } } catch { } } num++; if (num >= max) { break; } } return num; } private static bool IsTransformUnderShip(Transform child, Transform shipTransform) { Transform val = child; for (int i = 0; i < 24; i++) { if (!((Object)(object)val != (Object)null)) { break; } if ((Object)(object)val == (Object)(object)shipTransform) { return true; } val = val.parent; } return false; } internal static bool IsSailing(Ship ship) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ship == (Object)null) { return false; } string a = ((object)ship.GetSpeedSetting()).ToString(); if (!string.Equals(a, "Half", StringComparison.OrdinalIgnoreCase)) { return string.Equals(a, "Full", StringComparison.OrdinalIgnoreCase); } return true; } internal static bool ShouldUseTailwind(Ship ship) { if (Enabled == null || !Enabled.Value || (Object)(object)ship == (Object)null || !IsSailing(ship)) { return false; } if (AlwaysWindAtBack != null) { return AlwaysWindAtBack.Value; } return false; } internal static void Debug(string text) { if (DebugLogging != null && DebugLogging.Value && Log != null) { Log.LogInfo((object)text); } } } internal sealed class ShipConfig { internal ConfigEntry<bool> PerShipValues; internal ConfigEntry<float> RowSpeedMultiplier; internal ConfigEntry<float> SailSpeedMultiplier; internal ConfigEntry<float> TurningSpeedMultiplier; internal ConfigEntry<float> PropulsionRampSeconds; internal ConfigEntry<bool> RowersAffectSailing; internal ConfigEntry<float> PerRowerBonus; internal int SeatCount; } [HarmonyPatch(typeof(Ship), "Awake")] internal static class ShipBoardingAwakePatch { private static void Postfix(Ship __instance) { ShipBoardingInstaller.ConfigureShip(__instance); } } internal static class ShipBoardingInstaller { internal static void RefreshAllShips() { try { Ship[] array = Object.FindObjectsOfType<Ship>(); if (array != null) { for (int i = 0; i < array.Length; i++) { ConfigureShip(array[i]); } } } catch (Exception ex) { ShipSpeedPlugin.Debug("BoardShipAnywhere refresh failed: " + ex.Message); } } internal static void ConfigureShip(Ship ship) { if ((Object)(object)ship == (Object)null) { return; } if (ShipSpeedPlugin.Enabled == null || !ShipSpeedPlugin.Enabled.Value || ShipSpeedPlugin.BoardShipAnywhere == null || !ShipSpeedPlugin.BoardShipAnywhere.Value) { RemoveBoarders(ship); return; } Transform landing = FindLadderLanding(ship); BoardShipHullInteractable boardShipHullInteractable = ((Component)ship).gameObject.GetComponent<BoardShipHullInteractable>(); if ((Object)(object)boardShipHullInteractable == (Object)null) { boardShipHullInteractable = ((Component)ship).gameObject.AddComponent<BoardShipHullInteractable>(); } boardShipHullInteractable.Initialize(ship, landing); Collider[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Collider>(true); if (componentsInChildren == null) { return; } foreach (Collider val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !val.isTrigger && !HasExistingInteractionBeforeShipRoot(((Component)val).transform, ((Component)ship).transform)) { BoardShipHullInteractable boardShipHullInteractable2 = ((Component)val).GetComponent<BoardShipHullInteractable>(); if ((Object)(object)boardShipHullInteractable2 == (Object)null) { boardShipHullInteractable2 = ((Component)val).gameObject.AddComponent<BoardShipHullInteractable>(); } boardShipHullInteractable2.Initialize(ship, landing); } } } private static void RemoveBoarders(Ship ship) { BoardShipHullInteractable[] componentsInChildren = ((Component)ship).GetComponentsInChildren<BoardShipHullInteractable>(true); if (componentsInChildren == null) { return; } for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { Object.Destroy((Object)(object)componentsInChildren[i]); } } } private static bool HasExistingInteractionBeforeShipRoot(Transform start, Transform shipRoot) { Transform val = start; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)shipRoot) { MonoBehaviour[] components = ((Component)val).GetComponents<MonoBehaviour>(); foreach (MonoBehaviour val2 in components) { if (!((Object)(object)val2 == (Object)null) && !(val2 is BoardShipHullInteractable) && (val2 is Interactable || val2 is Hoverable)) { return true; } } val = val.parent; } return false; } private static Transform FindLadderLanding(Ship ship) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) Transform[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Transform>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return null; } Transform result = null; float num = float.MinValue; foreach (Transform val in componentsInChildren) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)((Component)ship).transform) { continue; } string text = BuildLowerPath(val, ((Component)ship).transform, 5); if (text.IndexOf("ladder", StringComparison.Ordinal) >= 0) { string text2 = (((Object)val).name ?? string.Empty).ToLowerInvariant(); float num2 = ((Component)ship).transform.InverseTransformPoint(val.position).y; if (text2.Contains("landing")) { num2 += 1000f; } if (text2.Contains("exit")) { num2 += 900f; } if (text2.Contains("attach")) { num2 += 800f; } if (text2.Contains("top")) { num2 += 700f; } if (text2.Contains("stand")) { num2 += 600f; } if (text2.Contains("ladder")) { num2 += 100f; } if (num2 > num) { num = num2; result = val; } } } return result; } private static string BuildLowerPath(Transform t, Transform root, int depth) { string text = (((Object)t).name ?? string.Empty).ToLowerInvariant(); Transform parent = t.parent; int num = 0; while (num < depth && (Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)root) { text = (((Object)parent).name ?? string.Empty).ToLowerInvariant() + "/" + text; num++; parent = parent.parent; } return text; } } internal sealed class BoardShipHullInteractable : MonoBehaviour, Hoverable, Interactable { private Ship _ship; private Transform _landing; internal void Initialize(Ship ship, Transform landing) { _ship = ship; _landing = landing; } public string GetHoverText() { if (!IsEnabled()) { return string.Empty; } string text = "[<color=yellow><b>$KEY_Use</b></color>] Board ship"; if (Localization.instance == null) { return text; } return Localization.instance.Localize(text); } public string GetHoverName() { if (!IsEnabled()) { return string.Empty; } return "Ship"; } public float GetHoverOffset() { return 0f; } public bool Interact(Humanoid user, bool hold, bool alt) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (hold || !IsEnabled()) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null || (Object)(object)_ship == (Object)null) { return false; } Vector3 boardPoint = GetBoardPoint(); try { Rigidbody component = ((Component)val).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.linearVelocity = Vector3.zero; component.angularVelocity = Vector3.zero; component.position = boardPoint; } ((Component)val).transform.position = boardPoint; return true; } catch (Exception ex) { ShipSpeedPlugin.Debug("BoardShipAnywhere interaction failed: " + ex.Message); return false; } } public bool UseItem(Humanoid user, ItemData item) { return false; } private bool IsEnabled() { if ((Object)(object)_ship != (Object)null && ShipSpeedPlugin.Enabled != null && ShipSpeedPlugin.Enabled.Value && ShipSpeedPlugin.BoardShipAnywhere != null) { return ShipSpeedPlugin.BoardShipAnywhere.Value; } return false; } private Vector3 GetBoardPoint() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (((Object)(object)_landing != (Object)null) ? _landing.position : FallbackDeckPoint()); Vector3 val2 = ((Component)_ship).transform.position - val; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val2 = -((Component)_ship).transform.right; } ((Vector3)(ref val2)).Normalize(); return val + val2 * 0.75f + Vector3.up * 0.45f; } private Vector3 FallbackDeckPoint() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) Collider[] componentsInChildren = ((Component)_ship).GetComponentsInChildren<Collider>(true); bool flag = false; Bounds bounds = default(Bounds); ((Bounds)(ref bounds))..ctor(((Component)_ship).transform.position, Vector3.zero); if (componentsInChildren != null) { foreach (Collider val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !val.isTrigger) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } } if (!flag) { return ((Component)_ship).transform.position + Vector3.up * 1.5f; } return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y + 0.1f, ((Bounds)(ref bounds)).center.z); } } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate", new Type[] { typeof(float) })] internal static class ShipCustomFixedUpdatePatch { private sealed class RampState { internal string Mode = "Stop"; internal float Sail = 1f; internal float Row = 1f; internal int LastSeenFrame; } internal sealed class State { internal bool Modified; internal float SailForceFactor; internal float BackwardForce; internal bool TurningModified; internal float SteerVelocityForceFactor; internal float SteerForce; internal float RudderSpeed; } private static readonly FieldInfo NViewField = AccessTools.Field(typeof(Ship), "m_nview"); private static readonly FieldInfo SteerVelocityForceFactorField = AccessTools.Field(typeof(Ship), "m_stearVelForceFactor"); private static readonly FieldInfo SteerForceField = AccessTools.Field(typeof(Ship), "m_stearForce"); private static readonly FieldInfo RudderSpeedField = AccessTools.Field(typeof(Ship), "m_rudderSpeed"); private static readonly Dictionary<int, RampState> RampStates = new Dictionary<int, RampState>(); private static int _lastRampCleanupFrame; private static float SmoothMultiplier(float current, float target, float dt, ShipConfig cfg) { float num = ((cfg != null) ? ShipSpeedPlugin.GetPropulsionRampSeconds(cfg) : 0f); if (num <= 0.001f) { return target; } float num2 = Math.Abs(target - 1f) * Math.Max(0f, dt) / num; if (num2 < 0.001f) { num2 = 0.001f; } return Mathf.MoveTowards(current, target, num2); } private static RampState GetRampState(Ship ship, string mode) { int instanceID = ((Object)ship).GetInstanceID(); if (!RampStates.TryGetValue(instanceID, out var value)) { value = new RampState(); RampStates[instanceID] = value; } value.LastSeenFrame = Time.frameCount; if (string.Equals(value.Mode, "Stop", StringComparison.OrdinalIgnoreCase) && !string.Equals(mode, "Stop", StringComparison.OrdinalIgnoreCase)) { value.Sail = 1f; value.Row = 1f; } if (string.Equals(mode, "Stop", StringComparison.OrdinalIgnoreCase)) { value.Sail = 1f; value.Row = 1f; } value.Mode = mode; if (Time.frameCount - _lastRampCleanupFrame > 3600) { _lastRampCleanupFrame = Time.frameCount; List<int> list = null; foreach (KeyValuePair<int, RampState> rampState in RampStates) { if (Time.frameCount - rampState.Value.LastSeenFrame > 7200) { if (list == null) { list = new List<int>(); } list.Add(rampState.Key); } } if (list != null) { foreach (int item in list) { RampStates.Remove(item); } } } return value; } private static ZNetView GetNView(Ship ship) { if ((Object)(object)ship == (Object)null) { return null; } if (NViewField != null) { object? value = NViewField.GetValue(ship); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } return ((Component)ship).GetComponent<ZNetView>(); } private static void Prefix(Ship __instance, float __0, out State __state) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) __state = new State(); if (ShipSpeedPlugin.Enabled == null || !ShipSpeedPlugin.Enabled.Value) { return; } ZNetView nView = GetNView(__instance); if ((Object)(object)__instance == (Object)null || (Object)(object)nView == (Object)null || !nView.IsValid() || !nView.IsOwner()) { return; } ShipConfig configForShip = ShipSpeedPlugin.GetConfigForShip(__instance); if (configForShip == null) { return; } string text = ((object)__instance.GetSpeedSetting()).ToString(); int num = ShipSpeedPlugin.CountActiveRowers(__instance, configForShip.SeatCount); float num2 = 1f + (float)num * ShipSpeedPlugin.GetPerRowerBonus(configForShip); float num3 = 1f; float num4 = 1f; bool flag = false; bool flag2 = false; if (string.Equals(text, "Half", StringComparison.OrdinalIgnoreCase)) { num3 = ShipSpeedPlugin.GetSailSpeedMultiplier(configForShip); if (ShipSpeedPlugin.GetRowersAffectSailing(configForShip)) { num3 *= num2; } flag = true; } else if (string.Equals(text, "Full", StringComparison.OrdinalIgnoreCase)) { num3 = ShipSpeedPlugin.GetSailSpeedMultiplier(configForShip); if (ShipSpeedPlugin.GetRowersAffectSailing(configForShip)) { num3 *= num2; } flag = true; } else if (string.Equals(text, "Slow", StringComparison.OrdinalIgnoreCase)) { num4 = ShipSpeedPlugin.GetRowSpeedMultiplier(configForShip) * num2; flag2 = true; } else if (string.Equals(text, "Back", StringComparison.OrdinalIgnoreCase)) { num4 = ShipSpeedPlugin.GetRowSpeedMultiplier(configForShip) * num2; flag2 = true; } RampState rampState = GetRampState(__instance, text); if (flag) { rampState.Sail = SmoothMultiplier(rampState.Sail, num3, __0, configForShip); num3 = rampState.Sail; } else { rampState.Sail = 1f; } if (flag2) { rampState.Row = SmoothMultiplier(rampState.Row, num4, __0, configForShip); num4 = rampState.Row; } else { rampState.Row = 1f; } float turningSpeedMultiplier = ShipSpeedPlugin.GetTurningSpeedMultiplier(configForShip); bool flag3 = Math.Abs(turningSpeedMultiplier - 1f) > 0.0001f; if (!flag && !flag2 && !flag3) { return; } __state.SailForceFactor = __instance.m_sailForceFactor; __state.BackwardForce = __instance.m_backwardForce; __state.Modified = true; if (flag3) { __state.TurningModified = true; if (SteerVelocityForceFactorField != null) { __state.SteerVelocityForceFactor = (float)SteerVelocityForceFactorField.GetValue(__instance); SteerVelocityForceFactorField.SetValue(__instance, __state.SteerVelocityForceFactor * turningSpeedMultiplier); } if (SteerForceField != null) { __state.SteerForce = (float)SteerForceField.GetValue(__instance); SteerForceField.SetValue(__instance, __state.SteerForce * turningSpeedMultiplier); } if (RudderSpeedField != null) { __state.RudderSpeed = (float)RudderSpeedField.GetValue(__instance); RudderSpeedField.SetValue(__instance, __state.RudderSpeed * turningSpeedMultiplier); } } if (flag) { __instance.m_sailForceFactor = __state.SailForceFactor * num3; } if (flag2) { __instance.m_backwardForce = __state.BackwardForce * num4; } ShipSpeedPlugin.Debug("Ship=" + ((Object)((Component)__instance).gameObject).name + " mode=" + text + " rowers=" + num + " crewX=" + num2.ToString("0.00") + " sailX=" + num3.ToString("0.00") + " rowX=" + num4.ToString("0.00") + " turnX=" + turningSpeedMultiplier.ToString("0.00")); } private static void Postfix(Ship __instance, State __state) { if ((Object)(object)__instance == (Object)null || __state == null || !__state.Modified) { return; } __instance.m_sailForceFactor = __state.SailForceFactor; __instance.m_backwardForce = __state.BackwardForce; if (__state.TurningModified) { if (SteerVelocityForceFactorField != null) { SteerVelocityForceFactorField.SetValue(__instance, __state.SteerVelocityForceFactor); } if (SteerForceField != null) { SteerForceField.SetValue(__instance, __state.SteerForce); } if (RudderSpeedField != null) { RudderSpeedField.SetValue(__instance, __state.RudderSpeed); } } } } [HarmonyPatch(typeof(EnvMan), "GetWindDir")] internal static class SailingWindDirectionPatch { private static readonly MethodInfo GetControlledShipMethod = AccessTools.Method(typeof(Player), "GetControlledShip", (Type[])null, (Type[])null); private static Ship GetLocallyControlledSailingShip() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || GetControlledShipMethod == null) { return null; } Ship val = null; try { object? obj = GetControlledShipMethod.Invoke(localPlayer, null); val = (Ship)((obj is Ship) ? obj : null); } catch { return null; } if ((Object)(object)val == (Object)null || !ShipSpeedPlugin.ShouldUseTailwind(val)) { return null; } return val; } private static void Postfix(ref Vector3 __result) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_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) Ship locallyControlledSailingShip = GetLocallyControlledSailingShip(); if (!((Object)(object)locallyControlledSailingShip == (Object)null)) { Vector3 forward = ((Component)locallyControlledSailingShip).transform.forward; forward.y = 0f; if (!(((Vector3)(ref forward)).sqrMagnitude <= 0.0001f)) { __result = ((Vector3)(ref forward)).normalized; } } } }