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 ImprovedAutoRunAndJump v1.0.0
ImprovedAutoRunAndJump.dll
Decompiled 3 days agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ImprovedAutoRunAndJump")] [assembly: AssemblyDescription("Auto-run that survives jumping, strafing and crouching; a sprint toggle that sticks; a jump buffer.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ImprovedAutoRunAndJump")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("7b1c4e2a-9d64-4f18-b3a7-6e0c52d81f93")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace ImprovedAutoRunAndJump; [BepInPlugin("Pip.ImprovedAutoRunAndJump", "Pip's Improved Auto-run and Jump", "1.0.0")] [BepInProcess("valheim.exe")] public class ImprovedAutoRunAndJump : BaseUnityPlugin { private struct AutoRunInput { public bool WasAutoRun; public Vector3 MoveDir; public bool Jump; public bool Crouch; public bool ReroutedJump; public bool Steering; public bool SuppressBlock; public bool Blocked; } [HarmonyPatch(typeof(Player), "SetControls")] private class Player_SetControls_AutoRun_Patch { private static void Prefix(Player __instance, Vector3 movedir, bool attack, bool attackHold, bool secondaryAttackHold, ref bool jump, ref bool crouch, ref bool dodge, ref bool block, ref bool blockHold, ref bool run, bool autoRun, out AutoRunInput __state) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) __state = default(AutoRunInput); if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (NoCrouchWhileFlying != null && NoCrouchWhileFlying.Value && ((Character)__instance).IsDebugFlying()) { crouch = false; if (ReadBool(CrouchToggledField, __instance, fallback: false)) { CrouchToggledField?.SetValue(__instance, false); LogInfo("Crouch dropped: it does nothing while flying"); } } __state.WasAutoRun = __instance.m_autoRun; __state.MoveDir = movedir; __state.Jump = jump; __state.Crouch = crouch; __state.Steering = autoRun | blockHold; if (__state.WasAutoRun && AutoRunSuppressBlock != null && AutoRunSuppressBlock.Value) { block = false; blockHold = false; __state.SuppressBlock = true; } if (__state.WasAutoRun && AutoRunSuppressDodge != null && AutoRunSuppressDodge.Value) { dodge = false; bool flag = blockHold || ((Character)__instance).IsBlocking() || ((Character)__instance).IsCrouching() || ReadBool(CrouchToggledField, __instance, fallback: false); if (jump && flag) { jump = false; __state.ReroutedJump = true; } } __state.Blocked = ((attack || attackHold || secondaryAttackHold) | dodge) || ((Character)__instance).InPlaceMode(); if (crouch && !((Character)__instance).IsDebugFlying() && !ReadBool(CrouchToggledField, __instance, fallback: false)) { run = false; PlayerController component = ((Component)__instance).GetComponent<PlayerController>(); if ((Object)(object)component != (Object)null) { ControllerRunField?.SetValue(component, false); } LogInfo("Sprint dropped so the crouch can take effect on this frame"); } } private static void Postfix(Player __instance, AutoRunInput __state, ref Vector3 ___m_moveDir, ref Vector3 ___m_lookDir) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (!__state.WasAutoRun) { _autoRunHeading = (__instance.m_autoRun ? ___m_moveDir : Vector3.zero); return; } if (!__instance.m_autoRun) { if (!ShouldKeepRunning(__state, out var strafe)) { _autoRunHeading = Vector3.zero; return; } __instance.m_autoRun = true; LogInfo("Auto-run kept through " + DescribeAutoRunCause(__state) + ((Mathf.Abs(strafe) > 0.001f) ? $" (strafe {strafe:0.##})" : string.Empty)); } if (__state.SuppressBlock) { BlockingField?.SetValue(__instance, false); } ApplyAutoRunHeading(__state, ref ___m_moveDir, ref ___m_lookDir); if (__state.ReroutedJump) { LogInfo("Auto-run jump taken over from the dodge handler"); ((Character)__instance).Jump(false); } } private static string DescribeAutoRunCause(AutoRunInput state) { if (state.Jump) { return "a jump"; } if (!state.Crouch) { return "a strafe"; } return "a crouch"; } private static bool ShouldKeepRunning(AutoRunInput state, out float strafe) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) strafe = 0f; if (state.Blocked) { return false; } if (Mathf.Abs(state.MoveDir.z) > 0.001f) { return false; } bool flag = Mathf.Abs(state.MoveDir.x) > 0.001f; if (flag && !AutoRunKeepOnStrafe.Value) { return false; } if (state.Jump && !AutoRunKeepOnJump.Value) { return false; } if (state.Crouch && !AutoRunKeepOnCrouch.Value) { return false; } if (!state.Jump && !flag && !state.Crouch) { return false; } strafe = (flag ? state.MoveDir.x : 0f); return true; } private static void ApplyAutoRunHeading(AutoRunInput state, ref Vector3 moveDir, ref Vector3 lookDir) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_0059: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_00a3: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) float num = (AutoRunKeepOnStrafe.Value ? state.MoveDir.x : 0f); bool flag = Mathf.Abs(num) > 0.001f; if (state.Steering) { Vector3 val = lookDir; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { _autoRunHeading = ((Vector3)(ref val)).normalized; } } if (((Vector3)(ref _autoRunHeading)).sqrMagnitude < 0.0001f) { _autoRunHeading = moveDir; } if (!(((Vector3)(ref _autoRunHeading)).sqrMagnitude < 0.0001f)) { Vector3 val2 = (_autoRunHeading = ((Vector3)(ref _autoRunHeading)).normalized); Vector3 val3 = val2; if (flag) { val3 = val2 + num * Vector3.Cross(Vector3.up, val2); } val3.y = 0f; if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0001f)) { moveDir = ((Vector3)(ref val3)).normalized; } } } } [HarmonyPatch(typeof(PlayerController), "FixedUpdate")] private class PlayerController_FixedUpdate_StickySprint_Patch { private static void Prefix(ref float ___m_lastMagnitude, Player ___m_character) { if (StickySprintToggle != null && StickySprintToggle.Value && ZInput.ToggleRun && (!((Object)(object)___m_character != (Object)null) || ((Character)___m_character).IsDebugFlying() || !ReadBool(CrouchToggledField, ___m_character, fallback: false))) { ___m_lastMagnitude = 1f; } } } private const string modGUID = "Pip.ImprovedAutoRunAndJump"; private const string modName = "Pip's Improved Auto-run and Jump"; private const string modVersion = "1.0.0"; private readonly Harmony harmony = new Harmony("Pip.ImprovedAutoRunAndJump"); private static ConfigEntry<bool> AutoRunKeepOnJump; private static ConfigEntry<bool> AutoRunKeepOnStrafe; private static ConfigEntry<bool> AutoRunKeepOnCrouch; private static ConfigEntry<bool> AutoRunSuppressDodge; private static ConfigEntry<bool> AutoRunSuppressBlock; private static ConfigEntry<bool> NoCrouchWhileFlying; private static ConfigEntry<bool> StickySprintToggle; private static ConfigEntry<float> JumpBufferSeconds; private static ConfigEntry<bool> LogJumpBlocks; private static ConfigEntry<bool> VerboseLogging; private static readonly MethodInfo TakeInputMethod = AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null); private static readonly FieldInfo LastGroundTouchField = AccessTools.Field(typeof(Character), "m_lastGroundTouch"); private static readonly FieldInfo JumpTimerField = AccessTools.Field(typeof(Character), "m_jumpTimer"); private static readonly FieldInfo CrouchToggledField = AccessTools.Field(typeof(Player), "m_crouchToggled"); private static readonly FieldInfo ControllerRunField = AccessTools.Field(typeof(PlayerController), "m_run"); private static readonly FieldInfo BlockingField = AccessTools.Field(typeof(Character), "m_blocking"); private static float _jumpPressedAt = float.NegativeInfinity; private static Vector3 _autoRunHeading; private void Awake() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown Game.isModded = true; AutoRunKeepOnJump = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto-run", "AutoRunKeepOnJump", true, "Let you jump without dropping out of auto-run. Vanilla cancels auto-run on any jump, which makes it useless for crossing anything but flat ground."); AutoRunKeepOnStrafe = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto-run", "AutoRunKeepOnStrafe", true, "Let you strafe without dropping out of auto-run, steering around a tree or a rock instead of stopping for it. Forward and back still cancel, and remain the way to stop. The strafe is applied as a sideways lean on your current heading rather than as a turn, so letting go of the key puts you back on the original line instead of leaving you running off at an angle."); AutoRunKeepOnCrouch = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto-run", "AutoRunKeepOnCrouch", true, "Let you crouch without dropping out of auto-run, so holding a line while sneaking works the same as it does while running. The crouch itself still happens, and because crouching and sprinting are mutually exclusive in this game it turns the sprint toggle off - so auto-run continues at sneak speed rather than stopping."); AutoRunSuppressDodge = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto-run", "AutoRunSuppressDodge", true, "Stop auto-run turning a jump into a dodge. Holding block is the only way to steer while auto-running, and with a shield up the game reads jump as a dodge roll - which ends the run and sends you somewhere you did not ask to go. Crouching does the same thing. With this on, a jump while auto-running is always a jump, and the dodge key does nothing until the run ends. Dodging is a combat move and auto-run is for covering ground, so the two never want each other."); AutoRunSuppressBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto-run", "AutoRunSuppressBlock", true, "Keep the shield down while auto-running. Vanilla already does this much: while you are only steering, it clears the block every frame. What it does not survive is steering and doing something else at the same time - jump, strafe or crouch and the shield goes up, slowing you and cutting stamina and eitr regeneration for a guard you never asked for. With this on the rule holds whatever else you press. Releasing block, or ending the run, gives the shield back immediately."); NoCrouchWhileFlying = ((BaseUnityPlugin)this).Config.Bind<bool>("Auto-run", "NoCrouchWhileFlying", true, "Take crouch out of the picture entirely while flying, so the crouch key only ever means descend. Only reachable with the console 'fly' command, and ignored entirely if you never use it. Sneaking is pointless in flight - nothing can target you, on the ground or otherwise - and a crouch left toggled on in the air is actively harmful: it halves nothing, hides nothing, and turns the climb key into a dodge roll. Any crouch already held is dropped as you take off."); StickySprintToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("Sprint", "StickySprintToggle", true, "Make the sprint toggle stay on when you stop moving, instead of being thrown away the moment you stand still. With this off, every pause - looting a body, opening a door, waiting for a boat - costs another press of the sprint key to get going again. Running out of stamina still turns it off, since that is the one case where the game needs you to notice. Requires the game's own toggle setting for it to be on, under Gameplay settings - Valheim labels that one \"run\" rather than sprint - because if you hold the key to sprint instead, there is no toggle to keep."); JumpBufferSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Jump", "JumpBufferSeconds", 0.3f, new ConfigDescription("How long a jump press is remembered and retried when the game refuses it, in seconds. A press that lands a fraction too early - while still in the air from a stride or a step off a ledge - is otherwise dropped silently. Set to 0 to turn the buffer off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); LogJumpBlocks = ((BaseUnityPlugin)this).Config.Bind<bool>("Jump", "LogJumpBlocks", false, "Diagnostic. On every jump press, log the state the game decides from: whether you are grounded, blocking, crouched, attacking and so on. Use it to find out what is actually swallowing a jump."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "VerboseLogging", false, "Log the moments this mod steps in - an auto-run kept through a jump, a jump taken over from the dodge handler, a crouch dropped on takeoff. Off by default because none of it is actionable when things are working, and writing to the log is not free. Turn it on if a setting does not seem to be doing anything."); harmony.PatchAll(); } private void Update() { UpdateJumpBuffer(); } private static void LogInfo(string message) { if (VerboseLogging != null && VerboseLogging.Value) { Debug.Log((object)message); } } private static bool IsTypingText() { if (Console.IsVisible() || TextInput.IsVisible() || Minimap.InTextInput() || PlayerCustomizaton.IsBarberGuiVisible()) { return true; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } BuildUi val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_buildUi : null); if ((Object)(object)val != (Object)null) { return val.SearchFieldFocused; } return false; } private static bool PlayerTakingInput(Player player) { if (TakeInputMethod == null) { return !IsTypingText(); } object obj = TakeInputMethod.Invoke(player, null); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static bool ReadBool(FieldInfo field, object target, bool fallback) { if (field == null || target == null) { return fallback; } object value = field.GetValue(target); if (value is bool) { return (bool)value; } return fallback; } private static float ReadFloat(FieldInfo field, object target, float fallback) { if (field == null || target == null) { return fallback; } object value = field.GetValue(target); if (value is float) { return (float)value; } return fallback; } private static void UpdateJumpBuffer() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (!PlayerTakingInput(localPlayer)) { _jumpPressedAt = float.NegativeInfinity; return; } bool buttonDown = ZInput.GetButtonDown("Jump"); if (buttonDown) { _jumpPressedAt = Time.time; if (LogJumpBlocks.Value) { LogJumpState(localPlayer); } } float value = JumpBufferSeconds.Value; if (!(value <= 0f) && _jumpPressedAt != float.NegativeInfinity) { if (Time.time - _jumpPressedAt > value) { _jumpPressedAt = float.NegativeInfinity; } else if (ReadFloat(JumpTimerField, localPlayer, 1f) < 0.1f) { _jumpPressedAt = float.NegativeInfinity; } else if (!buttonDown && ((Character)localPlayer).IsOnGround() && !((Character)localPlayer).InDodge() && !((Character)localPlayer).InAttack() && !((Character)localPlayer).IsStaggering()) { ((Character)localPlayer).Jump(false); } } } private static void LogJumpState(Player player) { float num = ReadFloat(LastGroundTouchField, player, -1f); object arg = CrouchToggledField?.GetValue(player); Debug.Log((object)("Jump pressed: " + $"onGround={((Character)player).IsOnGround()} lastGroundTouch={num:0.###} " + $"blocking={((Character)player).IsBlocking()} crouching={((Character)player).IsCrouching()} crouchToggled={arg} " + $"inAttack={((Character)player).InAttack()} inDodge={((Character)player).InDodge()} staggering={((Character)player).IsStaggering()} " + $"knockedBack={((Character)player).IsKnockedBack()} encumbered={((Character)player).IsEncumbered()} " + $"stamina={((Character)player).HaveStamina(((Character)player).m_jumpStaminaUsage)}")); } }