Compare commits
6 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ba1c1b5a60 | |||
| 9b0bd3c03f | |||
| 043e3a2def | |||
| 3327e9680c | |||
| 009dc313d4 | |||
| 0602b6ad4b |
6 changed files with 135 additions and 82 deletions
|
|
@ -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 270
|
#define JUMP_VELOCITY 275
|
||||||
|
|
||||||
#define TIMER_LAND 130
|
#define TIMER_LAND 130
|
||||||
#define TIMER_GESTURE (34*66+50)
|
#define TIMER_GESTURE (34*66+50)
|
||||||
|
|
@ -77,6 +77,8 @@ 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 );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,32 +353,55 @@ static void PM_SetMovementDir( void ) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=============
|
=============
|
||||||
PM_CheckJump
|
PM_Jump
|
||||||
|
|
||||||
|
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; // don't allow jump until all buttons are up
|
return qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// must wait for jump to be released
|
// QL: 100ms minimum delay between jumps.
|
||||||
if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
|
// Prevents same-frame double-fires. Step jumps launch the player
|
||||||
// clear upmove so cmdscale doesn't lower running speed
|
// high enough (~400ms airtime) that the next stair collision
|
||||||
pm->cmd.upmove = 0;
|
// naturally exceeds this threshold.
|
||||||
|
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 ) {
|
||||||
|
|
@ -388,6 +411,23 @@ static qboolean PM_CheckJump( 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,27 +234,23 @@ 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);
|
||||||
VectorCopy (pm->ps->velocity, start_v);
|
VectorCopy (pm->ps->velocity, start_v);
|
||||||
|
|
||||||
if ( PM_SlideMove( gravity ) == 0 ) {
|
if ( PM_SlideMove( gravity ) == 0 ) {
|
||||||
return; // we got exactly where we wanted to go first try
|
return; // we got exactly where we wanted to go first try
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorCopy(start_o, down);
|
// QL: compute projected position (where player would be without collision)
|
||||||
down[2] -= STEPSIZE;
|
VectorMA( start_o, pml.frametime, start_v, projected );
|
||||||
pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
|
|
||||||
VectorSet(up, 0, 0, 1);
|
// QL pm_airSteps: allow step-ups with upward velocity.
|
||||||
// never step up when you still have up velocity
|
// Q3 blocked step-ups during jumps unless ground was directly below.
|
||||||
if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 ||
|
// This prevented smooth stair traversal while bunny-hopping.
|
||||||
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);
|
||||||
|
|
@ -285,27 +281,29 @@ 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 ) {
|
||||||
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
|
// QL master gate: trace from original position to final position.
|
||||||
// if the down trace can trace back to the original position directly, don't step
|
// If the trace hits geometry (fraction < 1.0), a real step was crossed.
|
||||||
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask);
|
// On flat ground with micro-bumps, the trace is clear and this entire
|
||||||
if ( trace.fraction == 1.0 ) {
|
// block is skipped — filtering out false positives.
|
||||||
// use the original move
|
pm->trace( &trace, start_o, pm->mins, pm->maxs, pm->ps->origin,
|
||||||
VectorCopy (down_o, pm->ps->origin);
|
pm->ps->clientNum, pm->tracemask );
|
||||||
VectorCopy (down_v, pm->ps->velocity);
|
|
||||||
if ( pm->debugLevel ) {
|
if ( trace.fraction < 1.0 ) {
|
||||||
Com_Printf("%i:bend\n", c_pmove);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// use the step move
|
|
||||||
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 );
|
||||||
|
|
@ -317,6 +315,35 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1212,6 +1212,9 @@ 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;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1147,7 +1147,8 @@ 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 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -343,13 +343,20 @@ void IN_DIMouse( int *mx, int *my ) {
|
||||||
DIMOUSESTATE2 state;
|
DIMOUSESTATE2 state;
|
||||||
DWORD dwElements;
|
DWORD dwElements;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
int value;
|
int i, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch new events
|
// flush the buffered data — we only use it for wheel events.
|
||||||
|
// buttons are handled via immediate state below to avoid
|
||||||
|
// stuck keys from buffer overflow.
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
dwElements = 1;
|
dwElements = 1;
|
||||||
|
|
@ -361,51 +368,11 @@ void IN_DIMouse( int *mx, int *my ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unable to read data or no data available */
|
if ( FAILED(hr) || dwElements == 0 ) {
|
||||||
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) {
|
||||||
|
|
@ -419,16 +386,29 @@ void IN_DIMouse( int *mx, int *my ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the raw delta counter and ignore
|
// read immediate device state for axes and buttons
|
||||||
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue