/* =================== PM_AirMove =================== */ static void PM_AirMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; PM_Friction(); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; VectorNormalize (pml.forward); VectorNormalize (pml.right); for ( i = 0 ; i < 2 ; i++ ) { wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; } wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; // not on ground, so little effect on velocity PM_Accelerate (wishdir, wishspeed, 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 0 //ZOID: If we are on the grapple, try stair-stepping //this allows a player to use the grapple to pull himself //over a ledge if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) PM_StepSlideMove ( qtrue ); else PM_SlideMove ( qtrue ); #endif PM_StepSlideMove ( qtrue ); }
/* =================== PM_LadderMove() by: Calrathan [Arthur Tomlin] Right now all I know is that this works for VERTICAL ladders. Ladders with angles on them (urban2 for AQ2) haven't been tested. =================== */ static void PM_LadderMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; float vel; PM_Friction (); scale = PM_CmdScale( &pm->cmd ); // user intentions [what the user is attempting to do] if ( !scale ) { wishvel[0] = 0; wishvel[1] = 0; wishvel[2] = 0; } else { // if they're trying to move... lets calculate it for (i=0 ; i<3 ; i++) wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; wishvel[2] += scale * pm->cmd.upmove; } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if ( wishspeed > pm->ps->speed * pm_ladderScale ) { wishspeed = pm->ps->speed * pm_ladderScale; } PM_Accelerate (wishdir, wishspeed, pm_ladderAccelerate); // This SHOULD help us with sloped ladders, but it remains untested. if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) { vel = VectorLength(pm->ps->velocity); // slide along the ground plane [the ladder section under our feet] PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); } PM_SlideMove( qfalse ); // move without gravity }
/* =================== PM_WaterMove =================== */ static void PM_WaterMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; float vel; if ( PM_CheckWaterJump() ) { PM_WaterJumpMove(); return; } #if 0 // jump = head for surface if ( pm->cmd.upmove >= 10 ) { if (pm->ps->velocity[2] > -300) { if ( pm->watertype == CONTENTS_WATER ) { pm->ps->velocity[2] = 100; } else if (pm->watertype == CONTENTS_SLIME) { pm->ps->velocity[2] = 80; } else { pm->ps->velocity[2] = 50; } } } #endif PM_Friction (); scale = PM_CmdScale( &pm->cmd ); // // user intentions // if ( !scale ) { wishvel[0] = 0; wishvel[1] = 0; wishvel[2] = -60; // sink towards bottom } else { for (i=0 ; i<3 ; i++) wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; wishvel[2] += scale * pm->cmd.upmove; } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if ( wishspeed > pm->ps->speed * pm_swimScale ) { wishspeed = pm->ps->speed * pm_swimScale; } PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); // make sure we can go up slopes easily under water if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) { vel = VectorLength(pm->ps->velocity); // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); } PM_SlideMove( qfalse ); }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; qboolean isGiant = qfalse; bgEntity_t *pEnt; qboolean skipStep = qfalse; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( BG_InReboundHold( pm->ps->legsAnim ) ) { gravity = qfalse; } if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } pEnt = pm_entSelf; if (pm->ps->clientNum >= MAX_CLIENTS) { if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) { return; } } 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; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); if (pm->ps->clientNum >= MAX_CLIENTS) { // apply ground friction, even if on ladder if (pEnt && pEnt->s.NPC_class == CLASS_ATST || (pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) ) {//AT-STs can step high up[2] += 66.0f; isGiant = qtrue; } else if ( pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) {//also can step up high up[2] += 64.0f; isGiant = qtrue; } else { up[2] += STEPSIZE; } } else { up[2] += STEPSIZE; } // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); // 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); if ( pm->stepSlideFix ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) {//normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) {//Rancor don't step on clients if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } /* else if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_ATST && OnSameTeam( pEnt, traceEnt) ) {//NPC AT-ST's don't step up on allies VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } */ else { VectorCopy (trace.endpos, pm->ps->origin); if ( pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } else { if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { 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 #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
void PM_StepSlideMove_ (void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; int i, j; trace_t trace; vec3_t end; float time_left; numbumps = 4; VectorCopy (pml.velocity, primal_velocity); numplanes = 0; time_left = pml.frametime; for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) { for (i=0 ; i<3 ; i++) end[i] = pml.origin[i] + time_left * pml.velocity[i]; trace = pm->trace (pml.origin, pm->mins, pm->maxs, end); if (trace.allsolid) { // entity is trapped in another solid pml.velocity[2] = 0; // don't build up falling damage return; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pml.origin); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance // save entity for contact if (pm->numtouch < MAXTOUCH && trace.ent) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } time_left -= time_left * trace.fraction; // slide along this plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorCopy (vec3_origin, pml.velocity); break; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; #if 0 float rub; // // modify velocity so it parallels all of the clip planes // if (numplanes == 1) { // go along this plane VectorCopy (pml.velocity, dir); VectorNormalize (dir); rub = 1.0 + 0.5 * DotProduct (dir, planes[0]); // slide along the plane PM_ClipVelocity (pml.velocity, planes[0], pml.velocity, 1.01); // rub some extra speed off on xy axis // not on Z, or you can scrub down walls pml.velocity[0] *= rub; pml.velocity[1] *= rub; pml.velocity[2] *= rub; } else if (numplanes == 2) { // go along the crease VectorCopy (pml.velocity, dir); VectorNormalize (dir); rub = 1.0 + 0.5 * DotProduct (dir, planes[0]); // slide along the plane CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, pml.velocity); VectorScale (dir, d, pml.velocity); // rub some extra speed off VectorScale (pml.velocity, rub, pml.velocity); } else { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy (vec3_origin, pml.velocity); break; } #else // // modify original_velocity so it parallels all of the clip planes // for (i=0 ; i<numplanes ; i++) { PM_ClipVelocity (pml.velocity, planes[i], pml.velocity, 1.01); for (j=0 ; j<numplanes ; j++) if (j != i) { if (DotProduct (pml.velocity, planes[j]) < 0) break; // not ok } if (j == numplanes) break; } if (i != numplanes) { // go along this plane } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy (vec3_origin, pml.velocity); break; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, pml.velocity); VectorScale (dir, d, pml.velocity); } #endif // // if velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (pml.velocity, primal_velocity) <= 0) { VectorCopy (vec3_origin, pml.velocity); break; } } if (pm->s.pm_time) { VectorCopy (primal_velocity, pml.velocity); } }
/* ================== PM_StepSlideMove ================== */ bool PM_StepSlideMove( bool gravity, bool predictive ) { vec3_t start_o, start_v; #ifndef UNREALARENA vec3_t down_o, down_v; #endif trace_t trace; #ifndef UNREALARENA vec3_t normal; vec3_t step_v, step_vNormal; #endif vec3_t up, down; float stepSize; bool stepped = false; #ifndef UNREALARENA BG_GetClientNormal( pm->ps, normal ); #endif VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); if ( !PM_SlideMove( gravity ) ) { #ifdef UNREALARENA return stepped; // we got exactly where we wanted to go first try #else VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, 0 ); //we can step down if ( trace.fraction > 0.01f && trace.fraction < 1.0f && !trace.allsolid && pml.groundPlane ) { if ( pm->debugLevel > 1 ) { Log::Notice( "%d: step down\n", c_pmove ); } stepped = true; } #endif } else { VectorCopy( start_o, down ); #ifdef UNREALARENA down[ 2 ] -= STEPSIZE; #else VectorMA( down, -STEPSIZE, normal, down ); #endif pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, 0 ); #ifdef UNREALARENA VectorSet( up, 0.0f, 0.0f, 1.0f ); #endif // never step up when you still have up velocity #ifdef UNREALARENA if ( pm->ps->velocity[ 2 ] > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, up ) < 0.7f ) ) #else if ( DotProduct( trace.plane.normal, pm->ps->velocity ) > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, normal ) < 0.7f ) ) #endif { return stepped; } #ifndef UNREALARENA // never step up when flying upwards with the jetpack if ( pm->ps->velocity[ 2 ] > 0.0f && ( pm->ps->stats[ STAT_STATE2 ] & SS2_JETPACK_ACTIVE ) ) { return stepped; } VectorCopy( pm->ps->origin, down_o ); VectorCopy( pm->ps->velocity, down_v ); #endif VectorCopy( start_o, up ); #ifdef UNREALARENA up[ 2 ] += STEPSIZE; #else VectorMA( up, STEPSIZE, normal, up ); #endif // test the player position if they were a stepheight higher pm->trace( &trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, 0 ); if ( trace.allsolid ) { if ( pm->debugLevel > 1 ) { Log::Notice( "%i:bend can't step\n", c_pmove ); } return stepped; // can't step up } #ifdef UNREALARENA stepSize = trace.endpos[ 2 ] - start_o[ 2 ]; // if the new position is falling then do nothing if ( PM_CheckFallingFromLedge( trace.endpos ) ) { VectorCopy( start_o, pm->ps->origin ); return stepped; } #else VectorSubtract( trace.endpos, start_o, step_v ); VectorCopy( step_v, step_vNormal ); VectorNormalize( step_vNormal ); stepSize = DotProduct( normal, step_vNormal ) * VectorLength( step_v ); #endif // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); #ifdef UNREALARENA PM_SlideMove( gravity, stepSize ); #else if ( PM_SlideMove( gravity ) == 0 ) { if ( pm->debugLevel > 1 ) { Log::Notice( "%d: step up\n", c_pmove ); } stepped = true; } #endif // push down the final amount VectorCopy( pm->ps->origin, down ); #ifdef UNREALARENA down[ 2 ] -= stepSize; #else VectorMA( down, -stepSize, normal, down ); #endif pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, 0 ); if ( !trace.allsolid ) { #ifdef UNREALARENA // if the new position is falling then do nothing if ( PM_CheckFallingFromLedge( trace.endpos ) ) { VectorCopy( start_o, pm->ps->origin ); return stepped; } #endif VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction < 1.0f ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity ); } } #ifdef UNREALARENA if ( !predictive ) { stepped = true; // use the step move float delta; delta = pm->ps->origin[ 2 ] - start_o[ 2 ]; if ( delta > 2.0f ) { if ( delta < 7.0f ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11.0f ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15.0f ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel > 1 ) { Log::Notice( "%i:stepped\n", c_pmove ); } } #else if ( !predictive && stepped ) { PM_StepEvent( start_o, pm->ps->origin, normal ); } #endif return stepped; }
/* ================== PM_StepSlideMove ================== */ qboolean PM_StepSlideMove( qboolean gravity, qboolean predictive ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; vec3_t normal; vec3_t step_v, step_vNormal; vec3_t up, down; float stepSize; qboolean stepped = qfalse; BG_GetClientNormal( pm->ps, normal ); VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); if ( PM_SlideMove( gravity ) == 0 ) { VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); //we can step down if ( trace.fraction > 0.01f && trace.fraction < 1.0f && !trace.allsolid && pml.groundPlane != qfalse ) { if ( pm->debugLevel > 1 ) { Com_Printf( "%d: step down\n", c_pmove ); } stepped = qtrue; } } else { VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); // never step up when you still have up velocity if ( DotProduct( trace.plane.normal, pm->ps->velocity ) > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, normal ) < 0.7f ) ) { return stepped; } VectorCopy( pm->ps->origin, down_o ); VectorCopy( pm->ps->velocity, down_v ); VectorCopy( start_o, up ); VectorMA( up, STEPSIZE, normal, up ); // test the player position if they were a stepheight higher pm->trace( &trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask ); if ( trace.allsolid ) { if ( pm->debugLevel > 1 ) { Com_Printf( "%i:bend can't step\n", c_pmove ); } return stepped; // can't step up } VectorSubtract( trace.endpos, start_o, step_v ); VectorCopy( step_v, step_vNormal ); VectorNormalize( step_vNormal ); stepSize = DotProduct( normal, step_vNormal ) * VectorLength( step_v ); // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); if ( PM_SlideMove( gravity ) == 0 ) { if ( pm->debugLevel > 1 ) { Com_Printf( "%d: step up\n", c_pmove ); } stepped = qtrue; } // push down the final amount VectorCopy( pm->ps->origin, down ); VectorMA( down, -stepSize, normal, down ); pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); 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 ); } } if ( !predictive && stepped ) { PM_StepEvent( start_o, pm->ps->origin, normal ); } return stepped; }
int PM_FlyMove (void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (pmove.velocity, original_velocity); VectorCopy (pmove.velocity, primal_velocity); numplanes = 0; time_left = frametime; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { for (i = 0; i < 3; i++) end[i] = pmove.origin[i] + time_left * pmove.velocity[i]; trace = PM_PlayerMove (pmove.origin, end); if (trace.startsolid || trace.allsolid) { // entity is trapped in // another solid VectorCopy (vec3_origin, pmove.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pmove.origin); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance // save entity for contact pmove.touchindex[pmove.numtouch] = trace.entnum; pmove.numtouch++; if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor } if (!trace.plane.normal[2]) { blocked |= 2; // step } time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorCopy (vec3_origin, pmove.velocity); break; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i = 0; i < numplanes; i++) { PM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 1); for (j = 0; j < numplanes; j++) if (j != i) { if (DotProduct (pmove.velocity, planes[j]) < 0) break; // not ok } if (j == numplanes) break; } if (i != numplanes) { // go along this plane } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy (vec3_origin, pmove.velocity); break; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, pmove.velocity); VectorScale (dir, d, pmove.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (pmove.velocity, primal_velocity) <= 0) { VectorCopy (vec3_origin, pmove.velocity); break; } } if (pmove.waterjumptime) { VectorCopy (primal_velocity, pmove.velocity); } return blocked; }
void PM_StepSlideMove_ (void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; int i, j; trace_t trace; vec3_t end; float time_left; numbumps = 4; VectorCopy (pml.velocity, primal_velocity); numplanes = 0; time_left = pml.frametime; for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) { for (i=0 ; i<3 ; i++) end[i] = pml.origin[i] + time_left * pml.velocity[i]; trace = pm->trace (pml.origin, pm->mins, pm->maxs, end); if (trace.allsolid) { /* entity is trapped in another solid */ pml.velocity[2] = 0; /* don't build up falling damage */ return; } if (trace.fraction > 0) { /* actually covered some distance */ VectorCopy (trace.endpos, pml.origin); numplanes = 0; } if (trace.fraction == 1) break; /* moved the entire distance */ /* save entity for contact */ if (pm->numtouch < MAXTOUCH && trace.ent) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } time_left -= time_left * trace.fraction; /* slide along this plane */ if (numplanes >= MAX_CLIP_PLANES) { /* this shouldn't really happen */ VectorCopy (vec3_origin, pml.velocity); break; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; /* modify original_velocity so it parallels all of the clip planes */ for (i=0 ; i<numplanes ; i++) { PM_ClipVelocity (pml.velocity, planes[i], pml.velocity, 1.01f); for (j=0 ; j<numplanes ; j++) if (j != i) { if (DotProduct (pml.velocity, planes[j]) < 0) break; /* not ok */ } if (j == numplanes) break; } if (i != numplanes) { /* go along this plane */ } else { /* go along the crease */ if (numplanes != 2) { VectorCopy (vec3_origin, pml.velocity); break; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, pml.velocity); VectorScale (dir, d, pml.velocity); } /* if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners */ if (DotProduct (pml.velocity, primal_velocity) <= 0) { VectorCopy (vec3_origin, pml.velocity); break; } } if (pm->s.pm_time) { VectorCopy (primal_velocity, pml.velocity); } }
qboolean PM_SlideMove( float gravMod ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t normal, 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; qboolean damageSelf = qtrue; int slideMoveContents = pm->tracemask; if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) {//a non-player client, not an NPC under player control if ( pml.walking //walking on the ground || (pm->ps->groundEntityNum != ENTITYNUM_NONE //in air && PM_InSpecialJump( pm->ps->legsAnim )//in a special jump && !(pm->ps->eFlags&EF_FORCE_GRIPPED)//not being gripped && !(pm->ps->pm_flags&PMF_TIME_KNOCKBACK) && pm->gent && pm->gent->forcePushTime < level.time) )//not being pushed {// // If we're a vehicle, ignore this if we're being driven if ( !pm->gent //not an game ent || !pm->gent->client //not a client || pm->gent->client->NPC_class != CLASS_VEHICLE//not a vehicle || !pm->gent->m_pVehicle //no vehicle || !pm->gent->m_pVehicle->m_pPilot//no pilot || pm->gent->m_pVehicle->m_pPilot->s.number >= MAX_CLIENTS )//pilot is not the player {//then treat do not enter brushes as SOLID slideMoveContents |= CONTENTS_BOTCLIP; } } } numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravMod ) { VectorCopy( pm->ps->velocity, endVelocity ); if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) && !(pm->ps->eFlags&EF_FORCE_DRAINED) ) { endVelocity[2] -= pm->ps->gravity * pml.frametime * gravMod; } pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) ) {// 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] ); if ( !PM_GroundSlideOkay( planes[0][2] ) ) { planes[0][2] = 0; VectorNormalize( 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 ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, slideMoveContents ); if ( (trace.contents&CONTENTS_BOTCLIP) && (slideMoveContents&CONTENTS_BOTCLIP) ) {//hit a do not enter brush if ( trace.allsolid || trace.startsolid )//inside the botclip {//crap, we're in a do not enter brush, take it out for the remainder of the traces and re-trace this one right now without it slideMoveContents &= ~CONTENTS_BOTCLIP; pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, slideMoveContents ); } else if ( trace.plane.normal[2] > 0.0f ) {//on top of a do not enter brush, it, just redo this one trace without it pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, (slideMoveContents&~CONTENTS_BOTCLIP) ); } } if ( trace.allsolid ) {// entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if ( trace.fraction > 0 ) {// actually covered some distance VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction == 1 ) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); //Hit it if ( trace.surfaceFlags&SURF_NODAMAGE ) { damageSelf = qfalse; } else if ( trace.entityNum == ENTITYNUM_WORLD && trace.plane.normal[2] > 0.5f ) {//if we land on the ground, let falling damage do it's thing itself, otherwise do impact damage damageSelf = qfalse; } else { damageSelf = qtrue; } if ( PM_ClientImpact( &trace, damageSelf ) ) { continue; } if (pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && trace.plane.normal[2]<pm->gent->m_pVehicle->m_pVehicleInfo->maxSlope ) { pm->ps->pm_flags |= PMF_BUMPED; } time_left -= time_left * trace.fraction; if ( numplanes >= MAX_CLIP_PLANES ) {// this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } VectorCopy( trace.plane.normal, normal ); if ( !PM_GroundSlideOkay( normal[2] ) ) {//wall-running //never push up off a sloped wall normal[2] = 0; VectorNormalize( normal ); } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) ) {//no sliding if stuck to wall! for ( i = 0 ; i < numplanes ; i++ ) { if ( DotProduct( normal, planes[i] ) > 0.99 ) { VectorAdd( normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } } VectorCopy( 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.1 ) { 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.1 ) { 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 ) { 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.1 ) { 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 ( gravMod ) { 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 G_RollMissile( gentity_t *ent ) { 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; pml_t objPML; float bounceAmt = BUMPCLIP; gentity_t *hitEnt = NULL; memset( &objPML, 0, sizeof( objPML ) ); G_GroundTrace( ent, &objPML ); objPML.frametime = (level.time - level.previousTime)*0.001; numbumps = 4; VectorCopy ( ent->s.pos.trDelta, primal_velocity ); VectorCopy( ent->s.pos.trDelta, endVelocity ); endVelocity[2] -= g_gravity->value * objPML.frametime; ent->s.pos.trDelta[2] = ( ent->s.pos.trDelta[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( objPML.groundPlane ) {//FIXME: never happens! // slide along the ground plane PM_ClipVelocity( ent->s.pos.trDelta, objPML.groundTrace.plane.normal, ent->s.pos.trDelta, BUMPCLIP ); VectorScale( ent->s.pos.trDelta, 0.9f, ent->s.pos.trDelta ); } time_left = objPML.frametime; // never turn against the ground plane if ( objPML.groundPlane ) { numplanes = 1; VectorCopy( objPML.groundTrace.plane.normal, planes[0] ); } else { numplanes = 0; } // never turn against original velocity /* VectorNormalize2( ent->s.pos.trDelta, planes[numplanes] ); numplanes++; */ for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) { // calculate position we are trying to move to VectorMA( ent->currentOrigin, time_left, ent->s.pos.trDelta, end ); // see if we can make it there gi.trace ( &trace, ent->currentOrigin, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10 ); //TEMP HACK: //had to move this up above the trace.allsolid check now that for some reason ghoul2 impacts tell me I'm allsolid?! //this needs to be fixed, really if ( trace.entityNum < ENTITYNUM_WORLD ) {//hit another ent hitEnt = &g_entities[trace.entityNum]; if ( hitEnt && (hitEnt->takedamage || (hitEnt->contents&CONTENTS_LIGHTSABER) ) ) { G_MissileImpact( ent, &trace ); if ( ent->s.eType == ET_GENERAL ) {//exploded return; } } } if ( trace.allsolid ) { // entity is completely trapped in another solid //FIXME: this happens a lot now when we hit a G2 ent... WTF? ent->s.pos.trDelta[2] = 0; // don't build up falling damage, but allow sideways acceleration return;// qtrue; } if ( trace.fraction > 0 ) { // actually covered some distance VectorCopy( trace.endpos, ent->currentOrigin ); } if ( trace.fraction == 1 ) { break; // moved the entire distance } //pm->ps->pm_flags |= PMF_BUMPED; // save entity for contact //PM_AddTouchEnt( trace.entityNum ); //Hit it /* if ( PM_ClientImpact( trace.entityNum, qtrue ) ) { continue; } */ time_left -= time_left * trace.fraction; if ( numplanes >= MAX_CLIP_PLANES ) { // this shouldn't really happen VectorClear( ent->s.pos.trDelta ); 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.99 ) { VectorAdd( trace.plane.normal, ent->s.pos.trDelta, ent->s.pos.trDelta ); break; } } if ( i < numplanes ) { continue; } VectorCopy( trace.plane.normal, planes[numplanes] ); numplanes++; // // modify velocity so it parallels all of the clip planes // if ( &g_entities[trace.entityNum] != NULL && g_entities[trace.entityNum].client ) {//hit a person, bounce off much less bounceAmt = OVERCLIP; } else { bounceAmt = BUMPCLIP; } // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( ent->s.pos.trDelta, planes[i] ); if ( into >= 0.1 ) { 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( ent->s.pos.trDelta, planes[i], clipVelocity, bounceAmt ); // slide along the plane PM_ClipVelocity( endVelocity, planes[i], endClipVelocity, bounceAmt ); // 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.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, bounceAmt ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, bounceAmt ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, ent->s.pos.trDelta ); 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.1 ) { continue; // move doesn't interact with the plane } // stop dead at a triple plane interaction VectorClear( ent->s.pos.trDelta ); return;// qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, ent->s.pos.trDelta ); VectorCopy( endClipVelocity, endVelocity ); break; } VectorScale( endVelocity, 0.975f, endVelocity ); } VectorCopy( endVelocity, ent->s.pos.trDelta ); // don't change velocity if in a timer (FIXME: is this correct?) /* if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, ent->s.pos.trDelta ); } */ return;// ( bumpcount != 0 ); }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( pm->debugLevel ) { qboolean wassolid, slidesucceed; PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin ); wassolid = trace.allsolid; slidesucceed = (PM_SlideMove( gravity ) == 0) ? qtrue : qfalse; PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin ); if (trace.allsolid && !wassolid) Com_Printf("%i:PM_SlideMove solidified! (%f %f %f) -> (%f %f %f)\n", c_pmove, start_o[0], start_o[1], start_o[2], pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2] ); if (slidesucceed) return; } else { if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } } if ( pm->debugLevel ) { Com_Printf("%i:stepping\n", c_pmove); } VectorCopy(start_o, down); down[2] -= STEPSIZE; PM_TraceAll( &trace, start_o, down ); 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 (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher PM_TraceAll( &trace, up, up ); 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 ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= STEPSIZE; // check legs separately if ( pm->ps->eFlags & (EF_PRONE|EF_PLAYDEAD) ) { PM_TraceLegs( &trace, NULL, pm->ps->origin, down, NULL, pm->ps->viewangles, pm->trace, pm->ps->clientNum, pm->tracemask, (pm->ps->eFlags & EF_PRONE) ? true : false ); if ( trace.allsolid ) { // legs don't step, just fuzz. VectorCopy( down_o, pm->ps->origin ); VectorCopy( down_v, pm->ps->velocity ); if ( pm->debugLevel ) { Com_Printf("%i:legs unsteppable\n", c_pmove); } return; } } pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); if ( !trace.allsolid ) { VectorCopy (trace.endpos, pm->ps->origin); } if ( trace.fraction < 1.0 ) { 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_TraceAll( &trace, pm->ps->origin, start_o ); 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 #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
/* ================== PM_StepSlideMove ================== */ bool PM_StepSlideMove( bool gravity, bool predictive ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; vec3_t normal; vec3_t step_v, step_vNormal; vec3_t up, down; float stepSize; bool stepped = false; if( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBING ) { if( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) VectorSet( normal, 0.0f, 0.0f, -1.0f ); else VectorCopy( pm->ps->grapplePoint, normal ); } else VectorSet( normal, 0.0f, 0.0f, 1.0f ); VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); if( PM_SlideMove( gravity ) == 0 ) { VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); //we can step down if( trace.fraction > 0.01f && trace.fraction < 1.0f && !trace.allsolid && pml.groundPlane != false ) { if( pm->debugLevel ) Com_Printf( "%d: step down\n", c_pmove ); stepped = true; } } else { VectorCopy( start_o, down ); VectorMA( down, -STEPSIZE, normal, down ); pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); // never step up when you still have up velocity if( DotProduct( trace.plane.normal, pm->ps->velocity ) > 0.0f && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, normal ) < 0.7f ) ) { return stepped; } VectorCopy( pm->ps->origin, down_o ); VectorCopy( pm->ps->velocity, down_v ); VectorCopy( start_o, up ); VectorMA( up, STEPSIZE, normal, up ); // test the player position if they were a stepheight higher pm->trace( &trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask ); if( trace.allsolid ) { if( pm->debugLevel ) Com_Printf( "%i:bend can't step\n", c_pmove ); return stepped; // can't step up } VectorSubtract( trace.endpos, start_o, step_v ); VectorCopy( step_v, step_vNormal ); VectorNormalize( step_vNormal ); stepSize = DotProduct( normal, step_vNormal ) * VectorLength( step_v ); // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); if( PM_SlideMove( gravity ) == 0 ) { if( pm->debugLevel ) Com_Printf( "%d: step up\n", c_pmove ); stepped = true; } // push down the final amount VectorCopy( pm->ps->origin, down ); VectorMA( down, -stepSize, normal, down ); pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); 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 ); } if( !predictive && stepped ) PM_StepEvent( start_o, pm->ps->origin, normal ); return stepped; }
qbool PM_SlideMove(Pmove *pm, Pml *pml, qbool gravity) { int i, j, k, bumpcount, numbumps, numplanes; Vec3 dir; float d, time_left, into; Vec3 planes[MAX_CLIP_PLANES]; Vec3 primal_velocity, clipVelocity; Trace trace; Vec3 end, endVelocity, endClipVelocity; numbumps = 4; copyv3(pm->ps->velocity, primal_velocity); if(gravity){ copyv3(pm->ps->velocity, endVelocity); endVelocity[2] -= pm->ps->gravity * 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; copyv3(pml->groundTrace.plane.normal, planes[0]); }else numplanes = 0; /* never turn against original velocity */ norm2v3(pm->ps->velocity, planes[numplanes]); numplanes++; for(bumpcount=0; bumpcount < numbumps; bumpcount++){ /* calculate position we are trying to move to */ saddv3(pm->ps->origin, time_left, pm->ps->velocity, end); /* see if we can make it there */ pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); if(trace.allsolid){ /* entity is completely trapped in another solid */ pm->ps->velocity[2] = 0; /* don't build up falling damage, but allow sideways acceleration */ return qtrue; } if(trace.fraction > 0) /* actually covered some distance */ copyv3 (trace.endpos, pm->ps->origin); if(trace.fraction == 1) break; /* moved the entire distance */ /* save entity for contact */ PM_AddTouchEnt(pm, trace.entityNum); time_left -= time_left * trace.fraction; if(numplanes >= MAX_CLIP_PLANES){ /* this shouldn't really happen */ clearv3(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(dotv3(trace.plane.normal, planes[i]) > 0.99f){ addv3(trace.plane.normal, pm->ps->velocity, pm->ps->velocity); break; } if(i < numplanes) continue; copyv3 (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 = dotv3(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(dotv3(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(dotv3(clipVelocity, planes[i]) >= 0) continue; /* slide the original velocity along the crease */ crossv3 (planes[i], planes[j], dir); normv3(dir); d = dotv3(dir, pm->ps->velocity); scalev3(dir, d, clipVelocity); crossv3 (planes[i], planes[j], dir); normv3(dir); d = dotv3(dir, endVelocity); scalev3(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(dotv3(clipVelocity, planes[k]) >= 0.1f) continue; /* move doesn't interact with the plane */ /* stop dead at a triple plane interaction */ clearv3(pm->ps->velocity); return qtrue; } } /* if we have fixed all interactions, try another move */ copyv3(clipVelocity, pm->ps->velocity); copyv3(endClipVelocity, endVelocity); break; } } if(gravity) copyv3(endVelocity, pm->ps->velocity); /* don't change velocity if in a timer (FIXME: is this correct?) */ if(pm->ps->pm_time) copyv3(primal_velocity, pm->ps->velocity); return (bumpcount != 0); }
void PM_StepSlideMove(Pmove *pm, Pml *pml, qbool gravity) { Vec3 start_o, start_v; /* Vec3 down_o, down_v; */ Trace trace; /* float down_dist, up_dist; * Vec3 delta, delta2; */ Vec3 up, down; float stepSize; float delta; copyv3 (pm->ps->origin, start_o); copyv3 (pm->ps->velocity, start_v); if(PM_SlideMove(pm, pml, gravity) == 0) return; /* we got exactly where we wanted to go first try */ copyv3(start_o, down); down[2] -= STEPSIZE; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); setv3(up, 0, 0, 1); /* never step up when you still have up velocity */ if(pm->ps->velocity[2] > 0 && (trace.fraction == 1.0f || dotv3(trace.plane.normal, up) < 0.7f)) return; /* copyv3 (pm->ps->origin, down_o); * copyv3 (pm->ps->velocity, down_v); */ copyv3 (start_o, up); up[2] += STEPSIZE; /* test the player position if they were a stepheight higher */ pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if(trace.allsolid){ if(pm->debugLevel) comprintf("%u:bend can't step\n", cnt); return; /* can't step up */ } stepSize = trace.endpos[2] - start_o[2]; /* try slidemove from this position */ copyv3 (trace.endpos, pm->ps->origin); copyv3 (start_v, pm->ps->velocity); PM_SlideMove(pm, pml, gravity); /* push down the final amount */ copyv3 (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if(!trace.allsolid) copyv3 (trace.endpos, pm->ps->origin); if(trace.fraction < 1.0f) 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.0f){ /* use the original move */ copyv3 (down_o, pm->ps->origin); copyv3 (down_v, pm->ps->velocity); if(pm->debugLevel) comprintf("%u:bend\n", cnt); }else #endif /* use the step move */ delta = pm->ps->origin[2] - start_o[2]; if(delta > 2){ if(delta < 7) PM_AddEvent(pm, pml, EV_STEP_4); else if(delta < 11) PM_AddEvent(pm, pml, EV_STEP_8); else if(delta < 15) PM_AddEvent(pm, pml, EV_STEP_12); else PM_AddEvent(pm, pml, EV_STEP_16); } if(pm->debugLevel) comprintf("%u:stepped\n", cnt); }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( float gravMod ) { vec3_t start_o, start_v; vec3_t down_o, down_v; vec3_t slideMove, stepUpMove; trace_t trace; vec3_t up, down; qboolean cantStepUpFwd, isGiant = qfalse;; int stepSize = STEPSIZE; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_InReboundHold( pm->ps->legsAnim ) ) { gravMod = 0.0f; } if ( PM_SlideMove( gravMod ) == 0 ) { return; // we got exactly where we wanted to go first try }//else Bumped into something, see if we can step over it if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 ) {//Hovering vehicles don't do steps //FIXME: maybe make hovering vehicles go up steps, but not down them? return; } if ( pm->gent && pm->gent->client && (pm->gent->client->NPC_class == CLASS_ATST||pm->gent->client->NPC_class == CLASS_RANCOR) ) { isGiant = qtrue; if ( pm->gent->client->NPC_class == CLASS_RANCOR ) { if ( (pm->gent->spawnflags&1) ) { stepSize = 64;//hack for Mutant Rancor stepping } else { stepSize = 48;//hack for Rancor stepping } } else { stepSize = 70;//hack for AT-ST stepping, slightly taller than a standing stormtrooper } } else if ( pm->maxs[2] <= 0 ) {//short little guys can't go up steps... FIXME: just make this a flag for certain NPCs- especially ones that roll? stepSize = 4; } //Q3Final addition... 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; } if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {//All our velocity was cancelled sliding return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += stepSize; // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid || trace.startsolid || trace.fraction == 0) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } if ( pm->debugLevel ) { G_DebugLine(start_o,trace.endpos,2000,0xffffff,qtrue); } //===Another slidemove forward================================================================================ // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); cantStepUpFwd = PM_SlideMove( gravMod ); //===Another slidemove forward================================================================================ if ( pm->debugLevel ) { G_DebugLine(trace.endpos,pm->ps->origin,2000,0xffffff,qtrue); } //compare the initial slidemove and this slidemove from a step up position VectorSubtract( down_o, start_o, slideMove ); VectorSubtract( trace.endpos, pm->ps->origin, stepUpMove ); if ( fabs(stepUpMove[0]) < 0.1 && fabs(stepUpMove[1]) < 0.1 && VectorLengthSquared( slideMove ) > VectorLengthSquared( stepUpMove ) ) { //slideMove was better, use it VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { qboolean skipStep = qfalse; // 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); if ( pm->debugLevel ) { G_DebugLine(pm->ps->origin,trace.endpos,2000,0xffffff,qtrue); } if ( g_stepSlideFix->integer ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) {//normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { if ( pm->debugLevel ) { G_DebugLine(down_o,trace.endpos,2000,0x0000ff,qtrue); } skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum && isGiant && g_entities[trace.entityNum].client && pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) {//Rancor don't step on clients if ( g_stepSlideFix->integer ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } else if ( pm->ps->clientNum && isGiant && g_entities[trace.entityNum].client && g_entities[trace.entityNum].client->playerTeam == pm->gent->client->playerTeam ) {//AT-ST's don't step up on allies if ( g_stepSlideFix->integer ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } else { VectorCopy( trace.endpos, pm->ps->origin ); if ( g_stepSlideFix->integer ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } else { if ( g_stepSlideFix->integer ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !g_stepSlideFix->integer ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } /* if(cantStepUpFwd && pm->ps->origin[2] < start_o[2] + stepSize && pm->ps->origin[2] >= start_o[2]) {//We bumped into something we could not step up pm->ps->pm_flags |= PMF_BLOCKED; } else {//We did step up, clear the bumped flag } */ #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 #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
/* =================== PM_WalkMove =================== */ static void PM_WalkMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; float accelerate; float vel; if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) { // begin swimming PM_WaterMove(); return; } if ( PM_CheckJump () ) { // jumped away 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 ); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; // project the forward and right directions onto the ground plane PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP ); PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP ); // VectorNormalize (pml.forward); VectorNormalize (pml.right); for ( i = 0 ; i < 3 ; i++ ) { wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; } // when going up or down slopes the wish velocity should Not be zero // wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; // clamp the speed lower if ducking if ( pm->ps->pm_flags & PMF_DUCKED ) { if ( wishspeed > pm->ps->speed * pm_duckScale ) { wishspeed = pm->ps->speed * pm_duckScale; } } // clamp the speed lower if wading or walking on the bottom if ( pm->waterlevel ) { float waterScale; waterScale = pm->waterlevel / 3.0; waterScale = 1.0 - ( 1.0 - 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_airaccelerate; } else { accelerate = pm_accelerate; } PM_Accelerate (wishdir, wishspeed, accelerate); //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; } else { // don't reset the z velocity for slopes // pm->ps->velocity[2] = 0; } vel = VectorLength(pm->ps->velocity); // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); // don't decrease velocity when going up or down a slope VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); // don't do anything if standing still if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) { return; } PM_StepSlideMove( qfalse ); //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); }
qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t normal, 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; //qboolean damageSelf = qtrue; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); VectorCopy (pm->ps->velocity, endVelocity); if ( gravity ) { endVelocity[2] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) ) {// 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] ); if ( !PM_GroundSlideOkay( planes[0][2] ) ) { planes[0][2] = 0; VectorNormalize( 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 ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); //Normal player tracemask is SOLID,PLAYERCLIP,BODY,TERRAIN if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration. wallbug!? return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); if (pm->ps->clientNum >= MAX_CLIENTS) { bgEntity_t *pEnt = pm_entSelf; if (pEnt && pEnt->s.eType == ET_NPC && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle) { //do vehicle impact stuff then if (!pEnt->playerState->stats[STAT_RACEMODE]) PM_VehicleImpact(pEnt, &trace); } } #ifdef _GAME else { if ( PM_ClientImpact( &trace ) ) { continue; } } #endif time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } VectorCopy( trace.plane.normal, normal ); if ( !PM_GroundSlideOkay( normal[2] ) ) {//wall-running //never push up off a sloped wall normal[2] = 0; VectorNormalize( normal ); } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) ) {//no sliding if stuck to wall! for ( i = 0 ; i < numplanes ; i++ ) { if ( VectorCompare( normal, planes[i] ) ) {//DotProduct( normal, planes[i] ) > 0.99 ) { VectorAdd( normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } } VectorCopy (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.1 ) { 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.1 ) { 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 ) { 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.1 ) { 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 ); }
/* =================== MoveCmdToDesiredDir Given a movement command, computes the desired direction and speed of that movement, projected onto the ground plane (if any). The unitized direction is stored in the "dir" vector and the speed (magnitude) is the function's return value. "axies" are the forward, right, and up axial direction vectors for movement. Note that the up direction vector isn't actually used. "physics" is the physics description that applies to this command set. "max_speed" is the entity's maximum movement speed. That is, it's the velocity in units per second if the command's forward move value is 127. "water_level" is the entity's water level (1 for wading, 2 for chest deep, 3 for immersed, 0 for no water). Presumably if the water level is 2 or higher, the physics type will be PHYS_WATER, although this isn't guaranteed. FIXME: This function's interface could be simplified if just a motion state pointer was passed in instead of the user command, axies, maximum speed, and water level. The downside is that the user command and axies would either need to be recomputed each call or stored in the motion state. One of these options uses more processing time that necessary and the other uses more memory than necessary. Still, it might be in the interest of cleanliness to spend more time or memory for this purpose. Or it might not. =================== */ float MoveCmdToDesiredDir(usercmd_t *cmd, vec3_t *axies, physics_t *physics, float max_speed, float water_level, vec3_t move_dir) { int i, max_cmd_speed; float scale, move_speed; float wade_drag, max_wade_speed; vec3_t forward, right; // The axial directions may need to be modified (projected onto the XY // plane), so this local copy guarantees the inputted vectors will // remain unchanged VectorCopy(axies[0], forward); VectorCopy(axies[1], right); // Compute the speed conversion factor from the movement commands to the actual speed // // NOTE: This code is based on PM_CmdScale() from bg_pmove.c. max_cmd_speed = fabs(cmd->forwardmove); if (max_cmd_speed < fabs(cmd->rightmove)) max_cmd_speed = fabs(cmd->rightmove); if (max_cmd_speed < fabs(cmd->upmove)) max_cmd_speed = fabs(cmd->upmove); if (max_cmd_speed > 0) scale = (max_speed * max_cmd_speed) / (127.0 * sqrt(Square(cmd->forwardmove) + Square(cmd->rightmove) + Square(cmd->upmove))); else scale = 0; // Compute the velocity vector this set of movement commands translates to // // NOTE: This code is based in part on PM_WalkMove(), PM_WaterMove(), // PM_AirMove(), and PM_FlyMove() from from bg_pmove.c. // Air movement and ground movement require some special setup if ( (physics->type == PHYS_GRAVITY) || (physics->type == PHYS_GROUND) ) { // When doing air movement and normal walking, project the movement on the X-Y plane forward[2] = 0.0; right[2] = 0.0; // When moving on the ground, project movement onto the angle of the ground plane // // NOTE: This is one of the few functions in bg_pmove.c that can be called // by outside functions. // // NOTE: It *IS* correct that these clippings occur before the // renormalization required by projecting the forward and right // vectors to the X-Y plane. This may be a bug in bg_pmove.c, but // even in that case, this code is bug compliant. // // NOTE: It is intentional that this code checks specifically for // ground physics, even though there could be a (very steep) ground // normal in the gravity case. This strikes me as a bug in the // bg_pmove.c code but... Again, this code is just bug compliant. if (physics->type == PHYS_GROUND) { PM_ClipVelocity(forward, physics->ground, forward, OVERCLIP); PM_ClipVelocity( right, physics->ground, right, OVERCLIP); } // These direction vectors must be normalized again VectorNormalize(forward); VectorNormalize( right); } // The entity can sink when in water if ( (scale <= 0) && (physics->type == PHYS_WATER) ) { VectorSet(move_dir, 0, 0, -1); move_speed = 60.0; } // Otherwise compute the velocity normally else { // Project movement commands in each axis direction for (i = 0; i < 3; i++) move_dir[i] = forward[i] * cmd->forwardmove + right[i] * cmd->rightmove; // Also apply the upward movement if the entity's situation allows it if ( (physics->type == PHYS_WATER) || (physics->type == PHYS_FLIGHT) ) move_dir[2] += cmd->upmove; // Compute the movement speed in that direction and normalize the velocity // // NOTE: Technically it would be more precise to just normalize the movement // direction and then multiply the movement speed by the scale. Unfortunately, // the server code does the operation like this, and the rest of the physics // depends on this imprecision, so it must be copied here as well. VectorScale(move_dir, scale, move_dir); move_speed = VectorNormalize(move_dir); } // The player has a lower top speed when in water if ( (physics->type == PHYS_WATER) && (move_speed > max_speed * pm_swimScale) ) { move_speed = max_speed * pm_swimScale; } // Crouching on the ground also lowers top speed if ( (cmd->upmove < 0) && (physics->type == PHYS_GROUND) && (move_speed > max_speed * pm_duckScale) ) { move_speed = max_speed * pm_duckScale; } // Wading in water lowers top speed if ( (water_level > 0) && (physics->type == PHYS_GROUND) ) { // Compute the percentage of maximum speed lost from wading in water wade_drag = (1.0 - pm_swimScale) * (water_level / 3.0); // Compute the maximum speed allowed due to water drag max_wade_speed = max_speed * (1.0 - wade_drag); // Lower the top speed if it exceeds this new maximum speed if (move_speed > max_wade_speed) move_speed = max_wade_speed; } // Return the computed movement speed return move_speed; }
void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; qboolean isGiant = qfalse; bgEntity_t *pEnt; qboolean skipStep = qfalse; int NEW_STEPSIZE = STEPSIZE; const int moveStyle = PM_GetMovePhysics(); if (moveStyle == MV_CPM || moveStyle == MV_Q3 || moveStyle == MV_WSW || moveStyle == MV_RJQ3 || moveStyle == MV_RJCPM || moveStyle == MV_SLICK || moveStyle == MV_BOTCPM) { if (pm->ps->velocity[2] > 0 && pm->cmd.upmove > 0) { int jumpHeight = pm->ps->origin[2] - pm->ps->fd.forceJumpZStart; if (jumpHeight > 48) jumpHeight = 48; else if (jumpHeight < 22) jumpHeight = 22; NEW_STEPSIZE = 48 - jumpHeight + 22; //trap->SendServerCommand(-1, va("print \"new stepsize: %i, expected max end height: %i\n\"", NEW_STEPSIZE, NEW_STEPSIZE + (int)(pm->ps->origin[2] - pm->ps->fd.forceJumpZStart))); //This means that we can always clip things up to 48 units tall, if we are moving up when we hit it and from a bhop.. //It means we can sometimes clip things up to 70 units tall, if we hit it in right part of jump //Should it be higher..? some of the things in q3 are 56 units tall.. //NEW_STEPSIZE = 46; //Make stepsize equal to.. our current 48 - our current jumpheight ? } else NEW_STEPSIZE = 22; } VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( BG_InReboundHold( pm->ps->legsAnim ) ) { gravity = qfalse; } if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try, nospeed ramp returns here maybe } pEnt = pm_entSelf; if (pm->ps->clientNum >= MAX_CLIENTS) { if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) { return; } } VectorCopy(start_o, down); down[2] -= NEW_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; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); if (pm->ps->clientNum >= MAX_CLIENTS) { // apply ground friction, even if on ladder if (pEnt && (pEnt->s.NPC_class == CLASS_ATST || (pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) ) ) {//AT-STs can step high up[2] += 66.0f; isGiant = qtrue; } else if ( pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) {//also can step up high up[2] += 64.0f; isGiant = qtrue; } else { up[2] += NEW_STEPSIZE; } } else { up[2] += NEW_STEPSIZE; } // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up, nospeed ramp returns here maybe } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); pml.clipped = qtrue; //nospeed ramp fix, if we made it to this point there wont be a nospeed ramp // 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); if ( pm->stepSlideFix ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) {//normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) {//Rancor don't step on clients if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } /* else if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_ATST && OnSameTeam( pEnt, traceEnt) ) {//NPC AT-ST's don't step up on allies VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } */ else { VectorCopy (trace.endpos, pm->ps->origin); if (pm->stepSlideFix) { if (trace.fraction < 1.0) { if (moveStyle == MV_WSW || moveStyle == MV_SLICK) { //Make Warsow Rampjump not slow down your XY speed vec3_t oldVel, clipped_velocity, newVel; float oldSpeed, newSpeed; VectorCopy(pm->ps->velocity, oldVel); oldSpeed = oldVel[0] * oldVel[0] + oldVel[1] * oldVel[1]; PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, clipped_velocity, OVERCLIP ); //WSW RAMPJUMP 3 VectorCopy(clipped_velocity, newVel); newVel[2] = 0; newSpeed = newVel[0] * newVel[0] + newVel[1] * newVel[1]; if (newSpeed > oldSpeed) VectorCopy(clipped_velocity, pm->ps->velocity); } else { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } } else { if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { if (moveStyle == MV_WSW || moveStyle == MV_SLICK) { vec3_t oldVel, clipped_velocity, newVel; float oldSpeed, newSpeed; VectorCopy(pm->ps->velocity, oldVel); oldSpeed = oldVel[0] * oldVel[0] + oldVel[1] * oldVel[1]; PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, clipped_velocity, OVERCLIP ); //WSW RAMPJUMP 2 VectorCopy(clipped_velocity, newVel); newVel[2] = 0; newSpeed = newVel[0] * newVel[0] + newVel[1] * newVel[1]; if (newSpeed > oldSpeed) VectorCopy(clipped_velocity, pm->ps->velocity); } else { 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 #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove(qboolean gravity) { vec3_t start_o, start_v; // vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; 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); 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 (pm->ps->origin, down_o); //VectorCopy (pm->ps->velocity, down_v); VectorCopy(start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher pm->trace(&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { if (pm->debugLevel) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy(trace.endpos, pm->ps->origin); VectorCopy(start_v, pm->ps->velocity); PM_SlideMove(gravity); // 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); if (!trace.allsolid) { VectorCopy(trace.endpos, pm->ps->origin); } if (trace.fraction < 1.0) { 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 #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if (delta > 2) { if (delta < 7) { PM_AddEvent(EV_STEP_4); } else if (delta < 11) { PM_AddEvent(EV_STEP_8); } else if (delta < 15) { PM_AddEvent(EV_STEP_12); } else { PM_AddEvent(EV_STEP_16); } } if (pm->debugLevel) { Com_Printf("%i:stepped\n", c_pmove); } } }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( float gravMod ) { vec3_t start_o, start_v; vec3_t down_o, down_v; vec3_t slideMove, stepUpMove; trace_t trace; vec3_t up, down; qboolean cantStepUpFwd, isATST = qfalse;; int stepSize = STEPSIZE; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_SlideMove( gravMod ) == 0 ) { return; // we got exactly where we wanted to go first try }//else Bumped into something, see if we can step over it if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST) { isATST = qtrue; stepSize = 66;//hack for AT-ST stepping, slightly taller than a standing stormtrooper } else if ( pm->maxs[2] <= 0 ) {//short little guys can't go up steps... FIXME: just make this a flag for certain NPCs- especially ones that roll? stepSize = 4; } //Q3Final addition... VectorCopy(start_o, down); down[2] -= stepSize; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, G2_NOCOLLIDE, 0); 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; } if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {//All our velocity was cancelled sliding return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += stepSize; // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, G2_NOCOLLIDE, 0); if ( trace.allsolid || trace.startsolid || trace.fraction == 0) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); cantStepUpFwd = PM_SlideMove( gravMod ); //compare the initial slidemove and this slidemove from a step up position VectorSubtract( down_o, start_o, slideMove ); VectorSubtract( trace.endpos, pm->ps->origin, stepUpMove ); if ( fabs(stepUpMove[0]) < 0.1 && fabs(stepUpMove[1]) < 0.1 && VectorLengthSquared( slideMove ) > VectorLengthSquared( stepUpMove ) ) { //slideMove was better, use it VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { // 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, G2_NOCOLLIDE, 0); if ( !trace.allsolid ) { if ( pm->ps->clientNum && isATST && g_entities[trace.entityNum].client && g_entities[trace.entityNum].client->playerTeam == pm->gent->client->playerTeam ) {//AT-ST's don't step up on allies } else { VectorCopy( trace.endpos, pm->ps->origin ); } } if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } /* if(cantStepUpFwd && pm->ps->origin[2] < start_o[2] + stepSize && pm->ps->origin[2] >= start_o[2]) {//We bumped into something we could not step up pm->ps->pm_flags |= PMF_BLOCKED; } else {//We did step up, clear the bumped flag pm->ps->pm_flags &= ~PMF_BUMPED; } */ #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 #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
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; numbumps = 4; VectorCopy( pm->ps->velocity, primal_velocity ); VectorCopy( pm->ps->velocity, endVelocity ); if ( gravity ) { endVelocity[ 2 ] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[ 2 ] = ( pm->ps->velocity[ 2 ] + endVelocity[ 2 ] ) * 0.5; 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 ); } } 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 ); // see if we can make it there pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask ); if ( trace.allsolid ) { // entity is completely trapped in another solid pm->ps->velocity[ 2 ] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if ( trace.fraction > 0 ) { // actually covered some distance VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction == 1 ) { break; // moved the entire distance } // 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.99 ) { 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.1 ) { 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 ); // slide along the plane PM_ClipVelocity( endVelocity, planes[ i ], endClipVelocity ); // 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.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[ j ], clipVelocity ); PM_ClipVelocity( endClipVelocity, planes[ j ], endClipVelocity ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[ i ] ) >= 0 ) { 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 new move enters for ( k = 0; k < numplanes; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[ k ] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple 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 ); }
bool PM_SlideMove( bool gravity ) #endif { int bumpcount, numbumps; #ifdef UNREALARENA int fallingcount = 0; #endif 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; numbumps = 4; VectorCopy( pm->ps->velocity, primal_velocity ); #ifndef UNREALARENA VectorCopy( pm->ps->velocity, endVelocity ); #endif if ( gravity ) { #ifdef UNREALARENA VectorCopy( pm->ps->velocity, endVelocity ); #endif endVelocity[ 2 ] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[ 2 ] = ( pm->ps->velocity[ 2 ] + endVelocity[ 2 ] ) * 0.5; 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 ); } } 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 ); // see if we can make it there // spectators ignore movers, so that they can noclip through doors pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask, ( pm->ps->pm_type == PM_SPECTATOR ) ? CONTENTS_MOVER : 0 ); if ( trace.allsolid ) { // entity is completely trapped in another solid pm->ps->velocity[ 2 ] = 0; // don't build up falling damage, but allow sideways acceleration return true; } if ( trace.fraction > 0 ) { #ifdef UNREALARENA vec3_t pos; VectorCopy( trace.endpos, pos ); pos[ 2 ] -= stepsize; if ( PM_CheckFallingFromLedge( pos ) ) { vec3_t normal; // using pos in place of trace.endpos as it should give better results PM_FindLedgeNormal( pm->ps->origin, pos, normal ); // if we can't determine the ledge plane then stop everything if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && normal[ 2 ] == 0.0f ) { VectorClear( pm->ps->velocity ); fallingcount++; break; } // slide along the ledge plane PM_ClipVelocity( pm->ps->velocity, normal, pm->ps->velocity ); fallingcount++; continue; } #endif // actually covered some distance VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction == 1 ) { break; // moved the entire distance } // 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 true; } // // 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.99 ) { 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.1 ) { 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 ); #ifdef UNREALARENA if ( gravity ) { // slide along the plane PM_ClipVelocity( endVelocity, planes[ i ], endClipVelocity ); } #else // slide along the plane PM_ClipVelocity( endVelocity, planes[ i ], endClipVelocity ); #endif // 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.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[ j ], clipVelocity ); #ifdef UNREALARENA if ( gravity ) { PM_ClipVelocity( endClipVelocity, planes[ j ], endClipVelocity ); } #else PM_ClipVelocity( endClipVelocity, planes[ j ], endClipVelocity ); #endif // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[ i ] ) >= 0 ) { 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 ); #ifdef UNREALARENA if ( gravity ) { CrossProduct( planes[ i ], planes[ j ], dir ); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); } #else CrossProduct( planes[ i ], planes[ j ], dir ); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); #endif // see if there is a third plane the new move enters for ( k = 0; k < numplanes; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[ k ] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple plane interaction VectorClear( pm->ps->velocity ); return true; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); #ifdef UNREALARENA if ( gravity ) { VectorCopy( endClipVelocity, endVelocity ); } #else VectorCopy( endClipVelocity, endVelocity ); #endif 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 ); } #ifdef UNREALARENA bumpcount -= fallingcount; #endif return ( bumpcount != 0 ); }