Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

bg_pmove.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 // bg_pmove.c -- both games player movement code
00024 // takes a playerstate and a usercmd as input and returns a modifed playerstate
00025 
00026 #include "q_shared.h"
00027 #include "bg_public.h"
00028 #include "bg_local.h"
00029 
00030 pmove_t     *pm;
00031 pml_t       pml;
00032 
00033 // movement parameters
00034 float   pm_stopspeed = 100.0f;
00035 float   pm_duckScale = 0.25f;
00036 float   pm_swimScale = 0.50f;
00037 float   pm_wadeScale = 0.70f;
00038 
00039 float   pm_accelerate = 10.0f;
00040 float   pm_airaccelerate = 1.0f;
00041 float   pm_wateraccelerate = 4.0f;
00042 float   pm_flyaccelerate = 8.0f;
00043 
00044 float   pm_friction = 6.0f;
00045 float   pm_waterfriction = 1.0f;
00046 float   pm_flightfriction = 3.0f;
00047 float   pm_spectatorfriction = 5.0f;
00048 
00049 int     c_pmove = 0;
00050 
00051 
00052 /*
00053 ===============
00054 PM_AddEvent
00055 
00056 ===============
00057 */
00058 void PM_AddEvent( int newEvent ) {
00059     BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
00060 }
00061 
00062 /*
00063 ===============
00064 PM_AddTouchEnt
00065 ===============
00066 */
00067 void PM_AddTouchEnt( int entityNum ) {
00068     int     i;
00069 
00070     if ( entityNum == ENTITYNUM_WORLD ) {
00071         return;
00072     }
00073     if ( pm->numtouch == MAXTOUCH ) {
00074         return;
00075     }
00076 
00077     // see if it is already added
00078     for ( i = 0 ; i < pm->numtouch ; i++ ) {
00079         if ( pm->touchents[ i ] == entityNum ) {
00080             return;
00081         }
00082     }
00083 
00084     // add it
00085     pm->touchents[pm->numtouch] = entityNum;
00086     pm->numtouch++;
00087 }
00088 
00089 /*
00090 ===================
00091 PM_StartTorsoAnim
00092 ===================
00093 */
00094 static void PM_StartTorsoAnim( int anim ) {
00095     if ( pm->ps->pm_type >= PM_DEAD ) {
00096         return;
00097     }
00098     pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
00099         | anim;
00100 }
00101 static void PM_StartLegsAnim( int anim ) {
00102     if ( pm->ps->pm_type >= PM_DEAD ) {
00103         return;
00104     }
00105     if ( pm->ps->legsTimer > 0 ) {
00106         return;     // a high priority animation is running
00107     }
00108     pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
00109         | anim;
00110 }
00111 
00112 static void PM_ContinueLegsAnim( int anim ) {
00113     if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) {
00114         return;
00115     }
00116     if ( pm->ps->legsTimer > 0 ) {
00117         return;     // a high priority animation is running
00118     }
00119     PM_StartLegsAnim( anim );
00120 }
00121 
00122 static void PM_ContinueTorsoAnim( int anim ) {
00123     if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) {
00124         return;
00125     }
00126     if ( pm->ps->torsoTimer > 0 ) {
00127         return;     // a high priority animation is running
00128     }
00129     PM_StartTorsoAnim( anim );
00130 }
00131 
00132 static void PM_ForceLegsAnim( int anim ) {
00133     pm->ps->legsTimer = 0;
00134     PM_StartLegsAnim( anim );
00135 }
00136 
00137 
00138 /*
00139 ==================
00140 PM_ClipVelocity
00141 
00142 Slide off of the impacting surface
00143 ==================
00144 */
00145 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
00146     float   backoff;
00147     float   change;
00148     int     i;
00149     
00150     backoff = DotProduct (in, normal);
00151     
00152     if ( backoff < 0 ) {
00153         backoff *= overbounce;
00154     } else {
00155         backoff /= overbounce;
00156     }
00157 
00158     for ( i=0 ; i<3 ; i++ ) {
00159         change = normal[i]*backoff;
00160         out[i] = in[i] - change;
00161     }
00162 }
00163 
00164 
00165 /*
00166 ==================
00167 PM_Friction
00168 
00169 Handles both ground friction and water friction
00170 ==================
00171 */
00172 static void PM_Friction( void ) {
00173     vec3_t  vec;
00174     float   *vel;
00175     float   speed, newspeed, control;
00176     float   drop;
00177     
00178     vel = pm->ps->velocity;
00179     
00180     VectorCopy( vel, vec );
00181     if ( pml.walking ) {
00182         vec[2] = 0; // ignore slope movement
00183     }
00184 
00185     speed = VectorLength(vec);
00186     if (speed < 1) {
00187         vel[0] = 0;
00188         vel[1] = 0;     // allow sinking underwater
00189         // FIXME: still have z friction underwater?
00190         return;
00191     }
00192 
00193     drop = 0;
00194 
00195     // apply ground friction
00196     if ( pm->waterlevel <= 1 ) {
00197         if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
00198             // if getting knocked back, no friction
00199             if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
00200                 control = speed < pm_stopspeed ? pm_stopspeed : speed;
00201                 drop += control*pm_friction*pml.frametime;
00202             }
00203         }
00204     }
00205 
00206     // apply water friction even if just wading
00207     if ( pm->waterlevel ) {
00208         drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
00209     }
00210 
00211     // apply flying friction
00212     if ( pm->ps->powerups[PW_FLIGHT]) {
00213         drop += speed*pm_flightfriction*pml.frametime;
00214     }
00215 
00216     if ( pm->ps->pm_type == PM_SPECTATOR) {
00217         drop += speed*pm_spectatorfriction*pml.frametime;
00218     }
00219 
00220     // scale the velocity
00221     newspeed = speed - drop;
00222     if (newspeed < 0) {
00223         newspeed = 0;
00224     }
00225     newspeed /= speed;
00226 
00227     vel[0] = vel[0] * newspeed;
00228     vel[1] = vel[1] * newspeed;
00229     vel[2] = vel[2] * newspeed;
00230 }
00231 
00232 
00233 /*
00234 ==============
00235 PM_Accelerate
00236 
00237 Handles user intended acceleration
00238 ==============
00239 */
00240 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {
00241 #if 1
00242     // q2 style
00243     int         i;
00244     float       addspeed, accelspeed, currentspeed;
00245 
00246     currentspeed = DotProduct (pm->ps->velocity, wishdir);
00247     addspeed = wishspeed - currentspeed;
00248     if (addspeed <= 0) {
00249         return;
00250     }
00251     accelspeed = accel*pml.frametime*wishspeed;
00252     if (accelspeed > addspeed) {
00253         accelspeed = addspeed;
00254     }
00255     
00256     for (i=0 ; i<3 ; i++) {
00257         pm->ps->velocity[i] += accelspeed*wishdir[i];   
00258     }
00259 #else
00260     // proper way (avoids strafe jump maxspeed bug), but feels bad
00261     vec3_t      wishVelocity;
00262     vec3_t      pushDir;
00263     float       pushLen;
00264     float       canPush;
00265 
00266     VectorScale( wishdir, wishspeed, wishVelocity );
00267     VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
00268     pushLen = VectorNormalize( pushDir );
00269 
00270     canPush = accel*pml.frametime*wishspeed;
00271     if (canPush > pushLen) {
00272         canPush = pushLen;
00273     }
00274 
00275     VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
00276 #endif
00277 }
00278 
00279 
00280 
00281 /*
00282 ============
00283 PM_CmdScale
00284 
00285 Returns the scale factor to apply to cmd movements
00286 This allows the clients to use axial -127 to 127 values for all directions
00287 without getting a sqrt(2) distortion in speed.
00288 ============
00289 */
00290 static float PM_CmdScale( usercmd_t *cmd ) {
00291     int     max;
00292     float   total;
00293     float   scale;
00294 
00295     max = abs( cmd->forwardmove );
00296     if ( abs( cmd->rightmove ) > max ) {
00297         max = abs( cmd->rightmove );
00298     }
00299     if ( abs( cmd->upmove ) > max ) {
00300         max = abs( cmd->upmove );
00301     }
00302     if ( !max ) {
00303         return 0;
00304     }
00305 
00306     total = sqrt( cmd->forwardmove * cmd->forwardmove
00307         + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
00308     scale = (float)pm->ps->speed * max / ( 127.0 * total );
00309 
00310     return scale;
00311 }
00312 
00313 
00314 /*
00315 ================
00316 PM_SetMovementDir
00317 
00318 Determine the rotation of the legs reletive
00319 to the facing dir
00320 ================
00321 */
00322 static void PM_SetMovementDir( void ) {
00323     if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
00324         if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
00325             pm->ps->movementDir = 0;
00326         } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
00327             pm->ps->movementDir = 1;
00328         } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
00329             pm->ps->movementDir = 2;
00330         } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
00331             pm->ps->movementDir = 3;
00332         } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
00333             pm->ps->movementDir = 4;
00334         } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
00335             pm->ps->movementDir = 5;
00336         } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
00337             pm->ps->movementDir = 6;
00338         } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
00339             pm->ps->movementDir = 7;
00340         }
00341     } else {
00342         // if they aren't actively going directly sideways,
00343         // change the animation to the diagonal so they
00344         // don't stop too crooked
00345         if ( pm->ps->movementDir == 2 ) {
00346             pm->ps->movementDir = 1;
00347         } else if ( pm->ps->movementDir == 6 ) {
00348             pm->ps->movementDir = 7;
00349         } 
00350     }
00351 }
00352 
00353 
00354 /*
00355 =============
00356 PM_CheckJump
00357 =============
00358 */
00359 static qboolean PM_CheckJump( void ) {
00360     if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
00361         return qfalse;      // don't allow jump until all buttons are up
00362     }
00363 
00364     if ( pm->cmd.upmove < 10 ) {
00365         // not holding jump
00366         return qfalse;
00367     }
00368 
00369     // must wait for jump to be released
00370     if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
00371         // clear upmove so cmdscale doesn't lower running speed
00372         pm->cmd.upmove = 0;
00373         return qfalse;
00374     }
00375 
00376     pml.groundPlane = qfalse;       // jumping away
00377     pml.walking = qfalse;
00378     pm->ps->pm_flags |= PMF_JUMP_HELD;
00379 
00380     pm->ps->groundEntityNum = ENTITYNUM_NONE;
00381     pm->ps->velocity[2] = JUMP_VELOCITY;
00382     PM_AddEvent( EV_JUMP );
00383 
00384     if ( pm->cmd.forwardmove >= 0 ) {
00385         PM_ForceLegsAnim( LEGS_JUMP );
00386         pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
00387     } else {
00388         PM_ForceLegsAnim( LEGS_JUMPB );
00389         pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
00390     }
00391 
00392     return qtrue;
00393 }
00394 
00395 /*
00396 =============
00397 PM_CheckWaterJump
00398 =============
00399 */
00400 static qboolean PM_CheckWaterJump( void ) {
00401     vec3_t  spot;
00402     int     cont;
00403     vec3_t  flatforward;
00404 
00405     if (pm->ps->pm_time) {
00406         return qfalse;
00407     }
00408 
00409     // check for water jump
00410     if ( pm->waterlevel != 2 ) {
00411         return qfalse;
00412     }
00413 
00414     flatforward[0] = pml.forward[0];
00415     flatforward[1] = pml.forward[1];
00416     flatforward[2] = 0;
00417     VectorNormalize (flatforward);
00418 
00419     VectorMA (pm->ps->origin, 30, flatforward, spot);
00420     spot[2] += 4;
00421     cont = pm->pointcontents (spot, pm->ps->clientNum );
00422     if ( !(cont & CONTENTS_SOLID) ) {
00423         return qfalse;
00424     }
00425 
00426     spot[2] += 16;
00427     cont = pm->pointcontents (spot, pm->ps->clientNum );
00428     if ( cont ) {
00429         return qfalse;
00430     }
00431 
00432     // jump out of water
00433     VectorScale (pml.forward, 200, pm->ps->velocity);
00434     pm->ps->velocity[2] = 350;
00435 
00436     pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
00437     pm->ps->pm_time = 2000;
00438 
00439     return qtrue;
00440 }
00441 
00442 //============================================================================
00443 
00444 
00445 /*
00446 ===================
00447 PM_WaterJumpMove
00448 
00449 Flying out of the water
00450 ===================
00451 */
00452 static void PM_WaterJumpMove( void ) {
00453     // waterjump has no control, but falls
00454 
00455     PM_StepSlideMove( qtrue );
00456 
00457     pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
00458     if (pm->ps->velocity[2] < 0) {
00459         // cancel as soon as we are falling down again
00460         pm->ps->pm_flags &= ~PMF_ALL_TIMES;
00461         pm->ps->pm_time = 0;
00462     }
00463 }
00464 
00465 /*
00466 ===================
00467 PM_WaterMove
00468 
00469 ===================
00470 */
00471 static void PM_WaterMove( void ) {
00472     int     i;
00473     vec3_t  wishvel;
00474     float   wishspeed;
00475     vec3_t  wishdir;
00476     float   scale;
00477     float   vel;
00478 
00479     if ( PM_CheckWaterJump() ) {
00480         PM_WaterJumpMove();
00481         return;
00482     }
00483 #if 0
00484     // jump = head for surface
00485     if ( pm->cmd.upmove >= 10 ) {
00486         if (pm->ps->velocity[2] > -300) {
00487             if ( pm->watertype == CONTENTS_WATER ) {
00488                 pm->ps->velocity[2] = 100;
00489             } else if (pm->watertype == CONTENTS_SLIME) {
00490                 pm->ps->velocity[2] = 80;
00491             } else {
00492                 pm->ps->velocity[2] = 50;
00493             }
00494         }
00495     }
00496 #endif
00497     PM_Friction ();
00498 
00499     scale = PM_CmdScale( &pm->cmd );
00500     //
00501     // user intentions
00502     //
00503     if ( !scale ) {
00504         wishvel[0] = 0;
00505         wishvel[1] = 0;
00506         wishvel[2] = -60;       // sink towards bottom
00507     } else {
00508         for (i=0 ; i<3 ; i++)
00509             wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
00510 
00511         wishvel[2] += scale * pm->cmd.upmove;
00512     }
00513 
00514     VectorCopy (wishvel, wishdir);
00515     wishspeed = VectorNormalize(wishdir);
00516 
00517     if ( wishspeed > pm->ps->speed * pm_swimScale ) {
00518         wishspeed = pm->ps->speed * pm_swimScale;
00519     }
00520 
00521     PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
00522 
00523     // make sure we can go up slopes easily under water
00524     if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
00525         vel = VectorLength(pm->ps->velocity);
00526         // slide along the ground plane
00527         PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
00528             pm->ps->velocity, OVERCLIP );
00529 
00530         VectorNormalize(pm->ps->velocity);
00531         VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
00532     }
00533 
00534     PM_SlideMove( qfalse );
00535 }
00536 
00537 #ifdef MISSIONPACK
00538 /*
00539 ===================
00540 PM_InvulnerabilityMove
00541 
00542 Only with the invulnerability powerup
00543 ===================
00544 */
00545 static void PM_InvulnerabilityMove( void ) {
00546     pm->cmd.forwardmove = 0;
00547     pm->cmd.rightmove = 0;
00548     pm->cmd.upmove = 0;
00549     VectorClear(pm->ps->velocity);
00550 }
00551 #endif
00552 
00553 /*
00554 ===================
00555 PM_FlyMove
00556 
00557 Only with the flight powerup
00558 ===================
00559 */
00560 static void PM_FlyMove( void ) {
00561     int     i;
00562     vec3_t  wishvel;
00563     float   wishspeed;
00564     vec3_t  wishdir;
00565     float   scale;
00566 
00567     // normal slowdown
00568     PM_Friction ();
00569 
00570     scale = PM_CmdScale( &pm->cmd );
00571     //
00572     // user intentions
00573     //
00574     if ( !scale ) {
00575         wishvel[0] = 0;
00576         wishvel[1] = 0;
00577         wishvel[2] = 0;
00578     } else {
00579         for (i=0 ; i<3 ; i++) {
00580             wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
00581         }
00582 
00583         wishvel[2] += scale * pm->cmd.upmove;
00584     }
00585 
00586     VectorCopy (wishvel, wishdir);
00587     wishspeed = VectorNormalize(wishdir);
00588 
00589     PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
00590 
00591     PM_StepSlideMove( qfalse );
00592 }
00593 
00594 
00595 /*
00596 ===================
00597 PM_AirMove
00598 
00599 ===================
00600 */
00601 static void PM_AirMove( void ) {
00602     int         i;
00603     vec3_t      wishvel;
00604     float       fmove, smove;
00605     vec3_t      wishdir;
00606     float       wishspeed;
00607     float       scale;
00608     usercmd_t   cmd;
00609 
00610     PM_Friction();
00611 
00612     fmove = pm->cmd.forwardmove;
00613     smove = pm->cmd.rightmove;
00614 
00615     cmd = pm->cmd;
00616     scale = PM_CmdScale( &cmd );
00617 
00618     // set the movementDir so clients can rotate the legs for strafing
00619     PM_SetMovementDir();
00620 
00621     // project moves down to flat plane
00622     pml.forward[2] = 0;
00623     pml.right[2] = 0;
00624     VectorNormalize (pml.forward);
00625     VectorNormalize (pml.right);
00626 
00627     for ( i = 0 ; i < 2 ; i++ ) {
00628         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
00629     }
00630     wishvel[2] = 0;
00631 
00632     VectorCopy (wishvel, wishdir);
00633     wishspeed = VectorNormalize(wishdir);
00634     wishspeed *= scale;
00635 
00636     // not on ground, so little effect on velocity
00637     PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
00638 
00639     // we may have a ground plane that is very steep, even
00640     // though we don't have a groundentity
00641     // slide along the steep plane
00642     if ( pml.groundPlane ) {
00643         PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
00644             pm->ps->velocity, OVERCLIP );
00645     }
00646 
00647 #if 0
00648     //ZOID:  If we are on the grapple, try stair-stepping
00649     //this allows a player to use the grapple to pull himself
00650     //over a ledge
00651     if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
00652         PM_StepSlideMove ( qtrue );
00653     else
00654         PM_SlideMove ( qtrue );
00655 #endif
00656 
00657     PM_StepSlideMove ( qtrue );
00658 }
00659 
00660 /*
00661 ===================
00662 PM_GrappleMove
00663 
00664 ===================
00665 */
00666 static void PM_GrappleMove( void ) {
00667     vec3_t vel, v;
00668     float vlen;
00669 
00670     VectorScale(pml.forward, -16, v);
00671     VectorAdd(pm->ps->grapplePoint, v, v);
00672     VectorSubtract(v, pm->ps->origin, vel);
00673     vlen = VectorLength(vel);
00674     VectorNormalize( vel );
00675 
00676     if (vlen <= 100)
00677         VectorScale(vel, 10 * vlen, vel);
00678     else
00679         VectorScale(vel, 800, vel);
00680 
00681     VectorCopy(vel, pm->ps->velocity);
00682 
00683     pml.groundPlane = qfalse;
00684 }
00685 
00686 /*
00687 ===================
00688 PM_WalkMove
00689 
00690 ===================
00691 */
00692 static void PM_WalkMove( void ) {
00693     int         i;
00694     vec3_t      wishvel;
00695     float       fmove, smove;
00696     vec3_t      wishdir;
00697     float       wishspeed;
00698     float       scale;
00699     usercmd_t   cmd;
00700     float       accelerate;
00701     float       vel;
00702 
00703     if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
00704         // begin swimming
00705         PM_WaterMove();
00706         return;
00707     }
00708 
00709 
00710     if ( PM_CheckJump () ) {
00711         // jumped away
00712         if ( pm->waterlevel > 1 ) {
00713             PM_WaterMove();
00714         } else {
00715             PM_AirMove();
00716         }
00717         return;
00718     }
00719 
00720     PM_Friction ();
00721 
00722     fmove = pm->cmd.forwardmove;
00723     smove = pm->cmd.rightmove;
00724 
00725     cmd = pm->cmd;
00726     scale = PM_CmdScale( &cmd );
00727 
00728     // set the movementDir so clients can rotate the legs for strafing
00729     PM_SetMovementDir();
00730 
00731     // project moves down to flat plane
00732     pml.forward[2] = 0;
00733     pml.right[2] = 0;
00734 
00735     // project the forward and right directions onto the ground plane
00736     PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
00737     PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
00738     //
00739     VectorNormalize (pml.forward);
00740     VectorNormalize (pml.right);
00741 
00742     for ( i = 0 ; i < 3 ; i++ ) {
00743         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
00744     }
00745     // when going up or down slopes the wish velocity should Not be zero
00746 //  wishvel[2] = 0;
00747 
00748     VectorCopy (wishvel, wishdir);
00749     wishspeed = VectorNormalize(wishdir);
00750     wishspeed *= scale;
00751 
00752     // clamp the speed lower if ducking
00753     if ( pm->ps->pm_flags & PMF_DUCKED ) {
00754         if ( wishspeed > pm->ps->speed * pm_duckScale ) {
00755             wishspeed = pm->ps->speed * pm_duckScale;
00756         }
00757     }
00758 
00759     // clamp the speed lower if wading or walking on the bottom
00760     if ( pm->waterlevel ) {
00761         float   waterScale;
00762 
00763         waterScale = pm->waterlevel / 3.0;
00764         waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
00765         if ( wishspeed > pm->ps->speed * waterScale ) {
00766             wishspeed = pm->ps->speed * waterScale;
00767         }
00768     }
00769 
00770     // when a player gets hit, they temporarily lose
00771     // full control, which allows them to be moved a bit
00772     if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
00773         accelerate = pm_airaccelerate;
00774     } else {
00775         accelerate = pm_accelerate;
00776     }
00777 
00778     PM_Accelerate (wishdir, wishspeed, accelerate);
00779 
00780     //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
00781     //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
00782 
00783     if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
00784         pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
00785     } else {
00786         // don't reset the z velocity for slopes
00787 //      pm->ps->velocity[2] = 0;
00788     }
00789 
00790     vel = VectorLength(pm->ps->velocity);
00791 
00792     // slide along the ground plane
00793     PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
00794         pm->ps->velocity, OVERCLIP );
00795 
00796     // don't decrease velocity when going up or down a slope
00797     VectorNormalize(pm->ps->velocity);
00798     VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
00799 
00800     // don't do anything if standing still
00801     if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
00802         return;
00803     }
00804 
00805     PM_StepSlideMove( qfalse );
00806 
00807     //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
00808 
00809 }
00810 
00811 
00812 /*
00813 ==============
00814 PM_DeadMove
00815 ==============
00816 */
00817 static void PM_DeadMove( void ) {
00818     float   forward;
00819 
00820     if ( !pml.walking ) {
00821         return;
00822     }
00823 
00824     // extra friction
00825 
00826     forward = VectorLength (pm->ps->velocity);
00827     forward -= 20;
00828     if ( forward <= 0 ) {
00829         VectorClear (pm->ps->velocity);
00830     } else {
00831         VectorNormalize (pm->ps->velocity);
00832         VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
00833     }
00834 }
00835 
00836 
00837 /*
00838 ===============
00839 PM_NoclipMove
00840 ===============
00841 */
00842 static void PM_NoclipMove( void ) {
00843     float   speed, drop, friction, control, newspeed;
00844     int         i;
00845     vec3_t      wishvel;
00846     float       fmove, smove;
00847     vec3_t      wishdir;
00848     float       wishspeed;
00849     float       scale;
00850 
00851     pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
00852 
00853     // friction
00854 
00855     speed = VectorLength (pm->ps->velocity);
00856     if (speed < 1)
00857     {
00858         VectorCopy (vec3_origin, pm->ps->velocity);
00859     }
00860     else
00861     {
00862         drop = 0;
00863 
00864         friction = pm_friction*1.5; // extra friction
00865         control = speed < pm_stopspeed ? pm_stopspeed : speed;
00866         drop += control*friction*pml.frametime;
00867 
00868         // scale the velocity
00869         newspeed = speed - drop;
00870         if (newspeed < 0)
00871             newspeed = 0;
00872         newspeed /= speed;
00873 
00874         VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
00875     }
00876 
00877     // accelerate
00878     scale = PM_CmdScale( &pm->cmd );
00879 
00880     fmove = pm->cmd.forwardmove;
00881     smove = pm->cmd.rightmove;
00882     
00883     for (i=0 ; i<3 ; i++)
00884         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
00885     wishvel[2] += pm->cmd.upmove;
00886 
00887     VectorCopy (wishvel, wishdir);
00888     wishspeed = VectorNormalize(wishdir);
00889     wishspeed *= scale;
00890 
00891     PM_Accelerate( wishdir, wishspeed, pm_accelerate );
00892 
00893     // move
00894     VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
00895 }
00896 
00897 //============================================================================
00898 
00899 /*
00900 ================
00901 PM_FootstepForSurface
00902 
00903 Returns an event number apropriate for the groundsurface
00904 ================
00905 */
00906 static int PM_FootstepForSurface( void ) {
00907     if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) {
00908         return 0;
00909     }
00910     if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) {
00911         return EV_FOOTSTEP_METAL;
00912     }
00913     return EV_FOOTSTEP;
00914 }
00915 
00916 
00917 /*
00918 =================
00919 PM_CrashLand
00920 
00921 Check for hard landings that generate sound events
00922 =================
00923 */
00924 static void PM_CrashLand( void ) {
00925     float       delta;
00926     float       dist;
00927     float       vel, acc;
00928     float       t;
00929     float       a, b, c, den;
00930 
00931     // decide which landing animation to use
00932     if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
00933         PM_ForceLegsAnim( LEGS_LANDB );
00934     } else {
00935         PM_ForceLegsAnim( LEGS_LAND );
00936     }
00937 
00938     pm->ps->legsTimer = TIMER_LAND;
00939 
00940     // calculate the exact velocity on landing
00941     dist = pm->ps->origin[2] - pml.previous_origin[2];
00942     vel = pml.previous_velocity[2];
00943     acc = -pm->ps->gravity;
00944 
00945     a = acc / 2;
00946     b = vel;
00947     c = -dist;
00948 
00949     den =  b * b - 4 * a * c;
00950     if ( den < 0 ) {
00951         return;
00952     }
00953     t = (-b - sqrt( den ) ) / ( 2 * a );
00954 
00955     delta = vel + t * acc;
00956     delta = delta*delta * 0.0001;
00957 
00958     // ducking while falling doubles damage
00959     if ( pm->ps->pm_flags & PMF_DUCKED ) {
00960         delta *= 2;
00961     }
00962 
00963     // never take falling damage if completely underwater
00964     if ( pm->waterlevel == 3 ) {
00965         return;
00966     }
00967 
00968     // reduce falling damage if there is standing water
00969     if ( pm->waterlevel == 2 ) {
00970         delta *= 0.25;
00971     }
00972     if ( pm->waterlevel == 1 ) {
00973         delta *= 0.5;
00974     }
00975 
00976     if ( delta < 1 ) {
00977         return;
00978     }
00979 
00980     // create a local entity event to play the sound
00981 
00982     // SURF_NODAMAGE is used for bounce pads where you don't ever
00983     // want to take damage or play a crunch sound
00984     if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )  {
00985         if ( delta > 60 ) {
00986             PM_AddEvent( EV_FALL_FAR );
00987         } else if ( delta > 40 ) {
00988             // this is a pain grunt, so don't play it if dead
00989             if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
00990                 PM_AddEvent( EV_FALL_MEDIUM );
00991             }
00992         } else if ( delta > 7 ) {
00993             PM_AddEvent( EV_FALL_SHORT );
00994         } else {
00995             PM_AddEvent( PM_FootstepForSurface() );
00996         }
00997     }
00998 
00999     // start footstep cycle over
01000     pm->ps->bobCycle = 0;
01001 }
01002 
01003 /*
01004 =============
01005 PM_CheckStuck
01006 =============
01007 */
01008 /*
01009 void PM_CheckStuck(void) {
01010     trace_t trace;
01011 
01012     pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask);
01013     if (trace.allsolid) {
01014         //int shit = qtrue;
01015     }
01016 }
01017 */
01018 
01019 /*
01020 =============
01021 PM_CorrectAllSolid
01022 =============
01023 */
01024 static int PM_CorrectAllSolid( trace_t *trace ) {
01025     int         i, j, k;
01026     vec3_t      point;
01027 
01028     if ( pm->debugLevel ) {
01029         Com_Printf("%i:allsolid\n", c_pmove);
01030     }
01031 
01032     // jitter around
01033     for (i = -1; i <= 1; i++) {
01034         for (j = -1; j <= 1; j++) {
01035             for (k = -1; k <= 1; k++) {
01036                 VectorCopy(pm->ps->origin, point);
01037                 point[0] += (float) i;
01038                 point[1] += (float) j;
01039                 point[2] += (float) k;
01040                 pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
01041                 if ( !trace->allsolid ) {
01042                     point[0] = pm->ps->origin[0];
01043                     point[1] = pm->ps->origin[1];
01044                     point[2] = pm->ps->origin[2] - 0.25;
01045 
01046                     pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
01047                     pml.groundTrace = *trace;
01048                     return qtrue;
01049                 }
01050             }
01051         }
01052     }
01053 
01054     pm->ps->groundEntityNum = ENTITYNUM_NONE;
01055     pml.groundPlane = qfalse;
01056     pml.walking = qfalse;
01057 
01058     return qfalse;
01059 }
01060 
01061 
01062 /*
01063 =============
01064 PM_GroundTraceMissed
01065 
01066 The ground trace didn't hit a surface, so we are in freefall
01067 =============
01068 */
01069 static void PM_GroundTraceMissed( void ) {
01070     trace_t     trace;
01071     vec3_t      point;
01072 
01073     if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
01074         // we just transitioned into freefall
01075         if ( pm->debugLevel ) {
01076             Com_Printf("%i:lift\n", c_pmove);
01077         }
01078 
01079         // if they aren't in a jumping animation and the ground is a ways away, force into it
01080         // if we didn't do the trace, the player would be backflipping down staircases
01081         VectorCopy( pm->ps->origin, point );
01082         point[2] -= 64;
01083 
01084         pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
01085         if ( trace.fraction == 1.0 ) {
01086             if ( pm->cmd.forwardmove >= 0 ) {
01087                 PM_ForceLegsAnim( LEGS_JUMP );
01088                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
01089             } else {
01090                 PM_ForceLegsAnim( LEGS_JUMPB );
01091                 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
01092             }
01093         }
01094     }
01095 
01096     pm->ps->groundEntityNum = ENTITYNUM_NONE;
01097     pml.groundPlane = qfalse;
01098     pml.walking = qfalse;
01099 }
01100 
01101 
01102 /*
01103 =============
01104 PM_GroundTrace
01105 =============
01106 */
01107 static void PM_GroundTrace( void ) {
01108     vec3_t      point;
01109     trace_t     trace;
01110 
01111     point[0] = pm->ps->origin[0];
01112     point[1] = pm->ps->origin[1];
01113     point[2] = pm->ps->origin[2] - 0.25;
01114 
01115     pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
01116     pml.groundTrace = trace;
01117 
01118     // do something corrective if the trace starts in a solid...
01119     if ( trace.allsolid ) {
01120         if ( !PM_CorrectAllSolid(&trace) )
01121             return;
01122     }
01123 
01124     // if the trace didn't hit anything, we are in free fall
01125     if ( trace.fraction == 1.0 ) {
01126         PM_GroundTraceMissed();
01127         pml.groundPlane = qfalse;
01128         pml.walking = qfalse;
01129         return;
01130     }
01131 
01132     // check if getting thrown off the ground
01133     if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
01134         if ( pm->debugLevel ) {
01135             Com_Printf("%i:kickoff\n", c_pmove);
01136         }
01137         // go into jump animation
01138         if ( pm->cmd.forwardmove >= 0 ) {
01139             PM_ForceLegsAnim( LEGS_JUMP );
01140             pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
01141         } else {
01142             PM_ForceLegsAnim( LEGS_JUMPB );
01143             pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
01144         }
01145 
01146         pm->ps->groundEntityNum = ENTITYNUM_NONE;
01147         pml.groundPlane = qfalse;
01148         pml.walking = qfalse;
01149         return;
01150     }
01151     
01152     // slopes that are too steep will not be considered onground
01153     if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) {
01154         if ( pm->debugLevel ) {
01155             Com_Printf("%i:steep\n", c_pmove);
01156         }
01157         // FIXME: if they can't slide down the slope, let them
01158         // walk (sharp crevices)
01159         pm->ps->groundEntityNum = ENTITYNUM_NONE;
01160         pml.groundPlane = qtrue;
01161         pml.walking = qfalse;
01162         return;
01163     }
01164 
01165     pml.groundPlane = qtrue;
01166     pml.walking = qtrue;
01167 
01168     // hitting solid ground will end a waterjump
01169     if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
01170     {
01171         pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
01172         pm->ps->pm_time = 0;
01173     }
01174 
01175     if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
01176         // just hit the ground
01177         if ( pm->debugLevel ) {
01178             Com_Printf("%i:Land\n", c_pmove);
01179         }
01180         
01181         PM_CrashLand();
01182 
01183         // don't do landing time if we were just going down a slope
01184         if ( pml.previous_velocity[2] < -200 ) {
01185             // don't allow another jump for a little while
01186             pm->ps->pm_flags |= PMF_TIME_LAND;
01187             pm->ps->pm_time = 250;
01188         }
01189     }
01190 
01191     pm->ps->groundEntityNum = trace.entityNum;
01192 
01193     // don't reset the z velocity for slopes
01194 //  pm->ps->velocity[2] = 0;
01195 
01196     PM_AddTouchEnt( trace.entityNum );
01197 }
01198 
01199 
01200 /*
01201 =============
01202 PM_SetWaterLevel    FIXME: avoid this twice?  certainly if not moving
01203 =============
01204 */
01205 static void PM_SetWaterLevel( void ) {
01206     vec3_t      point;
01207     int         cont;
01208     int         sample1;
01209     int         sample2;
01210 
01211     //
01212     // get waterlevel, accounting for ducking
01213     //
01214     pm->waterlevel = 0;
01215     pm->watertype = 0;
01216 
01217     point[0] = pm->ps->origin[0];
01218     point[1] = pm->ps->origin[1];
01219     point[2] = pm->ps->origin[2] + MINS_Z + 1;  
01220     cont = pm->pointcontents( point, pm->ps->clientNum );
01221 
01222     if ( cont & MASK_WATER ) {
01223         sample2 = pm->ps->viewheight - MINS_Z;
01224         sample1 = sample2 / 2;
01225 
01226         pm->watertype = cont;
01227         pm->waterlevel = 1;
01228         point[2] = pm->ps->origin[2] + MINS_Z + sample1;
01229         cont = pm->pointcontents (point, pm->ps->clientNum );
01230         if ( cont & MASK_WATER ) {
01231             pm->waterlevel = 2;
01232             point[2] = pm->ps->origin[2] + MINS_Z + sample2;
01233             cont = pm->pointcontents (point, pm->ps->clientNum );
01234             if ( cont & MASK_WATER ){
01235                 pm->waterlevel = 3;
01236             }
01237         }
01238     }
01239 
01240 }
01241 
01242 /*
01243 ==============
01244 PM_CheckDuck
01245 
01246 Sets mins, maxs, and pm->ps->viewheight
01247 ==============
01248 */
01249 static void PM_CheckDuck (void)
01250 {
01251     trace_t trace;
01252 
01253     if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
01254         if ( pm->ps->pm_flags & PMF_INVULEXPAND ) {
01255             // invulnerability sphere has a 42 units radius
01256             VectorSet( pm->mins, -42, -42, -42 );
01257             VectorSet( pm->maxs, 42, 42, 42 );
01258         }
01259         else {
01260             VectorSet( pm->mins, -15, -15, MINS_Z );
01261             VectorSet( pm->maxs, 15, 15, 16 );
01262         }
01263         pm->ps->pm_flags |= PMF_DUCKED;
01264         pm->ps->viewheight = CROUCH_VIEWHEIGHT;
01265         return;
01266     }
01267     pm->ps->pm_flags &= ~PMF_INVULEXPAND;
01268 
01269     pm->mins[0] = -15;
01270     pm->mins[1] = -15;
01271 
01272     pm->maxs[0] = 15;
01273     pm->maxs[1] = 15;
01274 
01275     pm->mins[2] = MINS_Z;
01276 
01277     if (pm->ps->pm_type == PM_DEAD)
01278     {
01279         pm->maxs[2] = -8;
01280         pm->ps->viewheight = DEAD_VIEWHEIGHT;
01281         return;
01282     }
01283 
01284     if (pm->cmd.upmove < 0)
01285     {   // duck
01286         pm->ps->pm_flags |= PMF_DUCKED;
01287     }
01288     else
01289     {   // stand up if possible
01290         if (pm->ps->pm_flags & PMF_DUCKED)
01291         {
01292             // try to stand up
01293             pm->maxs[2] = 32;
01294             pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
01295             if (!trace.allsolid)
01296                 pm->ps->pm_flags &= ~PMF_DUCKED;
01297         }
01298     }
01299 
01300     if (pm->ps->pm_flags & PMF_DUCKED)
01301     {
01302         pm->maxs[2] = 16;
01303         pm->ps->viewheight = CROUCH_VIEWHEIGHT;
01304     }
01305     else
01306     {
01307         pm->maxs[2] = 32;
01308         pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
01309     }
01310 }
01311 
01312 
01313 
01314 //===================================================================
01315 
01316 
01317 /*
01318 ===============
01319 PM_Footsteps
01320 ===============
01321 */
01322 static void PM_Footsteps( void ) {
01323     float       bobmove;
01324     int         old;
01325     qboolean    footstep;
01326 
01327     //
01328     // calculate speed and cycle to be used for
01329     // all cyclic walking effects
01330     //
01331     pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
01332         +  pm->ps->velocity[1] * pm->ps->velocity[1] );
01333 
01334     if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
01335 
01336         if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
01337             PM_ContinueLegsAnim( LEGS_IDLECR );
01338         }
01339         // airborne leaves position in cycle intact, but doesn't advance
01340         if ( pm->waterlevel > 1 ) {
01341             PM_ContinueLegsAnim( LEGS_SWIM );
01342         }
01343         return;
01344     }
01345 
01346     // if not trying to move
01347     if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
01348         if (  pm->xyspeed < 5 ) {
01349             pm->ps->bobCycle = 0;   // start at beginning of cycle again
01350             if ( pm->ps->pm_flags & PMF_DUCKED ) {
01351                 PM_ContinueLegsAnim( LEGS_IDLECR );
01352             } else {
01353                 PM_ContinueLegsAnim( LEGS_IDLE );
01354             }
01355         }
01356         return;
01357     }
01358     
01359 
01360     footstep = qfalse;
01361 
01362     if ( pm->ps->pm_flags & PMF_DUCKED ) {
01363         bobmove = 0.5;  // ducked characters bob much faster
01364         if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
01365             PM_ContinueLegsAnim( LEGS_BACKCR );
01366         }
01367         else {
01368             PM_ContinueLegsAnim( LEGS_WALKCR );
01369         }
01370         // ducked characters never play footsteps
01371     /*
01372     } else  if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
01373         if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
01374             bobmove = 0.4;  // faster speeds bob faster
01375             footstep = qtrue;
01376         } else {
01377             bobmove = 0.3;
01378         }
01379         PM_ContinueLegsAnim( LEGS_BACK );
01380     */
01381     } else {
01382         if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
01383             bobmove = 0.4f; // faster speeds bob faster
01384             if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
01385                 PM_ContinueLegsAnim( LEGS_BACK );
01386             }
01387             else {
01388                 PM_ContinueLegsAnim( LEGS_RUN );
01389             }
01390             footstep = qtrue;
01391         } else {
01392             bobmove = 0.3f; // walking bobs slow
01393             if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
01394                 PM_ContinueLegsAnim( LEGS_BACKWALK );
01395             }
01396             else {
01397                 PM_ContinueLegsAnim( LEGS_WALK );
01398             }
01399         }
01400     }
01401 
01402     // check for footstep / splash sounds
01403     old = pm->ps->bobCycle;
01404     pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
01405 
01406     // if we just crossed a cycle boundary, play an apropriate footstep event
01407     if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {
01408         if ( pm->waterlevel == 0 ) {
01409             // on ground will only play sounds if running
01410             if ( footstep && !pm->noFootsteps ) {
01411                 PM_AddEvent( PM_FootstepForSurface() );
01412             }
01413         } else if ( pm->waterlevel == 1 ) {
01414             // splashing
01415             PM_AddEvent( EV_FOOTSPLASH );
01416         } else if ( pm->waterlevel == 2 ) {
01417             // wading / swimming at surface
01418             PM_AddEvent( EV_SWIM );
01419         } else if ( pm->waterlevel == 3 ) {
01420             // no sound when completely underwater
01421 
01422         }
01423     }
01424 }
01425 
01426 /*
01427 ==============
01428 PM_WaterEvents
01429 
01430 Generate sound events for entering and leaving water
01431 ==============
01432 */
01433 static void PM_WaterEvents( void ) {        // FIXME?
01434     //
01435     // if just entered a water volume, play a sound
01436     //
01437     if (!pml.previous_waterlevel && pm->waterlevel) {
01438         PM_AddEvent( EV_WATER_TOUCH );
01439     }
01440 
01441     //
01442     // if just completely exited a water volume, play a sound
01443     //
01444     if (pml.previous_waterlevel && !pm->waterlevel) {
01445         PM_AddEvent( EV_WATER_LEAVE );
01446     }
01447 
01448     //
01449     // check for head just going under water
01450     //
01451     if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
01452         PM_AddEvent( EV_WATER_UNDER );
01453     }
01454 
01455     //
01456     // check for head just coming out of water
01457     //
01458     if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
01459         PM_AddEvent( EV_WATER_CLEAR );
01460     }
01461 }
01462 
01463 
01464 /*
01465 ===============
01466 PM_BeginWeaponChange
01467 ===============
01468 */
01469 static void PM_BeginWeaponChange( int weapon ) {
01470     if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
01471         return;
01472     }
01473 
01474     if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
01475         return;
01476     }
01477     
01478     if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
01479         return;
01480     }
01481 
01482     PM_AddEvent( EV_CHANGE_WEAPON );
01483     pm->ps->weaponstate = WEAPON_DROPPING;
01484     pm->ps->weaponTime += 200;
01485     PM_StartTorsoAnim( TORSO_DROP );
01486 }
01487 
01488 
01489 /*
01490 ===============
01491 PM_FinishWeaponChange
01492 ===============
01493 */
01494 static void PM_FinishWeaponChange( void ) {
01495     int     weapon;
01496 
01497     weapon = pm->cmd.weapon;
01498     if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
01499         weapon = WP_NONE;
01500     }
01501 
01502     if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
01503         weapon = WP_NONE;
01504     }
01505 
01506     pm->ps->weapon = weapon;
01507     pm->ps->weaponstate = WEAPON_RAISING;
01508     pm->ps->weaponTime += 250;
01509     PM_StartTorsoAnim( TORSO_RAISE );
01510 }
01511 
01512 
01513 /*
01514 ==============
01515 PM_TorsoAnimation
01516 
01517 ==============
01518 */
01519 static void PM_TorsoAnimation( void ) {
01520     if ( pm->ps->weaponstate == WEAPON_READY ) {
01521         if ( pm->ps->weapon == WP_GAUNTLET ) {
01522             PM_ContinueTorsoAnim( TORSO_STAND2 );
01523         } else {
01524             PM_ContinueTorsoAnim( TORSO_STAND );
01525         }
01526         return;
01527     }
01528 }
01529 
01530 
01531 /*
01532 ==============
01533 PM_Weapon
01534 
01535 Generates weapon events and modifes the weapon counter
01536 ==============
01537 */
01538 static void PM_Weapon( void ) {
01539     int     addTime;
01540 
01541     // don't allow attack until all buttons are up
01542     if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
01543         return;
01544     }
01545 
01546     // ignore if spectator
01547     if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
01548         return;
01549     }
01550 
01551     // check for dead player
01552     if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
01553         pm->ps->weapon = WP_NONE;
01554         return;
01555     }
01556 
01557     // check for item using
01558     if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
01559         if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
01560             if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
01561                 && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
01562                 // don't use medkit if at max health
01563             } else {
01564                 pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
01565                 PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
01566                 pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
01567             }
01568             return;
01569         }
01570     } else {
01571         pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
01572     }
01573 
01574 
01575     // make weapon function
01576     if ( pm->ps->weaponTime > 0 ) {
01577         pm->ps->weaponTime -= pml.msec;
01578     }
01579 
01580     // check for weapon change
01581     // can't change if weapon is firing, but can change
01582     // again if lowering or raising
01583     if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
01584         if ( pm->ps->weapon != pm->cmd.weapon ) {
01585             PM_BeginWeaponChange( pm->cmd.weapon );
01586         }
01587     }
01588 
01589     if ( pm->ps->weaponTime > 0 ) {
01590         return;
01591     }
01592 
01593     // change weapon if time
01594     if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
01595         PM_FinishWeaponChange();
01596         return;
01597     }
01598 
01599     if ( pm->ps->weaponstate == WEAPON_RAISING ) {
01600         pm->ps->weaponstate = WEAPON_READY;
01601         if ( pm->ps->weapon == WP_GAUNTLET ) {
01602             PM_StartTorsoAnim( TORSO_STAND2 );
01603         } else {
01604             PM_StartTorsoAnim( TORSO_STAND );
01605         }
01606         return;
01607     }
01608 
01609     // check for fire
01610     if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) {
01611         pm->ps->weaponTime = 0;
01612         pm->ps->weaponstate = WEAPON_READY;
01613         return;
01614     }
01615 
01616     // start the animation even if out of ammo
01617     if ( pm->ps->weapon == WP_GAUNTLET ) {
01618         // the guantlet only "fires" when it actually hits something
01619         if ( !pm->gauntletHit ) {
01620             pm->ps->weaponTime = 0;
01621             pm->ps->weaponstate = WEAPON_READY;
01622             return;
01623         }
01624         PM_StartTorsoAnim( TORSO_ATTACK2 );
01625     } else {
01626         PM_StartTorsoAnim( TORSO_ATTACK );
01627     }
01628 
01629     pm->ps->weaponstate = WEAPON_FIRING;
01630 
01631     // check for out of ammo
01632     if ( ! pm->ps->ammo[ pm->ps->weapon ] ) {
01633         PM_AddEvent( EV_NOAMMO );
01634         pm->ps->weaponTime += 500;
01635         return;
01636     }
01637 
01638     // take an ammo away if not infinite
01639     if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) {
01640         pm->ps->ammo[ pm->ps->weapon ]--;
01641     }
01642 
01643     // fire weapon
01644     PM_AddEvent( EV_FIRE_WEAPON );
01645 
01646     switch( pm->ps->weapon ) {
01647     default:
01648     case WP_GAUNTLET:
01649         addTime = 400;
01650         break;
01651     case WP_LIGHTNING:
01652         addTime = 50;
01653         break;
01654     case WP_SHOTGUN:
01655         addTime = 1000;
01656         break;
01657     case WP_MACHINEGUN:
01658         addTime = 100;
01659         break;
01660     case WP_GRENADE_LAUNCHER:
01661         addTime = 800;
01662         break;
01663     case WP_ROCKET_LAUNCHER:
01664         addTime = 800;
01665         break;
01666     case WP_PLASMAGUN:
01667         addTime = 100;
01668         break;
01669     case WP_RAILGUN:
01670         addTime = 1500;
01671         break;
01672     case WP_BFG:
01673         addTime = 200;
01674         break;
01675     case WP_GRAPPLING_HOOK:
01676         addTime = 400;
01677         break;
01678 #ifdef MISSIONPACK
01679     case WP_NAILGUN:
01680         addTime = 1000;
01681         break;
01682     case WP_PROX_LAUNCHER:
01683         addTime = 800;
01684         break;
01685     case WP_CHAINGUN:
01686         addTime = 30;
01687         break;
01688 #endif
01689     }
01690 
01691 #ifdef MISSIONPACK
01692     if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
01693         addTime /= 1.5;
01694     }
01695     else
01696     if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
01697         addTime /= 1.3;
01698   }
01699   else
01700 #endif
01701     if ( pm->ps->powerups[PW_HASTE] ) {
01702         addTime /= 1.3;
01703     }
01704 
01705     pm->ps->weaponTime += addTime;
01706 }
01707 
01708 /*
01709 ================
01710 PM_Animate
01711 ================
01712 */
01713 
01714 static void PM_Animate( void ) {
01715     if ( pm->cmd.buttons & BUTTON_GESTURE ) {
01716         if ( pm->ps->torsoTimer == 0 ) {
01717             PM_StartTorsoAnim( TORSO_GESTURE );
01718             pm->ps->torsoTimer = TIMER_GESTURE;
01719             PM_AddEvent( EV_TAUNT );
01720         }
01721 #ifdef MISSIONPACK
01722     } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
01723         if ( pm->ps->torsoTimer == 0 ) {
01724             PM_StartTorsoAnim( TORSO_GETFLAG );
01725             pm->ps->torsoTimer = 600;   //TIMER_GESTURE;
01726         }
01727     } else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
01728         if ( pm->ps->torsoTimer == 0 ) {
01729             PM_StartTorsoAnim( TORSO_GUARDBASE );
01730             pm->ps->torsoTimer = 600;   //TIMER_GESTURE;
01731         }
01732     } else if ( pm->cmd.buttons & BUTTON_PATROL ) {
01733         if ( pm->ps->torsoTimer == 0 ) {
01734             PM_StartTorsoAnim( TORSO_PATROL );
01735             pm->ps->torsoTimer = 600;   //TIMER_GESTURE;
01736         }
01737     } else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
01738         if ( pm->ps->torsoTimer == 0 ) {
01739             PM_StartTorsoAnim( TORSO_FOLLOWME );
01740             pm->ps->torsoTimer = 600;   //TIMER_GESTURE;
01741         }
01742     } else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
01743         if ( pm->ps->torsoTimer == 0 ) {
01744             PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
01745             pm->ps->torsoTimer = 600;   //TIMER_GESTURE;
01746         }
01747     } else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
01748         if ( pm->ps->torsoTimer == 0 ) {
01749             PM_StartTorsoAnim( TORSO_NEGATIVE );
01750             pm->ps->torsoTimer = 600;   //TIMER_GESTURE;
01751         }
01752 #endif
01753     }
01754 }
01755 
01756 
01757 /*
01758 ================
01759 PM_DropTimers
01760 ================
01761 */
01762 static void PM_DropTimers( void ) {
01763     // drop misc timing counter
01764     if ( pm->ps->pm_time ) {
01765         if ( pml.msec >= pm->ps->pm_time ) {
01766             pm->ps->pm_flags &= ~PMF_ALL_TIMES;
01767             pm->ps->pm_time = 0;
01768         } else {
01769             pm->ps->pm_time -= pml.msec;
01770         }
01771     }
01772 
01773     // drop animation counter
01774     if ( pm->ps->legsTimer > 0 ) {
01775         pm->ps->legsTimer -= pml.msec;
01776         if ( pm->ps->legsTimer < 0 ) {
01777             pm->ps->legsTimer = 0;
01778         }
01779     }
01780 
01781     if ( pm->ps->torsoTimer > 0 ) {
01782         pm->ps->torsoTimer -= pml.msec;
01783         if ( pm->ps->torsoTimer < 0 ) {
01784             pm->ps->torsoTimer = 0;
01785         }
01786     }
01787 }
01788 
01789 /*
01790 ================
01791 PM_UpdateViewAngles
01792 
01793 This can be used as another entry point when only the viewangles
01794 are being updated isntead of a full move
01795 ================
01796 */
01797 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
01798     short       temp;
01799     int     i;
01800 
01801     if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
01802         return;     // no view changes at all
01803     }
01804 
01805     if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
01806         return;     // no view changes at all
01807     }
01808 
01809     // circularly clamp the angles with deltas
01810     for (i=0 ; i<3 ; i++) {
01811         temp = cmd->angles[i] + ps->delta_angles[i];
01812         if ( i == PITCH ) {
01813             // don't let the player look up or down more than 90 degrees
01814             if ( temp > 16000 ) {
01815                 ps->delta_angles[i] = 16000 - cmd->angles[i];
01816                 temp = 16000;
01817             } else if ( temp < -16000 ) {
01818                 ps->delta_angles[i] = -16000 - cmd->angles[i];
01819                 temp = -16000;
01820             }
01821         }
01822         ps->viewangles[i] = SHORT2ANGLE(temp);
01823     }
01824 
01825 }
01826 
01827 
01828 /*
01829 ================
01830 PmoveSingle
01831 
01832 ================
01833 */
01834 void trap_SnapVector( float *v );
01835 
01836 void PmoveSingle (pmove_t *pmove) {
01837     pm = pmove;
01838 
01839     // this counter lets us debug movement problems with a journal
01840     // by setting a conditional breakpoint fot the previous frame
01841     c_pmove++;
01842 
01843     // clear results
01844     pm->numtouch = 0;
01845     pm->watertype = 0;
01846     pm->waterlevel = 0;
01847 
01848     if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
01849         pm->tracemask &= ~CONTENTS_BODY;    // corpses can fly through bodies
01850     }
01851 
01852     // make sure walking button is clear if they are running, to avoid
01853     // proxy no-footsteps cheats
01854     if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
01855         pm->cmd.buttons &= ~BUTTON_WALKING;
01856     }
01857 
01858     // set the talk balloon flag
01859     if ( pm->cmd.buttons & BUTTON_TALK ) {
01860         pm->ps->eFlags |= EF_TALK;
01861     } else {
01862         pm->ps->eFlags &= ~EF_TALK;
01863     }
01864 
01865     // set the firing flag for continuous beam weapons
01866     if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
01867         && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
01868         pm->ps->eFlags |= EF_FIRING;
01869     } else {
01870         pm->ps->eFlags &= ~EF_FIRING;
01871     }
01872 
01873     // clear the respawned flag if attack and use are cleared
01874     if ( pm->ps->stats[STAT_HEALTH] > 0 && 
01875         !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
01876         pm->ps->pm_flags &= ~PMF_RESPAWNED;
01877     }
01878 
01879     // if talk button is down, dissallow all other input
01880     // this is to prevent any possible intercept proxy from
01881     // adding fake talk balloons
01882     if ( pmove->cmd.buttons & BUTTON_TALK ) {
01883         // keep the talk button set tho for when the cmd.serverTime > 66 msec
01884         // and the same cmd is used multiple times in Pmove
01885         pmove->cmd.buttons = BUTTON_TALK;
01886         pmove->cmd.forwardmove = 0;
01887         pmove->cmd.rightmove = 0;
01888         pmove->cmd.upmove = 0;
01889     }
01890 
01891     // clear all pmove local vars
01892     memset (&pml, 0, sizeof(pml));
01893 
01894     // determine the time
01895     pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
01896     if ( pml.msec < 1 ) {
01897         pml.msec = 1;
01898     } else if ( pml.msec > 200 ) {
01899         pml.msec = 200;
01900     }
01901     pm->ps->commandTime = pmove->cmd.serverTime;
01902 
01903     // save old org in case we get stuck
01904     VectorCopy (pm->ps->origin, pml.previous_origin);
01905 
01906     // save old velocity for crashlanding
01907     VectorCopy (pm->ps->velocity, pml.previous_velocity);
01908 
01909     pml.frametime = pml.msec * 0.001;
01910 
01911     // update the viewangles
01912     PM_UpdateViewAngles( pm->ps, &pm->cmd );
01913 
01914     AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
01915 
01916     if ( pm->cmd.upmove < 10 ) {
01917         // not holding jump
01918         pm->ps->pm_flags &= ~PMF_JUMP_HELD;
01919     }
01920 
01921     // decide if backpedaling animations should be used
01922     if ( pm->cmd.forwardmove < 0 ) {
01923         pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
01924     } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
01925         pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
01926     }
01927 
01928     if ( pm->ps->pm_type >= PM_DEAD ) {
01929         pm->cmd.forwardmove = 0;
01930         pm->cmd.rightmove = 0;
01931         pm->cmd.upmove = 0;
01932     }
01933 
01934     if ( pm->ps->pm_type == PM_SPECTATOR ) {
01935         PM_CheckDuck ();
01936         PM_FlyMove ();
01937         PM_DropTimers ();
01938         return;
01939     }
01940 
01941     if ( pm->ps->pm_type == PM_NOCLIP ) {
01942         PM_NoclipMove ();
01943         PM_DropTimers ();
01944         return;
01945     }
01946 
01947     if (pm->ps->pm_type == PM_FREEZE) {
01948         return;     // no movement at all
01949     }
01950 
01951     if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) {
01952         return;     // no movement at all
01953     }
01954 
01955     // set watertype, and waterlevel
01956     PM_SetWaterLevel();
01957     pml.previous_waterlevel = pmove->waterlevel;
01958 
01959     // set mins, maxs, and viewheight
01960     PM_CheckDuck ();
01961 
01962     // set groundentity
01963     PM_GroundTrace();
01964 
01965     if ( pm->ps->pm_type == PM_DEAD ) {
01966         PM_DeadMove ();
01967     }
01968 
01969     PM_DropTimers();
01970 
01971 #ifdef MISSIONPACK
01972     if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
01973         PM_InvulnerabilityMove();
01974     } else
01975 #endif
01976     if ( pm->ps->powerups[PW_FLIGHT] ) {
01977         // flight powerup doesn't allow jump and has different friction
01978         PM_FlyMove();
01979     } else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) {
01980         PM_GrappleMove();
01981         // We can wiggle a bit
01982         PM_AirMove();
01983     } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
01984         PM_WaterJumpMove();
01985     } else if ( pm->waterlevel > 1 ) {
01986         // swimming
01987         PM_WaterMove();
01988     } else if ( pml.walking ) {
01989         // walking on ground
01990         PM_WalkMove();
01991     } else {
01992         // airborne
01993         PM_AirMove();
01994     }
01995 
01996     PM_Animate();
01997 
01998     // set groundentity, watertype, and waterlevel
01999     PM_GroundTrace();
02000     PM_SetWaterLevel();
02001 
02002     // weapons
02003     PM_Weapon();
02004 
02005     // torso animation
02006     PM_TorsoAnimation();
02007 
02008     // footstep events / legs animations
02009     PM_Footsteps();
02010 
02011     // entering / leaving water splashes
02012     PM_WaterEvents();
02013 
02014     // snap some parts of playerstate to save network bandwidth
02015     trap_SnapVector( pm->ps->velocity );
02016 }
02017 
02018 
02019 /*
02020 ================
02021 Pmove
02022 
02023 Can be called by either the server or the client
02024 ================
02025 */
02026 void Pmove (pmove_t *pmove) {
02027     int         finalTime;
02028 
02029     finalTime = pmove->cmd.serverTime;
02030 
02031     if ( finalTime < pmove->ps->commandTime ) {
02032         return; // should not happen
02033     }
02034 
02035     if ( finalTime > pmove->ps->commandTime + 1000 ) {
02036         pmove->ps->commandTime = finalTime - 1000;
02037     }
02038 
02039     pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
02040 
02041     // chop the move up if it is too long, to prevent framerate
02042     // dependent behavior
02043     while ( pmove->ps->commandTime != finalTime ) {
02044         int     msec;
02045 
02046         msec = finalTime - pmove->ps->commandTime;
02047 
02048         if ( pmove->pmove_fixed ) {
02049             if ( msec > pmove->pmove_msec ) {
02050                 msec = pmove->pmove_msec;
02051             }
02052         }
02053         else {
02054             if ( msec > 66 ) {
02055                 msec = 66;
02056             }
02057         }
02058         pmove->cmd.serverTime = pmove->ps->commandTime + msec;
02059         PmoveSingle( pmove );
02060 
02061         if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
02062             pmove->cmd.upmove = 20;
02063         }
02064     }
02065 
02066     //PM_CheckStuck();
02067 
02068 }
02069 

Generated on Thu Aug 25 12:37:30 2005 for Quake III Arena by  doxygen 1.3.9.1