codemp/game/bg_pmove.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // bg_pmove.c -- both games player movement code
00004 // takes a playerstate and a usercmd as input and returns a modifed playerstate
00005 
00006 #include "q_shared.h"
00007 #include "bg_public.h"
00008 #include "bg_local.h"
00009 #include "bg_strap.h"
00010 #include "../ghoul2/G2.h"
00011 
00012 #ifdef QAGAME
00013 #include "g_local.h" //ahahahahhahahaha@$!$!
00014 #endif
00015 
00016 #define MAX_WEAPON_CHARGE_TIME 5000
00017 
00018 #ifdef QAGAME
00019 extern void G_CheapWeaponFire(int entNum, int ev);
00020 extern qboolean TryGrapple(gentity_t *ent); //g_cmds.c
00021 extern void trap_FX_PlayEffect( const char *file, vec3_t org, vec3_t fwd, int vol, int rad );
00022 #endif
00023 
00024 #include "../namespace_begin.h"
00025 extern qboolean BG_FullBodyTauntAnim( int anim );
00026 extern float PM_WalkableGroundDistance(void);
00027 extern qboolean PM_GroundSlideOkay( float zNormal );
00028 extern saberInfo_t *BG_MySaber( int clientNum, int saberNum );
00029 
00030 pmove_t         *pm;
00031 pml_t           pml;
00032 
00033 bgEntity_t *pm_entSelf = NULL;
00034 bgEntity_t *pm_entVeh = NULL;
00035 
00036 qboolean gPMDoSlowFall = qfalse;
00037 
00038 qboolean pm_cancelOutZoom = qfalse;
00039 
00040 // movement parameters
00041 float   pm_stopspeed = 100.0f;
00042 float   pm_duckScale = 0.50f;
00043 float   pm_swimScale = 0.50f;
00044 float   pm_wadeScale = 0.70f;
00045 
00046 float   pm_vehicleaccelerate = 36.0f;
00047 float   pm_accelerate = 10.0f;
00048 float   pm_airaccelerate = 1.0f;
00049 float   pm_wateraccelerate = 4.0f;
00050 float   pm_flyaccelerate = 8.0f;
00051 
00052 float   pm_friction = 6.0f;
00053 float   pm_waterfriction = 1.0f;
00054 float   pm_flightfriction = 3.0f;
00055 float   pm_spectatorfriction = 5.0f;
00056 
00057 int             c_pmove = 0;
00058 
00059 float forceSpeedLevels[4] = 
00060 {
00061         1, //rank 0?
00062         1.25,
00063         1.5,
00064         1.75
00065 };
00066 
00067 int forcePowerNeeded[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] = 
00068 {
00069         { //nothing should be usable at rank 0..
00070                 999,//FP_HEAL,//instant
00071                 999,//FP_LEVITATION,//hold/duration
00072                 999,//FP_SPEED,//duration
00073                 999,//FP_PUSH,//hold/duration
00074                 999,//FP_PULL,//hold/duration
00075                 999,//FP_TELEPATHY,//instant
00076                 999,//FP_GRIP,//hold/duration
00077                 999,//FP_LIGHTNING,//hold/duration
00078                 999,//FP_RAGE,//duration
00079                 999,//FP_PROTECT,//duration
00080                 999,//FP_ABSORB,//duration
00081                 999,//FP_TEAM_HEAL,//instant
00082                 999,//FP_TEAM_FORCE,//instant
00083                 999,//FP_DRAIN,//hold/duration
00084                 999,//FP_SEE,//duration
00085                 999,//FP_SABER_OFFENSE,
00086                 999,//FP_SABER_DEFENSE,
00087                 999//FP_SABERTHROW,
00088                 //NUM_FORCE_POWERS
00089         },
00090         {
00091                 65,//FP_HEAL,//instant //was 25, but that was way too little
00092                 10,//FP_LEVITATION,//hold/duration
00093                 50,//FP_SPEED,//duration
00094                 20,//FP_PUSH,//hold/duration
00095                 20,//FP_PULL,//hold/duration
00096                 20,//FP_TELEPATHY,//instant
00097                 30,//FP_GRIP,//hold/duration
00098                 1,//FP_LIGHTNING,//hold/duration
00099                 50,//FP_RAGE,//duration
00100                 50,//FP_PROTECT,//duration
00101                 50,//FP_ABSORB,//duration
00102                 50,//FP_TEAM_HEAL,//instant
00103                 50,//FP_TEAM_FORCE,//instant
00104                 20,//FP_DRAIN,//hold/duration
00105                 20,//FP_SEE,//duration
00106                 0,//FP_SABER_OFFENSE,
00107                 2,//FP_SABER_DEFENSE,
00108                 20//FP_SABERTHROW,
00109                 //NUM_FORCE_POWERS
00110         },
00111         {
00112                 60,//FP_HEAL,//instant
00113                 10,//FP_LEVITATION,//hold/duration
00114                 50,//FP_SPEED,//duration
00115                 20,//FP_PUSH,//hold/duration
00116                 20,//FP_PULL,//hold/duration
00117                 20,//FP_TELEPATHY,//instant
00118                 30,//FP_GRIP,//hold/duration
00119                 1,//FP_LIGHTNING,//hold/duration
00120                 50,//FP_RAGE,//duration
00121                 25,//FP_PROTECT,//duration
00122                 25,//FP_ABSORB,//duration
00123                 33,//FP_TEAM_HEAL,//instant
00124                 33,//FP_TEAM_FORCE,//instant
00125                 20,//FP_DRAIN,//hold/duration
00126                 20,//FP_SEE,//duration
00127                 0,//FP_SABER_OFFENSE,
00128                 1,//FP_SABER_DEFENSE,
00129                 20//FP_SABERTHROW,
00130                 //NUM_FORCE_POWERS
00131         },
00132         {
00133                 50,//FP_HEAL,//instant //You get 5 points of health.. for 50 force points!
00134                 10,//FP_LEVITATION,//hold/duration
00135                 50,//FP_SPEED,//duration
00136                 20,//FP_PUSH,//hold/duration
00137                 20,//FP_PULL,//hold/duration
00138                 20,//FP_TELEPATHY,//instant
00139                 60,//FP_GRIP,//hold/duration
00140                 1,//FP_LIGHTNING,//hold/duration
00141                 50,//FP_RAGE,//duration
00142                 10,//FP_PROTECT,//duration
00143                 10,//FP_ABSORB,//duration
00144                 25,//FP_TEAM_HEAL,//instant
00145                 25,//FP_TEAM_FORCE,//instant
00146                 20,//FP_DRAIN,//hold/duration
00147                 20,//FP_SEE,//duration
00148                 0,//FP_SABER_OFFENSE,
00149                 0,//FP_SABER_DEFENSE,
00150                 20//FP_SABERTHROW,
00151                 //NUM_FORCE_POWERS
00152         }
00153 };
00154 
00155 float forceJumpHeight[NUM_FORCE_POWER_LEVELS] = 
00156 {
00157         32,//normal jump (+stepheight+crouchdiff = 66)
00158         96,//(+stepheight+crouchdiff = 130)
00159         192,//(+stepheight+crouchdiff = 226)
00160         384//(+stepheight+crouchdiff = 418)
00161 };
00162 
00163 float forceJumpStrength[NUM_FORCE_POWER_LEVELS] = 
00164 {
00165         JUMP_VELOCITY,//normal jump
00166         420,
00167         590,
00168         840
00169 };
00170 
00171 //rww - Get a pointer to the bgEntity by the index
00172 bgEntity_t *PM_BGEntForNum( int num )
00173 {
00174         bgEntity_t *ent;
00175 
00176         if (!pm)
00177         {
00178                 assert(!"You cannot call PM_BGEntForNum outside of pm functions!");
00179                 return NULL;
00180         }
00181 
00182         if (!pm->baseEnt)
00183         {
00184                 assert(!"Base entity address not set");
00185                 return NULL;
00186         }
00187 
00188         if (!pm->entSize)
00189         {
00190                 assert(!"sizeof(ent) is 0, impossible (not set?)");
00191                 return NULL;
00192         }
00193 
00194         assert(num >= 0 && num < MAX_GENTITIES);
00195 
00196     ent = (bgEntity_t *)((byte *)pm->baseEnt + pm->entSize*(num));
00197 
00198         return ent;
00199 }
00200 
00201 qboolean BG_SabersOff( playerState_t *ps )
00202 {
00203         if ( !ps->saberHolstered )
00204         {
00205                 return qfalse;
00206         }
00207         if ( ps->fd.saberAnimLevelBase == SS_DUAL
00208                 || ps->fd.saberAnimLevelBase == SS_STAFF )
00209         {
00210                 if ( ps->saberHolstered < 2 )
00211                 {
00212                         return qfalse;
00213                 }
00214         }
00215         return qtrue;
00216 }
00217 
00218 qboolean BG_KnockDownable(playerState_t *ps)
00219 {
00220         if (!ps)
00221         { //just for safety
00222                 return qfalse;
00223         }
00224 
00225         if (ps->m_iVehicleNum)
00226         { //riding a vehicle, don't knock me down
00227                 return qfalse;
00228         }
00229 
00230         if (ps->emplacedIndex)
00231         { //using emplaced gun or eweb, can't be knocked down
00232                 return qfalse;
00233         }
00234 
00235         //ok, I guess?
00236         return qtrue;
00237 }
00238 
00239 //I should probably just do a global inline sometime.
00240 #ifndef __LCC__
00241 #define PM_INLINE ID_INLINE
00242 #else
00243 #define PM_INLINE //none
00244 #endif
00245 
00246 //hacky assumption check, assume any client non-humanoid is a rocket trooper
00247 qboolean PM_INLINE PM_IsRocketTrooper(void)
00248 {
00249         /*
00250         if (pm->ps->clientNum < MAX_CLIENTS &&
00251                 pm->gametype == GT_SIEGE &&
00252                 pm->nonHumanoid)
00253         {
00254                 return qtrue;
00255         }
00256         */
00257 
00258         return qfalse;
00259 }
00260 
00261 int PM_GetSaberStance(void)
00262 {
00263         int anim = BOTH_STAND2;
00264         saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
00265         saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
00266 
00267         if (!pm->ps->saberEntityNum)
00268         { //lost it
00269                 return BOTH_STAND1;
00270         }
00271 
00272         if ( BG_SabersOff( pm->ps ) )
00273         {
00274                 return BOTH_STAND1;
00275         }
00276 
00277         if ( saber1
00278                 && saber1->readyAnim != -1 )
00279         {
00280                 return saber1->readyAnim;
00281         }
00282 
00283         if ( saber2
00284                 && saber2->readyAnim != -1 )
00285         {
00286                 return saber2->readyAnim;
00287         }
00288 
00289         if ( saber1 
00290                 && saber2
00291                 && !pm->ps->saberHolstered )
00292         {//dual sabers, both on
00293                 return BOTH_SABERDUAL_STANCE;
00294         }
00295 
00296         switch ( pm->ps->fd.saberAnimLevel )
00297         {
00298         case SS_DUAL:
00299                 anim = BOTH_SABERDUAL_STANCE;
00300                 break;
00301         case SS_STAFF:
00302                 anim = BOTH_SABERSTAFF_STANCE;
00303                 break;
00304         case SS_FAST:
00305         case SS_TAVION:
00306                 anim = BOTH_SABERFAST_STANCE;
00307                 break;
00308         case SS_STRONG:
00309                 anim = BOTH_SABERSLOW_STANCE;
00310                 break;
00311         case SS_NONE:
00312         case SS_MEDIUM:
00313         case SS_DESANN:
00314         default:
00315                 anim = BOTH_STAND2;
00316                 break;
00317         }
00318         return anim;
00319 }
00320 
00321 qboolean PM_DoSlowFall(void)
00322 {
00323         if ( ( (pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && pm->ps->legsTimer > 500 )
00324         {
00325                 return qtrue;
00326         }
00327 
00328         return qfalse;
00329 }
00330 
00331 //begin vehicle functions crudely ported from sp -rww
00332 /*
00333 ====================================================================
00334 void pitch_roll_for_slope (edict_t *forwhom, vec3_t *slope, vec3_t storeAngles )
00335 
00336 MG
00337 
00338 This will adjust the pitch and roll of a monster to match
00339 a given slope - if a non-'0 0 0' slope is passed, it will
00340 use that value, otherwise it will use the ground underneath
00341 the monster.  If it doesn't find a surface, it does nothinh\g
00342 and returns.
00343 ====================================================================
00344 */
00345 
00346 void PM_pitch_roll_for_slope( bgEntity_t *forwhom, vec3_t pass_slope, vec3_t storeAngles )
00347 {
00348         vec3_t  slope;
00349         vec3_t  nvf, ovf, ovr, startspot, endspot, new_angles = { 0, 0, 0 };
00350         float   pitch, mod, dot;
00351 
00352         //if we don't have a slope, get one
00353         if( !pass_slope || VectorCompare( vec3_origin, pass_slope ) )
00354         {
00355                 trace_t trace;
00356 
00357                 VectorCopy( pm->ps->origin, startspot );
00358                 startspot[2] += pm->mins[2] + 4;
00359                 VectorCopy( startspot, endspot );
00360                 endspot[2] -= 300;
00361                 pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, endspot, forwhom->s.number, MASK_SOLID );
00362 //              if(trace_fraction>0.05&&forwhom.movetype==MOVETYPE_STEP)
00363 //                      forwhom.flags(-)FL_ONGROUND;
00364 
00365                 if ( trace.fraction >= 1.0 )
00366                         return;
00367 
00368                 if( !( &trace.plane ) )
00369                         return;
00370 
00371                 if ( VectorCompare( vec3_origin, trace.plane.normal ) )
00372                         return;
00373 
00374                 VectorCopy( trace.plane.normal, slope );
00375         }
00376         else
00377         {
00378                 VectorCopy( pass_slope, slope );
00379         }
00380 
00381         if ( forwhom->s.NPC_class == CLASS_VEHICLE )
00382         {//special code for vehicles
00383                 Vehicle_t *pVeh = forwhom->m_pVehicle;
00384                 vec3_t tempAngles;
00385 
00386                 tempAngles[PITCH] = tempAngles[ROLL] = 0;
00387                 tempAngles[YAW] = pVeh->m_vOrientation[YAW];
00388                 AngleVectors( tempAngles, ovf, ovr, NULL );
00389         }
00390         else
00391         {
00392                 AngleVectors( pm->ps->viewangles, ovf, ovr, NULL );
00393         }
00394 
00395         vectoangles( slope, new_angles );
00396         pitch = new_angles[PITCH] + 90;
00397         new_angles[ROLL] = new_angles[PITCH] = 0;
00398 
00399         AngleVectors( new_angles, nvf, NULL, NULL );
00400 
00401         mod = DotProduct( nvf, ovr );
00402 
00403         if ( mod<0 )
00404                 mod = -1;
00405         else
00406                 mod = 1;
00407 
00408         dot = DotProduct( nvf, ovf );
00409 
00410         if ( storeAngles )
00411         {
00412                 storeAngles[PITCH] = dot * pitch;
00413                 storeAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
00414         }
00415         else //if ( forwhom->client )
00416         {
00417                 float oldmins2;
00418 
00419                 pm->ps->viewangles[PITCH] = dot * pitch;
00420                 pm->ps->viewangles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
00421                 oldmins2 = pm->mins[2];
00422                 pm->mins[2] = -24 + 12 * fabs(pm->ps->viewangles[PITCH])/180.0f;
00423                 //FIXME: if it gets bigger, move up
00424                 if ( oldmins2 > pm->mins[2] )
00425                 {//our mins is now lower, need to move up
00426                         //FIXME: trace?
00427                         pm->ps->origin[2] += (oldmins2 - pm->mins[2]);
00428                         //forwhom->currentOrigin[2] = forwhom->client->ps.origin[2];
00429                         //gi.linkentity( forwhom );
00430                 }
00431         }
00432         /*
00433         else
00434         {
00435                 forwhom->currentAngles[PITCH] = dot * pitch;
00436                 forwhom->currentAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
00437         }
00438         */
00439 }
00440 
00441 #define         FLY_NONE        0
00442 #define         FLY_NORMAL      1
00443 #define         FLY_VEHICLE     2
00444 #define         FLY_HOVER       3
00445 static int pm_flying = FLY_NONE;
00446 
00447 void PM_SetSpecialMoveValues (void)
00448 {
00449         bgEntity_t *pEnt;
00450         
00451         if (pm->ps->clientNum < MAX_CLIENTS)
00452         { //we know that real players aren't vehs
00453                 pm_flying = FLY_NONE;
00454                 return;
00455         }
00456 
00457         //default until we decide otherwise
00458         pm_flying = FLY_NONE;
00459 
00460         pEnt = pm_entSelf;
00461 
00462         if ( pEnt )
00463         {
00464                 if ( (pm->ps->eFlags2&EF2_FLYING) )// pm->gent->client->moveType == MT_FLYSWIM )
00465                 {
00466                         pm_flying = FLY_NORMAL;
00467                 }
00468                 else if ( pEnt->s.NPC_class == CLASS_VEHICLE )
00469                 {
00470                         if ( pEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
00471                         {
00472                                 pm_flying = FLY_VEHICLE;
00473                         }
00474                         else if ( pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
00475                         {
00476                                 pm_flying = FLY_HOVER;
00477                         }
00478                 }
00479         }
00480 }
00481 
00482 static void PM_SetVehicleAngles( vec3_t normal )
00483 {
00484         bgEntity_t *pEnt = pm_entSelf;
00485         Vehicle_t *pVeh;
00486         vec3_t  vAngles;
00487         float vehicleBankingSpeed;
00488         float pitchBias;
00489         int i;
00490 
00491         if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
00492         {
00493                 return;
00494         }
00495 
00496         pVeh = pEnt->m_pVehicle;
00497         
00498         //float curVehicleBankingSpeed;
00499         vehicleBankingSpeed = (pVeh->m_pVehicleInfo->bankingSpeed*32.0f)*pml.frametime;//0.25f
00500 
00501         if ( vehicleBankingSpeed <= 0 
00502                 || ( pVeh->m_pVehicleInfo->pitchLimit == 0 && pVeh->m_pVehicleInfo->rollLimit == 0 ) )
00503         {//don't bother, this vehicle doesn't bank
00504                 return;
00505         }
00506         //FIXME: do 3 traces to define a plane and use that... smoothes it out some, too...
00507         //pitch_roll_for_slope( pm->gent, normal, vAngles );
00508         //FIXME: maybe have some pitch control in water and/or air?
00509         
00510         if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
00511         {
00512                 pitchBias = 0.0f;
00513         }
00514         else
00515         {
00516                 //FIXME: gravity does not matter in SPACE!!!
00517                 //center of gravity affects pitch in air/water (FIXME: what about roll?)
00518                 pitchBias = 90.0f*pVeh->m_pVehicleInfo->centerOfGravity[0];//if centerOfGravity is all the way back (-1.0f), vehicle pitches up 90 degrees when in air
00519         }
00520 
00521         VectorClear( vAngles );
00522         if ( pm->waterlevel > 0 )
00523         {//in water
00524                 //view pitch has some influence when in water
00525                 //FIXME: take center of gravity into account?
00526                 vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5);
00527         }
00528         else if ( normal )
00529         {//have a valid surface below me
00530                 PM_pitch_roll_for_slope( pEnt, normal, vAngles );
00531                 if ( (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
00532                 {//on water
00533                         //view pitch has some influence when on a fluid surface
00534                         //FIXME: take center of gravity into account
00535                         vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.5f + (pitchBias*0.5f);
00536                 }
00537         }
00538         else
00539         {//in air, let pitch match view...?
00540                 //FIXME: take center of gravity into account
00541                 vAngles[PITCH] = pm->ps->viewangles[PITCH]*0.5f + pitchBias;
00542                 //don't bank so fast when in the air
00543                 vehicleBankingSpeed *= (0.125f*pml.frametime);
00544         }
00545         //NOTE: if angles are flat and we're moving through air (not on ground),
00546         //              then pitch/bank?
00547         if ( pVeh->m_pVehicleInfo->rollLimit > 0 )
00548         {
00549                 //roll when banking
00550                 vec3_t  velocity;
00551                 float   speed;
00552                 VectorCopy( pm->ps->velocity, velocity );
00553                 velocity[2] = 0.0f;
00554                 speed = VectorNormalize( velocity );
00555                 if ( speed > 32.0f || speed < -32.0f ) 
00556                 {
00557                         vec3_t  rt, tempVAngles;
00558                         float   side;
00559                         float   dp;
00560 
00561                         // Magic number fun!  Speed is used for banking, so modulate the speed by a sine wave
00562                         //FIXME: this banks too early
00563                         speed *= sin( (150 + pml.frametime) * 0.003 );
00564 
00565                         // Clamp to prevent harsh rolling
00566                         if ( speed > 60 )
00567                                 speed = 60;
00568 
00569                         VectorCopy( pVeh->m_vOrientation, tempVAngles );
00570                         tempVAngles[ROLL] = 0;
00571                         AngleVectors( tempVAngles, NULL, rt, NULL );
00572                         dp = DotProduct( velocity, rt );
00573                         side = speed * dp;
00574                         vAngles[ROLL] -= side;
00575                 }
00576         }
00577 
00578         //cap
00579         if ( pVeh->m_pVehicleInfo->pitchLimit != -1 )
00580         {
00581                 if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
00582                 {
00583                         vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
00584                 }
00585                 else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit )
00586                 {
00587                         vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
00588                 }
00589         }
00590 
00591         if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
00592         {
00593                 vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
00594         }
00595         else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit )
00596         {
00597                 vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
00598         }
00599         
00600         //do it
00601         for ( i = 0; i < 3; i++ )
00602         {
00603                 if ( i == YAW )
00604                 {//yawing done elsewhere
00605                         continue;
00606                 }
00607                 //bank faster the higher the difference is
00608                 /*
00609                 else if ( i == PITCH )
00610                 {
00611                         curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f);
00612                 }
00613                 else if ( i == ROLL )
00614                 {
00615                         curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f);
00616                 }
00617 
00618                 if ( curVehicleBankingSpeed )
00619                 */
00620                 {
00621                         if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed )
00622                         {
00623                                 pVeh->m_vOrientation[i] -= vehicleBankingSpeed;
00624                         }
00625                         else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed )
00626                         {
00627                                 pVeh->m_vOrientation[i] += vehicleBankingSpeed;
00628                         }
00629                         else
00630                         {
00631                                 pVeh->m_vOrientation[i] = vAngles[i];
00632                         }
00633                 }
00634         }
00635 }
00636 
00637 #ifndef QAGAME
00638 extern vmCvar_t cg_paused;
00639 #endif
00640 
00641 void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS )
00642 {
00643 /*
00644         float pitchSubtract, pitchDelta, yawDelta;
00645         //Com_Printf( S_COLOR_RED"PITCH: %4.2f, YAW: %4.2f, ROLL: %4.2f\n", riderPS->viewangles[0],riderPS->viewangles[1],riderPS->viewangles[2]);
00646         yawDelta = AngleSubtract(riderPS->viewangles[YAW],pVeh->m_vPrevRiderViewAngles[YAW]);
00647 #ifndef QAGAME
00648         if ( !cg_paused.integer )
00649         {
00650                 //Com_Printf( "%d - yawDelta %4.2f\n", pm->cmd.serverTime, yawDelta );
00651         }
00652 #endif
00653         yawDelta *= (4.0f*pVeh->m_fTimeModifier);
00654         pVeh->m_vOrientation[ROLL] -= yawDelta;
00655 
00656         pitchDelta = AngleSubtract(riderPS->viewangles[PITCH],pVeh->m_vPrevRiderViewAngles[PITCH]);
00657         pitchDelta *= (2.0f*pVeh->m_fTimeModifier);
00658         pitchSubtract = pitchDelta * (fabs(pVeh->m_vOrientation[ROLL])/90.0f);
00659         pVeh->m_vOrientation[PITCH] += pitchDelta-pitchSubtract;
00660         if ( pVeh->m_vOrientation[ROLL] > 0 )
00661         {
00662                 pVeh->m_vOrientation[YAW] += pitchSubtract;
00663         }
00664         else
00665         {
00666                 pVeh->m_vOrientation[YAW] -= pitchSubtract;
00667         }
00668         pVeh->m_vOrientation[PITCH] = AngleNormalize180( pVeh->m_vOrientation[PITCH] );
00669         pVeh->m_vOrientation[YAW] = AngleNormalize360( pVeh->m_vOrientation[YAW] );
00670         pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
00671 
00672         VectorCopy( riderPS->viewangles, pVeh->m_vPrevRiderViewAngles );
00673 */
00674 }
00675 
00676 void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride )
00677 {
00678         if ( pVeh && pVeh->m_pVehicleInfo )
00679         {
00680                 float speedFrac = 1.0f;
00681                 if ( pVeh->m_pVehicleInfo->speedDependantTurning )
00682                 {
00683                         if ( pVeh->m_LandTrace.fraction >= 1.0f 
00684                                 || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
00685                         {
00686                                 speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f));
00687                                 if ( speedFrac < 0.25f )
00688                                 {
00689                                         speedFrac = 0.25f;
00690                                 }
00691                                 else if ( speedFrac > 1.0f )
00692                                 {
00693                                         speedFrac = 1.0f;
00694                                 }
00695                         }
00696                 }
00697                 if ( pVeh->m_pVehicleInfo->mousePitch )
00698                 {
00699                         *mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac;
00700                 }
00701                 if ( pVeh->m_pVehicleInfo->mouseYaw )
00702                 {
00703                         *mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac;
00704                 }
00705         }
00706 }
00707 
00708 #include "../namespace_end.h"
00709 
00710 // Following couple things don't belong in the DLL namespace!
00711 #ifdef QAGAME
00712 typedef struct gentity_s gentity_t;
00713 gentity_t *G_PlayEffectID(const int fxID, vec3_t org, vec3_t ang);
00714 #endif
00715 
00716 #include "../namespace_begin.h"
00717 
00718 static void PM_GroundTraceMissed( void );
00719 void PM_HoverTrace( void )
00720 {
00721         Vehicle_t *pVeh;
00722         float hoverHeight;
00723         vec3_t          point, vAng, fxAxis[3];
00724         trace_t         *trace;
00725         float relativeWaterLevel;
00726 
00727         bgEntity_t *pEnt = pm_entSelf;
00728         if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
00729         {
00730                 return;
00731         }
00732 
00733         pVeh = pEnt->m_pVehicle;
00734         hoverHeight = pVeh->m_pVehicleInfo->hoverHeight;
00735         trace = &pml.groundTrace;
00736 
00737         pml.groundPlane = qfalse;
00738 
00739         //relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2]));
00740         relativeWaterLevel = pm->waterlevel; //I.. guess this works
00741         if ( pm->waterlevel && relativeWaterLevel >= 0 )
00742         {//in water
00743                 if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f )
00744                 {//sink like a rock
00745                 }
00746                 else
00747                 {//rise up
00748                         float floatHeight = (pVeh->m_pVehicleInfo->bouyancy * ((pm->maxs[2]-pm->mins[2])*0.5f)) - (hoverHeight*0.5f);//1.0f should make you float half-in, half-out of water
00749                         if ( relativeWaterLevel > floatHeight )
00750                         {//too low, should rise up
00751                                 pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier;
00752                         }
00753                 }
00754                 //if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] )
00755                 if (pm->waterlevel <= 1)
00756                 {//part of us is sticking out of water
00757                         if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
00758                         {//moving at a decent speed
00759                                 if ( Q_irand( pml.frametime, 100 ) >= 50 )
00760                                 {//splash
00761                                         vec3_t wakeOrg;
00762 
00763                                         vAng[PITCH] = vAng[ROLL] = 0;
00764                                         vAng[YAW] = pVeh->m_vOrientation[YAW];
00765                                         AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
00766                                         VectorCopy( pm->ps->origin, wakeOrg );
00767                                         //wakeOrg[2] = pm->ps->waterheight;
00768                                         if (pm->waterlevel >= 2)
00769                                         {
00770                                                 wakeOrg[2] = pm->ps->origin[2]+16;
00771                                         }
00772                                         else
00773                                         {
00774                                                 wakeOrg[2] = pm->ps->origin[2];
00775                                         }
00776 #ifdef QAGAME //yeah, this is kind of crappy and makes no use of prediction whatsoever
00777                                         if ( pVeh->m_pVehicleInfo->iWakeFX )
00778                                         {
00779                                                 //G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis[0] );
00780                                                 //tempent use bad!
00781                                                 G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pVeh->m_pVehicleInfo->iWakeFX);
00782                                         }
00783 #endif
00784                                 }
00785                         }
00786                 }
00787         }
00788         else
00789         {
00790                 int traceContents;
00791                 float minNormal = (float)MIN_WALK_NORMAL;
00792                 minNormal = pVeh->m_pVehicleInfo->maxSlope;
00793 
00794                 point[0] = pm->ps->origin[0];
00795                 point[1] = pm->ps->origin[1];
00796                 point[2] = pm->ps->origin[2] - hoverHeight;
00797 
00798                 //FIXME: check for water, too?  If over water, go slower and make wave effect
00799                 //              If *in* water, go really slow and use bouyancy stat to determine how far below surface to float
00800 
00801                 //NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground.
00802                 //              if it's 1.0f, you sink halfway into water.  If it's 0, you sink...
00803                 traceContents = pm->tracemask;
00804                 if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f )
00805                 {//sit on water
00806                         traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
00807                 }
00808                 pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents );
00809                 if (trace->plane.normal[0] > 0.5f || trace->plane.normal[0] < -0.5f ||
00810                         trace->plane.normal[1] > 0.5f || trace->plane.normal[1] < -0.5f)
00811                 { //steep slanted hill, don't go up it.
00812                         float d = fabs(trace->plane.normal[0]);
00813                         float e = fabs(trace->plane.normal[1]);
00814                         if (e > d)
00815                         {
00816                                 d = e;
00817                         }
00818                         pm->ps->velocity[2] = -300.0f*d;
00819                 }
00820                 else if ( trace->plane.normal[2] >= minNormal ) 
00821                 {//not a steep slope, so push us up
00822                         if ( trace->fraction < 1.0f )
00823                         {//push up off ground
00824                                 float hoverForce = pVeh->m_pVehicleInfo->hoverStrength;
00825                                 if ( trace->fraction > 0.5f )
00826                                 {
00827                                         pm->ps->velocity[2] += (1.0f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier;
00828                                 }
00829                                 else
00830                                 {
00831                                         pm->ps->velocity[2] += (0.5f-(trace->fraction*trace->fraction))*hoverForce*2.0f*pVeh->m_fTimeModifier;
00832                                 }
00833                                 if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
00834                                 {//hovering on water, make a spash if moving
00835                                         if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
00836                                         {//moving at a decent speed
00837                                                 if ( Q_irand( pml.frametime, 100 ) >= 50 )
00838                                                 {//splash
00839                                                         vAng[PITCH] = vAng[ROLL] = 0;
00840                                                         vAng[YAW] = pVeh->m_vOrientation[YAW];
00841                                                         AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
00842 #ifdef QAGAME
00843                                                         if ( pVeh->m_pVehicleInfo->iWakeFX )
00844                                                         {
00845                                                                 G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis[0] );
00846                                                         }
00847 #endif
00848                                                 }
00849                                         }
00850                                 }
00851                                 pml.groundPlane = qtrue;
00852                         }
00853                 }
00854         }
00855         if ( pml.groundPlane )
00856         {
00857                 PM_SetVehicleAngles( pml.groundTrace.plane.normal );
00858                 // We're on the ground.
00859                 pVeh->m_ulFlags &= ~VEH_FLYING;
00860 
00861                 pVeh->m_vAngularVelocity = 0.0f;
00862         }
00863         else
00864         {
00865                 PM_SetVehicleAngles( NULL );
00866                 // We're flying in the air.
00867                 pVeh->m_ulFlags |= VEH_FLYING;
00868                 //groundTrace
00869 
00870                 if (pVeh->m_vAngularVelocity==0.0f)
00871                 {
00872                         pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
00873                         if (pVeh->m_vAngularVelocity<-15.0f)
00874                         {
00875                                 pVeh->m_vAngularVelocity = -15.0f;
00876                         }
00877                         if (pVeh->m_vAngularVelocity> 15.0f)
00878                         {
00879                                 pVeh->m_vAngularVelocity =  15.0f;
00880                         }
00881                 }
00882                 //pVeh->m_vAngularVelocity *= 0.95f;            // Angular Velocity Decays Over Time
00883                 if (pVeh->m_vAngularVelocity > 0.0f)
00884                 {
00885                         pVeh->m_vAngularVelocity -= pml.frametime;
00886                         if (pVeh->m_vAngularVelocity < 0.0f)
00887                         {
00888                                 pVeh->m_vAngularVelocity = 0.0f;
00889                         }
00890                 }
00891                 else if (pVeh->m_vAngularVelocity < 0.0f)
00892                 {
00893                         pVeh->m_vAngularVelocity += pml.frametime;
00894                         if (pVeh->m_vAngularVelocity > 0.0f)
00895                         {
00896                                 pVeh->m_vAngularVelocity = 0.0f;
00897                         }
00898                 }
00899         }
00900         PM_GroundTraceMissed();
00901 }
00902 //end vehicle functions crudely ported from sp -rww
00903 
00904 /*
00905 ===============
00906 PM_AddEvent
00907 
00908 ===============
00909 */
00910 void PM_AddEvent( int newEvent ) {
00911         BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
00912 }
00913 
00914 void PM_AddEventWithParm( int newEvent, int parm ) 
00915 {
00916         BG_AddPredictableEventToPlayerstate( newEvent, parm, pm->ps );
00917 }
00918 
00919 /*
00920 ===============
00921 PM_AddTouchEnt
00922 ===============
00923 */
00924 void PM_AddTouchEnt( int entityNum ) {
00925         int             i;
00926 
00927         if ( entityNum == ENTITYNUM_WORLD ) {
00928                 return;
00929         }
00930         if ( pm->numtouch == MAXTOUCH ) {
00931                 return;
00932         }
00933 
00934         // see if it is already added
00935         for ( i = 0 ; i < pm->numtouch ; i++ ) {
00936                 if ( pm->touchents[ i ] == entityNum ) {
00937                         return;
00938                 }
00939         }
00940 
00941         // add it
00942         pm->touchents[pm->numtouch] = entityNum;
00943         pm->numtouch++;
00944 }
00945 
00946 
00947 /*
00948 ==================
00949 PM_ClipVelocity
00950 
00951 Slide off of the impacting surface
00952 ==================
00953 */
00954 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
00955         float   backoff;
00956         float   change;
00957         float   oldInZ;
00958         int             i;
00959         
00960         if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
00961         {//no sliding!
00962                 VectorCopy( in, out );
00963                 return;
00964         }
00965         oldInZ = in[2];
00966 
00967         backoff = DotProduct (in, normal);
00968         
00969         if ( backoff < 0 ) {
00970                 backoff *= overbounce;
00971         } else {
00972                 backoff /= overbounce;
00973         }
00974 
00975         for ( i=0 ; i<3 ; i++ ) {
00976                 change = normal[i]*backoff;
00977                 out[i] = in[i] - change;
00978         }
00979         if ( pm->stepSlideFix )
00980         {
00981                 if ( pm->ps->clientNum < MAX_CLIENTS//normal player
00982                         && pm->ps->groundEntityNum != ENTITYNUM_NONE//on the ground
00983                         && normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope
00984                 {//if walking on the ground, don't slide up slopes that are too steep to walk on
00985                         out[2] = oldInZ;
00986                 }
00987         }
00988 }
00989 
00990 
00991 /*
00992 ==================
00993 PM_Friction
00994 
00995 Handles both ground friction and water friction
00996 ==================
00997 */
00998 static void PM_Friction( void ) {
00999         vec3_t  vec;
01000         float   *vel;
01001         float   speed, newspeed, control;
01002         float   drop;
01003         bgEntity_t *pEnt = NULL;
01004         
01005         vel = pm->ps->velocity;
01006         
01007         VectorCopy( vel, vec );
01008         if ( pml.walking ) {
01009                 vec[2] = 0;     // ignore slope movement
01010         }
01011 
01012         speed = VectorLength(vec);
01013         if (speed < 1) {
01014                 vel[0] = 0;
01015                 vel[1] = 0;             // allow sinking underwater
01016                 if (pm->ps->pm_type == PM_SPECTATOR)
01017                 {
01018                         vel[2] = 0;
01019                 }
01020                 // FIXME: still have z friction underwater?
01021                 return;
01022         }
01023 
01024         drop = 0;
01025 
01026         if (pm->ps->clientNum >= MAX_CLIENTS)
01027         {
01028                 pEnt = pm_entSelf;
01029         }
01030 
01031         // apply ground friction, even if on ladder
01032         if (pm_flying != FLY_VEHICLE &&
01033                 pEnt &&
01034                 pEnt->s.NPC_class == CLASS_VEHICLE &&
01035                 pEnt->m_pVehicle &&
01036                 pEnt->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL &&
01037                 pEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
01038                 pEnt->m_pVehicle->m_pVehicleInfo->friction )
01039         {
01040                 float friction = pEnt->m_pVehicle->m_pVehicleInfo->friction;
01041                 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) /*&& !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)*/ ) 
01042                 {
01043                         control = speed < pm_stopspeed ? pm_stopspeed : speed;
01044                         drop += control*friction*pml.frametime;
01045                         /*
01046                         if ( Flying == FLY_HOVER )
01047                         {
01048                                 if ( pm->cmd.rightmove )
01049                                 {//if turning, increase friction
01050                                         control *= 2.0f;
01051                                 }
01052                                 if ( pm->ps->groundEntityNum < ENTITYNUM_NONE ) 
01053                                 {//on the ground
01054                                         drop += control*friction*pml.frametime;
01055                                 }
01056                                 else if ( pml.groundPlane )
01057                                 {//on a slope
01058                                         drop += control*friction*2.0f*pml.frametime;
01059                                 }
01060                                 else
01061                                 {//in air
01062                                         drop += control*2.0f*friction*pml.frametime;
01063                                 }
01064                         }
01065                         */
01066                 }
01067         }
01068         else if ( pm_flying != FLY_NORMAL && pm_flying != FLY_VEHICLE )
01069         {
01070                 // apply ground friction
01071                 if ( pm->waterlevel <= 1 ) {
01072                         if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
01073                                 // if getting knocked back, no friction
01074                                 if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
01075                                         control = speed < pm_stopspeed ? pm_stopspeed : speed;
01076                                         drop += control*pm_friction*pml.frametime;
01077                                 }
01078                         }
01079                 }
01080         }
01081 
01082         if ( pm_flying == FLY_VEHICLE )
01083         {
01084                 if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) 
01085                 {
01086                         control = speed;// < pm_stopspeed ? pm_stopspeed : speed;
01087                         drop += control*pm_friction*pml.frametime;
01088                 }
01089         }
01090 
01091         // apply water friction even if just wading
01092         if ( pm->waterlevel ) {
01093                 drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
01094         }
01095         // If on a client then there is no friction
01096         else if ( pm->ps->groundEntityNum < MAX_CLIENTS )
01097         {
01098                 drop = 0;
01099         }
01100 
01101         if ( pm->ps->pm_type == PM_SPECTATOR || pm->ps->pm_type == PM_FLOAT )
01102         {
01103                 if (pm->ps->pm_type == PM_FLOAT)
01104                 { //almost no friction while floating
01105                         drop += speed*0.1*pml.frametime;
01106                 }
01107                 else
01108                 {
01109                         drop += speed*pm_spectatorfriction*pml.frametime;
01110                 }
01111         }
01112 
01113         // scale the velocity
01114         newspeed = speed - drop;
01115         if (newspeed < 0) {
01116                 newspeed = 0;
01117         }
01118         newspeed /= speed;
01119 
01120         vel[0] = vel[0] * newspeed;
01121         vel[1] = vel[1] * newspeed;
01122         vel[2] = vel[2] * newspeed;
01123 }
01124 
01125 
01126 /*
01127 ==============
01128 PM_Accelerate
01129 
01130 Handles user intended acceleration
01131 ==============
01132 */
01133 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
01134 {
01135         if (pm->gametype != GT_SIEGE 
01136                 || pm->ps->m_iVehicleNum 
01137                 || pm->ps->clientNum >= MAX_CLIENTS 
01138                 || pm->ps->pm_type != PM_NORMAL)
01139         { //standard method, allows "bunnyhopping" and whatnot
01140                 int                     i;
01141                 float           addspeed, accelspeed, currentspeed;
01142 
01143                 currentspeed = DotProduct (pm->ps->velocity, wishdir);
01144                 addspeed = wishspeed - currentspeed;
01145                 if (addspeed <= 0 && pm->ps->clientNum < MAX_CLIENTS) {
01146                         return;
01147                 }
01148 
01149                 if (addspeed < 0)
01150                 {
01151                         accelspeed = (-accel)*pml.frametime*wishspeed;
01152                         if (accelspeed < addspeed) {
01153                                 accelspeed = addspeed;
01154                         }
01155                 }
01156                 else
01157                 {
01158                         accelspeed = accel*pml.frametime*wishspeed;
01159                         if (accelspeed > addspeed) {
01160                                 accelspeed = addspeed;
01161                         }
01162                 }
01163 
01164                 for (i=0 ; i<3 ; i++) {
01165                         pm->ps->velocity[i] += accelspeed*wishdir[i];   
01166                 }
01167         }
01168         else
01169         { //use the proper way for siege
01170                 vec3_t          wishVelocity;
01171                 vec3_t          pushDir;
01172                 float           pushLen;
01173                 float           canPush;
01174 
01175                 VectorScale( wishdir, wishspeed, wishVelocity );
01176                 VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
01177                 pushLen = VectorNormalize( pushDir );
01178 
01179                 canPush = accel*pml.frametime*wishspeed;
01180                 if (canPush > pushLen) {
01181                         canPush = pushLen;
01182                 }
01183 
01184                 VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
01185         }
01186 }
01187 
01188 
01189 
01190 /*
01191 ============
01192 PM_CmdScale
01193 
01194 Returns the scale factor to apply to cmd movements
01195 This allows the clients to use axial -127 to 127 values for all directions
01196 without getting a sqrt(2) distortion in speed.
01197 ============
01198 */
01199 static float PM_CmdScale( usercmd_t *cmd ) {
01200         int             max;
01201         float   total;
01202         float   scale;
01203         int             umove = 0; //cmd->upmove;
01204                         //don't factor upmove into scaling speed
01205 
01206         max = abs( cmd->forwardmove );
01207         if ( abs( cmd->rightmove ) > max ) {
01208                 max = abs( cmd->rightmove );
01209         }
01210         if ( abs( umove ) > max ) {
01211                 max = abs( umove );
01212         }
01213         if ( !max ) {
01214                 return 0;
01215         }
01216 
01217         total = sqrt( (float)(cmd->forwardmove * cmd->forwardmove
01218                 + cmd->rightmove * cmd->rightmove + umove * umove) );
01219         scale = (float)pm->ps->speed * max / ( 127.0 * total );
01220 
01221         return scale;
01222 }
01223 
01224 
01225 /*
01226 ================
01227 PM_SetMovementDir
01228 
01229 Determine the rotation of the legs reletive
01230 to the facing dir
01231 ================
01232 */
01233 static void PM_SetMovementDir( void ) {
01234         if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
01235                 if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
01236                         pm->ps->movementDir = 0;
01237                 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
01238                         pm->ps->movementDir = 1;
01239                 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
01240                         pm->ps->movementDir = 2;
01241                 } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
01242                         pm->ps->movementDir = 3;
01243                 } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
01244                         pm->ps->movementDir = 4;
01245                 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
01246                         pm->ps->movementDir = 5;
01247                 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
01248                         pm->ps->movementDir = 6;
01249                 } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
01250                         pm->ps->movementDir = 7;
01251                 }
01252         } else {
01253                 // if they aren't actively going directly sideways,
01254                 // change the animation to the diagonal so they
01255                 // don't stop too crooked
01256                 if ( pm->ps->movementDir == 2 ) {
01257                         pm->ps->movementDir = 1;
01258                 } else if ( pm->ps->movementDir == 6 ) {
01259                         pm->ps->movementDir = 7;
01260                 } 
01261         }
01262 }
01263 
01264 #define METROID_JUMP 1
01265 
01266 qboolean PM_ForceJumpingUp(void)
01267 {
01268         if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) && pm->ps->fd.forceJumpCharge )
01269         {//already jumped and let go
01270                 return qfalse;
01271         }
01272 
01273         if ( BG_InSpecialJump( pm->ps->legsAnim ) )
01274         {
01275                 return qfalse;
01276         }
01277 
01278         if (BG_SaberInSpecial(pm->ps->saberMove))
01279         {
01280                 return qfalse;
01281         }
01282 
01283         if (BG_SaberInSpecialAttack(pm->ps->legsAnim))
01284         {
01285                 return qfalse;
01286         }
01287 
01288         if (BG_HasYsalamiri(pm->gametype, pm->ps))
01289         {
01290                 return qfalse;
01291         }
01292 
01293         if (!BG_CanUseFPNow(pm->gametype, pm->ps, pm-&g