Compare commits

..

No commits in common. "implant2" and "master" have entirely different histories.

6 changed files with 82 additions and 135 deletions

View file

@ -26,7 +26,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define STEPSIZE 18 #define STEPSIZE 18
#define JUMP_VELOCITY 275 #define JUMP_VELOCITY 270
#define TIMER_LAND 130 #define TIMER_LAND 130
#define TIMER_GESTURE (34*66+50) #define TIMER_GESTURE (34*66+50)
@ -77,8 +77,6 @@ void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce );
void PM_AddTouchEnt( int entityNum ); void PM_AddTouchEnt( int entityNum );
void PM_AddEvent( int newEvent ); void PM_AddEvent( int newEvent );
qboolean PM_CanJump( void );
void PM_Jump( void );
qboolean PM_SlideMove( qboolean gravity ); qboolean PM_SlideMove( qboolean gravity );
void PM_StepSlideMove( qboolean gravity ); void PM_StepSlideMove( qboolean gravity );

View file

@ -353,55 +353,32 @@ static void PM_SetMovementDir( void ) {
/* /*
============= =============
PM_Jump PM_CheckJump
Applies jump velocity, event, and animation.
Extracted from PM_CheckJump so it can be called
from other contexts (step jump, double jump, etc).
============= =============
*/ */
/* static qboolean PM_CheckJump( void ) {
=============
PM_CanJump
Returns qtrue if a jump would succeed right now.
Checks both player state AND input (upmove >= 10).
Used by PM_StepSlideMove to decide whether stepping
up stairs should trigger a jump.
=============
*/
qboolean PM_CanJump( void ) {
if ( pm->ps->pm_flags & PMF_RESPAWNED ) { if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return qfalse; return qfalse; // don't allow jump until all buttons are up
}
if ( pm->ps->pm_type != PM_NORMAL ) {
return qfalse;
} }
if ( pm->cmd.upmove < 10 ) { if ( pm->cmd.upmove < 10 ) {
// not holding jump
return qfalse; return qfalse;
} }
// QL: 100ms minimum delay between jumps. // must wait for jump to be released
// Prevents same-frame double-fires. Step jumps launch the player if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
// high enough (~400ms airtime) that the next stair collision // clear upmove so cmdscale doesn't lower running speed
// naturally exceeds this threshold. pm->cmd.upmove = 0;
if ( pm->cmd.serverTime - pm->ps->lastJumpTime < 100 ) {
return qfalse; return qfalse;
} }
return qtrue;
}
void PM_Jump( void ) {
pml.groundPlane = qfalse; // jumping away pml.groundPlane = qfalse; // jumping away
pml.walking = qfalse; pml.walking = qfalse;
pm->ps->pm_flags |= PMF_JUMP_HELD; pm->ps->pm_flags |= PMF_JUMP_HELD;
pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->groundEntityNum = ENTITYNUM_NONE;
pm->ps->velocity[2] = JUMP_VELOCITY; pm->ps->velocity[2] = JUMP_VELOCITY;
pm->ps->lastJumpTime = pm->cmd.serverTime;
PM_AddEvent( EV_JUMP ); PM_AddEvent( EV_JUMP );
if ( pm->cmd.forwardmove >= 0 ) { if ( pm->cmd.forwardmove >= 0 ) {
@ -411,23 +388,6 @@ void PM_Jump( void ) {
PM_ForceLegsAnim( LEGS_JUMPB ); PM_ForceLegsAnim( LEGS_JUMPB );
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
} }
}
/*
=============
PM_CheckJump
=============
*/
static qboolean PM_CheckJump( void ) {
if ( !PM_CanJump() ) {
return qfalse;
}
// QL autohop: no PMF_JUMP_HELD gate here.
// The Pmove() outer loop forces upmove=20 when
// PMF_JUMP_HELD is set, allowing continuous bunny hopping.
PM_Jump();
return qtrue; return qtrue;
} }

View file

