//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/bg_pmove.c $ // $Revision:: 62 $ // $Date:: 10/13/03 9:42a $ // // Copyright (C) 1999 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // // DESCRIPTION: // #include "q_shared.h" #include "bg_public.h" // all of the locals will be zeroed before each // pmove, just to make sure we don't have // any differences when running on client or server typedef struct { vec3_t forward, left, up; vec3_t flat_forward, flat_left, flat_up; //float forward_speed, side_speed; float frametime; int msec; qboolean walking; qboolean groundPlane; trace_t groundTrace; float impactSpeed; vec3_t previous_origin; vec3_t previous_velocity; int previous_waterlevel; } pml_t; pmove_t *pm; pml_t pml; qboolean slopeSlideFlag = qfalse; // movement parameters const float pm_swimScale = 0.50f; const float pm_wadeScale = 0.70f; const float pm_slipperyfriction = 0.25f; const float pm_leaninspeed = 75.0f; const float pm_leanoutspeed = 150.0f; const float pm_leanmaxdelta = 35.0f; int c_pmove = 0; void PM_CrashLand( void ); qboolean PM_ShouldCrashLand( void ); /* =============== PM_AddTouchEnt =============== */ void PM_AddTouchEnt ( int entityNum ) { int i; if ( entityNum == ENTITYNUM_WORLD ) { return; } if ( pm->numtouch == MAXTOUCH ) { return; } // see if it is already added for( i = 0; i < pm->numtouch; i++ ) { if ( pm->touchents[ i ] == entityNum ) { return; } } // add it pm->touchents[ pm->numtouch ] = entityNum; pm->numtouch++; } /* ================== PM_ClipVelocity Slide off of the impacting surface ================== */ void PM_ClipVelocity ( const vec3_t in, const vec3_t normal, vec3_t out, float overbounce ) { float backoff; float change; int i; backoff = DotProduct( in, normal ); if ( backoff < 0.0f ) { backoff *= overbounce; } else { backoff /= overbounce; } for( i = 0; i < 3; i++ ) { change = normal[ i ] * backoff; out[ i ] = in[ i ] - change; } } /* ================== PM_SlideMove Returns qtrue if the velocity was clipped in some way ================== */ #define MAX_CLIP_PLANES 5 qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; int gravtmp; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravity ) { if ( pm->ps->pm_flags & PMF_FLIGHT ) gravtmp = 0; else gravtmp = pm->ps->gravity; VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[2] -= gravtmp * pml.frametime; pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5f; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } time_left = pml.frametime; // never turn against the ground plane if ( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[0] ); } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[numplanes] ); numplanes++; for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // add exact pull from gravity // if ( gravity && !(pm->ps->pm_flags & PMF_FLIGHT) ) { // pm->ps->origin[2] += 0.5 * time_left * time_left; // } // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask, qtrue ); if ( trace.startsolid && trace.entityNum != ENTITYNUM_WORLD ) { // stuck in an entity, so try to pretend it's not there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, trace.entityNum, pm->tracemask, qtrue ); } if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0.0f; // don't build up falling damage, but allow sideways acceleration return qtrue; } if ( pm->trypush && pm->trypush( trace.entityNum, pm->ps->origin, end ) ) continue; if (trace.fraction > 0.0f) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1.0f) { break; // moved the entire distance } /*if ( ( trace.plane.normal[ 2 ] < MIN_WALK_NORMAL ) && ( trace.plane.normal[ 2 ] > 0 ) ) { // treat steep walls as vertical trace.plane.normal[ 2 ] = 0; VectorNormalize( trace.plane.normal ); }*/ // save entity for contact PM_AddTouchEnt( trace.entityNum ); time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // for ( i = 0 ; i < numplanes ; i++ ) { if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99f ) { VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( pm->ps->velocity, planes[i] ); if ( into >= 0.1f ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if ( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1f ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0.0f ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1f ) { continue; // move doesn't interact with the plane } // stop dead at a triple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if ( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } // don't change velocity if in a timer (FIXME: is this correct?) if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, pm->ps->velocity ); } return ( bumpcount != 0 ); } /* void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; vec3_t down_o, down_v; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_SlideMove( gravity ) == 0 ) { 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, qtrue); 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; } VectorCopy (start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher pm->trace (&trace, up, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, qtrue); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } // try slidemove from this position VectorCopy (up, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= STEPSIZE; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, qtrue ); if ( !trace.allsolid ) { if ( ( trace.fraction < 1.0 ) && trace.plane.normal[ 2 ] < MIN_WALK_NORMAL ) { slopeSlideFlag = qtrue; //VectorCopy (start_o, pm->ps->origin); //return; } else slopeSlideFlag = qfalse; } VectorCopy (trace.endpos, pm->ps->origin); if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } // use the step move pm->stepped = qtrue; if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } }*/ float PM_TryStepSlideMove( qboolean gravity, float stepSize ) { trace_t trace; vec3_t up, down; vec3_t diff; float distance; VectorCopy( pm->ps->origin, up ); up[2] += stepSize; // test the player position if they were a stepheight higher pm->trace( &trace, up, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, qtrue ); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_WPrintf("%i:bend can't step\n", c_pmove); } return 0.0f; // can't step up } // try slidemove from this position VectorCopy( up, pm->ps->origin ); //VectorCopy( start_v, pm->ps->velocity ); PM_SlideMove( gravity ); VectorSubtract( pm->ps->origin, up, diff ); distance = VectorLength( diff ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, qtrue ); if ( !trace.allsolid ) { VectorCopy (trace.endpos, pm->ps->origin); } if ( trace.fraction < 1.0f ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } return distance; } void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; float try1Distance, try2Distance, simpleTryDistance; vec3_t try1_o, try1_v; vec3_t simpleTry1_o, simpleTry1_v; vec3_t simpleDiff; // Save off the starting info VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); // Try the movement directly forward first if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } // Save off attempt VectorCopy( pm->ps->origin, simpleTry1_o ); VectorCopy( pm->ps->velocity, simpleTry1_v ); VectorSubtract( simpleTry1_o, start_o, simpleDiff ); simpleTryDistance = VectorLength( simpleDiff ); // Reset everything that could have changed so far VectorCopy( start_o, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); // Try movement when stepping up a full stepsize try1Distance = PM_TryStepSlideMove( gravity, STEPSIZE ); // Save off attempt VectorCopy( pm->ps->origin, try1_o ); VectorCopy( pm->ps->velocity, try1_v ); // Reset everything that could have changed so far VectorCopy( start_o, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); // Try movement when stepping up half the stepsize try2Distance = PM_TryStepSlideMove( gravity, STEPSIZE / 2.0f ); // Pick the move that went the furthest forward if ( try1Distance > try2Distance ) { VectorCopy( try1_o, pm->ps->origin ); VectorCopy( try1_v, pm->ps->velocity ); } if ( ( try1Distance < 0.001f ) && ( try2Distance < 0.001f ) && ( simpleTryDistance > 0.0f ) ) { VectorCopy( simpleTry1_o, pm->ps->origin ); VectorCopy( simpleTry1_v, pm->ps->velocity ); } pm->stepped = qtrue; // allow client to smooth out the step if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } /* ================== PM_Friction Handles both ground friction and water friction ================== */ void PM_Friction ( void ) { vec3_t vec; float *vel; float speed; float newspeed; float control; float drop; vel = pm->ps->velocity; VectorCopy( vel, vec ); if ( pml.walking ) { // ignore slope movement vec[ 2 ] = 0.0f; } speed = VectorLength( vec ); if ( speed < 1.0f ) { // allow sinking underwater vel[ 0 ] = 0; vel[ 1 ] = 0; return; } drop = 0; // apply ground friction if ( pml.walking ) { // if getting knocked back, no friction if ( !( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) { control = ( speed < (float)pm->ps->pm_stopspeed ) ? (float)pm->ps->pm_stopspeed : speed; if ( pml.groundTrace.surfaceFlags & SURF_SLICK ) { drop += control * pm_slipperyfriction * pml.frametime; } else { drop += control * (float)pm->ps->pm_friction * pml.frametime; } } } // apply water friction if ( pm->waterlevel ) { if ( pm->watertype & CONTENTS_SLIME ) { drop += speed * (float)pm->ps->pm_waterfriction * 5.0f * pm->waterlevel * pml.frametime; //drop += speed * pm->ps->pm_waterfriction * 2 * pml.frametime; } else { drop += speed * (float)pm->ps->pm_waterfriction * (float)pm->waterlevel * pml.frametime; } } // scale the velocity newspeed = speed - drop; if ( newspeed < 0.0f ) { newspeed = 0; } newspeed /= speed; vel[0] = vel[ 0 ] * newspeed; vel[1] = vel[ 1 ] * newspeed; vel[2] = vel[ 2 ] * newspeed; } /* ============== PM_Accelerate Handles user intended acceleration ============== */ void PM_Accelerate ( const vec3_t wishdir, float wishspeed, float accel ) { int i; float addspeed; float accelspeed; float currentspeed; currentspeed = DotProduct( pm->ps->velocity, wishdir ); addspeed = wishspeed - currentspeed; if ( addspeed <= 0.0f ) { return; } accelspeed = accel * pml.frametime * wishspeed; if ( accelspeed > addspeed ) { accelspeed = addspeed; } for( i = 0; i < 3; i++ ) { pm->ps->velocity[ i ] += accelspeed * wishdir[ i ]; } } /* ============ PM_CmdScale Returns the scale factor to apply to cmd movements This allows the clients to use axial -127 to 127 values for all directions without getting a sqrt(2) distortion in speed. ============ */ float PM_CmdScale ( const usercmd_t *cmd ) { int max; float total; float scale; max = abs( cmd->forwardmove ); if ( abs( cmd->rightmove ) > max ) { max = abs( cmd->rightmove ); } if ( abs( cmd->upmove ) > max ) { max = abs( cmd->upmove ); } if ( !max ) { return 0; } if ( ( pm->ps->pm_flags & PMF_DUCKED ) && ( cmd->upmove >= 0 ) ) { // This means we are being forced to duck (take into account this forced down movement) total = sqrt( ( cmd->forwardmove * cmd->forwardmove ) + ( cmd->rightmove * cmd->rightmove ) + ( -127 * -127 ) ); } else { // This is the normal case total = sqrt( ( cmd->forwardmove * cmd->forwardmove ) + ( cmd->rightmove * cmd->rightmove ) + ( cmd->upmove * cmd->upmove ) ); } scale = (float)pm->ps->speed * (float)max / ( 127.0f * total ); return scale; } /* ============= PM_CheckJump ============= */ qboolean PM_CheckJump( void ) { //If we are ducked, then we can't jump //PMF_DUCKED means the player is either //pressing duck, or the player is ducked in //an area where they cannot stand up. if(pm->ps->pm_flags & PMF_DUCKED) return qfalse; /* jhefty/jwaters -- another strafe-jump fix if (pm->ps->commandTime < pm->ps->pm_landtime) return qfalse; */ /* if ( pm->ps->pm_flags & PMF_TIME_LAND ) { // hasn't been long enough since landing to jump again return qfalse; } */ if ( pm->cmd.upmove < 10 ) { // not holding jump return qfalse; } // must wait for jump to be released if ( pm->ps->pm_flags & PMF_JUMP_HELD ) { return qfalse; } pml.groundPlane = qfalse; // jumping away pml.walking = qfalse; pm->ps->pm_flags |= PMF_JUMP_HELD; pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->velocity[2] = pm->ps->jumpvelocity; return qtrue; } /* ============= PM_CheckWaterJump ============= */ qboolean PM_CheckWaterJump ( void ) { vec3_t spot; int cont; if ( pm->ps->pm_time ) { return qfalse; } // check for water jump if ( ( pm->waterlevel < 2 ) ) { return qfalse; } // if we are below the surface and not in slime, return if ( !( pm->watertype & CONTENTS_SLIME ) && ( pm->waterlevel == 3 ) ) { return qfalse; } VectorMA( pm->ps->origin, 80.0f, pml.flat_forward, spot ); spot[ 2 ] += pm->ps->viewheight - 16.0f; cont = pm->pointcontents( spot, pm->ps->clientNum ); if ( !( cont & pm->tracemask ) ) { return qfalse; } spot[ 2 ] += 48.0f; cont = pm->pointcontents( spot, pm->ps->clientNum ); if ( cont ) { return qfalse; } // jump out of water VectorScale( pml.flat_forward, 150, pm->ps->velocity ); pm->ps->velocity[ 2 ] = 600; pm->ps->pm_flags |= PMF_TIME_WATERJUMP; pm->ps->pm_time = 2000; return qtrue; } //============================================================================ /* =================== PM_WaterJumpMove Flying out of the water =================== */ void PM_WaterJumpMove ( void ) { // waterjump has no control, but falls PM_StepSlideMove( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); pm->ps->velocity[ 2 ] -= (float)pm->ps->gravity * pml.frametime; if ( pm->ps->velocity[ 2 ] < 0.0f ) { // cancel as soon as we are falling down again pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } } #define SLIME_SINK_SPEED -10.0f /* =================== PM_WaterMove =================== */ void PM_WaterMove ( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; if ( PM_CheckWaterJump() ) { PM_WaterJumpMove(); return; } // // clamp our speed if we are in slime // if ( pm->watertype & CONTENTS_SLIME ) { if ( pm->ps->velocity[ 2 ] < SLIME_SINK_SPEED ) { pm->ps->velocity[ 2 ] = SLIME_SINK_SPEED; } } PM_Friction(); scale = PM_CmdScale( &pm->cmd ); // // user intentions // if ( !scale ) { wishvel[ 0 ] = 0; wishvel[ 1 ] = 0; if ( pm->watertype & CONTENTS_SLIME ) { wishvel[ 2 ] = SLIME_SINK_SPEED; // sink towards bottom } else { wishvel[ 2 ] = -60; // sink towards bottom } } else { for( i = 0; i < 3; i++ ) { wishvel[ i ] = ( scale * pml.flat_forward[ i ] * pm->cmd.forwardmove ) - ( scale * pml.flat_left[ i ] * pm->cmd.rightmove ); } wishvel[ 2 ] += scale * pm->cmd.upmove; } if ( ( pm->watertype & CONTENTS_SLIME ) && ( pm->waterlevel > 2 ) && ( wishvel[ 2 ] < 0.0f ) ) { wishvel[ 2 ] = 0; } VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); if ( wishspeed > ( pm->ps->speed * pm_swimScale ) ) { wishspeed = pm->ps->speed * pm_swimScale; } PM_Accelerate( wishdir, wishspeed, pm->ps->pm_wateraccelerate ); PM_SlideMove( qfalse ); } /* =================== PM_StuckJumpMove Flying out of someplace we were stuck =================== */ void PM_StuckJumpMove ( void ) { // stuckjump has no control, but falls PM_StepSlideMove( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); pm->ps->velocity[ 2 ] -= pm->ps->gravity * pml.frametime; if ( pm->ps->velocity[ 2 ] < -48.0f ) { // cancel as soon as we are falling at decent clip again pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } } /* =================== PM_FlyMove Only with the flight powerup =================== */ void PM_FlyMove( void ) { float speed; float drop; float friction; float control; float newspeed; int i; vec3_t wishvel; float fmove; float smove; vec3_t wishdir; float wishspeed; float scale; // friction speed = VectorLength( pm->ps->velocity ); if ( speed < 1.0f ) { VectorCopy( vec3_origin, pm->ps->velocity ); } else { drop = 0; // extra friction friction = pm->ps->pm_friction * 1.5f; control = speed < pm->ps->pm_stopspeed ? pm->ps->pm_stopspeed : speed; drop += control * friction * pml.frametime; // scale the velocity newspeed = speed - drop; if ( newspeed < 0.0f ) { newspeed = 0; } newspeed /= speed; VectorScale( pm->ps->velocity, newspeed, pm->ps->velocity ); } // accelerate scale = PM_CmdScale( &pm->cmd ); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; pm->ps->pm_runtime = 0; for( i = 0; i < 3; i++ ) { wishvel[ i ] = ( pml.flat_forward[ i ] * fmove ) - ( pml.flat_left[ i ] * smove ); } wishvel[ 2 ] += pm->cmd.upmove; VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); wishspeed *= scale; PM_Accelerate( wishdir, wishspeed, pm->ps->pm_accelerate ); // move PM_StepSlideMove( qtrue ); //VectorMA( pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin ); } /* ============= PM_CheckStuckJump ============= */ //#define MAX_XY_VELOCITY 50 qboolean PM_CheckStuckJump ( void ) { vec3_t diff; if ( pm->ps->pm_time ) { return qfalse; } if ( pm->waterlevel > 1 ) { return qfalse; } VectorSubtract( pm->ps->origin, pml.previous_origin, diff ); if ( VectorLength( diff ) ) { return qfalse; } if ( VectorLength( pm->ps->velocity ) < 100.0f ) { return qfalse; } // we have been falling, we haven't moved and our velocity is getting dangerously high // let's give ourselves a boost straight up and opposite our current velocity /* pm->ps->velocity[ 0 ] = -pm->ps->velocity[ 0 ]; if ( pm->ps->velocity[ 0 ] > MAX_XY_VELOCITY ) pm->ps->velocity[ 0 ] = MAX_XY_VELOCITY; else if ( pm->ps->velocity[ 0 ] < -MAX_XY_VELOCITY ) pm->ps->velocity[ 0 ] = -MAX_XY_VELOCITY; pm->ps->velocity[ 1 ] = -pm->ps->velocity[ 1 ]; if ( pm->ps->velocity[ 1 ] > MAX_XY_VELOCITY ) pm->ps->velocity[ 1 ] = MAX_XY_VELOCITY; else if ( pm->ps->velocity[ 1 ] < -MAX_XY_VELOCITY ) pm->ps->velocity[ 1 ] = -MAX_XY_VELOCITY; pm->ps->velocity[ 2 ] = 500; pm->ps->pm_flags |= PMF_TIME_STUCKJUMP; pm->ps->pm_time = 2000;*/ return qtrue; } /* ============= PM_CheckTerminalVelocity ============= */ #define TERMINAL_VELOCITY 1200.0f void PM_CheckTerminalVelocity ( void ) { float oldspeed; float speed; // // how fast were we falling // oldspeed = -pml.previous_velocity[ 2 ]; // // how fast are we falling // speed = -pm->ps->velocity[ 2 ]; if ( speed <= 0.0f ) { return; } if ( ( oldspeed <= TERMINAL_VELOCITY ) && ( speed > TERMINAL_VELOCITY ) ) { pm->pmoveEvent = EV_TERMINAL_VELOCITY; } } /* =================== PM_AirMove =================== */ void PM_AirMove ( void ) { vec3_t wishvel; float fmove; float smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; vec3_t original_start; vec3_t original_end; float old_speed; vec3_t original_diff; float original_dist; vec3_t new_diff; float new_dist; float new_speed; float max_new_speed; // Save some information about original state of things VectorCopy( pm->ps->origin, original_start ); VectorMA( pm->ps->origin, pml.frametime, pm->ps->velocity, original_end ); old_speed = VectorLength( pm->ps->velocity ); //PM_Friction(); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; pm->ps->pm_runtime = 0; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); wishvel[ 0 ] = ( pml.flat_forward[ 0 ] * fmove ) - ( pml.flat_left[ 0 ] * smove ); wishvel[ 1 ] = ( pml.flat_forward[ 1 ] * fmove ) - ( pml.flat_left[ 1 ] * smove ); wishvel[ 2 ] = 0.0f; VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); wishspeed *= scale; // not on ground, so little effect on velocity PM_Accelerate( wishdir, wishspeed, pm->ps->pm_airaccelerate ); // we may have a ground plane that is very steep, even // though we don't have a groundentity // slide along the steep plane if ( pml.groundPlane ) { PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } if ( !pml.walking && pml.groundPlane ) { vec3_t vel; VectorCopy( pm->ps->velocity, vel ); vel[ 2 ] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[ 2 ] = ( pm->ps->velocity[ 2 ] + vel[ 2 ] ) * 0.5f; PM_SlideMove( qfalse ); VectorCopy( vel, pm->ps->velocity ); } else { PM_SlideMove( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); } if ( PM_CheckStuckJump() ) { PM_StuckJumpMove(); } // Get the distance we tried to move VectorSubtract( original_start, original_end, original_diff ); original_dist = VectorLength( original_diff ); // Get the distance we actually moved VectorSubtract( original_start, pm->ps->origin, new_diff ); new_dist = VectorLength( new_diff ); if ( ( new_dist < original_dist ) && ( new_dist > 0 ) ) { // Modify our velocity based on how far we got to move max_new_speed = old_speed * ( new_dist / original_dist ); new_speed = VectorLength( pm->ps->velocity ); if ( new_speed > max_new_speed ) { VectorNormalize( pm->ps->velocity ); VectorScale( pm->ps->velocity, max_new_speed, pm->ps->velocity ); } } PM_CheckTerminalVelocity(); } void AddPlane ( const vec3_t norm, vec3_t planes[ MAX_CLIP_PLANES ], int *numplanes ) { int i; if ( *numplanes >= MAX_CLIP_PLANES ) { return; } for( i = 0; i < *numplanes; i++ ) { if ( VectorCompare( planes[ i ], norm ) ) { // don't add the plane twice return; } } VectorCopy( norm, planes[ *numplanes ] ); ( *numplanes )++; } void PM_StepMove ( void ) { trace_t trace; vec3_t up; vec3_t down; vec3_t oldvelocity; vec3_t oldorigin; vec3_t velocity1; vec3_t origin1; VectorCopy( pm->ps->velocity, oldvelocity ); VectorCopy( pm->ps->origin, oldorigin ); if ( PM_SlideMove( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ) ) { VectorCopy( pm->ps->velocity, velocity1 ); VectorCopy( pm->ps->origin, origin1 ); VectorCopy( oldvelocity, pm->ps->velocity ); VectorCopy( oldorigin, up ); up[ 2 ] += STEPSIZE; //pm->trace( &trace, oldorigin, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, qtrue ); pm->trace( &trace, up, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, qtrue ); VectorCopy( trace.endpos, pm->ps->origin ); PM_SlideMove( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); VectorCopy( pm->ps->origin, down ); down[ 2 ] = oldorigin[ 2 ]; pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, qtrue ); if ( trace.plane.normal[ 2 ] < MIN_WALK_NORMAL ) { // use the first move VectorCopy( velocity1, pm->ps->velocity ); VectorCopy( origin1, pm->ps->origin ); } else { VectorCopy( trace.endpos, pm->ps->origin ); pm->stepped = qtrue; } } } /* =================== PM_WalkMove =================== */ void PM_WalkMove ( void ) { int i; vec3_t wishvel; float fmove; float smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; float accelerate; float waterScale; if ( ( pm->waterlevel > 2 ) && ( DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0.0f ) ) { // begin swimming PM_WaterMove(); return; } if ( pm->ps->instantJump ) if ( PM_CheckJump () ) { // jumped away pm->ps->jumped = qtrue; pm->ps->pm_time = 150; pm->ps->pm_flags |= PMF_TIME_JUMP_START; if ( pm->waterlevel > 1 ) { PM_WaterMove(); } else { PM_AirMove(); } return; } PM_Friction(); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); if ( ( pm->cmd.buttons & BUTTON_RUN ) && fmove && !smove ) { pm->ps->pm_runtime += pml.msec; } else { pm->ps->pm_runtime = 0; } // // only run faster if we have exceeded our running time // /* if ( ( pm->ps->stats[STAT_WATER_LEVEL] >= MINIMUM_WATER_FOR_TURBO ) && ( pm->ps->pm_runtime > WATER_TURBO_TIME ) ) { scale *= WATER_TURBO_SPEED; }*/ // project the forward and right directions onto the ground plane PM_ClipVelocity (pml.flat_forward, pml.groundTrace.plane.normal, pml.flat_forward, OVERCLIP ); PM_ClipVelocity (pml.flat_left, pml.groundTrace.plane.normal, pml.flat_left, OVERCLIP ); VectorNormalize (pml.flat_forward); VectorNormalize (pml.flat_left); for( i = 0 ; i < 3 ; i++ ) { wishvel[ i ] = pml.flat_forward[ i ] * fmove - pml.flat_left[ i ] * smove; } VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); wishspeed *= scale; // clamp the speed lower if wading or walking on the bottom if ( pm->waterlevel ) { waterScale = pm->waterlevel / 3.0f; waterScale = 1.0f - ( 1.0f - pm_swimScale ) * waterScale; if ( wishspeed > ( pm->ps->speed * waterScale ) ) { wishspeed = pm->ps->speed * waterScale; } } // when a player gets hit, they temporarily lose // full control, which allows them to be moved a bit if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || ( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) { accelerate = pm->ps->pm_airaccelerate; } else { accelerate = pm->ps->pm_accelerate; } PM_Accelerate( wishdir, wishspeed, accelerate ); if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || ( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) { pm->ps->velocity[ 2 ] -= pm->ps->gravity * pml.frametime; } // don't do anything if standing still if ( !pm->ps->velocity[ 0 ] && !pm->ps->velocity[ 1 ] && !pm->ps->velocity[ 2 ] ) return; PM_StepSlideMove ( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); } /* ============== PM_DeadMove ============== */ void PM_DeadMove ( void ) { float forward; if ( !pml.walking ) { return; } // extra friction forward = VectorLength( pm->ps->velocity ); forward -= 20.0f; if ( forward <= 0.0f ) { VectorClear( pm->ps->velocity ); } else { VectorNormalize( pm->ps->velocity ); VectorScale( pm->ps->velocity, forward, pm->ps->velocity ); } } /* =============== PM_NoclipMove =============== */ void PM_VehicleMove ( void ) { vec3_t wishvel; //float fmove; //float smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; float accelerate; //fmove = pm->cmd.forwardmove; //smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); pm->ps->pm_runtime = 0; wishvel[0] = pm->ps->velocity[0]; wishvel[1] = pm->ps->velocity[1]; wishvel[2] = pm->ps->velocity[2]; VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); wishspeed *= scale; accelerate = pm->ps->pm_accelerate; PM_Accelerate( wishdir, wishspeed, accelerate ); // don't do anything if standing still if ( !pm->ps->velocity[ 0 ] && !pm->ps->velocity[ 1 ] ) return; //PM_StepSlideMove ( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); } /* =============== PM_NoclipMove =============== */ void PM_NoclipMove ( void ) { float speed; float drop; float friction; float control; float newspeed; int i; vec3_t wishvel; float fmove; float smove; vec3_t wishdir; float wishspeed; float scale; pm->ps->viewheight = pm->ps->pm_defaultviewheight; // friction speed = VectorLength( pm->ps->velocity ); if ( speed < 1.0f ) { VectorCopy( vec3_origin, pm->ps->velocity ); } else { drop = 0; // extra friction friction = pm->ps->pm_friction * 1.5f; control = speed < pm->ps->pm_stopspeed ? pm->ps->pm_stopspeed : speed; drop += control * friction * pml.frametime; // scale the velocity newspeed = speed - drop; if ( newspeed < 0.0f ) { newspeed = 0; } newspeed /= speed; VectorScale( pm->ps->velocity, newspeed, pm->ps->velocity ); } // accelerate // allow the player to move twice as fast in noclip scale = PM_CmdScale( &pm->cmd ) * 2.0f; fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; pm->ps->pm_runtime = 0; for( i = 0; i < 3; i++ ) { wishvel[ i ] = ( pml.flat_forward[ i ] * fmove ) - ( pml.flat_left[ i ] * smove ); } wishvel[ 2 ] += pm->cmd.upmove; VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); wishspeed *= scale; PM_Accelerate( wishdir, wishspeed, pm->ps->pm_accelerate ); // move VectorMA( pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin ); } //============================================================================ /* ============= PM_CorrectAllSolid ============= */ void PM_CorrectAllSolid ( void ) { if ( pm->debugLevel ) { Com_Printf( "%i:allsolid\n", c_pmove ); } // FIXME: jitter around pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; } /* ============= PM_GroundTrace ============= */ void PM_GroundTrace ( qboolean onlyTrace ) { vec3_t point; vec3_t tmporg; trace_t trace; point[ 0 ] = pm->ps->origin[ 0 ]; point[ 1 ] = pm->ps->origin[ 1 ]; point[ 2 ] = pm->ps->origin[ 2 ] - 9.0f; // long trace to avoid potential terrain hitches pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, qtrue ); if (!(trace.surfaceFlags & SURF_TERRAIN )) // if we're not on terrain, go back to 0.25f trace distance { point[ 2 ] = pm->ps->origin[ 2 ] - 0.25f; pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, qtrue ); } pml.groundTrace = trace; pm->ps->groundTrace = trace; // do something corrective if the trace starts in a solid... if ( trace.allsolid ) { // We're gonna start with our origin a tad higher and see if it works tmporg[ 0 ] = pm->ps->origin[ 0 ]; tmporg[ 1 ] = pm->ps->origin[ 1 ]; tmporg[ 2 ] = pm->ps->origin[ 2 ] + 2.0f; // Trace down to the same point pm->trace( &trace, tmporg, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, qtrue ); pml.groundTrace = trace; pm->ps->groundTrace = trace; // It's hopeless if we start all solid again if ( trace.allsolid ) { PM_CorrectAllSolid(); pm->ps->walking = pml.walking; pm->ps->groundPlane = pml.groundPlane; return; } } // if the trace didn't hit anything, we are in free fall if ( trace.fraction == 1.0f ) { if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) { if ( pm->debugLevel ) { Com_Printf( "%i:lift\n", c_pmove ); } } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; pm->ps->walking = pml.walking; pm->ps->groundPlane = pml.groundPlane; return; } // slopes that are too steep will not be considered onground // check slopeSlideFlag to avoid the "falling" bug if ( trace.plane.normal[ 2 ] < MIN_WALK_NORMAL ) { vec3_t oldvel; float d; if ( pm->debugLevel ) { Com_Printf( "%i:steep\n", c_pmove ); } // if they can't slide down the slope, let them // walk (sharp crevices) VectorCopy( pm->ps->velocity, oldvel ); VectorSet( pm->ps->velocity, 0.0f, 0.0f, -1.0f / pml.frametime ); PM_SlideMove( qfalse ); d = VectorLength( pm->ps->velocity ); VectorCopy( oldvel, pm->ps->velocity ); if ( d > ( 0.1f / pml.frametime ) ) { pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qtrue; pml.walking = qfalse; pm->ps->walking = pml.walking; pm->ps->groundPlane = pml.groundPlane; return; } } // check if getting thrown off the ground if ( ( pm->ps->velocity[ 2 ] > 0.0f ) && ( DotProduct( pm->ps->velocity, trace.plane.normal ) > 10.0f ) ) { if ( pm->debugLevel ) { Com_Printf( "%i:kickoff\n", c_pmove ); } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; pm->ps->walking = pml.walking; pm->ps->groundPlane = pml.groundPlane; return; } pml.groundPlane = qtrue; pml.walking = qtrue; // hitting solid ground will end a waterjump if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) { //pm->ps->pm_flags &= ~( PMF_TIME_WATERJUMP | PMF_TIME_LAND); pm->ps->pm_flags &= ~( PMF_TIME_WATERJUMP ); pm->ps->pm_time = 0; } // hitting solid ground will end a stuckjump if ( pm->ps->pm_flags & PMF_TIME_STUCKJUMP ) { pm->ps->pm_flags &= ~( PMF_TIME_STUCKJUMP ); pm->ps->pm_time = 0; } if ( !onlyTrace && !( pml.groundTrace.surfaceFlags & SURF_SLICK ) ) pm->ps->velocity[ 2 ] = 0; pm->ps->groundEntityNum = trace.entityNum; PM_AddTouchEnt( trace.entityNum ); pm->ps->walking = pml.walking; pm->ps->groundPlane = pml.groundPlane; } /* ============= PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving ============= */ void PM_SetWaterLevel ( void ) { vec3_t point; int cont; int sample1; int sample2; // // get waterlevel, accounting for ducking // pm->waterlevel = 0; pm->watertype = 0; sample2 = pm->ps->viewheight - MINS_Z; sample1 = sample2 * 3 / 4; VectorCopy( pm->ps->origin, point ); point[ 2 ] += MINS_Z + 1.0f; cont = pm->pointcontents( point, pm->ps->clientNum ); if ( cont & MASK_WATER ) { pm->watertype = cont; pm->waterlevel = 1; point[ 2 ] = pm->ps->origin[ 2 ] + MINS_Z + sample1; cont = pm->pointcontents( point, 0 ); if ( cont & MASK_WATER ) { pm->waterlevel = 2; point[ 2 ] = pm->ps->origin[ 2 ] + MINS_Z + sample2; cont = pm->pointcontents( point, 0 ); if ( cont & MASK_WATER ) { pm->waterlevel = 3; } } } } /* ============== PM_CheckDuck Sets mins, maxs, and pm->ps->viewheight ============== */ void PM_CheckDuck ( void ) { pm->mins[ 0 ] = MINS_X; pm->mins[ 1 ] = MINS_Y; pm->mins[ 2 ] = MINS_Z; pm->maxs[ 0 ] = MAXS_X; pm->maxs[ 1 ] = MAXS_Y; pm->maxs[ 2 ] = MAXS_Z; pm->ps->viewheight = pm->ps->pm_defaultviewheight; if ( pm->ps->pm_type == PM_DEAD ) { pm->maxs[ 2 ] = DEAD_MINS_Z; return; } if ( pm->ps->pm_flags & PMF_DUCKED ) { pm->maxs[ 2 ] = CROUCH_MAXS_Z; pm->ps->viewheight = CROUCH_VIEWHEIGHT; } } //----------------------------------------------------- // // Name: PM_CheckCrouchJump // Class: None // // Description: Checks to see if the player is ducking and jumping, which is a crouch jump. // // Parameters: None // // Returns: None //----------------------------------------------------- void PM_CheckCrouchJump(void) { //if the player is not on the ground and is ducking, //set the crouch jump flag. if ( pm->ps->jumped && ( ( ( pm->ps->pm_flags & PMF_DUCKED ) && ( pm->ps->pm_flags & PMF_TIME_JUMP_START ) ) || ( !( pm->ps->pm_flags & PMF_TIME_JUMP_START ) && ( pm->ps->pm_flags & PMF_JUMP_HELD ) ) ) ) { if(pm->ps->crouchjumpset == qfalse) { pm->ps->pm_flags |= PMF_TIME_CROUCH_JUMP; pm->ps->crouchjumpset = qtrue; } } //This really sucks to put this code here, since the //primary function of this procedure is to check if //crouch jump is active. Since CheckDuck sets the //default height down if duck is pressed, this moves //it back up if the player is crouch jumping if(pm->ps->crouchjumpset == qtrue) { pm->ps->viewheight = pm->ps->pm_defaultviewheight; pm->maxs[ 2 ] = MAXS_Z; } if(pm->ps->groundPlane) { pm->ps->crouchjumpset = qfalse; } } //----------------------------------------------------- // // Name: PM_CrouchJumpMove // Class: None // // Description: Allows the player to clear objects a little higher than a normal jump could. // // Parameters: None // // Returns: None //----------------------------------------------------- void PM_CrouchJumpMove(void) { PM_StepSlideMove( !( pm->ps->pm_flags & PMF_NO_GRAVITY ) ); pm->ps->velocity[2] += pm->ps->crouchjumpvelocity; //turn off the jump crouch flag. pm->ps->pm_flags &= ~PMF_TIME_CROUCH_JUMP; } //=================================================================== qboolean PM_ShouldCrashLand( void ) { /* jhefty/jwaters -- another strafe-jump fix if (pml.impactSpeed) pm->ps->pm_landtime = pm->ps->commandTime + 175; */ if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) { //vec3_t diff; float delta; // We're on the ground, see if we hit it hard enough for a crash land //VectorSubtract( pm->ps->velocity, pml.previous_velocity, diff ); //delta = VectorLength( diff ); //delta = abs( pm->ps->velocity[ 2 ] - pml.previous_velocity[ 2 ] ); delta = pm->ps->velocity[ 2 ] - pml.previous_velocity[ 2 ]; if ( delta < 0.0f || pml.previous_velocity[ 2 ] > -400 ) return qfalse; if ( delta > 600.0f ) { return qtrue; } } return qfalse; } /* ================= PM_CrashLand Check for hard landings that generate sound events fall from 128: 400 = 160000 fall from 256: 580 = 336400 fall from 384: 720 = 518400 fall from 512: 800 = 640000 fall from 640: 960 = damage = deltavelocity*deltavelocity * 0.0001 ================= */ void PM_CrashLand ( void ) { float delta; /* float dist; float vel; float acc; float t; float a, b, c, den; */ //vec3_t diff; // calculate the exact velocity on landing /* dist = pm->ps->origin[ 2 ] - pml.previous_origin[ 2 ]; vel = pml.previous_velocity[ 2 ]; acc = -pm->ps->gravity; a = acc / 2; b = vel; c = -dist; den = b * b - 4 * a * c; if ( den < 0 ) { return; } t = ( -b - sqrt( den ) ) / ( 2 * a ); delta = vel + t * acc; if (delta < -200) { pm->ps->pm_flags |= PMF_TIME_LAND; // don't allow another jump for a little while if (delta < -400) pm->ps->pm_time = 200; else pm->ps->pm_time = 144; } */ // Get the change in speed //VectorSubtract( pm->ps->velocity, pml.previous_velocity, diff ); //delta = VectorLength( diff ); //delta = abs( pm->ps->velocity[ 2 ] - pml.previous_velocity[ 2 ] ); delta = pm->ps->velocity[ 2 ] - pml.previous_velocity[ 2 ]; if ( delta < 0.0f || pml.previous_velocity[ 2 ] > -400 ) return; //delta = delta * delta * 0.0001f; // never take falling damage if completely underwater if ( pm->waterlevel == 3 ) { return; } // reduce falling damage if there is standing water if ( pm->waterlevel == 2 ) { delta *= 0.25f; } if ( pm->waterlevel == 1 ) { delta *= 0.5f; } if ( delta < 1.0f ) { return; } pm->landed = qtrue; pm->landedVelocity = pml.previous_velocity[2]; // SURF_NODAMAGE is used for bounce pads where you don't ever // want to take damage or play a crunch sound if ( !( pml.groundTrace.surfaceFlags & SURF_NODAMAGE ) ) { if ( delta > 1500.0f ) { pm->pmoveEvent = EV_FALL_FATAL; } else if ( delta > 1300.0f ) { pm->pmoveEvent = EV_FALL_VERY_FAR; } else if ( delta > 1100.0f ) { pm->pmoveEvent = EV_FALL_FAR; } else if ( delta > 900.0f ) { pm->pmoveEvent = EV_FALL_MEDIUM; } else if ( delta > 750.0f ) { pm->pmoveEvent = EV_FALL_SHORT; } else if ( delta > 600.0f ) { pm->pmoveEvent = EV_FALL_VERY_SHORT; } } } /* ============== PM_WaterEvents Generate sound events for entering and leaving water ============== */ void PM_WaterEvents ( void ) { if ( ( pm->ps->pm_type == PM_SPECTATOR ) || ( pm->ps->pm_type == PM_SPECTATOR_FOLLOW ) ) return; // FIXME? // // if just entered a water volume, play a sound // if ( !pml.previous_waterlevel && pm->waterlevel ) { pm->pmoveEvent = EV_WATER_TOUCH; } // // if just completely exited a water volume, play a sound // if ( pml.previous_waterlevel && ! pm->waterlevel ) { pm->pmoveEvent = EV_WATER_LEAVE; } // // check for head just going under water // if ( ( pml.previous_waterlevel != 3 ) && ( pm->waterlevel == 3 ) ) { pm->pmoveEvent = EV_WATER_UNDER; } // // check for head just coming out of water // if ( ( pml.previous_waterlevel == 3 ) && ( pm->waterlevel != 3 ) ) { pm->pmoveEvent = EV_WATER_CLEAR; } } /* ================ PM_DropTimers ================ */ void PM_DropTimers ( void ) { // drop misc timing counter if ( pm->ps->pm_time ) { if ( pml.msec >= pm->ps->pm_time ) { pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } else { pm->ps->pm_time -= pml.msec; } } } //----------------------------------------------------- // // Name: PM_UpdateLeanIn // Class: None // // Description: Calculates the lean in movement // // Parameters: // // Returns: //----------------------------------------------------- void PM_UpdateLeanIn( playerState_t* ps, const usercmd_t* cmd ) { vec3_t viewOrigin; vec3_t leanTraceMins; vec3_t leanTraceMaxs; vec3_t leftVector; vec3_t leanTraceEnd; vec3_t playerViewAngles; trace_t leanTrace; float leanDelta = ps->leanDelta; if( cmd->lean > 0 ) { //We are leaning left, so calculate the new lean delta. //If lean delta is greater then the max lean delta //then set lean delta to be the max. leanDelta += ((( float ) pml.msec / ( float ) pm_leaninspeed) * pm_leanmaxdelta); if( leanDelta > pm_leanmaxdelta ) leanDelta = pm_leanmaxdelta; } else { //We are leaning right, so calculate the new lean delta. //If lean delta is less than the negative max lean delta //then set lean delta to be the negative max lean delta. leanDelta -= ((( float ) pml.msec / ( float ) pm_leaninspeed) * pm_leanmaxdelta); if( leanDelta < -pm_leanmaxdelta ) leanDelta = -pm_leanmaxdelta; } //We calculate our lean by scaling our view origin vector by the amount of the lean delta, //then adding the left vector to get the delta amount of the lean. //Get the view origin VectorCopy( ps->origin, viewOrigin ); viewOrigin[2] += ps->viewheight; //Get the view angles, remove all roll from the view angles. VectorCopy( ps->viewangles, playerViewAngles ); playerViewAngles[ROLL] = 0; AngleVectors( ps->viewangles, NULL, leftVector, NULL ); VectorMA( viewOrigin, leanDelta, leftVector, leanTraceEnd ); //Run a trace to check if we collide with any object when we lean. VectorSet( leanTraceMins, -5, -5, -4 ); VectorSet( leanTraceMaxs, 5, 5, 4 ); if( pm != 0) { pm->trace(&leanTrace, viewOrigin, leanTraceMins, leanTraceMaxs, leanTraceEnd, ps->clientNum, MASK_PLAYERSOLID, qfalse); //Scale the lean delta by the end result of the trace. //This will scale our delta to the object that the trace has hit. leanDelta *= leanTrace.fraction; } ps->leanDelta = leanDelta; } //----------------------------------------------------- // // Name: PM_UpdateLeanOut // Class: // // Description: Calculates the lean out movement. // // Parameters: ps - the current player state. // // Returns: //----------------------------------------------------- void PM_UpdateLeanOut( playerState_t* ps ) { float leanDelta = ps->leanDelta; if( leanDelta > 0 ) { leanDelta -= (( (float) pml.msec / (float) pm_leanoutspeed) * pm_leanmaxdelta); if( leanDelta < 0 ) leanDelta = 0; } else if ( leanDelta < 0 ) { leanDelta += (((float)pml.msec / ( float ) pm_leanoutspeed) * pm_leanmaxdelta); if( leanDelta > 0) leanDelta = 0; } ps->leanDelta = leanDelta; } //----------------------------------------------------- // // Name: PM_UpdateLeanView // Class: // // Description: Calculates the lean movement. // // Parameters: ps - the player state // cmd - the input commands struct. // // Returns: None //----------------------------------------------------- void PM_UpdateLeanView( playerState_t* ps, const usercmd_t* cmd ) { // We are not leaning, so move back to center position if( cmd->lean == 0 ) { PM_UpdateLeanOut(ps); } else { PM_UpdateLeanIn(ps, cmd); } } /* ================ PM_UpdateViewAngles This can be used as another entry point when only the viewangles are being updated isntead of a full move ================ */ void PM_UpdateViewAngles ( playerState_t *ps, const usercmd_t *cmd ) { short temp; int i; if ( ps->pm_flags & PMF_FROZEN ) { // no view changes at all return; } /* if ( ps->stats[ STAT_HEALTH ] <= 0 ) { // no view changes at all return; } */ // circularly clamp the angles with deltas for( i = 0; i < 3; i++ ) { temp = cmd->angles[ i ] + ps->delta_angles[ i ]; if ( i == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) { ps->delta_angles[ i ] = 16000 - cmd->angles[ i ]; temp = 16000; } else if ( temp < -16000 ) { ps->delta_angles[ i ] = -16000 - cmd->angles[ i ]; temp = -16000; } } ps->viewangles[ i ] = SHORT2ANGLE( temp ); } PM_UpdateLeanView( ps, cmd ); } void Pmove_GroundTrace ( pmove_t *pmove ) { memset (&pml, 0, sizeof(pml)); pml.msec = 1; pml.frametime = 0.001f; pm = pmove; if ( !( pm->ps->pm_flags & PMF_NO_MOVE ) ) { PM_CheckDuck(); PM_CheckCrouchJump(); } PM_GroundTrace( qtrue ); } /* ================ Pmove Can be called by either the server or the client ================ */ void PmoveSingle (pmove_t *pmove) { vec3_t tempVec; qboolean walking; vec3_t temp; pm = pmove; // this counter lets us debug movement problems with a journal // by setting a conditional breakpoint fot the previous frame c_pmove++; // clear results pm->numtouch = 0; pm->watertype = 0; pm->waterlevel = 0; pm->landed = qfalse; if(pm->ps->groundPlane) { //only set this to false if we are on the ground. //when jump is hit, this is set to true. This //allows the code to know if the player jumped or not. pm->ps->jumped = qfalse; } if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies } // adding fake talk balloons /* if ( pmove->cmd.buttons & BUTTON_TALK ) { pmove->cmd.buttons = 0; pmove->cmd.forwardmove = 0; pmove->cmd.rightmove = 0; pmove->cmd.upmove = 0; pmove->cmd.lean = 0; } */ // clear all pmove local vars memset (&pml, 0, sizeof(pml)); // determine the time pml.msec = pmove->cmd.serverTime - pm->ps->commandTime; if ( pml.msec < 1 ) { pml.msec = 1; } else if ( pml.msec > 200 ) { pml.msec = 200; } pm->ps->commandTime = pmove->cmd.serverTime; // save old org in case we get stuck VectorCopy( pm->ps->origin, pml.previous_origin ); // save old velocity for crashlanding VectorCopy( pm->ps->velocity, pml.previous_velocity ); VectorCopy( pm->ps->velocity, temp ); pml.frametime = pml.msec * 0.001f; // update the viewangles if (!pm->ps->in_vehicle ) { PM_UpdateViewAngles( pm->ps, &pm->cmd ); AngleVectors( pm->ps->viewangles, pml.forward, pml.left, pml.up ); VectorClear( tempVec ); tempVec[ YAW ] = pm->ps->viewangles[ YAW ]; AngleVectors( tempVec, pml.flat_forward, pml.flat_left, pml.flat_up ); } if ( pm->ps->in_vehicle ) { PM_VehicleMove(); return; } if ( pm->cmd.upmove < 10 ) { // not holding jump pm->ps->pm_flags &= ~PMF_JUMP_HELD; } if ( pm->ps->pm_type == PM_DEAD ) { pm->cmd.forwardmove = 0; pm->cmd.rightmove = 0; pm->cmd.upmove = 0; pm->cmd.lean = 0; } if ( pm->ps->pm_type == PM_NOCLIP ) { PM_NoclipMove (); PM_DropTimers (); return; } if ( ( pm->ps->pm_flags & PMF_FROZEN ) || ( pm->ps->pm_flags & PMF_NO_MOVE ) ) { return; // no movement at all } PM_CheckDuck(); PM_CheckCrouchJump(); // set watertype, and waterlevel PM_SetWaterLevel(); pml.previous_waterlevel = pmove->waterlevel; // set groundentity PM_GroundTrace( qfalse ); if ( pm->ps->pm_type == PM_DEAD ) { PM_DeadMove(); } PM_DropTimers(); if ( pm->ps->pm_flags & PMF_FLIGHT ) // flight powerup doesn't allow jump and has different friction PM_FlyMove(); else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) { PM_WaterJumpMove(); } else if ( pml.walking ) { // walking on ground PM_WalkMove(); } else if ( pm->waterlevel > 1 ) { // swimming PM_WaterMove(); } else if ( pm->ps->pm_flags & PMF_TIME_STUCKJUMP ) { PM_StuckJumpMove(); } else if( pm->ps->pm_flags & PMF_TIME_CROUCH_JUMP) { PM_CrouchJumpMove(); } else { // airborne if ( !pm->ps->in_vehicle ) PM_AirMove(); } walking = pml.walking; // set groundentity, watertype, and waterlevel PM_GroundTrace( qfalse ); PM_SetWaterLevel(); // don't fall down stairs or do really short falls if ( !pml.walking && ( walking || ( ( pml.previous_velocity[ 2 ] >= 0.0f ) && ( pm->ps->velocity[ 2 ] <= 0.0f ) ) ) ) { vec3_t point; trace_t trace; point[ 0 ] = pm->ps->origin[ 0 ]; point[ 1 ] = pm->ps->origin[ 1 ]; point[ 2 ] = pm->ps->origin[ 2 ] - 0.5f; pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, qtrue ); if ( ( trace.fraction < 1.0f ) && ( !trace.allsolid ) ) { VectorCopy( trace.endpos, pm->ps->origin ); // allow client to smooth out the step pm->stepped = qtrue; // requantify the player's position PM_GroundTrace( qfalse ); PM_SetWaterLevel(); } } // entering / leaving water splashes PM_WaterEvents(); // test stuff // snap some parts of playerstate to save network bandwidth SnapVector( pm->ps->velocity ); // If the player is off the ground cap his xy velocity so he can't strafe-jump and use other cheats // to go too fast if ( !pm->ps->strafeJumpingAllowed && ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) && ( pm->ps->groundEntityNum == ENTITYNUM_WORLD ) ) { float speed; vec3_t xyVelocity; // Get xy velocity //jhefty/jwaters modifications VectorCopy( pm->ps->velocity, xyVelocity ); //xyVelocity[ 2 ] = 0.0f; // See if the xy velocity is too fast speed = VectorLength( xyVelocity ); if ( speed > pm->ps->speed ) { // Cap the real velocity VectorNormalize( xyVelocity ); VectorScale( xyVelocity, pm->ps->speed, pm->ps->velocity ); //pm->ps->velocity[ 0 ] = xyVelocity[ 0 ]; //pm->ps->velocity[ 1 ] = xyVelocity[ 1 ]; } } if ( PM_ShouldCrashLand() ) { // just hit the ground if ( pm->debugLevel ) { Com_Printf( "%i:Land\n", c_pmove ); } PM_CrashLand(); } } /* ================ Pmove Can be called by either the server or the client ================ */ void Pmove (pmove_t *pmove) { int finalTime; finalTime = pmove->cmd.serverTime; if ( finalTime < pmove->ps->commandTime ) { return; // should not happen } if ( finalTime > ( pmove->ps->commandTime + 1000 ) ) { pmove->ps->commandTime = finalTime - 1000; } // chop the move up if it is too long, to prevent framerate // dependent behavior while ( pmove->ps->commandTime != finalTime ) { int msec; msec = finalTime - pmove->ps->commandTime; if ( msec > 50 ) { msec = 50; } pmove->cmd.serverTime = pmove->ps->commandTime + msec; PmoveSingle( pmove ); //Com_Printf("Vel: %f\n",pm->ps->velocity[2]); } }