From 6de5824395c45e914bbf7733da4b7611c8f1f470 Mon Sep 17 00:00:00 2001 From: serge_shubin Date: Sat, 21 Mar 2026 06:08:10 +0800 Subject: [PATCH] QL stair momentum: conditional velocity clip + air steps + step jump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit THE key QL stair mechanic: skip velocity clip in PM_StepSlideMove when velocity is moving away from the step surface (dot product >= 0). Q3 always clips, zeroing vertical momentum on every step. QL preserves upward velocity through steps, enabling smooth bunny-hop stair traversal. Also includes: - Remove Q3 velocity[2]>0 gate (pm_airSteps): allow step-ups during jumps - pml.isStepJump flag for step jump context in PM_Jump - PM_Jump: additive velocity for step jumps (+=270, min 270, max 700) - 100ms jump cooldown via lastJumpTime Has known glitches — saved for reference before fixing. Co-Authored-By: Claude Opus 4.6 (1M context) --- code/game/bg_local.h | 4 ++++ code/game/bg_pmove.c | 26 +++++++++++++++++++++++++- code/game/bg_slidemove.c | 20 ++++++++++---------- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/code/game/bg_local.h b/code/game/bg_local.h index d3ede18..f9cb6f5 100644 --- a/code/game/bg_local.h +++ b/code/game/bg_local.h @@ -51,6 +51,10 @@ typedef struct { vec3_t previous_origin; vec3_t previous_velocity; int previous_waterlevel; + + // QL step jump context flag — set before calling PM_Jump + // from PM_StepSlideMove to get additive velocity instead of flat set + qboolean isStepJump; } pml_t; extern pmove_t *pm; diff --git a/code/game/bg_pmove.c b/code/game/bg_pmove.c index 36bae08..e9e1d89 100644 --- a/code/game/bg_pmove.c +++ b/code/game/bg_pmove.c @@ -383,6 +383,14 @@ qboolean PM_CanJump( void ) { return qfalse; } + // QL: 100ms minimum delay between jumps. + // Prevents same-frame double-fires. Step jumps launch the player + // high enough (~400ms airtime) that the next stair collision + // naturally exceeds this threshold. + if ( pm->cmd.serverTime - pm->ps->lastJumpTime < 100 ) { + return qfalse; + } + return qtrue; } @@ -392,7 +400,23 @@ void PM_Jump( void ) { pm->ps->pm_flags |= PMF_JUMP_HELD; pm->ps->groundEntityNum = ENTITYNUM_NONE; - pm->ps->velocity[2] = JUMP_VELOCITY; + + if ( pml.isStepJump ) { + // QL step jump: additive velocity, preserving existing upward motion. + // This launches the player high enough to skip several stairs, + // so the next collision is >100ms away and the cooldown works. + pm->ps->velocity[2] += JUMP_VELOCITY; + if ( pm->ps->velocity[2] < JUMP_VELOCITY ) { + pm->ps->velocity[2] = JUMP_VELOCITY; + } + if ( pm->ps->velocity[2] > 700 ) { + pm->ps->velocity[2] = 700; + } + } else { + // Normal ground jump: flat velocity set (Q3 behavior) + pm->ps->velocity[2] = JUMP_VELOCITY; + } + pm->ps->lastJumpTime = pm->cmd.serverTime; PM_AddEvent( EV_JUMP ); diff --git a/code/game/bg_slidemove.c b/code/game/bg_slidemove.c index ed1349f..6c7b307 100644 --- a/code/game/bg_slidemove.c +++ b/code/game/bg_slidemove.c @@ -246,15 +246,9 @@ void PM_StepSlideMove( qboolean gravity ) { return; // we got exactly where we wanted to go first try } - VectorCopy(start_o, down); - down[2] -= STEPSIZE; - pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); - VectorSet(up, 0, 0, 1); - // never step up when you still have up velocity - if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || - DotProduct(trace.plane.normal, up) < 0.7)) { - return; - } + // QL pm_airSteps: allow step-ups with upward velocity. + // Q3 blocked step-ups during jumps unless ground was directly below. + // This prevented smooth stair traversal while bunny-hopping. VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); @@ -285,8 +279,14 @@ void PM_StepSlideMove( qboolean gravity ) { if ( !trace.allsolid ) { VectorCopy (trace.endpos, pm->ps->origin); } + // QL: only clip velocity to step surface when moving INTO it. + // Skip clip when velocity is already moving away (preserves + // upward momentum through stair steps during bunny-hopping). if ( trace.fraction < 1.0 ) { - PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); + float vdotn = DotProduct( pm->ps->velocity, trace.plane.normal ); + if ( vdotn < 0 || fabs( vdotn ) < 0.001f ) { + PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); + } } #if 0