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.1.0
plugins\TommysShipSpeed\TommysShipSpeed.dll
Decompiled 19 hours agousing System; using System.Collections; using System.Collections.Generic; 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; [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInPlugin("tommy.valheim.shipspeed", "Tommy's Ship Speed", "1.1.0")] [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.1.0"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DebugLogging; internal static ConfigEntry<float> PropulsionRampSeconds; internal static ShipConfig Raft; internal static ShipConfig Karve; internal static ShipConfig Longship; internal static ShipConfig Drakkar; private Harmony _harmony; internal static ManualLogSource Log; 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_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "General", "Enabled", true, "Master switch for the mod.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); DebugLogging = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "General", "DebugLogging", false, "Write ship mode, prefab and rower-count information to the BepInEx log.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); PropulsionRampSeconds = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "General", "PropulsionRampSeconds", 0.35f, "Smoothly ramps modded propulsion when starting/changing speed to prevent a one-tick launch/jump. 0 disables smoothing.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); Raft = CreateShipConfig("Raft", 1); Karve = CreateShipConfig("Karve", 2); Longship = CreateShipConfig("Longship", 6); 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.1.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 ShipConfig CreateShipConfig(string section, int defaultMaxRowers) { ShipConfig shipConfig = new ShipConfig(); shipConfig.HalfSailMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, section, "HalfSailSpeedMultiplier", 1f, "Multiplier applied to wind/sail propulsion while using half sail.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.FullSailMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, section, "FullSailSpeedMultiplier", 1f, "Multiplier applied to wind/sail propulsion while using full sail.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.ForwardRowingMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, section, "ForwardRowingMultiplier", 1f, "Base propulsion multiplier while paddling/rowing forward.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.ReverseRowingMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, section, "ReverseRowingMultiplier", 1f, "Base propulsion multiplier while paddling/rowing in reverse.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.PerRowerBonus = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, section, "PerOccupiedRowerBonus", 0.25f, "Extra rowing propulsion per additional seated/attached rower. 0.25 means +25% per rower.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.MaxRowers = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, section, "MaxRowers", defaultMaxRowers, "Maximum number of additional seated rowers that can contribute propulsion on this ship.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 32), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.RowersAffectSailing = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, section, "RowersAffectSailing", false, "If enabled, occupied rowers also multiply half/full sail propulsion. Disabled by default.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.AlwaysWindAtBack = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, section, "AlwaysWindAtBack", false, "If enabled, while the local player is actively sailing this ship at half/full sail, Valheim's reported wind direction is forced behind that ship. This changes the sailing wind and HUD wind indicator on that player's client.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); shipConfig.TurningSpeedMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, section, "TurningSpeedMultiplier", 1f, "Multiplier for steering/rudder response and turning force on this ship. 1 = vanilla, 2 = twice as responsive, 0.5 = half speed.", true, (int?)null, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); return shipConfig; } 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; } ShipConfig configForShip = GetConfigForShip(ship); if (configForShip != null && configForShip.AlwaysWindAtBack != null) { return configForShip.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<float> HalfSailMultiplier; internal ConfigEntry<float> FullSailMultiplier; internal ConfigEntry<float> ForwardRowingMultiplier; internal ConfigEntry<float> ReverseRowingMultiplier; internal ConfigEntry<float> PerRowerBonus; internal ConfigEntry<int> MaxRowers; internal ConfigEntry<bool> RowersAffectSailing; internal ConfigEntry<bool> AlwaysWindAtBack; internal ConfigEntry<float> TurningSpeedMultiplier; } [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 Force; internal float BackwardForce; internal bool TurningModified; internal float SteerVelocityForceFactor; internal float SteerForce; internal float RudderSpeed; } private static readonly FieldInfo ForceField = AccessTools.Field(typeof(Ship), "m_force"); 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) { float num = ((ShipSpeedPlugin.PropulsionRampSeconds != null) ? ShipSpeedPlugin.PropulsionRampSeconds.Value : 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.MaxRowers.Value); float num2 = 1f + (float)num * configForShip.PerRowerBonus.Value; float num3 = 1f; float num4 = 1f; bool flag = false; bool flag2 = false; if (string.Equals(text, "Half", StringComparison.OrdinalIgnoreCase)) { num3 = configForShip.HalfSailMultiplier.Value; if (configForShip.RowersAffectSailing.Value) { num3 *= num2; } flag = true; } else if (string.Equals(text, "Full", StringComparison.OrdinalIgnoreCase)) { num3 = configForShip.FullSailMultiplier.Value; if (configForShip.RowersAffectSailing.Value) { num3 *= num2; } flag = true; } else if (string.Equals(text, "Slow", StringComparison.OrdinalIgnoreCase)) { num4 = configForShip.ForwardRowingMultiplier.Value * num2; flag2 = true; } else if (string.Equals(text, "Back", StringComparison.OrdinalIgnoreCase)) { num4 = configForShip.ReverseRowingMultiplier.Value * num2; flag2 = true; } RampState rampState = GetRampState(__instance, text); if (flag) { rampState.Sail = SmoothMultiplier(rampState.Sail, num3, __0); num3 = rampState.Sail; } else { rampState.Sail = 1f; } if (flag2) { rampState.Row = SmoothMultiplier(rampState.Row, num4, __0); num4 = rampState.Row; } else { rampState.Row = 1f; } float num5 = ((configForShip.TurningSpeedMultiplier != null) ? configForShip.TurningSpeedMultiplier.Value : 1f); bool flag3 = Math.Abs(num5 - 1f) > 0.0001f; if (!flag && !flag2 && !flag3) { return; } __state.SailForceFactor = __instance.m_sailForceFactor; __state.Force = ((ForceField != null) ? ((float)ForceField.GetValue(__instance)) : 0f); __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 * num5); } if (SteerForceField != null) { __state.SteerForce = (float)SteerForceField.GetValue(__instance); SteerForceField.SetValue(__instance, __state.SteerForce * num5); } if (RudderSpeedField != null) { __state.RudderSpeed = (float)RudderSpeedField.GetValue(__instance); RudderSpeedField.SetValue(__instance, __state.RudderSpeed * num5); } } if (flag) { __instance.m_sailForceFactor = __state.SailForceFactor * num3; } if (flag2) { if (ForceField != null) { ForceField.SetValue(__instance, __state.Force * num4); } __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=" + num5.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; if (ForceField != null) { ForceField.SetValue(__instance, __state.Force); } __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; } } } }