@ -234,8 +234,9 @@ void PM_StepSlideMove( qboolean gravity ) {
vec3_t start_o, start_v; vec3_t start_o, start_v;
vec3_t down_o, down_v; vec3_t down_o, down_v;
trace_t trace; trace_t trace;
// float down_dist, up_dist;
// vec3_t delta, delta2;
vec3_t up, down; vec3_t up, down;
vec3_t projected;
float stepSize; float stepSize;
VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->origin, start_o);
@ -245,12 +246,15 @@ void PM_StepSlideMove( qboolean gravity ) {
return; // we got exactly where we wanted to go first try return; // we got exactly where we wanted to go first try
} }
// QL: compute projected position (where player would be without collision) VectorCopy(start_o, down);
VectorMA( start_o, pml.frametime, start_v, projected ); down[2] -= STEPSIZE;
pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
// QL pm_airSteps: allow step-ups with upward velocity. VectorSet(up, 0, 0, 1);
// Q3 blocked step-ups during jumps unless ground was directly below. // never step up when you still have up velocity
// This prevented smooth stair traversal while bunny-hopping. if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 ||
DotProduct(trace.plane.normal, up) < 0.7)) {
return;
}
VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->origin, down_o);
VectorCopy (pm->ps->velocity, down_v); VectorCopy (pm->ps->velocity, down_v);
@ -281,29 +285,27 @@ void PM_StepSlideMove( qboolean gravity ) {
if ( !trace.allsolid ) { if ( !trace.allsolid ) {
VectorCopy (trace.endpos, pm->ps->origin); 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 ) { if ( trace.fraction < 1.0 ) {
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 ); PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
} }
#if 0
// if the down trace can trace back to the original position directly, don't step
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask);
if ( trace.fraction == 1.0 ) {
// use the original move
VectorCopy (down_o, pm->ps->origin);
VectorCopy (down_v, pm->ps->velocity);
if ( pm->debugLevel ) {
Com_Printf("%i:bend\n", c_pmove);
} }
} else
// QL master gate: trace from original position to final position. #endif
// If the trace hits geometry (fraction < 1.0), a real step was crossed. {
// On flat ground with micro-bumps, the trace is clear and this entire // use the step move
// block is skipped — filtering out false positives.
pm->trace( &trace, start_o, pm->mins, pm->maxs, pm->ps->origin,
pm->ps->clientNum, pm->tracemask );
if ( trace.fraction < 1.0 ) {
float delta; float delta;
delta = pm->ps->origin[2] - start_o[2]; delta = pm->ps->origin[2] - start_o[2];
// step sound events
if ( delta > 2 ) { if ( delta > 2 ) {
if ( delta < 7 ) { if ( delta < 7 ) {
PM_AddEvent( EV_STEP_4 ); PM_AddEvent( EV_STEP_4 );
@ -315,35 +317,6 @@ void PM_StepSlideMove( qboolean gravity ) {
PM_AddEvent( EV_STEP_16 ); PM_AddEvent( EV_STEP_16 );
} }
} }
// QL air-step friction: 3% horizontal speed penalty per airborne step-up
if ( !pml.groundPlane && delta > 0 && start_v[2] > 0 ) {
pm->ps->velocity[0] *= 0.97f;
pm->ps->velocity[1] *= 0.97f;
}
// QL step jump gate: validate stair geometry at projected position.
// Traces DOWN at where the player would be without collision.
// Walls fail (startsolid), flat ground fails (delta<=0), stairs pass.
if ( delta > 0 && pm->ps->pm_type == PM_NORMAL
&& pm->waterlevel < 2 && PM_CanJump() ) {
vec3_t stepStart, stepEnd;
trace_t stepTrace;
VectorCopy( projected, stepStart );
VectorCopy( projected, stepEnd );
stepStart[2] += STEPSIZE;
stepEnd[2] -= STEPSIZE;
pm->trace( &stepTrace, stepStart, pm->mins, pm->maxs,
stepEnd, pm->ps->clientNum, pm->tracemask );
if ( !stepTrace.startsolid && !stepTrace.allsolid
&& stepTrace.plane.normal[2] >= MIN_WALK_NORMAL ) {
PM_Jump();
}
}
if ( pm->debugLevel ) { if ( pm->debugLevel ) {
Com_Printf("%i:stepped\n", c_pmove); Com_Printf("%i:stepped\n", c_pmove);
} }

View file

@ -1212,9 +1212,6 @@ typedef struct playerState_s {
int pmove_framecount; // FIXME: don't transmit over the network int pmove_framecount; // FIXME: don't transmit over the network
int jumppad_frame; int jumppad_frame;
int entityEventSequence; int entityEventSequence;
// QL additions
int lastJumpTime; // serverTime of last jump, for 100ms cooldown
} playerState_t; } playerState_t;

View file

@ -1147,8 +1147,7 @@ netField_t playerStateFields[] =
{ PSF(grapplePoint[1]), 0 }, { PSF(grapplePoint[1]), 0 },
{ PSF(grapplePoint[2]), 0 }, { PSF(grapplePoint[2]), 0 },
{ PSF(jumppad_ent), 10 }, { PSF(jumppad_ent), 10 },
{ PSF(loopSound), 16 }, { PSF(loopSound), 16 }
{ PSF(lastJumpTime), 32 }
}; };
/* /*

View file

@ -343,20 +343,13 @@ void IN_DIMouse( int *mx, int *my ) {
DIMOUSESTATE2 state; DIMOUSESTATE2 state;
DWORD dwElements; DWORD dwElements;
HRESULT hr; HRESULT hr;
int i, value; int value;
static BYTE oldButtons[8];
static const int buttonKeys[5] = {
K_MOUSE1, K_MOUSE2, K_MOUSE3, K_MOUSE4, K_MOUSE5
};
if ( !g_pMouse ) { if ( !g_pMouse ) {
return; return;
} }
// flush the buffered data — we only use it for wheel events. // fetch new events
// buttons are handled via immediate state below to avoid
// stuck keys from buffer overflow.
for (;;) for (;;)
{ {
dwElements = 1; dwElements = 1;
@ -368,11 +361,51 @@ void IN_DIMouse( int *mx, int *my ) {
return; return;
} }
if ( FAILED(hr) || dwElements == 0 ) { /* Unable to read data or no data available */
if ( FAILED(hr) ) {
break;
}
if ( dwElements == 0 ) {
break; break;
} }
switch (od.dwOfs) { switch (od.dwOfs) {
case DIMOFS_BUTTON0:
if (od.dwData & 0x80)
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE1, qtrue, 0, NULL );
else
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE1, qfalse, 0, NULL );
break;
case DIMOFS_BUTTON1:
if (od.dwData & 0x80)
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE2, qtrue, 0, NULL );
else
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE2, qfalse, 0, NULL );
break;
case DIMOFS_BUTTON2:
if (od.dwData & 0x80)
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE3, qtrue, 0, NULL );
else
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE3, qfalse, 0, NULL );
break;
case DIMOFS_BUTTON3:
if (od.dwData & 0x80)
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE4, qtrue, 0, NULL );
else
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE4, qfalse, 0, NULL );
break;
case DIMOFS_BUTTON4:
if (od.dwData & 0x80)
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE5, qtrue, 0, NULL );
else
Sys_QueEvent( od.dwTimeStamp, SE_KEY, K_MOUSE5, qfalse, 0, NULL );
break;
case DIMOFS_Z: case DIMOFS_Z:
value = od.dwData; value = od.dwData;
if (value < 0) { if (value < 0) {
@ -386,29 +419,16 @@ void IN_DIMouse( int *mx, int *my ) {
} }
} }
// read immediate device state for axes and buttons // read the raw delta counter and ignore
// the individual sample time / values
hr = IDirectInputDevice8_GetDeviceState(g_pMouse, hr = IDirectInputDevice8_GetDeviceState(g_pMouse,
sizeof(DIMOUSESTATE2), &state); sizeof(DIMOUSESTATE2), &state);
if ( FAILED(hr) ) { if ( FAILED(hr) ) {
*mx = *my = 0; *mx = *my = 0;
return; return;
} }
*mx = state.lX; *mx = state.lX;
*my = state.lY; *my = state.lY;
// generate button press/release events from immediate state.
// comparing against previous state avoids stuck buttons entirely —
// if a press or release was missed in the buffer, the immediate
// state catches it next frame.
for ( i = 0; i < 5; i++ ) {
BYTE down = state.rgbButtons[i] & 0x80;
if ( down != oldButtons[i] ) {
Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, buttonKeys[i],
down ? qtrue : qfalse, 0, NULL );
oldButtons[i] = down;
}
}
} }
/* /*