local function PreThinkJump(player) P_DoJump(player, true); -- Prevent using abilities immediately, by removing jump inputs. player.cmd.buttons = $1 | BT_JUMP; player.pflags = $1 | PF_JUMPDOWN; end local function RestoreAbility(player) player.secondjump = 0; player.pflags = $1 & ~PF_THOKKED; end local function RemoveAbility(player) player.secondjump = UINT8_MAX; player.pflags = $1 | PF_THOKKED; end local function JumpLeniencyThink(player) if not (player.mo and player.mo.valid) return; end local mo = player.mo; local latency = 0; -- Default to 0 for EXEs without cmd.latency. pcall(function() latency = player.cmd.latency; end); -- Init variables if (mo.coyoteTime == nil) mo.coyoteTime = 0; end if (mo.recoveryWait == nil) mo.recoveryWait = 0; end if (player.exiting) -- Can't control anyway, don't need to do this. return; end local pressedJump = false; if (player.cmd.buttons & BT_JUMP) and not (player.pflags & PF_JUMPDOWN) pressedJump = true; end if (P_PlayerInPain(player) == true) mo.coyoteTime = 0; -- Reset coyote time mo.recoveryWait = $1 + 1; -- Increment recovery wait time. -- The recovery jump from SA2, where you can jump out of your pain state. if (pressedJump == true) local baseRecoveryWait = (2*TICRATE)/3; -- 0.667 seconds of waiting, - your latency. if (mo.recoveryWait > baseRecoveryWait - latency) PreThinkJump(player); --[[ RemoveAbility(player); -- Reset momentum so you can move a bit. player.mo.momx = 0; player.mo.momy = 0; --]] RestoreAbility(player); end end -- Don't do any of the coyote time thinking. return; end -- Reset recovery wait time outside of pain state. mo.recoveryWait = 0; -- "Coyote time" is how much time you have after leaving the ground where you can jump off. -- Many modern platformers do this, especially 3D. -- Prevents lots of "the jump didn't jump". local baseCoyoteTime = TICRATE/4; -- 0.25 seconds, + your latency. -- Check if you're in a state where you would normally be allowed to jump. local canJump = false; if ((P_IsObjectOnGround(mo) == true) or (P_InQuicksand(mo) == true)) and (player.powers[pw_carry] == CR_NONE) canJump = true; end if (player.pflags & PF_JUMPED) -- We jumped. We should not have coyote time. mo.coyoteTime = 0; elseif (canJump == true) -- Set the coyote time while in a state where you can jump. mo.coyoteTime = baseCoyoteTime + latency; else if (pressedJump == true) and (mo.coyoteTime > 0) -- Pressed jump in a state where you can't jump, -- but you have coyote time. So we'll give you a jump anyway! PreThinkJump(player); RestoreAbility(player); mo.coyoteTime = 0; end if (mo.coyoteTime > 0) -- Reduce coyote timer while in a state where you can't jump. mo.coyoteTime = $1 - 1; end end end addHook("PreThinkFrame", function() for player in players.iterate do JumpLeniencyThink(player); end end);