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->cmd.serverTime, FP_LEVITATION))
01294         {
01295                 return qfalse;
01296         }
01297 
01298         if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && //in air
01299                 (pm->ps->pm_flags & PMF_JUMP_HELD) && //jumped
01300                 pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && //force-jump capable
01301                 pm->ps->velocity[2] > 0 )//going up
01302         {
01303                 return qtrue;
01304         }
01305         return qfalse;
01306 }
01307 
01308 static void PM_JumpForDir( void )
01309 {
01310         int anim = BOTH_JUMP1;
01311         if ( pm->cmd.forwardmove > 0 ) 
01312         {
01313                 anim = BOTH_JUMP1;
01314                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
01315         } 
01316         else if ( pm->cmd.forwardmove < 0 )
01317         {
01318                 anim = BOTH_JUMPBACK1;
01319                 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
01320         }
01321         else if ( pm->cmd.rightmove > 0 ) 
01322         {
01323                 anim = BOTH_JUMPRIGHT1;
01324                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
01325         }
01326         else if ( pm->cmd.rightmove < 0 ) 
01327         {
01328                 anim = BOTH_JUMPLEFT1;
01329                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
01330         }
01331         else
01332         {
01333                 anim = BOTH_JUMP1;
01334                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
01335         }
01336         if(!BG_InDeathAnim(pm->ps->legsAnim))
01337         {
01338                 PM_SetAnim(SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE, 100);
01339         }
01340 }
01341 
01342 void PM_SetPMViewAngle(playerState_t *ps, vec3_t angle, usercmd_t *ucmd)
01343 {
01344         int                     i;
01345 
01346         for (i=0 ; i<3 ; i++)
01347         { // set the delta angle
01348                 int             cmdAngle;
01349 
01350                 cmdAngle = ANGLE2SHORT(angle[i]);
01351                 ps->delta_angles[i] = cmdAngle - ucmd->angles[i];
01352         }
01353         VectorCopy (angle, ps->viewangles);
01354 }
01355 
01356 qboolean PM_AdjustAngleForWallRun( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
01357 {
01358         if (( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && ps->legsTimer > 500 )
01359         {//wall-running and not at end of anim
01360                 //stick to wall, if there is one
01361                 vec3_t  fwd, rt, traceTo, mins, maxs, fwdAngles;
01362                 trace_t trace;
01363                 float   dist, yawAdjust;
01364 
01365                 VectorSet(mins, -15, -15, 0);
01366                 VectorSet(maxs, 15, 15, 24);
01367                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
01368 
01369                 AngleVectors( fwdAngles, fwd, rt, NULL );
01370                 if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
01371                 {
01372                         dist = 128;
01373                         yawAdjust = -90;
01374                 }
01375                 else
01376                 {
01377                         dist = -128;
01378                         yawAdjust = 90;
01379                 }
01380                 VectorMA( ps->origin, dist, rt, traceTo );
01381                 
01382                 pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
01383 
01384                 if ( trace.fraction < 1.0f 
01385                         && (trace.plane.normal[2] >= 0.0f && trace.plane.normal[2] <= 0.4f) )//&& ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
01386                 {
01387                         trace_t trace2;
01388                         vec3_t traceTo2;
01389                         vec3_t  wallRunFwd, wallRunAngles;
01390                         
01391                         VectorClear( wallRunAngles );
01392                         wallRunAngles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
01393                         AngleVectors( wallRunAngles, wallRunFwd, NULL, NULL );
01394 
01395                         VectorMA( pm->ps->origin, 32, wallRunFwd, traceTo2 );
01396                         pm->trace( &trace2, pm->ps->origin, mins, maxs, traceTo2, pm->ps->clientNum, MASK_PLAYERSOLID );
01397                         if ( trace2.fraction < 1.0f && DotProduct( trace2.plane.normal, wallRunFwd ) <= -0.999f )
01398                         {//wall we can't run on in front of us
01399                                 trace.fraction = 1.0f;//just a way to get it to kick us off the wall below
01400                         }
01401                 } 
01402 
01403                 if ( trace.fraction < 1.0f 
01404                         && (trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2] <= 0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
01405                 {//still a wall there
01406                         if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
01407                         {
01408                                 ucmd->rightmove = 127;
01409                         }
01410                         else
01411                         {
01412                                 ucmd->rightmove = -127;
01413                         }
01414                         if ( ucmd->upmove < 0 )
01415                         {
01416                                 ucmd->upmove = 0;
01417                         }
01418                         //make me face perpendicular to the wall
01419                         ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
01420 
01421                         PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
01422 
01423                         ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
01424                         if ( doMove )
01425                         {
01426                                 //push me forward
01427                                 float   zVel = ps->velocity[2];
01428                                 if ( ps->legsTimer > 500 )
01429                                 {//not at end of anim yet
01430                                         float speed = 175;
01431                                         if ( ucmd->forwardmove < 0 )
01432                                         {//slower
01433                                                 speed = 100;
01434                                         }
01435                                         else if ( ucmd->forwardmove > 0 )
01436                                         {
01437                                                 speed = 250;//running speed
01438                                         }
01439                                         VectorScale( fwd, speed, ps->velocity );
01440                                 }
01441                                 ps->velocity[2] = zVel;//preserve z velocity
01442                                 //pull me toward the wall, too
01443                                 VectorMA( ps->velocity, dist, rt, ps->velocity );
01444                         }
01445                         ucmd->forwardmove = 0;
01446                         return qtrue;
01447                 }
01448                 else if ( doMove )
01449                 {//stop it
01450                         if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
01451                         {
01452                                 PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_RIGHT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01453                         }
01454                         else if ( (ps->legsAnim) == BOTH_WALL_RUN_LEFT )
01455                         {
01456                                 PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_LEFT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01457                         }
01458                 }
01459         }
01460 
01461         return qfalse;
01462 }
01463 
01464 qboolean PM_AdjustAnglesForWallRunUpFlipAlt( usercmd_t *ucmd )
01465 {
01466 //      ucmd->angles[PITCH] = ANGLE2SHORT( pm->ps->viewangles[PITCH] ) - pm->ps->delta_angles[PITCH];
01467 //      ucmd->angles[YAW] = ANGLE2SHORT( pm->ps->viewangles[YAW] ) - pm->ps->delta_angles[YAW];
01468         PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
01469         return qtrue;
01470 }
01471 
01472 qboolean PM_AdjustAngleForWallRunUp( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
01473 {
01474         if ( ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
01475         {//wall-running up
01476                 //stick to wall, if there is one
01477                 vec3_t  fwd, traceTo, mins, maxs, fwdAngles;
01478                 trace_t trace;
01479                 float   dist = 128;
01480 
01481                 VectorSet(mins, -15,-15,0);
01482                 VectorSet(maxs, 15,15,24);
01483                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
01484 
01485                 AngleVectors( fwdAngles, fwd, NULL, NULL );
01486                 VectorMA( ps->origin, dist, fwd, traceTo );
01487                 pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
01488                 if ( trace.fraction > 0.5f )
01489                 {//hmm, some room, see if there's a floor right here
01490                         trace_t trace2;
01491                         vec3_t  top, bottom;
01492 
01493                         VectorCopy( trace.endpos, top );
01494                         top[2] += (pm->mins[2]*-1) + 4.0f;
01495                         VectorCopy( top, bottom );
01496                         bottom[2] -= 64.0f;
01497                         pm->trace( &trace2, top, pm->mins, pm->maxs, bottom, ps->clientNum, MASK_PLAYERSOLID );
01498                         if ( !trace2.allsolid 
01499                                 && !trace2.startsolid 
01500                                 && trace2.fraction < 1.0f 
01501                                 && trace2.plane.normal[2] > 0.7f )//slope we can stand on
01502                         {//cool, do the alt-flip and land on whetever it is we just scaled up
01503                                 VectorScale( fwd, 100, pm->ps->velocity );
01504                                 pm->ps->velocity[2] += 400;
01505                                 PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_ALT, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01506                                 pm->ps->pm_flags |= PMF_JUMP_HELD;
01507                                 //ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
01508                                 //ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
01509                                 //G_AddEvent( ent, EV_JUMP, 0 );
01510                                 PM_AddEvent(EV_JUMP);
01511                                 ucmd->upmove = 0;
01512                                 return qfalse;
01513                         }
01514                 }
01515 
01516                 if ( //ucmd->upmove <= 0 && 
01517                         ps->legsTimer > 0 &&
01518                         ucmd->forwardmove > 0 &&
01519                         trace.fraction < 1.0f && 
01520                         (trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
01521                 {//still a vertical wall there
01522                         //make sure there's not a ceiling above us!
01523                         trace_t trace2;
01524                         VectorCopy( ps->origin, traceTo );
01525                         traceTo[2] += 64;
01526                         pm->trace( &trace2, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
01527                         if ( trace2.fraction < 1.0f )
01528                         {//will hit a ceiling, so force jump-off right now
01529                                 //NOTE: hits any entity or clip brush in the way, too, not just architecture!
01530                         }
01531                         else
01532                         {//all clear, keep going
01533                                 //FIXME: don't pull around 90 turns
01534                                 //FIXME: simulate stepping up steps here, somehow?
01535                                 ucmd->forwardmove = 127;
01536                                 if ( ucmd->upmove < 0 )
01537                                 {
01538                                         ucmd->upmove = 0;
01539                                 }
01540                                 //make me face the wall
01541                                 ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+180;
01542                                 PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
01543                                 /*
01544                                 if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
01545                                 {//don't clamp angles when looking through a viewEntity
01546                                         SetClientViewAngle( ent, ent->client->ps.viewangles );
01547                                 }
01548                                 */
01549                                 ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
01550                                 //if ( ent->s.number || !player_locked )
01551                                 if (1) //aslkfhsakf
01552                                 {
01553                                         if ( doMove )
01554                                         {
01555                                                 //pull me toward the wall
01556                                                 VectorScale( trace.plane.normal, -dist*trace.fraction, ps->velocity );
01557                                                 //push me up
01558                                                 if ( ps->legsTimer > 200 )
01559                                                 {//not at end of anim yet
01560                                                         float speed = 300;
01561                                                         /*
01562                                                         if ( ucmd->forwardmove < 0 )
01563                                                         {//slower
01564                                                                 speed = 100;
01565                                                         }
01566                                                         else if ( ucmd->forwardmove > 0 )
01567                                                         {
01568                                                                 speed = 250;//running speed
01569                                                         }
01570                                                         */
01571                                                         ps->velocity[2] = speed;//preserve z velocity
01572                                                 }
01573                                         }
01574                                 }
01575                                 ucmd->forwardmove = 0;
01576                                 return qtrue;
01577                         }
01578                 }
01579                 //failed!
01580                 if ( doMove )
01581                 {//stop it
01582                         VectorScale( fwd, -300.0f, ps->velocity );
01583                         ps->velocity[2] += 200;
01584                         //NPC_SetAnim( ent, SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
01585                         //why?!?#?#@!%$R@$KR#F:Hdl;asfm
01586                         PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01587                         ps->pm_flags |= PMF_JUMP_HELD;
01588                         //ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
01589 
01590                         //FIXME do I need this in mp?
01591                         //ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
01592                         PM_AddEvent(EV_JUMP);
01593                         ucmd->upmove = 0;
01594                         //return qtrue;
01595                 }
01596         }
01597         return qfalse;
01598 }
01599 
01600 #define JUMP_OFF_WALL_SPEED     200.0f
01601 //nice...
01602 static float BG_ForceWallJumpStrength( void )
01603 {
01604         return (forceJumpStrength[FORCE_LEVEL_3]/2.5f);
01605 }
01606 
01607 qboolean PM_AdjustAngleForWallJump( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
01608 {
01609         if ( ( ( BG_InReboundJump( ps->legsAnim ) || BG_InReboundHold( ps->legsAnim ) )
01610                         && ( BG_InReboundJump( ps->torsoAnim ) || BG_InReboundHold( ps->torsoAnim ) ) )
01611                 || (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
01612         {//hugging wall, getting ready to jump off
01613                 //stick to wall, if there is one
01614                 vec3_t  checkDir, traceTo, mins, maxs, fwdAngles;
01615                 trace_t trace;
01616                 float   dist = 128.0f, yawAdjust;
01617 
01618                 VectorSet(mins, pm->mins[0],pm->mins[1],0);
01619                 VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
01620                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
01621 
01622                 switch ( ps->legsAnim )
01623                 {
01624                 case BOTH_FORCEWALLREBOUND_RIGHT:
01625                 case BOTH_FORCEWALLHOLD_RIGHT:
01626                         AngleVectors( fwdAngles, NULL, checkDir, NULL );
01627                         yawAdjust = -90;
01628                         break;
01629                 case BOTH_FORCEWALLREBOUND_LEFT:
01630                 case BOTH_FORCEWALLHOLD_LEFT:
01631                         AngleVectors( fwdAngles, NULL, checkDir, NULL );
01632                         VectorScale( checkDir, -1, checkDir );
01633                         yawAdjust = 90;
01634                         break;
01635                 case BOTH_FORCEWALLREBOUND_FORWARD:
01636                 case BOTH_FORCEWALLHOLD_FORWARD:
01637                         AngleVectors( fwdAngles, checkDir, NULL, NULL );
01638                         yawAdjust = 180;
01639                         break;
01640                 case BOTH_FORCEWALLREBOUND_BACK:
01641                 case BOTH_FORCEWALLHOLD_BACK:
01642                         AngleVectors( fwdAngles, checkDir, NULL, NULL );
01643                         VectorScale( checkDir, -1, checkDir );
01644                         yawAdjust = 0;
01645                         break;
01646                 default:
01647                         //WTF???
01648                         pm->ps->pm_flags &= ~PMF_STUCK_TO_WALL;
01649                         return qfalse;
01650                         break;
01651                 }
01652                 if ( pm->debugMelee )
01653                 {//uber-skillz
01654                         if ( ucmd->upmove > 0 )
01655                         {//hold on until you let go manually
01656                                 if ( BG_InReboundHold( ps->legsAnim ) )
01657                                 {//keep holding
01658                                         if ( ps->legsTimer < 150 )
01659                                         {
01660                                                 ps->legsTimer = 150;
01661                                         }
01662                                 }
01663                                 else
01664                                 {//if got to hold part of anim, play hold anim
01665                                         if ( ps->legsTimer <= 300 )
01666                                         {
01667                                                 ps->saberHolstered = 2;
01668                                                 PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01669                                                 ps->legsTimer = ps->torsoTimer = 150;
01670                                         }
01671                                 }
01672                         }
01673                 }
01674                 VectorMA( ps->origin, dist, checkDir, traceTo );
01675                 pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
01676                 if ( //ucmd->upmove <= 0 && 
01677                         ps->legsTimer > 100 &&
01678                         trace.fraction < 1.0f && 
01679                         fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/ )
01680                 {//still a vertical wall there
01681                         //FIXME: don't pull around 90 turns
01682                         /*
01683                         if ( ent->s.number || !player_locked )
01684                         {
01685                                 ucmd->forwardmove = 127;
01686                         }
01687                         */
01688                         if ( ucmd->upmove < 0 )
01689                         {
01690                                 ucmd->upmove = 0;
01691                         }
01692                         //align me to the wall
01693                         ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
01694                         PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
01695                         /*
01696                         if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
01697                         {//don't clamp angles when looking through a viewEntity
01698                                 SetClientViewAngle( ent, ent->client->ps.viewangles );
01699                         }
01700                         */
01701                         ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
01702                         //if ( ent->s.number || !player_locked )
01703                         if (1)
01704                         {
01705                                 if ( doMove )
01706                                 {
01707                                         //pull me toward the wall
01708                                         VectorScale( trace.plane.normal, -128.0f, ps->velocity );
01709                                 }
01710                         }
01711                         ucmd->upmove = 0;
01712                         ps->pm_flags |= PMF_STUCK_TO_WALL;
01713                         return qtrue;
01714                 }
01715                 else if ( doMove 
01716                         && (ps->pm_flags&PMF_STUCK_TO_WALL))
01717                 {//jump off
01718                         //push off of it!
01719                         ps->pm_flags &= ~PMF_STUCK_TO_WALL;
01720                         ps->velocity[0] = ps->velocity[1] = 0;
01721                         VectorScale( checkDir, -JUMP_OFF_WALL_SPEED, ps->velocity );
01722                         ps->velocity[2] = BG_ForceWallJumpStrength();
01723                         ps->pm_flags |= PMF_JUMP_HELD;//PMF_JUMPING|PMF_JUMP_HELD;
01724                         //G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
01725                         ps->fd.forceJumpSound = 1; //this is a stupid thing, i should fix it.
01726                         //ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
01727                         if (ps->origin[2] < ps->fd.forceJumpZStart)
01728                         {
01729                                 ps->fd.forceJumpZStart = ps->origin[2];
01730                         }
01731                         //FIXME do I need this?
01732 
01733                         BG_ForcePowerDrain( ps, FP_LEVITATION, 10 );
01734                         //no control for half a second
01735                         ps->pm_flags |= PMF_TIME_KNOCKBACK;
01736                         ps->pm_time = 500;
01737                         ucmd->forwardmove = 0;
01738                         ucmd->rightmove = 0;
01739                         ucmd->upmove = 127;
01740 
01741                         if ( BG_InReboundHold( ps->legsAnim ) )
01742                         {//if was in hold pose, release now
01743                                 PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01744                         }
01745                         else
01746                         {
01747                                 //PM_JumpForDir();
01748                                 PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART, 0);
01749                         }
01750 
01751                         //return qtrue;
01752                 }
01753         }
01754         ps->pm_flags &= ~PMF_STUCK_TO_WALL;
01755         return qfalse;
01756 }
01757 
01758 //Set the height for when a force jump was started. If it's 0, nuge it up (slight hack to prevent holding jump over slopes)
01759 void PM_SetForceJumpZStart(float value)
01760 {
01761         pm->ps->fd.forceJumpZStart = value;
01762         if (!pm->ps->fd.forceJumpZStart)
01763         {
01764                 pm->ps->fd.forceJumpZStart -= 0.1;
01765         }
01766 }
01767 
01768 float forceJumpHeightMax[NUM_FORCE_POWER_LEVELS] = 
01769 {
01770         66,//normal jump (32+stepheight(18)+crouchdiff(24) = 74)
01771         130,//(96+stepheight(18)+crouchdiff(24) = 138)
01772         226,//(192+stepheight(18)+crouchdiff(24) = 234)
01773         418//(384+stepheight(18)+crouchdiff(24) = 426)
01774 };
01775 
01776 void PM_GrabWallForJump( int anim )
01777 {//NOTE!!! assumes an appropriate anim is being passed in!!!
01778         PM_SetAnim( SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01779         PM_AddEvent( EV_JUMP );//make sound for grab
01780         pm->ps->pm_flags |= PMF_STUCK_TO_WALL;
01781 }
01782 
01783 /*
01784 =============
01785 PM_CheckJump
01786 =============
01787 */
01788 static qboolean PM_CheckJump( void ) 
01789 {
01790         qboolean allowFlips = qtrue;
01791 
01792         if (pm->ps->clientNum >= MAX_CLIENTS)
01793         {
01794                 bgEntity_t *pEnt = pm_entSelf;
01795 
01796                 if (pEnt->s.eType == ET_NPC &&
01797                         pEnt->s.NPC_class == CLASS_VEHICLE)
01798                 { //no!
01799                         return qfalse;
01800                 }
01801         }
01802 
01803         if (pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
01804                 pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
01805                 pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
01806         {
01807                 return qfalse;
01808         }
01809 
01810         if (pm->ps->pm_type == PM_JETPACK)
01811         { //there's no actual jumping while we jetpack
01812                 return qfalse;
01813         }
01814 
01815         //Don't allow jump until all buttons are up
01816         if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
01817                 return qfalse;          
01818         }
01819 
01820         if ( PM_InKnockDown( pm->ps ) || BG_InRoll( pm->ps, pm->ps->legsAnim ) ) 
01821         {//in knockdown
01822                 return qfalse;          
01823         }
01824 
01825         if ( pm->ps->weapon == WP_SABER )
01826         {
01827                 saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
01828                 saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
01829                 if ( saber1
01830                         && (saber1->saberFlags&SFL_NO_FLIPS) )
01831                 {
01832                         allowFlips = qfalse;
01833                 }
01834                 if ( saber2
01835                         && (saber2->saberFlags&SFL_NO_FLIPS) )
01836                 {
01837                         allowFlips = qfalse;
01838                 }
01839         }
01840 
01841         if (pm->ps->groundEntityNum != ENTITYNUM_NONE || pm->ps->origin[2] < pm->ps->fd.forceJumpZStart)
01842         {
01843                 pm->ps->fd.forcePowersActive &= ~(1<<FP_LEVITATION);
01844         }
01845 
01846         if (pm->ps->fd.forcePowersActive & (1 << FP_LEVITATION))
01847         { //Force jump is already active.. continue draining power appropriately until we land.
01848                 if (pm->ps->fd.forcePowerDebounce[FP_LEVITATION] < pm->cmd.serverTime)
01849                 {
01850                         if ( pm->gametype == GT_DUEL 
01851                                 || pm->gametype == GT_POWERDUEL )
01852                         {//jump takes less power
01853                                 BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 1 );
01854                         }
01855                         else
01856                         {
01857                                 BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
01858                         }
01859                         if (pm->ps->fd.forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_2)
01860                         {
01861                                 pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 300;
01862                         }
01863                         else
01864                         {
01865                                 pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 200;
01866                         }
01867                 }
01868         }
01869 
01870         if (pm->ps->forceJumpFlip)
01871         { //Forced jump anim
01872                 int anim = BOTH_FORCEINAIR1;
01873                 int     parts = SETANIM_BOTH;
01874                 if ( allowFlips )
01875                 {
01876                         if ( pm->cmd.forwardmove > 0 )
01877                         {
01878                                 anim = BOTH_FLIP_F;
01879                         }
01880                         else if ( pm->cmd.forwardmove < 0 )
01881                         {
01882                                 anim = BOTH_FLIP_B;
01883                         }
01884                         else if ( pm->cmd.rightmove > 0 )
01885                         {
01886                                 anim = BOTH_FLIP_R;
01887                         }
01888                         else if ( pm->cmd.rightmove < 0 )
01889                         {
01890                                 anim = BOTH_FLIP_L;
01891                         }
01892                 }
01893                 else
01894                 {
01895                         if ( pm->cmd.forwardmove > 0 )
01896                         {
01897                                 anim = BOTH_FORCEINAIR1;
01898                         }
01899                         else if ( pm->cmd.forwardmove < 0 )
01900                         {
01901                                 anim = BOTH_FORCEINAIRBACK1;
01902                         }
01903                         else if ( pm->cmd.rightmove > 0 )
01904                         {
01905                                 anim = BOTH_FORCEINAIRRIGHT1;
01906                         }
01907                         else if ( pm->cmd.rightmove < 0 )
01908                         {
01909                                 anim = BOTH_FORCEINAIRLEFT1;
01910                         }
01911                 }
01912                 if ( pm->ps->weaponTime )
01913                 {//FIXME: really only care if we're in a saber attack anim...
01914                         parts = SETANIM_LEGS;
01915                 }
01916 
01917                 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
01918                 pm->ps->forceJumpFlip = qfalse;
01919                 return qtrue;
01920         }
01921 #if METROID_JUMP
01922         if ( pm->waterlevel < 2 ) 
01923         {
01924                 if ( pm->ps->gravity > 0 )
01925                 {//can't do this in zero-G
01926                         if ( PM_ForceJumpingUp() )
01927                         {//holding jump in air
01928                                 float curHeight = pm->ps->origin[2] - pm->ps->fd.forceJumpZStart;
01929                                 //check for max force jump level and cap off & cut z vel
01930                                 if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height
01931                                                 (pm->ps->fd.forcePower&&pm->cmd.upmove>=10) ) &&
01932                                         curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] &&
01933                                         pm->ps->fd.forceJumpZStart)//still below maximum jump height
01934                                 {//can still go up
01935                                         if ( curHeight > forceJumpHeight[0] )
01936                                         {//passed normal jump height  *2?
01937                                                 if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) )//haven't started forcejump yet
01938                                                 {
01939                                                         //start force jump
01940                                                         pm->ps->fd.forcePowersActive |= (1<<FP_LEVITATION);
01941                                                         pm->ps->fd.forceJumpSound = 1;
01942                                                         //play flip
01943                                                         if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir
01944                                                                 (pm->ps->legsAnim) != BOTH_FLIP_F &&//not already flipping
01945                                                                 (pm->ps->legsAnim) != BOTH_FLIP_B &&
01946                                                                 (pm->ps->legsAnim) != BOTH_FLIP_R &&
01947                                                                 (pm->ps->legsAnim) != BOTH_FLIP_L 
01948                                                                 && allowFlips )
01949                                                         { 
01950                                                                 int anim = BOTH_FORCEINAIR1;
01951                                                                 int     parts = SETANIM_BOTH;
01952 
01953                                                                 if ( pm->cmd.forwardmove > 0 )
01954                                                                 {
01955                                                                         anim = BOTH_FLIP_F;
01956                                                                 }
01957                                                                 else if ( pm->cmd.forwardmove < 0 )
01958                                                                 {
01959                                                                         anim = BOTH_FLIP_B;
01960                                                                 }
01961                                                                 else if ( pm->cmd.rightmove > 0 )
01962                                                                 {
01963                                                                         anim = BOTH_FLIP_R;
01964                                                                 }
01965                                                                 else if ( pm->cmd.rightmove < 0 )
01966                                                                 {
01967                                                                         anim = BOTH_FLIP_L;
01968                                                                 }
01969                                                                 if ( pm->ps->weaponTime )
01970                                                                 {
01971                                                                         parts = SETANIM_LEGS;
01972                                                                 }
01973 
01974                                                                 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
01975                                                         }
01976                                                         else if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
01977                                                         {
01978                                                                 vec3_t facingFwd, facingRight, facingAngles;
01979                                                                 int     anim = -1;
01980                                                                 float dotR, dotF;
01981                                                                 
01982                                                                 VectorSet(facingAngles, 0, pm->ps->viewangles[YAW], 0);
01983 
01984                                                                 AngleVectors( facingAngles, facingFwd, facingRight, NULL );
01985                                                                 dotR = DotProduct( facingRight, pm->ps->velocity );
01986                                                                 dotF = DotProduct( facingFwd, pm->ps->velocity );
01987 
01988                                                                 if ( fabs(dotR) > fabs(dotF) * 1.5 )
01989                                                                 {
01990                                                                         if ( dotR > 150 )
01991                                                                         {
01992                                                                                 anim = BOTH_FORCEJUMPRIGHT1;
01993                                                                         }
01994                                                                         else if ( dotR < -150 )
01995                                                                         {
01996                                                                                 anim = BOTH_FORCEJUMPLEFT1;
01997                                                                         }
01998                                                                 }
01999                                                                 else
02000                                                                 {
02001                                                                         if ( dotF > 150 )
02002                                                                         {
02003                                                                                 anim = BOTH_FORCEJUMP1;
02004                                                                         }
02005                                                                         else if ( dotF < -150 )
02006                                                                         {
02007                                                                                 anim = BOTH_FORCEJUMPBACK1;
02008                                                                         }
02009                                                                 }
02010                                                                 if ( anim != -1 )
02011                                                                 {
02012                                                                         int parts = SETANIM_BOTH;
02013                                                                         if ( pm->ps->weaponTime )
02014                                                                         {//FIXME: really only care if we're in a saber attack anim...
02015                                                                                 parts = SETANIM_LEGS;
02016                                                                         }
02017 
02018                                                                         PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
02019                                                                 }
02020                                                         }
02021                                                 }
02022                                                 else
02023                                                 { //jump is already active (the anim has started)
02024                                                         if ( pm->ps->legsTimer < 1 )
02025                                                         {//not in the middle of a legsAnim
02026                                                                 int anim = (pm->ps->legsAnim);
02027                                                                 int newAnim = -1;
02028                                                                 switch ( anim )
02029                                                                 {
02030                                                                 case BOTH_FORCEJUMP1:
02031                                                                         newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
02032                                                                         break;
02033                                                                 case BOTH_FORCEJUMPBACK1:
02034                                                                         newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
02035                                                                         break;
02036                                                                 case BOTH_FORCEJUMPLEFT1:
02037                                                                         newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
02038                                                                         break;
02039                                                                 case BOTH_FORCEJUMPRIGHT1:
02040                                                                         newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
02041                                                                         break;
02042                                                                 }
02043                                                                 if ( newAnim != -1 )
02044                                                                 {
02045                                                                         int parts = SETANIM_BOTH;
02046                                                                         if ( pm->ps->weaponTime )
02047                                                                         {
02048                                                                                 parts = SETANIM_LEGS;
02049                                                                         }
02050 
02051                                                                         PM_SetAnim( parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
02052                                                                 }
02053                                                         }
02054                                                 }
02055                                         }
02056 
02057                                         //need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel
02058                                         pm->ps->velocity[2] = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->fd.forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY;
02059                                         pm->ps->velocity[2] /= 10;
02060                                         pm->ps->velocity[2] += JUMP_VELOCITY;
02061                                         pm->ps->pm_flags |= PMF_JUMP_HELD;
02062                                 }
02063                                 else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] )
02064                                 {//still have some headroom, don't totally stop it
02065                                         if ( pm->ps->velocity[2] > JUMP_VELOCITY )
02066                                         {
02067                                                 pm->ps->velocity[2] = JUMP_VELOCITY;
02068                                         }
02069                                 }
02070                                 else
02071                                 {
02072                                         //pm->ps->velocity[2] = 0;
02073                                         //rww - changed for the sake of balance in multiplayer
02074 
02075                                         if ( pm->ps->velocity[2] > JUMP_VELOCITY )
02076                                         {
02077                                                 pm->ps->velocity[2] = JUMP_VELOCITY;
02078                                         }
02079                                 }
02080                                 pm->cmd.upmove = 0;
02081                                 return qfalse;
02082                         }
02083                 }
02084         }
02085 
02086 #endif
02087 
02088         //Not jumping
02089         if ( pm->cmd.upmove < 10 && pm->ps->groundEntityNum != ENTITYNUM_NONE) {
02090                 return qfalse;
02091         }
02092 
02093         // must wait for jump to be released
02094         if ( pm->ps->pm_flags & PMF_JUMP_HELD ) 
02095         {
02096                 // clear upmove so cmdscale doesn't lower running speed
02097                 pm->cmd.upmove = 0;
02098                 return qfalse;
02099         }
02100 
02101         if ( pm->ps->gravity <= 0 )
02102         {//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of
02103                 vec3_t  forward, back;
02104                 trace_t trace;
02105 
02106                 AngleVectors( pm->ps->viewangles, forward, NULL, NULL );
02107                 VectorMA( pm->ps->origin, -8, forward, back );
02108                 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask );
02109 
02110                 if ( trace.fraction <= 1.0f )
02111                 {
02112                         VectorMA( pm->ps->velocity, JUMP_VELOCITY*2, forward, pm->ps->velocity );
02113                         PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART, 150);
02114                 }//else no surf close enough to push off of
02115                 pm->cmd.upmove = 0;
02116         }
02117         else if ( pm->cmd.upmove > 0 && pm->waterlevel < 2 &&
02118                 pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 &&
02119                 !(pm->ps->pm_flags&PMF_JUMP_HELD) &&
02120                 (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE) &&
02121                 !PM_IsRocketTrooper() &&
02122                 !BG_HasYsalamiri(pm->gametype, pm->ps) &&
02123                 BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION) )
02124         {
02125                 qboolean allowWallRuns = qtrue;
02126                 qboolean allowWallFlips = qtrue;
02127                 qboolean allowFlips = qtrue;
02128                 qboolean allowWallGrabs = qtrue;
02129                 if ( pm->ps->weapon == WP_SABER )
02130                 {
02131                         saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
02132                         saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
02133                         if ( saber1
02134                                 && (saber1->saberFlags&SFL_NO_WALL_RUNS) )
02135                         {
02136                                 allowWallRuns = qfalse;
02137                         }
02138                         if ( saber2
02139                                 && (saber2->saberFlags&SFL_NO_WALL_RUNS) )
02140                         {
02141                                 allowWallRuns = qfalse;
02142                         }
02143                         if ( saber1
02144                                 && (saber1->saberFlags&SFL_NO_WALL_FLIPS) )
02145                         {
02146                                 allowWallFlips = qfalse;
02147                         }
02148                         if ( saber2
02149                                 && (saber2->saberFlags&SFL_NO_WALL_FLIPS) )
02150                         {
02151                                 allowWallFlips = qfalse;
02152                         }
02153                         if ( saber1
02154                                 && (saber1->saberFlags&SFL_NO_FLIPS) )
02155                         {
02156                                 allowFlips = qfalse;
02157                         }
02158                         if ( saber2
02159                                 && (saber2->saberFlags&SFL_NO_FLIPS) )
02160                         {
02161                                 allowFlips = qfalse;
02162                         }
02163                         if ( saber1
02164                                 && (saber1->saberFlags&SFL_NO_WALL_GRAB) )
02165                         {
02166                                 allowWallGrabs = qfalse;
02167                         }
02168                         if ( saber2
02169                                 && (saber2->saberFlags&SFL_NO_WALL_GRAB) )
02170                         {
02171                                 allowWallGrabs = qfalse;
02172                         }
02173                 }
02174 
02175                 if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
02176                 {//on the ground
02177                         //check for left-wall and right-wall special jumps
02178                         int anim = -1;
02179                         float   vertPush = 0;
02180                         if ( pm->cmd.rightmove > 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
02181                         {//strafing right
02182                                 if ( pm->cmd.forwardmove > 0 )
02183                                 {//wall-run
02184                                         if ( allowWallRuns )
02185                                         {
02186                                                 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
02187                                                 anim = BOTH_WALL_RUN_RIGHT;
02188                                         }
02189                                 }
02190                                 else if ( pm->cmd.forwardmove == 0 )
02191                                 {//wall-flip
02192                                         if ( allowWallFlips )
02193                                         {
02194                                                 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
02195                                                 anim = BOTH_WALL_FLIP_RIGHT;
02196                                         }
02197                                 }
02198                         }
02199                         else if ( pm->cmd.rightmove < 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
02200                         {//strafing left
02201                                 if ( pm->cmd.forwardmove > 0 )
02202                                 {//wall-run
02203                                         if ( allowWallRuns )
02204                                         {
02205                                                 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
02206                                                 anim = BOTH_WALL_RUN_LEFT;
02207                                         }
02208                                 }
02209                                 else if ( pm->cmd.forwardmove == 0 )
02210                                 {//wall-flip
02211                                         if ( allowWallFlips )
02212                                         {
02213                                                 vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
02214                                                 anim = BOTH_WALL_FLIP_LEFT;
02215                                         }
02216                                 }
02217                         }
02218                         else if ( pm->cmd.forwardmove < 0 && !(pm->cmd.buttons&BUTTON_ATTACK) )
02219                         {//backflip
02220                                 if ( allowFlips )
02221                                 {
02222                                         vertPush = JUMP_VELOCITY;
02223                                         anim = BOTH_FLIP_BACK1;//BG_PickAnim( BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 );
02224                                 }
02225                         }
02226 
02227                         vertPush += 128; //give them an extra shove
02228 
02229                         if ( anim != -1 )
02230                         {
02231                                 vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
02232                                 vec3_t  idealNormal={0}, wallNormal={0};
02233                                 trace_t trace;
02234                                 qboolean doTrace = qfalse;
02235                                 int contents = MASK_SOLID;//MASK_PLAYERSOLID;
02236 
02237                                 VectorSet(mins, pm->mins[0],pm->mins[1],0);
02238                                 VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
02239                                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
02240 
02241                                 memset(&trace, 0, sizeof(trace)); //to shut the compiler up
02242 
02243                                 AngleVectors( fwdAngles, fwd, right, NULL );
02244 
02245                                 //trace-check for a wall, if necc.
02246                                 switch ( anim )
02247                                 {
02248                                 case BOTH_WALL_FLIP_LEFT:
02249                                         //NOTE: purposely falls through to next case!
02250                                 case BOTH_WALL_RUN_LEFT:
02251                                         doTrace = qtrue;
02252                                         VectorMA( pm->ps->origin, -16, right, traceto );
02253                                         break;
02254 
02255                                 case BOTH_WALL_FLIP_RIGHT:
02256                                         //NOTE: purposely falls through to next case!
02257                                 case BOTH_WALL_RUN_RIGHT:
02258                                         doTrace = qtrue;
02259                                         VectorMA( pm->ps->origin, 16, right, traceto );
02260                                         break;
02261 
02262                                 case BOTH_WALL_FLIP_BACK1:
02263                                         doTrace = qtrue;
02264                                         VectorMA( pm->ps->origin, 16, fwd, traceto );
02265                                         break;
02266                                 }
02267 
02268                                 if ( doTrace )
02269                                 {
02270                                         pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );
02271                                         VectorCopy( trace.plane.normal, wallNormal );
02272                                         VectorNormalize( wallNormal );
02273                                         VectorSubtract( pm->ps->origin, traceto, idealNormal );
02274                                         VectorNormalize( idealNormal );
02275                                 }
02276 
02277                                 if ( !doTrace || (trace.fraction < 1.0f && (trace.entityNum < MAX_CLIENTS || DotProduct(wallNormal,idealNormal) > 0.7)) )
02278                                 {//there is a wall there.. or hit a client
02279                                         if ( (anim != BOTH_WALL_RUN_LEFT 
02280                                                         && anim != BOTH_WALL_RUN_RIGHT
02281                                                         && anim != BOTH_FORCEWALLRUNFLIP_START) 
02282                                                 || (wallNormal[2] >= 0.0f&&wallNormal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
02283                                         {//wall-runs can only run on perfectly flat walls, sorry. 
02284                                                 int parts;
02285                                                 //move me to side
02286                                                 if ( anim == BOTH_WALL_FLIP_LEFT )
02287                                                 {
02288                                                         pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
02289                                                         VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
02290                                                 }
02291                                                 else if ( anim == BOTH_WALL_FLIP_RIGHT )
02292                                                 {
02293                                                         pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
02294                                                         VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
02295                                                 }
02296                                                 else if ( anim == BOTH_FLIP_BACK1 
02297                                                         || anim == BOTH_FLIP_BACK2 
02298                                                         || anim == BOTH_FLIP_BACK3 
02299                                                         || anim == BOTH_WALL_FLIP_BACK1 )
02300                                                 {
02301                                                         pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
02302                                                         VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
02303                                                 }
02304 
02305                                                 /*
02306                                                 if ( doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT )
02307                                                 {
02308                                                         if (trace.entityNum < MAX_CLIENTS)
02309                                                         {
02310                                                                 pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
02311                                                         }
02312                                                 }
02313                                                 */
02314 
02315                                                 //up
02316                                                 if ( vertPush )
02317                                                 {
02318                                                         pm->ps->velocity[2] = vertPush;
02319                                                         pm->ps->fd.forcePowersActive |= (1 << FP_LEVITATION);
02320                                                 }
02321                                                 //animate me
02322                                                 parts = SETANIM_LEGS;
02323                                                 if ( anim == BOTH_BUTTERFLY_LEFT )
02324                                                 {
02325                                                         parts = SETANIM_BOTH;
02326                                                         pm->cmd.buttons&=~BUTTON_ATTACK;
02327                                                         pm->ps->saberMove = LS_NONE;
02328                                                 }
02329                                                 else if ( !pm->ps->weaponTime )
02330                                                 {
02331                                                         parts = SETANIM_BOTH;
02332                                                 }
02333                                                 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
02334                                                 if ( anim == BOTH_BUTTERFLY_LEFT )
02335                                                 {
02336                                                         pm->ps->weaponTime = pm->ps->torsoTimer;
02337                                                 }
02338                                                 PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
02339                                                 pm->ps->pm_flags |= PMF_JUMP_HELD;
02340                                                 pm->cmd.upmove = 0;
02341                                                 pm->ps->fd.forceJumpSound = 1;
02342                                         }
02343                                 }
02344                         }
02345                 }
02346                 else 
02347                 {//in the air
02348                         int legsAnim = pm->ps->legsAnim;
02349 
02350                         if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT )
02351                         {//running on a wall
02352                                 vec3_t right, traceto, mins, maxs, fwdAngles;
02353                                 trace_t trace;
02354                                 int             anim = -1;
02355 
02356                                 VectorSet(mins, pm->mins[0], pm->mins[0], 0);
02357                                 VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24);
02358                                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
02359 
02360                                 AngleVectors( fwdAngles, NULL, right, NULL );
02361 
02362                                 if ( legsAnim == BOTH_WALL_RUN_LEFT )
02363                                 {
02364                                         if ( pm->ps->legsTimer > 400 )
02365                                         {//not at the end of the anim
02366                                                 float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_LEFT );
02367                                                 if ( pm->ps->legsTimer < animLen - 400 )
02368                                                 {//not at start of anim
02369                                                         VectorMA( pm->ps->origin, -16, right, traceto );
02370                                                         anim = BOTH_WALL_RUN_LEFT_FLIP;
02371                                                 }
02372                                         }
02373                                 }
02374                                 else if ( legsAnim == BOTH_WALL_RUN_RIGHT )
02375                                 {
02376                                         if ( pm->ps->legsTimer > 400 )
02377                                         {//not at the end of the anim
02378                                                 float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_RIGHT );
02379                                                 if ( pm->ps->legsTimer < animLen - 400 )
02380                                                 {//not at start of anim
02381                                                         VectorMA( pm->ps->origin, 16, right, traceto );
02382                                                         anim = BOTH_WALL_RUN_RIGHT_FLIP;
02383                                                 }
02384                                         }
02385                                 }
02386                                 if ( anim != -1 )
02387                                 {
02388                                         pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
02389                                         if ( trace.fraction < 1.0f )
02390                                         {//flip off wall
02391                                                 int parts = 0;
02392 
02393                                                 if ( anim == BOTH_WALL_RUN_LEFT_FLIP )
02394                                                 {
02395                                                         pm->ps->velocity[0] *= 0.5f;
02396                                                         pm->ps->velocity[1] *= 0.5f;
02397                                                         VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
02398                                                 }
02399                                                 else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP )
02400                                                 {
02401                                                         pm->ps->velocity[0] *= 0.5f;
02402                                                         pm->ps->velocity[1] *= 0.5f;
02403                                                         VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
02404                                                 }
02405                                                 parts = SETANIM_LEGS;
02406                                                 if ( !pm->ps->weaponTime )
02407                                                 {
02408                                                         parts = SETANIM_BOTH;
02409                                                 }
02410                                                 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
02411                                                 pm->cmd.upmove = 0;
02412                                         }
02413                                 }
02414                                 if ( pm->cmd.upmove != 0 )
02415                                 {//jump failed, so don't try to do normal jump code, just return
02416                                         return qfalse;
02417                                 }
02418                         }
02419                         //NEW JKA
02420                         else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
02421                         {
02422                                 vec3_t fwd, traceto, mins, maxs, fwdAngles;
02423                                 trace_t trace;
02424                                 int             anim = -1;
02425                                 float animLen;
02426 
02427                                 VectorSet(mins, pm->mins[0], pm->mins[0], 0.0f);
02428                                 VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24.0f);
02429                                 //hmm, did you mean [1] and [1]?
02430                                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
02431                                 AngleVectors( fwdAngles, fwd, NULL, NULL );
02432 
02433                                 assert(pm_entSelf); //null pm_entSelf would be a Bad Thing<tm>
02434                                 animLen = BG_AnimLength( pm_entSelf->localAnimIndex, BOTH_FORCEWALLRUNFLIP_START );
02435                                 if ( pm->ps->legsTimer < animLen - 400 )
02436                                 {//not at start of anim
02437                                         VectorMA( pm->ps->origin, 16, fwd, traceto );
02438                                         anim = BOTH_FORCEWALLRUNFLIP_END;
02439                                 }
02440                                 if ( anim != -1 )
02441                                 {
02442                                         pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
02443                                         if ( trace.fraction < 1.0f )
02444                                         {//flip off wall
02445                                                 int parts = SETANIM_LEGS;
02446 
02447                                                 pm->ps->velocity[0] *= 0.5f;
02448                                                 pm->ps->velocity[1] *= 0.5f;
02449                                                 VectorMA( pm->ps->velocity, -300, fwd, pm->ps->velocity );
02450                                                 pm->ps->velocity[2] += 200;
02451                                                 if ( !pm->ps->weaponTime )
02452                                                 {//not attacking, set anim on both
02453                                                         parts = SETANIM_BOTH;
02454                                                 }
02455                                                 PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
02456                                                 //FIXME: do damage to traceEnt, like above?
02457                                                 //pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
02458                                                 //ha ha, so silly with your silly jumpy fally flags.
02459                                                 pm->cmd.upmove = 0;
02460                                                 PM_AddEvent( EV_JUMP );
02461                                         }
02462                                 }
02463                                 if ( pm->cmd.upmove != 0 )
02464                                 {//jump failed, so don't try to do normal jump code, just return
02465                                         return qfalse;
02466                                 }
02467                         }
02468                         /*
02469                         else if ( pm->cmd.forwardmove > 0 //pushing forward
02470                                 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
02471                                 && pm->ps->velocity[2] > 200
02472                                 && PM_GroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
02473                                 && !BG_InSpecialJump(pm->ps->legsAnim))
02474                         {//run up wall, flip backwards
02475                                 vec3_t fwd, traceto, mins, maxs, fwdAngles;
02476                                 trace_t trace;
02477                                 vec3_t  idealNormal;
02478 
02479                                 VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]);
02480                                 VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->maxs[2]);
02481                                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
02482 
02483                                 AngleVectors( fwdAngles, fwd, NULL, NULL );
02484                                 VectorMA( pm->ps->origin, 32, fwd, traceto );
02485 
02486                                 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, MASK_PLAYERSOLID );//FIXME: clip brushes too?
02487                                 VectorSubtract( pm->ps->origin, traceto, idealNormal );
02488                                 VectorNormalize( idealNormal );
02489                                 
02490                                 if ( trace.fraction < 1.0f )
02491                                 {//there is a wall there
02492                                         int parts = SETANIM_LEGS;
02493 
02494                                         pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
02495                                         VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
02496                                         pm->ps->velocity[2] += 128;
02497 
02498                                         if ( !pm->ps->weaponTime )
02499                                         {
02500                                                 parts = SETANIM_BOTH;
02501                                         }
02502                                         PM_SetAnim( parts, BOTH_WALL_FLIP_BACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
02503 
02504                                         pm->ps->legsTimer -= 600; //I force this anim to play to the end to prevent landing on your head and suddenly flipping over.
02505                                                                                           //It is a bit too long at the end though, so I'll just shorten it.
02506 
02507                                         PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
02508                                         pm->cmd.upmove = 0;
02509                                         pm->ps->fd.forceJumpSound = 1;
02510                                         BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
02511 
02512                                         if (trace.entityNum < MAX_CLIENTS)
02513                                         {
02514                                                 pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
02515                                         }
02516                                 }
02517                         }
02518                         */
02519                         else if ( pm->cmd.forwardmove > 0 //pushing forward
02520                                 && pm->ps->fd.forceRageRecoveryTime < pm->cmd.serverTime        //not in a force Rage recovery period
02521                                 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 
02522                                 && PM_WalkableGroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
02523                                 && (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
02524                         {//run up wall, flip backwards
02525                                 if ( allowWallRuns )
02526                                 {
02527                                         //FIXME: have to be moving... make sure it's opposite the wall... or at least forward?
02528                                         int wallWalkAnim = BOTH_WALL_FLIP_BACK1;
02529                                         int parts = SETANIM_LEGS;
02530                                         int contents = MASK_SOLID;//MASK_PLAYERSOLID;//CONTENTS_SOLID;
02531                                         //qboolean kick = qtrue;
02532                                         if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
02533                                         {
02534                                                 wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START;
02535                                                 parts = SETANIM_BOTH;
02536                                                 //kick = qfalse;
02537                                         }
02538                                         else
02539                                         {
02540                                                 if ( !pm->ps->weaponTime )
02541                                                 {
02542                                                         parts = SETANIM_BOTH;
02543                                                 }
02544                                         }
02545                                         //if ( PM_HasAnimation( pm->gent, wallWalkAnim ) )
02546                                         if (1) //sure, we have it! Because I SAID SO.
02547                                         {
02548                                                 vec3_t fwd, traceto, mins, maxs, fwdAngles;
02549                                                 trace_t trace;
02550                                                 vec3_t  idealNormal;
02551                                                 bgEntity_t *traceEnt;
02552 
02553                                                 VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
02554                                                 VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
02555                                                 VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
02556 
02557                                                 AngleVectors( fwdAngles, fwd, NULL, NULL );
02558                                                 VectorMA( pm->ps->origin, 32, fwd, traceto );
02559 
02560                                                 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too?
02561                                                 VectorSubtract( pm->ps->origin, traceto, idealNormal );
02562                                                 VectorNormalize( idealNormal );
02563                                                 traceEnt = PM_BGEntForNum(trace.entityNum);
02564                                                 
02565                                                 if ( trace.fraction < 1.0f
02566                                                         &&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
02567                                                 {//there is a wall there
02568                                                         pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
02569                                                         if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START )
02570                                                         {
02571                                                                 pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f;
02572                                                         }
02573                                                         else
02574                                                         {
02575                                                                 VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
02576                                                                 pm->ps->velocity[2] += 150.0f;
02577                                                         }
02578                                                         //animate me
02579                                                         PM_SetAnim( parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
02580         //                                              pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
02581                                                         //again with the flags!
02582                                                         //G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
02583                                                         //yucky!
02584                                                         PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
02585                                                         pm->cmd.upmove = 0;
02586                                                         pm->ps->fd.forceJumpSound = 1;
02587                                                         BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
02588 
02589                                                         //kick if jumping off an ent
02590                                                         /*
02591                                                         if ( kick && traceEnt && (traceEnt->s.eType == ET_PLAYER || traceEnt->s.eType == ET_NPC) )
02592                                                         { //kick that thang!
02593                                                                 pm->ps->forceKickFlip = traceEnt->s.number+1;
02594                                                         }
02595                                                         */
02596                                                         pm->cmd.rightmove = pm->cmd.forwardmove= 0;
02597                                                 }
02598                                         }
02599                                 }
02600                         }
02601                         else if ( (!BG_InSpecialJump( legsAnim )//not in a special jump anim 
02602                                                 ||BG_InReboundJump( legsAnim )//we're already in a rebound
02603                                                 ||BG_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you)
02604                                         //&& pm->ps->velocity[2] <= 0 
02605                                         && pm->ps->velocity[2] > -1200 //not falling down very fast
02606                                         && !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press
02607                                         && (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction
02608                                         //&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
02609                                         && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better
02610                                         //&& WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 10 )//have enough force power to do another one
02611                                         && BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION)
02612                                         && (pm->ps->origin[2]-pm->ps->fd.forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(BG_ForceWallJumpStrength()/2.0f)) //can fit at least one more wall jump in (yes, using "magic numbers"... for now)
02613                                         //&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
02614                                         )
02615                         {//see if we're pushing at a wall and jump off it if so
02616                                 if ( allowWallGrabs )
02617                                 {
02618                                         //FIXME: make sure we have enough force power
02619                                         //FIXME: check  to see if we can go any higher
02620                                         //FIXME: limit to a certain number of these in a row?
02621                                         //FIXME: maybe don't require a ucmd direction, just check all 4?
02622                                         //FIXME: should stick to the wall for a second, then push off...
02623                                         vec3_t checkDir, traceto, mins, maxs, fwdAngles;
02624                                         trace_t trace;
02625                                         vec3_t  idealNormal;
02626                                         int             anim = -1;
02627 
02628                                         VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
02629                                         VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
02630                                         VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
02631 
02632                                         if ( pm->cmd.rightmove )
02633                                         {
02634                                                 if ( pm->cmd.rightmove > 0 )
02635                                                 {
02636                                                         anim = BOTH_FORCEWALLREBOUND_RIGHT;
02637                                                         AngleVectors( fwdAngles, NULL, checkDir, NULL );
02638                                                 }
02639                                                 else if ( pm->cmd.rightmove < 0 )
02640                                                 {
02641                                                         anim = BOTH_FORCEWALLREBOUND_LEFT;
02642                                                         AngleVectors( fwdAngles, NULL, checkDir, NULL );
02643                                                         VectorScale( checkDir, -1, checkDir );
02644                                                 }
02645                                         }
02646                                         else if ( pm->cmd.forwardmove > 0 )
02647                                         {
02648                                                 anim = BOTH_FORCEWALLREBOUND_FORWARD;
02649                                                 AngleVectors( fwdAngles, checkDir, NULL, NULL );
02650                                         }
02651                                         else if ( pm->cmd.forwardmove < 0 )
02652                                         {
02653                                                 anim = BOTH_FORCEWALLREBOUND_BACK;
02654                                                 AngleVectors( fwdAngles, checkDir, NULL, NULL );
02655                                                 VectorScale( checkDir, -1, checkDir );
02656                                         }
02657                                         if ( anim != -1 )
02658                                         {//trace in the dir we're pushing in and see if there's a vertical wall there
02659                                                 bgEntity_t *traceEnt;
02660 
02661                                                 VectorMA( pm->ps->origin, 8, checkDir, traceto );
02662                                                 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );//FIXME: clip brushes too?
02663                                                 VectorSubtract( pm->ps->origin, traceto, idealNormal );
02664                                                 VectorNormalize( idealNormal );
02665                                                 traceEnt = PM_BGEntForNum(trace.entityNum);
02666                                                 if ( trace.fraction < 1.0f
02667                                                         &&fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/
02668                                                         &&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
02669                                                 {//there is a wall there
02670                                                         float dot = DotProduct( pm->ps->velocity, trace.plane.normal );
02671                                                         if ( dot < 1.0f )
02672                                                         {//can't be heading *away* from the wall!
02673                                                                 //grab it!
02674                                                                 PM_GrabWallForJump( anim );
02675                                                         }
02676                                                 }
02677                                         }
02678                                 }
02679                         }
02680                         else
02681                         {
02682                                 //FIXME: if in a butterfly, kick people away?
02683                         }
02684                         //END NEW JKA
02685                 }
02686         }
02687 
02688         /*
02689         if ( pm->cmd.upmove > 0 
02690                 && (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
02691                 && !PM_IsRocketTrooper()
02692                 && (pm->ps->weaponTime > 0||pm->cmd.buttons&BUTTON_ATTACK) )
02693         {//okay, we just jumped and we're in an attack
02694                 if ( !BG_InRoll( pm->ps, pm->ps->legsAnim )
02695                         && !PM_InKnockDown( pm->ps )
02696                         && !BG_InDeathAnim(pm->ps->legsAnim)
02697                         && !BG_FlippingAnim( pm->ps->legsAnim )
02698                         && !PM_SpinningAnim( pm->ps->legsAnim )
02699                         && !BG_SaberInSpecialAttack( pm->ps->torsoAnim )
02700                         && ( BG_SaberInAttack( pm->ps->saberMove ) ) )
02701                 {//not in an anim we shouldn't interrupt
02702                         //see if it's not too late to start a special jump-attack
02703                         float animLength = PM_AnimLength( 0, (animNumber_t)pm->ps->torsoAnim );
02704                         if ( animLength - pm->ps->torsoTimer < 500 )
02705                         {//just started the saberMove
02706                                 //check for special-case jump attacks
02707                                 if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_2 )
02708                                 {//using medium attacks
02709                                         if (PM_GroundDistance() < 32 &&
02710                                                 !BG_InSpecialJump(pm->ps->legsAnim))
02711                                         { //FLIP AND DOWNWARD ATTACK
02712                                                 //trace_t tr;
02713 
02714                                                 //if (PM_SomeoneInFront(&tr))
02715                                                 {
02716                                                         PM_SetSaberMove(PM_SaberFlipOverAttackMove());
02717                                                         pml.groundPlane = qfalse;
02718                                                         pml.walking = qfalse;
02719                                                         pm->ps->pm_flags |= PMF_JUMP_HELD;
02720                                                         pm->ps->groundEntityNum = ENTITYNUM_NONE;
02721                                                         VectorClear(pml.groundTrace.plane.normal);
02722 
02723                                                         pm->ps->weaponTime = pm->ps->torsoTimer;
02724                                                 }
02725                                         }
02726                                 }
02727                                 else if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_3 )
02728                                 {//using strong attacks
02729                                         if ( pm->cmd.forwardmove > 0 && //going forward
02730                                                 (pm->cmd.buttons & BUTTON_ATTACK) && //must be holding attack still
02731                                                 PM_GroundDistance() < 32 &&
02732                                                 !BG_InSpecialJump(pm->ps->legsAnim))
02733                                         {//strong attack: jump-hack
02734                                                 PM_SetSaberMove( PM_SaberJumpAttackMove() );
02735                                                 pml.groundPlane = qfalse;
02736                                                 pml.walking = qfalse;
02737                                                 pm->ps->pm_flags |= PMF_JUMP_HELD;
02738                                                 pm->ps->groundEntityNum = ENTITYNUM_NONE;
02739                                                 VectorClear(pml.groundTrace.plane.normal);
02740 
02741                                                 pm->ps->weaponTime = pm->ps->torsoTimer;
02742                                         }
02743                                 }
02744                         }
02745                 }
02746         }
02747         */
02748         if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
02749         {
02750                 return qfalse;
02751         }
02752         if ( pm->cmd.upmove > 0 )
02753         {//no special jumps
02754                 pm->ps->velocity[2] = JUMP_VELOCITY;
02755                 PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
02756                 pm->ps->pm_flags |= PMF_JUMP_HELD;
02757         }
02758 
02759         //Jumping
02760         pml.groundPlane = qfalse;
02761         pml.walking = qfalse;
02762         pm->ps->pm_flags |= PMF_JUMP_HELD;
02763         pm->ps->groundEntityNum = ENTITYNUM_NONE;
02764         PM_SetForceJumpZStart(pm->ps->origin[2]);
02765 
02766         PM_AddEvent( EV_JUMP );
02767 
02768         //Set the animations
02769         if ( pm->ps->gravity > 0 && !BG_InSpecialJump( pm->ps->legsAnim ) )
02770         {
02771                 PM_JumpForDir();
02772         }
02773 
02774         return qtrue;
02775 }
02776 /*
02777 =============
02778 PM_CheckWaterJump
02779 =============
02780 */
02781 static qboolean PM_CheckWaterJump( void ) {
02782         vec3_t  spot;
02783         int             cont;
02784         vec3_t  flatforward;
02785 
02786         if (pm->ps->pm_time) {
02787                 return qfalse;
02788         }
02789 
02790         // check for water jump
02791         if ( pm->waterlevel != 2 ) {
02792                 return qfalse;
02793         }
02794 
02795         flatforward[0] = pml.forward[0];
02796         flatforward[1] = pml.forward[1];
02797         flatforward[2] = 0;
02798         VectorNormalize (flatforward);
02799 
02800         VectorMA (pm->ps->origin, 30, flatforward, spot);
02801         spot[2] += 4;
02802         cont = pm->pointcontents (spot, pm->ps->clientNum );
02803         if ( !(cont & CONTENTS_SOLID) ) {
02804                 return qfalse;
02805         }
02806 
02807         spot[2] += 16;
02808         cont = pm->pointcontents (spot, pm->ps->clientNum );
02809         if ( cont ) {
02810                 return qfalse;
02811         }
02812 
02813         // jump out of water
02814         VectorScale (pml.forward, 200, pm->ps->velocity);
02815         pm->ps->velocity[2] = 350;
02816 
02817         pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
02818         pm->ps->pm_time = 2000;
02819 
02820         return qtrue;
02821 }
02822 
02823 //============================================================================
02824 
02825 
02826 /*
02827 ===================
02828 PM_WaterJumpMove
02829 
02830 Flying out of the water
02831 ===================
02832 */
02833 static void PM_WaterJumpMove( void ) {
02834         // waterjump has no control, but falls
02835 
02836         PM_StepSlideMove( qtrue );
02837 
02838         pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
02839         if (pm->ps->velocity[2] < 0) {
02840                 // cancel as soon as we are falling down again
02841                 pm->ps->pm_flags &= ~PMF_ALL_TIMES;
02842                 pm->ps->pm_time = 0;
02843         }
02844 }
02845 
02846 /*
02847 ===================
02848 PM_WaterMove
02849 
02850 ===================
02851 */
02852 static void PM_WaterMove( void ) {
02853         int             i;
02854         vec3_t  wishvel;
02855         float   wishspeed;
02856         vec3_t  wishdir;
02857         float   scale;
02858         float   vel;
02859 
02860         if ( PM_CheckWaterJump() ) {
02861                 PM_WaterJumpMove();
02862                 return;
02863         }
02864 #if 0
02865         // jump = head for surface
02866         if ( pm->cmd.upmove >= 10 ) {
02867                 if (pm->ps->velocity[2] > -300) {
02868                         if ( pm->watertype == CONTENTS_WATER ) {
02869                                 pm->ps->velocity[2] = 100;
02870                         } else if (pm->watertype == CONTENTS_SLIME) {
02871                                 pm->ps->velocity[2] = 80;
02872                         } else {
02873                                 pm->ps->velocity[2] = 50;
02874                         }
02875                 }
02876         }
02877 #endif
02878         PM_Friction ();
02879 
02880         scale = PM_CmdScale( &pm->cmd );
02881         //
02882         // user intentions
02883         //
02884         if ( !scale ) {
02885                 wishvel[0] = 0;
02886                 wishvel[1] = 0;
02887                 wishvel[2] = -60;               // sink towards bottom
02888         } else {
02889                 for (i=0 ; i<3 ; i++)
02890                         wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
02891 
02892                 wishvel[2] += scale * pm->cmd.upmove;
02893         }
02894 
02895         VectorCopy (wishvel, wishdir);
02896         wishspeed = VectorNormalize(wishdir);
02897 
02898         if ( wishspeed > pm->ps->speed * pm_swimScale ) {
02899                 wishspeed = pm->ps->speed * pm_swimScale;
02900         }
02901 
02902         PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
02903 
02904         // make sure we can go up slopes easily under water
02905         if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
02906                 vel = VectorLength(pm->ps->velocity);
02907                 // slide along the ground plane
02908                 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
02909                         pm->ps->velocity, OVERCLIP );
02910 
02911                 VectorNormalize(pm->ps->velocity);
02912                 VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
02913         }
02914 
02915         PM_SlideMove( qfalse );
02916 }
02917 
02918 /*
02919 ===================
02920 PM_FlyVehicleMove
02921 
02922 ===================
02923 */
02924 static void PM_FlyVehicleMove( void ) 
02925 {
02926         int             i;
02927         vec3_t  wishvel;
02928         float   wishspeed;
02929         vec3_t  wishdir;
02930         float   scale;
02931         float   zVel;
02932         float   fmove = 0.0f, smove = 0.0f;
02933 
02934         // We don't use these here because we pre-calculate the movedir in the vehicle update anyways, and if
02935         // you leave this, you get strange motion during boarding (the player can move the vehicle).
02936         //fmove = pm->cmd.forwardmove;
02937         //smove = pm->cmd.rightmove;
02938 
02939         // normal slowdown
02940         if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE )
02941         {//falling
02942                 zVel = pm->ps->velocity[2];
02943                 PM_Friction ();
02944                 pm->ps->velocity[2] = zVel;
02945         }
02946         else
02947         {
02948                 PM_Friction ();
02949                 if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
02950                 {
02951                         pm->ps->velocity[2] = 0;        // ignore slope movement
02952                 }
02953         }
02954 
02955         scale = PM_CmdScale( &pm->cmd );
02956 
02957         // Get The WishVel And WishSpeed
02958         //-------------------------------   
02959         if ( pm->ps->clientNum >= MAX_CLIENTS )
02960         {//NPC
02961                 
02962                 // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
02963                 //--------------------------------------------------------------------------------------------
02964                 if ((fmove!=0.0f || smove!=0.0f) &&     VectorCompare(pm->ps->moveDir, vec3_origin))
02965                 {
02966                         //gi.Printf("Generating MoveDir\n");
02967                         for ( i = 0 ; i < 3 ; i++ ) 
02968                         {
02969                                 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
02970                         }
02971 
02972                         VectorCopy( wishvel, wishdir );
02973                         wishspeed = VectorNormalize(wishdir);
02974                         wishspeed *= scale;
02975                 }
02976                 // Otherwise, Use The Move Dir
02977                 //-----------------------------
02978                 else
02979                 {
02980                         wishspeed = pm->ps->speed;
02981                         VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
02982                         VectorCopy( pm->ps->moveDir, wishdir );
02983                 }
02984         }
02985         else
02986         {
02987                 for ( i = 0 ; i < 3 ; i++ ) {
02988                         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
02989                 }
02990                 // when going up or down slopes the wish velocity should Not be zero
02991         //      wishvel[2] = 0;
02992 
02993                 VectorCopy (wishvel, wishdir);
02994                 wishspeed = VectorNormalize(wishdir);
02995                 wishspeed *= scale;
02996         }
02997 
02998         // Handle negative speed.
02999         if ( wishspeed < 0 )
03000         {
03001                 wishspeed = wishspeed * -1.0f;
03002                 VectorScale( wishvel, -1.0f, wishvel );
03003                 VectorScale( wishdir, -1.0f, wishdir );
03004         }
03005 
03006         VectorCopy( wishvel, wishdir );
03007         wishspeed = VectorNormalize( wishdir );
03008 
03009         PM_Accelerate( wishdir, wishspeed, 100 );
03010 
03011         PM_StepSlideMove( 1 );
03012 }
03013 
03014 /*
03015 ===================
03016 PM_FlyMove
03017 
03018 Only with the flight powerup
03019 ===================
03020 */
03021 static void PM_FlyMove( void ) {
03022         int             i;
03023         vec3_t  wishvel;
03024         float   wishspeed;
03025         vec3_t  wishdir;
03026         float   scale;
03027 
03028         // normal slowdown
03029         PM_Friction ();
03030 
03031         scale = PM_CmdScale( &pm->cmd );
03032         
03033         if ( pm->ps->pm_type == PM_SPECTATOR && pm->cmd.buttons & BUTTON_ALT_ATTACK) {
03034                 //turbo boost
03035                 scale *= 10;
03036         }
03037 
03038         //
03039         // user intentions
03040         //
03041         if ( !scale ) {
03042                 wishvel[0] = 0;
03043                 wishvel[1] = 0;
03044                 wishvel[2] = pm->ps->speed * (pm->cmd.upmove/127.0f);
03045         } else {
03046                 for (i=0 ; i<3 ; i++) {
03047                         wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
03048                 }
03049 
03050                 wishvel[2] += scale * pm->cmd.upmove;
03051         }
03052 
03053         VectorCopy (wishvel, wishdir);
03054         wishspeed = VectorNormalize(wishdir);
03055 
03056         PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
03057 
03058         PM_StepSlideMove( qfalse );
03059 }
03060 
03061 
03062 /*
03063 ===================
03064 PM_AirMove
03065 
03066 ===================
03067 */
03068 static void PM_AirMove( void ) {
03069         int                     i;
03070         vec3_t          wishvel;
03071         float           fmove, smove;
03072         vec3_t          wishdir;
03073         float           wishspeed;
03074         float           scale;
03075         float           accelerate;
03076         usercmd_t       cmd;
03077         Vehicle_t       *pVeh = NULL;
03078 
03079         if (pm->ps->clientNum >= MAX_CLIENTS)
03080         {
03081                 bgEntity_t      *pEnt = pm_entSelf;
03082 
03083                 if ( pEnt && pEnt->s.NPC_class == CLASS_VEHICLE )
03084                 {
03085                         pVeh = pEnt->m_pVehicle;
03086                 }
03087         }
03088 
03089         if (pm->ps->pm_type != PM_SPECTATOR)
03090         {
03091 #if METROID_JUMP
03092                 PM_CheckJump();
03093 #else
03094                 if (pm->ps->fd.forceJumpZStart &&
03095                         pm->ps->forceJumpFlip)
03096                 {
03097                         PM_CheckJump();
03098                 }
03099 #endif
03100         }
03101         PM_Friction();
03102 
03103         fmove = pm->cmd.forwardmove;
03104         smove = pm->cmd.rightmove;
03105 
03106         cmd = pm->cmd;
03107         scale = PM_CmdScale( &cmd );
03108 
03109         // set the movementDir so clients can rotate the legs for strafing
03110         PM_SetMovementDir();
03111 
03112         // project moves down to flat plane
03113         pml.forward[2] = 0;
03114         pml.right[2] = 0;
03115         VectorNormalize (pml.forward);
03116         VectorNormalize (pml.right);
03117 
03118         if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 )
03119         {//in a hovering vehicle, have air control
03120                 if ( 1 )
03121                 {
03122                         wishspeed = pm->ps->speed;
03123                         VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
03124                         VectorCopy( pm->ps->moveDir, wishdir );
03125                         scale = 1.0f;
03126                 }
03127 #if 0
03128                 else
03129                 {
03130                         float controlMod = 1.0f;
03131                         if ( pml.groundPlane )
03132                         {//on a slope of some kind, shouldn't have much control and should slide a lot
03133                                 controlMod = pml.groundTrace.plane.normal[2];
03134                         }
03135 
03136                         vec3_t  vfwd, vrt;
03137                         vec3_t  vAngles;
03138 
03139                         VectorCopy( pVeh->m_vOrientation, vAngles );
03140                         vAngles[ROLL] = 0;//since we're a hovercraft, we really don't want to stafe up into the air if we're banking
03141                         AngleVectors( vAngles, vfwd, vrt, NULL );
03142 
03143                         float speed = pm->ps->speed;
03144                         float strafeSpeed = 0;
03145 
03146                         if ( fmove < 0 )
03147                         {//going backwards
03148                                 if ( speed < 0 )
03149                                 {//speed is negative, but since our command is reverse, make speed positive
03150                                         speed = fabs( speed );
03151                                         /*
03152                                         if ( pml.groundPlane )
03153                                         {//on a slope, still have some control
03154                                                 speed = fabs( speed );
03155                                         }
03156                                         else
03157                                         {//can't reverse in air
03158                                                 speed = 0;
03159                                         }
03160                                         */
03161                                 }
03162                                 else if ( speed > 0 )
03163                                 {//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually)
03164                                         speed = 0;
03165                                 }
03166                         }
03167 
03168                         if ( pm->ps->clientNum < MAX_CLIENTS )
03169                         {//do normal adding to wishvel
03170                                 VectorScale( vfwd, speed*controlMod*(fmove/127.0f), wishvel );
03171                                 //just add strafing
03172                                 if ( pVeh->m_pVehicleInfo->strafePerc )
03173                                 {//we can strafe
03174                                         if ( smove )
03175                                         {//trying to strafe
03176                                                 float minSpeed = pVeh->m_pVehicleInfo->speedMax * 0.5f * pVeh->m_pVehicleInfo->strafePerc;
03177                                                 strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ))*pVeh->m_pVehicleInfo->strafePerc;
03178                                                 if ( strafeSpeed < minSpeed )
03179                                                 {
03180                                                         strafeSpeed = minSpeed;
03181                                                 }
03182                                                 strafeSpeed *= controlMod*((float)(smove))/127.0f;
03183                                                 if ( strafeSpeed < 0 )
03184                                                 {//pm_accelerate does not understand negative numbers
03185                                                         strafeSpeed *= -1;
03186                                                         VectorScale( vrt, -1, vrt );
03187                                                 }
03188                                                 //now just add it to actual velocity
03189                                                 PM_Accelerate( vrt, strafeSpeed, pVeh->m_pVehicleInfo->traction );
03190                                         }
03191                                 }
03192                         }
03193                         else
03194                         {
03195                                 if ( pVeh->m_pVehicleInfo->strafePerc )
03196                                 {//we can strafe
03197                                         if ( pm->ps->clientNum )
03198                                         {//alternate control scheme: can strafe
03199                                                 if ( smove )
03200                                                 {
03201                                                         /*
03202                                                         if ( fmove > 0 )
03203                                                         {//actively accelerating
03204                                                                 strafeSpeed = pm->ps->speed;
03205                                                         }
03206                                                         else
03207                                                         {//not stepping on accelerator, only strafe based on magnitude of current forward velocity
03208                                                                 strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ));
03209                                                         }
03210                                                         */
03211                                                         strafeSpeed = ((float)(smove))/127.0f;
03212                                                 }
03213                                         }
03214                                 }
03215                                 //strafing takes away from forward speed
03216                                 VectorScale( vfwd, (fmove/127.0f)*(1.0f-pVeh->m_pVehicleInfo->strafePerc), wishvel );
03217                                 if ( strafeSpeed )
03218                                 {
03219                                         VectorMA( wishvel, strafeSpeed*pVeh->m_pVehicleInfo->strafePerc, vrt, wishvel );
03220                                 }
03221                                 VectorNormalize( wishvel );
03222                                 VectorScale( wishvel, speed*controlMod, wishvel );
03223                         }
03224                 }
03225 #endif
03226         }
03227         else if ( gPMDoSlowFall )
03228         {//no air-control
03229                 VectorClear( wishvel );
03230         }
03231         else if (pm->ps->pm_type == PM_JETPACK)
03232         { //reduced air control while not jetting
03233                 for ( i = 0 ; i < 2 ; i++ )
03234                 {
03235                         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
03236                 }
03237                 wishvel[2] = 0;
03238 
03239                 if (pm->cmd.upmove <= 0)
03240                 {
03241             VectorScale(wishvel, 0.8f, wishvel);
03242                 }
03243                 else
03244                 { //if we are jetting then we have more control than usual
03245             VectorScale(wishvel, 2.0f, wishvel);
03246                 }
03247         }
03248         else
03249         {
03250                 for ( i = 0 ; i < 2 ; i++ )
03251                 {
03252                         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
03253                 }
03254                 wishvel[2] = 0;
03255         }
03256 
03257         VectorCopy (wishvel, wishdir);
03258         wishspeed = VectorNormalize(wishdir);
03259         wishspeed *= scale;
03260 
03261         accelerate = pm_airaccelerate;
03262         if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER )
03263         {//speeders have more control in air
03264                 //in mid-air
03265                 accelerate = pVeh->m_pVehicleInfo->traction;
03266                 if ( pml.groundPlane )
03267                 {//on a slope of some kind, shouldn't have much control and should slide a lot
03268                         accelerate *= 0.5f;
03269                 }
03270         }
03271         // not on ground, so little effect on velocity
03272         PM_Accelerate (wishdir, wishspeed, accelerate);
03273 
03274         // we may have a ground plane that is very steep, even
03275         // though we don't have a groundentity
03276         // slide along the steep plane
03277         if ( pml.groundPlane ) 
03278         {
03279                 if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
03280                 {//don't slide when stuck to a wall
03281                         if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) )
03282                         {
03283                                 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
03284                                         pm->ps->velocity, OVERCLIP );
03285                         }
03286                 }
03287         }
03288 
03289         if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
03290         {//no grav when stuck to wall
03291                 PM_StepSlideMove( qfalse );
03292         }
03293         else
03294         {
03295                 PM_StepSlideMove( qtrue );
03296         }
03297 }
03298 
03299 /*
03300 ===================
03301 PM_WalkMove
03302 
03303 ===================
03304 */
03305 static void PM_WalkMove( void ) {
03306         int                     i;
03307         vec3_t          wishvel;
03308         float           fmove, smove;
03309         vec3_t          wishdir;
03310         float           wishspeed = 0.0f;
03311         float           scale;
03312         usercmd_t       cmd;
03313         float           accelerate;
03314         float           vel;
03315         qboolean        npcMovement = qfalse;
03316 
03317         if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
03318                 // begin swimming
03319                 PM_WaterMove();
03320                 return;
03321         }
03322 
03323 
03324         if (pm->ps->pm_type != PM_SPECTATOR)
03325         {
03326                 if ( PM_CheckJump () ) {
03327                         // jumped away
03328                         if ( pm->waterlevel > 1 ) {
03329                                 PM_WaterMove();
03330                         } else {
03331                                 PM_AirMove();
03332                         }
03333                         return;
03334                 }
03335         }
03336 
03337         PM_Friction ();
03338 
03339         fmove = pm->cmd.forwardmove;
03340         smove = pm->cmd.rightmove;
03341 
03342         cmd = pm->cmd;
03343         scale = PM_CmdScale( &cmd );
03344 
03345         // set the movementDir so clients can rotate the legs for strafing
03346         PM_SetMovementDir();
03347 
03348         // project moves down to flat plane
03349         pml.forward[2] = 0;
03350         pml.right[2] = 0;
03351 
03352         // project the forward and right directions onto the ground plane
03353         PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
03354         PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
03355         //
03356         VectorNormalize (pml.forward);
03357         VectorNormalize (pml.right);
03358 
03359         // Get The WishVel And WishSpeed
03360         //-------------------------------  
03361         if ( pm->ps->clientNum >= MAX_CLIENTS && !VectorCompare( pm->ps->moveDir, vec3_origin ) )
03362         {//NPC
03363                 bgEntity_t *pEnt = pm_entSelf;
03364 
03365                 if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
03366                 {
03367                         // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
03368                         //--------------------------------------------------------------------------------------------
03369                         if ((fmove!=0.0f || smove!=0.0f) &&     VectorCompare(pm->ps->moveDir, vec3_origin))
03370                         {
03371                                 //gi.Printf("Generating MoveDir\n");
03372                                 for ( i = 0 ; i < 3 ; i++ ) 
03373                                 {
03374                                         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
03375                                 }
03376 
03377                                 VectorCopy( wishvel, wishdir );
03378                                 wishspeed = VectorNormalize(wishdir);
03379                                 wishspeed *= scale;
03380                         }
03381                         // Otherwise, Use The Move Dir
03382                         //-----------------------------
03383                         else
03384                         {
03385                                 //wishspeed = pm->ps->speed;
03386                                 VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
03387                                 VectorCopy (wishvel, wishdir);
03388                                 wishspeed = VectorNormalize(wishdir);
03389                         }
03390 
03391                         npcMovement = qtrue;
03392                 }
03393         }
03394 
03395         if (!npcMovement)
03396         {
03397                 for ( i = 0 ; i < 3 ; i++ ) {
03398                         wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
03399                 }
03400                 // when going up or down slopes the wish velocity should Not be zero
03401 
03402                 VectorCopy (wishvel, wishdir);
03403                 wishspeed = VectorNormalize(wishdir);
03404                 wishspeed *= scale;
03405         }
03406 
03407         // clamp the speed lower if ducking
03408         if ( pm->ps->pm_flags & PMF_DUCKED ) {
03409                 if ( wishspeed > pm->ps->speed * pm_duckScale ) {
03410                         wishspeed = pm->ps->speed * pm_duckScale;
03411                 }
03412         }
03413         else if ( (pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
03414                 !PM_InRollComplete(pm->ps, pm->ps->legsAnim))
03415         {
03416                 if ( wishspeed > pm->ps->speed * pm_duckScale ) {
03417                         wishspeed = pm->ps->speed * pm_duckScale;
03418                 }
03419         }
03420 
03421         // clamp the speed lower if wading or walking on the bottom
03422         if ( pm->waterlevel ) {
03423                 float   waterScale;
03424 
03425                 waterScale = pm->waterlevel / 3.0;
03426                 waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
03427                 if ( wishspeed > pm->ps->speed * waterScale ) {
03428                         wishspeed = pm->ps->speed * waterScale;
03429                 }
03430         }
03431 
03432         // when a player gets hit, they temporarily lose
03433         // full control, which allows them to be moved a bit
03434         if ( pm_flying == FLY_HOVER )
03435         {
03436                 accelerate = pm_vehicleaccelerate;
03437         }
03438         else if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
03439         {
03440                 accelerate = pm_airaccelerate;
03441         }
03442         else
03443         {
03444                 accelerate = pm_accelerate;
03445         }
03446 
03447         PM_Accelerate (wishdir, wishspeed, accelerate);
03448         /*
03449         if (pm->ps->clientNum >= MAX_CLIENTS)
03450         {
03451 #ifdef QAGAME
03452                 Com_Printf("^1S: %f, %f\n", wishspeed, pm->ps->speed);
03453 #else
03454                 Com_Printf("^2C: %f, %f\n", wishspeed, pm->ps->speed);
03455 #endif
03456         }
03457         */
03458 
03459         //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
03460         //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
03461 
03462         if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
03463         {
03464                 pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
03465         }
03466 
03467         vel = VectorLength(pm->ps->velocity);
03468 
03469         // slide along the ground plane
03470         PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
03471                 pm->ps->velocity, OVERCLIP );
03472 
03473         // don't decrease velocity when going up or down a slope
03474         VectorNormalize(pm->ps->velocity);
03475         VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
03476 
03477         // don't do anything if standing still
03478         if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
03479                 return;
03480         }
03481 
03482         PM_StepSlideMove( qfalse );
03483 
03484         //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
03485 }
03486 
03487 
03488 /*
03489 ==============
03490 PM_DeadMove
03491 ==============
03492 */
03493 static void PM_DeadMove( void ) {
03494         float   forward;
03495 
03496         if ( !pml.walking ) {
03497                 return;
03498         }
03499 
03500         // extra friction
03501 
03502         forward = VectorLength (pm->ps->velocity);
03503         forward -= 20;
03504         if ( forward <= 0 ) {
03505                 VectorClear (pm->ps->velocity);
03506         } else {
03507                 VectorNormalize (pm->ps->velocity);
03508                 VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
03509         }
03510 }
03511 
03512 
03513 /*
03514 ===============
03515 PM_NoclipMove
03516 ===============
03517 */
03518 static void PM_NoclipMove( void ) {
03519         float   speed, drop, friction, control, newspeed;
03520         int                     i;
03521         vec3_t          wishvel;
03522         float           fmove, smove;
03523         vec3_t          wishdir;
03524         float           wishspeed;
03525         float           scale;
03526 
03527         pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
03528 
03529         // friction
03530 
03531         speed = VectorLength (pm->ps->velocity);
03532         if (speed < 1)
03533         {
03534                 VectorCopy (vec3_origin, pm->ps->velocity);
03535         }
03536         else
03537         {
03538                 drop = 0;
03539 
03540                 friction = pm_friction*1.5;     // extra friction
03541                 control = speed < pm_stopspeed ? pm_stopspeed : speed;
03542                 drop += control*friction*pml.frametime;
03543 
03544                 // scale the velocity
03545                 newspeed = speed - drop;
03546                 if (newspeed < 0)
03547                         newspeed = 0;
03548                 newspeed /= speed;
03549 
03550                 VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
03551         }
03552 
03553         // accelerate
03554         scale = PM_CmdScale( &pm->cmd );
03555         if (pm->cmd.buttons & BUTTON_ATTACK) {  //turbo boost
03556                 scale *= 10;
03557         }
03558         if (pm->cmd.buttons & BUTTON_ALT_ATTACK) {      //turbo boost
03559                 scale *= 10;
03560         }
03561 
03562         fmove = pm->cmd.forwardmove;
03563         smove = pm->cmd.rightmove;
03564         
03565         for (i=0 ; i<3 ; i++)
03566                 wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
03567         wishvel[2] += pm->cmd.upmove;
03568 
03569         VectorCopy (wishvel, wishdir);
03570         wishspeed = VectorNormalize(wishdir);
03571         wishspeed *= scale;
03572 
03573         PM_Accelerate( wishdir, wishspeed, pm_accelerate );
03574 
03575         // move
03576         VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
03577 }
03578 
03579 //============================================================================
03580 
03581 /*
03582 ================
03583 PM_FootstepForSurface
03584 
03585 Returns an event number apropriate for the groundsurface
03586 ================
03587 */
03588 static int PM_FootstepForSurface( void )
03589 {
03590         if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) 
03591         {
03592                 return 0;
03593         }
03594         return ( pml.groundTrace.surfaceFlags & MATERIAL_MASK );
03595 }
03596 
03597 extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
03598 static int PM_TryRoll( void )
03599 {
03600         trace_t trace;
03601         int             anim = -1;
03602         vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
03603 
03604         if ( BG_SaberInAttack( pm->ps->saberMove ) || BG_SaberInSpecialAttack( pm->ps->torsoAnim ) 
03605                 || BG_SpinningSaberAnim( pm->ps->legsAnim ) 
03606                 || PM_SaberInStart( pm->ps->saberMove ) )
03607         {//attacking or spinning (or, if player, starting an attack)
03608                 if ( PM_CanRollFromSoulCal( pm->ps ) )
03609                 {//hehe
03610                 }
03611                 else
03612                 {
03613                         return 0;
03614                 }
03615         }
03616 
03617         if ((pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE) ||
03618                 PM_IsRocketTrooper() ||
03619                 BG_HasYsalamiri(pm->gametype, pm->ps) ||
03620                 !BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
03621         { //Not using saber, or can't use jump
03622                 return 0;
03623         }
03624 
03625         if ( pm->ps->weapon == WP_SABER )
03626         {
03627                 saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 );
03628                 if ( saber
03629                         && (saber->saberFlags&SFL_NO_ROLLS) )
03630                 {
03631                         return 0;
03632                 }
03633                 saber = BG_MySaber( pm->ps->clientNum, 1 );
03634                 if ( saber
03635                         && (saber->saberFlags&SFL_NO_ROLLS) )
03636                 {
03637                         return 0;
03638                 }
03639         }
03640 
03641         VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]+STEPSIZE);
03642         VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->ps->crouchheight);
03643 
03644         VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
03645 
03646         AngleVectors( fwdAngles, fwd, right, NULL );
03647 
03648         if ( pm->cmd.forwardmove )
03649         { //check forward/backward rolls
03650                 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) 
03651                 {
03652                         anim = BOTH_ROLL_B;
03653                         VectorMA( pm->ps->origin, -64, fwd, traceto );
03654                 }
03655                 else
03656                 {
03657                         anim = BOTH_ROLL_F;
03658                         VectorMA( pm->ps->origin, 64, fwd, traceto );
03659                 }
03660         }
03661         else if ( pm->cmd.rightmove > 0 )
03662         { //right
03663                 anim = BOTH_ROLL_R;
03664                 VectorMA( pm->ps->origin, 64, right, traceto );
03665         }
03666         else if ( pm->cmd.rightmove < 0 )
03667         { //left
03668                 anim = BOTH_ROLL_L;
03669                 VectorMA( pm->ps->origin, -64, right, traceto );
03670         }
03671 
03672         if ( anim != -1 )
03673         { //We want to roll. Perform a trace to see if we can, and if so, send us into one.
03674                 pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );
03675                 if ( trace.fraction >= 1.0f )
03676                 {
03677                         pm->ps->saberMove = LS_NONE;
03678                         return anim;
03679                 }
03680         }
03681         return 0;
03682 }
03683 
03684 #ifdef QAGAME
03685 static void PM_CrashLandEffect( void )
03686 {
03687         float delta;
03688         if ( pm->waterlevel )
03689         {
03690                 return;
03691         }
03692         delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );?
03693         if ( delta >= 30 ) 
03694         {
03695                 vec3_t bottom;
03696                 int     effectID = -1;
03697                 int material = (pml.groundTrace.surfaceFlags&MATERIAL_MASK);
03698                 VectorSet( bottom, pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1 );
03699                 switch ( material )
03700                 {
03701                 case MATERIAL_MUD:
03702                         effectID = EFFECT_LANDING_MUD;
03703                         break;
03704                 case MATERIAL_SAND:                     
03705                         effectID = EFFECT_LANDING_SAND;
03706                         break;
03707                 case MATERIAL_DIRT:
03708                         effectID = EFFECT_LANDING_DIRT;
03709                         break;
03710                 case MATERIAL_SNOW:                     
03711                         effectID = EFFECT_LANDING_SNOW;
03712                         break;
03713                 case MATERIAL_GRAVEL:
03714                         effectID = EFFECT_LANDING_GRAVEL;
03715                         break;
03716                 }
03717 
03718                 if ( effectID != -1 )
03719                 {
03720                         G_PlayEffect( effectID, bottom, pml.groundTrace.plane.normal );
03721                 }
03722         }
03723 }
03724 #endif
03725 /*
03726 =================
03727 PM_CrashLand
03728 
03729 Check for hard landings that generate sound events
03730 =================
03731 */
03732 static void PM_CrashLand( void ) {
03733         float           delta;
03734         float           dist;
03735         float           vel, acc;
03736         float           t;
03737         float           a, b, c, den;
03738         qboolean        didRoll = qfalse;
03739 
03740         // calculate the exact velocity on landing
03741         dist = pm->ps->origin[2] - pml.previous_origin[2];
03742         vel = pml.previous_velocity[2];
03743         acc = -pm->ps->gravity;
03744 
03745         a = acc / 2;
03746         b = vel;
03747         c = -dist;
03748 
03749         den =  b * b - 4 * a * c;
03750         if ( den < 0 ) {
03751                 pm->ps->inAirAnim = qfalse;
03752                 return;
03753         }
03754         t = (-b - sqrt( den ) ) / ( 2 * a );
03755 
03756         delta = vel + t * acc;
03757         delta = delta*delta * 0.0001;
03758 
03759 #ifdef QAGAME
03760         PM_CrashLandEffect();
03761 #endif
03762         // ducking while falling doubles damage
03763         if ( pm->ps->pm_flags & PMF_DUCKED ) {
03764                 delta *= 2;
03765         }
03766 
03767         if (pm->ps->legsAnim == BOTH_A7_KICK_F_AIR ||
03768                 pm->ps->legsAnim == BOTH_A7_KICK_B_AIR ||
03769                 pm->ps->legsAnim == BOTH_A7_KICK_R_AIR ||
03770                 pm->ps->legsAnim == BOTH_A7_KICK_L_AIR)
03771         {
03772                 int landAnim = -1;
03773                 switch ( pm->ps->legsAnim )
03774                 {
03775                 case BOTH_A7_KICK_F_AIR:
03776                         landAnim = BOTH_FORCELAND1;
03777                         break;
03778                 case BOTH_A7_KICK_B_AIR:
03779                         landAnim = BOTH_FORCELANDBACK1;
03780                         break;
03781                 case BOTH_A7_KICK_R_AIR:
03782                         landAnim = BOTH_FORCELANDRIGHT1;
03783                         break;
03784                 case BOTH_A7_KICK_L_AIR:
03785                         landAnim = BOTH_FORCELANDLEFT1;
03786                         break;
03787                 }
03788                 if ( landAnim != -1 )
03789                 {
03790                         if ( pm->ps->torsoAnim == pm->ps->legsAnim )
03791                         {
03792                                 PM_SetAnim(SETANIM_BOTH, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
03793                         }
03794                         else
03795                         {
03796                                 PM_SetAnim(SETANIM_LEGS, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
03797                         }
03798                 }
03799         }
03800         else if (pm->ps->legsAnim == BOTH_FORCEJUMPLEFT1 ||
03801                         pm->ps->legsAnim == BOTH_FORCEJUMPRIGHT1 ||
03802                         pm->ps->legsAnim == BOTH_FORCEJUMPBACK1 ||
03803                         pm->ps->legsAnim == BOTH_FORCEJUMP1)
03804         {
03805                 int fjAnim;
03806                 switch (pm->ps->legsAnim)
03807                 {
03808                 case BOTH_FORCEJUMPLEFT1:
03809                         fjAnim = BOTH_LANDLEFT1;
03810                         break;
03811                 case BOTH_FORCEJUMPRIGHT1:
03812                         fjAnim = BOTH_LANDRIGHT1;
03813                         break;
03814                 case BOTH_FORCEJUMPBACK1:
03815                         fjAnim = BOTH_LANDBACK1;
03816                         break;
03817                 default:
03818                         fjAnim = BOTH_LAND1;
03819                         break;
03820                 }
03821                 PM_SetAnim(SETANIM_BOTH, fjAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
03822         }
03823         // decide which landing animation to use
03824         else if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim && !pm->ps->m_iVehicleNum)
03825         { //only play a land animation if we transitioned into an in-air animation while off the ground
03826                 if (!BG_SaberInSpecial(pm->ps->saberMove))
03827                 {
03828                         if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
03829                                 PM_ForceLegsAnim( BOTH_LANDBACK1 );
03830                         } else {
03831                                 PM_ForceLegsAnim( BOTH_LAND1 );
03832                         }
03833                 }
03834         }
03835 
03836         if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
03837         { //saber handles its own anims
03838                 //This will push us back into our weaponready stance from the land anim.
03839                 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
03840                 {
03841                         PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
03842                 }
03843                 else
03844                 {
03845                         if (pm->ps->weapon == WP_EMPLACED_GUN)
03846                         {
03847                                 PM_StartTorsoAnim( BOTH_GUNSIT1 );
03848                         }
03849                         else
03850                         {
03851                                 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
03852                         }
03853                 }
03854         }
03855 
03856         if (!BG_InSpecialJump(pm->ps->legsAnim) ||
03857                 pm->ps->legsTimer < 1 ||
03858                 (pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ||
03859                 (pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT)
03860         { //Only set the timer if we're in an anim that can be interrupted (this would not be, say, a flip)
03861                 if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim)
03862                 {
03863                         if (!BG_SaberInSpecial(pm->ps->saberMove) || pm->ps->weapon != WP_SABER)
03864                         {
03865                                 if (pm->ps->legsAnim != BOTH_FORCELAND1 &&
03866                                         pm->ps->legsAnim != BOTH_FORCELANDBACK1 &&
03867                                         pm->ps->legsAnim != BOTH_FORCELANDRIGHT1 &&
03868                                         pm->ps->legsAnim != BOTH_FORCELANDLEFT1)
03869                                 { //don't override if we have started a force land
03870                                         pm->ps->legsTimer = TIMER_LAND;
03871                                 }
03872                         }
03873                 }
03874         }
03875 
03876         pm->ps->inAirAnim = qfalse;
03877 
03878         if (pm->ps->m_iVehicleNum)
03879         { //don't do fall stuff while on a vehicle
03880                 return;
03881         }
03882 
03883         // never take falling damage if completely underwater
03884         if ( pm->waterlevel == 3 ) {
03885                 return;
03886         }
03887 
03888         // reduce falling damage if there is standing water
03889         if ( pm->waterlevel == 2 ) {
03890                 delta *= 0.25;
03891         }
03892         if ( pm->waterlevel == 1 ) {
03893                 delta *= 0.5;
03894         }
03895 
03896         if ( delta < 1 ) {
03897                 return;
03898         }
03899 
03900         if ( pm->ps->pm_flags & PMF_DUCKED ) 
03901         {
03902                 if( delta >= 2 && !PM_InOnGroundAnim( pm->ps->legsAnim ) && !PM_InKnockDown( pm->ps ) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
03903                         pm->ps->forceHandExtend == HANDEXTEND_NONE )
03904                 {//roll!
03905                         int anim = PM_TryRoll();
03906 
03907                         if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
03908                         {
03909                                 anim = 0;
03910                                 pm->ps->legsTimer = 0;
03911                                 pm->ps->legsAnim = 0;
03912                                 PM_SetAnim(SETANIM_BOTH,BOTH_LAND1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
03913                                 pm->ps->legsTimer = TIMER_LAND;
03914                         }
03915 
03916                         if ( anim )
03917                         {//absorb some impact
03918                                 pm->ps->legsTimer = 0;
03919                                 delta /= 3; // /= 2 just cancels out the above delta *= 2 when landing while crouched, the roll itself should absorb a little damage
03920                                 pm->ps->legsAnim = 0;
03921                                 if (pm->ps->torsoAnim == BOTH_A7_SOULCAL)
03922                                 { //get out of it on torso
03923                                         pm->ps->torsoTimer = 0;
03924                                 }
03925                                 PM_SetAnim(SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
03926                                 didRoll = qtrue;
03927                         }
03928                 }
03929         }
03930 
03931         // SURF_NODAMAGE is used for bounce pads where you don't ever
03932         // want to take damage or play a crunch sound
03933         if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )  {
03934                 if (delta > 7)
03935                 {
03936                         int delta_send = (int)delta;
03937 
03938                         if (delta_send > 600)
03939                         { //will never need to know any value above this
03940                                 delta_send = 600;
03941                         }
03942 
03943                         if (pm->ps->fd.forceJumpZStart)
03944                         {
03945                                 if ((int)pm->ps->origin[2] >= (int)pm->ps->fd.forceJumpZStart)
03946                                 { //was force jumping, landed on higher or same level as when force jump was started
03947                                         if (delta_send > 8)
03948                                         {
03949                                                 delta_send = 8;
03950                                         }
03951                                 }
03952                                 else
03953                                 {
03954                                         if (delta_send > 8)
03955                                         {
03956                                                 int dif = ((int)pm->ps->fd.forceJumpZStart - (int)pm->ps->origin[2]);
03957                                                 int dmgLess = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - dif);
03958 
03959                                                 if (dmgLess < 0)
03960                                                 {
03961                                                         dmgLess = 0;
03962                                                 }
03963 
03964                                                 delta_send -= (dmgLess*0.3);
03965 
03966                                                 if (delta_send < 8)
03967                                                 {
03968                                                         delta_send = 8;
03969                                                 }
03970 
03971                                                 //Com_Printf("Damage sub: %i\n", (int)((dmgLess*0.1)));
03972                                         }
03973                                 }
03974                         }
03975 
03976                         if (didRoll)
03977                         { //Add the appropriate event..
03978                                 PM_AddEventWithParm( EV_ROLL, delta_send );
03979                         }
03980                         else
03981                         {
03982                                 PM_AddEventWithParm( EV_FALL, delta_send );
03983                         }
03984                 }
03985                 else
03986                 {
03987                         if (didRoll)
03988                         {
03989                                 PM_AddEventWithParm( EV_ROLL, 0 );
03990                         }
03991                         else
03992                         {
03993                                 PM_AddEventWithParm( EV_FOOTSTEP, PM_FootstepForSurface() );
03994                         }
03995                 }
03996         }
03997 
03998         // make sure velocity resets so we don't bounce back up again in case we miss the clear elsewhere
03999         pm->ps->velocity[2] = 0;
04000 
04001         // start footstep cycle over
04002         pm->ps->bobCycle = 0;
04003 }
04004 
04005 /*
04006 =============
04007 PM_CorrectAllSolid
04008 =============
04009 */
04010 static int PM_CorrectAllSolid( trace_t *trace ) {
04011         int                     i, j, k;
04012         vec3_t          point;
04013 
04014         if ( pm->debugLevel ) {
04015                 Com_Printf("%i:allsolid\n", c_pmove);
04016         }
04017 
04018         // jitter around
04019         for (i = -1; i <= 1; i++) {
04020                 for (j = -1; j <= 1; j++) {
04021                         for (k = -1; k <= 1; k++) {
04022                                 VectorCopy(pm->ps->origin, point);
04023                                 point[0] += (float) i;
04024                                 point[1] += (float) j;
04025                                 point[2] += (float) k;
04026                                 pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
04027                                 if ( !trace->allsolid ) {
04028                                         point[0] = pm->ps->origin[0];
04029                                         point[1] = pm->ps->origin[1];
04030                                         point[2] = pm->ps->origin[2] - 0.25;
04031 
04032                                         pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
04033                                         pml.groundTrace = *trace;
04034                                         return qtrue;
04035                                 }
04036                         }
04037                 }
04038         }
04039 
04040         pm->ps->groundEntityNum = ENTITYNUM_NONE;
04041         pml.groundPlane = qfalse;
04042         pml.walking = qfalse;
04043 
04044         return qfalse;
04045 }
04046 
04047 /*
04048 =============
04049 PM_GroundTraceMissed
04050 
04051 The ground trace didn't hit a surface, so we are in freefall
04052 =============
04053 */
04054 static void PM_GroundTraceMissed( void ) {
04055         trace_t         trace;
04056         vec3_t          point;
04057 
04058         //rww - don't want to do this when handextend_choke, because you can be standing on the ground
04059         //while still holding your throat.
04060         if ( pm->ps->pm_type == PM_FLOAT ) 
04061         {
04062                 //we're assuming this is because you're being choked
04063                 int parts = SETANIM_LEGS;
04064 
04065                 //rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
04066                 //a proper anim even when on the ground.
04067                 PM_SetAnim(parts, BOTH_CHOKE3, SETANIM_FLAG_OVERRIDE, 100);
04068         }
04069         else if ( pm->ps->pm_type == PM_JETPACK ) 
04070         {//jetpacking
04071                 //rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
04072                 //a proper anim even when on the ground.
04073                 //PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE, 100);
04074         }
04075         //If the anim is choke3, act like we just went into the air because we aren't in a float
04076         else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE || (pm->ps->legsAnim) == BOTH_CHOKE3 ) 
04077         {
04078                 // we just transitioned into freefall
04079                 if ( pm->debugLevel ) {
04080                         Com_Printf("%i:lift\n", c_pmove);
04081                 }
04082 
04083                 // if they aren't in a jumping animation and the ground is a ways away, force into it
04084                 // if we didn't do the trace, the player would be backflipping down staircases
04085                 VectorCopy( pm->ps->origin, point );
04086                 point[2] -= 64;
04087 
04088                 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
04089                 if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT ) {
04090                         if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD))
04091                         {
04092                                 //PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE, 100);
04093                                 PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,0, 100);
04094                                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
04095                         }
04096                         else if ( pm->cmd.forwardmove >= 0 ) 
04097                         {
04098                                 PM_SetAnim(SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE, 100);
04099                                 pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
04100                         } 
04101                         else 
04102                         {
04103                                 PM_SetAnim(SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE, 100);
04104                                 pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
04105                         }
04106 
04107                         pm->ps->inAirAnim = qtrue;
04108                 }
04109         }
04110         else if (!pm->ps->inAirAnim)
04111         {
04112                 // if they aren't in a jumping animation and the ground is a ways away, force into it
04113                 // if we didn't do the trace, the player would be backflipping down staircases
04114                 VectorCopy( pm->ps->origin, point );
04115                 point[2] -= 64;
04116 
04117                 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
04118                 if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT )
04119                 {
04120                         pm->ps->inAirAnim = qtrue;
04121                 }
04122         }
04123 
04124         if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
04125         { //Client won't catch an animation restart because it only checks frame against incoming frame, so if you roll when you land after rolling
04126           //off of something it won't replay the roll anim unless we switch it off in the air. This fixes that.
04127                 PM_SetAnim(SETANIM_BOTH,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
04128                 pm->ps->inAirAnim = qtrue;
04129         }
04130 
04131         pm->ps->groundEntityNum = ENTITYNUM_NONE;
04132         pml.groundPlane = qfalse;
04133         pml.walking = qfalse;
04134 }
04135 
04136 
04137 /*
04138 =============
04139 PM_GroundTrace
04140 =============
04141 */
04142 static void PM_GroundTrace( void ) {
04143         vec3_t          point;
04144         trace_t         trace;
04145         float minNormal = (float)MIN_WALK_NORMAL;
04146 
04147         if ( pm->ps->clientNum >= MAX_CLIENTS)
04148         {
04149                 bgEntity_t *pEnt = pm_entSelf;
04150 
04151                 if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
04152                 {
04153                         minNormal = pEnt->m_pVehicle->m_pVehicleInfo->maxSlope;
04154                 }
04155         }
04156 
04157         point[0] = pm->ps->origin[0];
04158         point[1] = pm->ps->origin[1];
04159         point[2] = pm->ps->origin[2] - 0.25;
04160 
04161         pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
04162         pml.groundTrace = trace;
04163 
04164         // do something corrective if the trace starts in a solid...
04165         if ( trace.allsolid ) {
04166                 if ( !PM_CorrectAllSolid(&trace) )
04167                         return;
04168         }
04169 
04170         if (pm->ps->pm_type == PM_FLOAT || pm->ps->pm_type == PM_JETPACK)
04171         {
04172                 PM_GroundTraceMissed();
04173                 pml.groundPlane = qfalse;
04174                 pml.walking = qfalse;
04175                 return;
04176         }
04177 
04178         // if the trace didn't hit anything, we are in free fall
04179         if ( trace.fraction == 1.0 ) {
04180                 PM_GroundTraceMissed();
04181                 pml.groundPlane = qfalse;
04182                 pml.walking = qfalse;
04183                 return;
04184         }
04185 
04186         // check if getting thrown off the ground
04187         if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
04188                 if ( pm->debugLevel ) {
04189                         Com_Printf("%i:kickoff\n", c_pmove);
04190                 }
04191                 // go into jump animation
04192                 if ( pm->cmd.forwardmove >= 0 ) {
04193                         PM_ForceLegsAnim( BOTH_JUMP1 );
04194                         pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
04195                 } else {
04196                         PM_ForceLegsAnim( BOTH_JUMPBACK1 );
04197                         pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
04198                 }
04199 
04200                 pm->ps->groundEntityNum = ENTITYNUM_NONE;
04201                 pml.groundPlane = qfalse;
04202                 pml.walking = qfalse;
04203                 return;
04204         }
04205         
04206         // slopes that are too steep will not be considered onground
04207         if ( trace.plane.normal[2] < minNormal ) {
04208                 if ( pm->debugLevel ) {
04209                         Com_Printf("%i:steep\n", c_pmove);
04210                 }
04211                 pm->ps->groundEntityNum = ENTITYNUM_NONE;
04212                 pml.groundPlane = qtrue;
04213                 pml.walking = qfalse;
04214                 return;
04215         }
04216 
04217         pml.groundPlane = qtrue;
04218         pml.walking = qtrue;
04219 
04220         // hitting solid ground will end a waterjump
04221         if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
04222         {
04223                 pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
04224                 pm->ps->pm_time = 0;
04225         }
04226 
04227         if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
04228                 // just hit the ground
04229                 if ( pm->debugLevel ) {
04230                         Com_Printf("%i:Land\n", c_pmove);
04231                 }
04232                 
04233                 PM_CrashLand();
04234 
04235 #ifdef QAGAME
04236                 if (pm->ps->clientNum < MAX_CLIENTS &&
04237                         !pm->ps->m_iVehicleNum &&
04238                         trace.entityNum < ENTITYNUM_WORLD &&
04239                         trace.entityNum >= MAX_CLIENTS &&
04240                         !pm->ps->zoomMode &&
04241                         pm_entSelf)
04242                 { //check if we landed on a vehicle
04243                         gentity_t *trEnt = &g_entities[trace.entityNum];
04244                         if (trEnt->inuse && trEnt->client && trEnt->s.eType == ET_NPC && trEnt->s.NPC_class == CLASS_VEHICLE &&
04245                                 !trEnt->client->ps.m_iVehicleNum &&
04246                                 trEnt->m_pVehicle &&
04247                                 trEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
04248                                 trEnt->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
04249                         { //it's a vehicle alright, let's board it.. if it's not an atst or ship
04250                                 if (!BG_SaberInSpecial(pm->ps->saberMove) &&
04251                                         pm->ps->forceHandExtend == HANDEXTEND_NONE &&
04252                                         pm->ps->weaponTime <= 0)
04253                                 {
04254                                         gentity_t *servEnt = (gentity_t *)pm_entSelf;
04255                                         if (g_gametype.integer < GT_TEAM ||
04256                                                 !trEnt->alliedTeam ||
04257                                                 (trEnt->alliedTeam == servEnt->client->sess.sessionTeam))
04258                                         { //not belonging to a team, or client is on same team
04259                                                 trEnt->m_pVehicle->m_pVehicleInfo->Board(trEnt->m_pVehicle, pm_entSelf);
04260                                         }
04261                                 }
04262                         }
04263                 }
04264 #endif
04265 
04266                 // don't do landing time if we were just going down a slope
04267                 if ( pml.previous_velocity[2] < -200 ) {
04268                         // don't allow another jump for a little while
04269                         pm->ps->pm_flags |= PMF_TIME_LAND;
04270                         pm->ps->pm_time = 250;
04271                 }
04272         }
04273 
04274         pm->ps->groundEntityNum = trace.entityNum;
04275         pm->ps->lastOnGround = pm->cmd.serverTime;
04276 
04277         PM_AddTouchEnt( trace.entityNum );      
04278 }
04279 
04280 
04281 /*
04282 =============
04283 PM_SetWaterLevel
04284 =============
04285 */
04286 static void PM_SetWaterLevel( void ) {
04287         vec3_t          point;
04288         int                     cont;
04289         int                     sample1;
04290         int                     sample2;
04291 
04292         //
04293         // get waterlevel, accounting for ducking
04294         //
04295         pm->waterlevel = 0;
04296         pm->watertype = 0;
04297 
04298         point[0] = pm->ps->origin[0];
04299         point[1] = pm->ps->origin[1];
04300         point[2] = pm->ps->origin[2] + MINS_Z + 1;      
04301         cont = pm->pointcontents( point, pm->ps->clientNum );
04302 
04303         if ( cont & MASK_WATER ) {
04304                 sample2 = pm->ps->viewheight - MINS_Z;
04305                 sample1 = sample2 / 2;
04306 
04307                 pm->watertype = cont;
04308                 pm->waterlevel = 1;
04309                 point[2] = pm->ps->origin[2] + MINS_Z + sample1;
04310                 cont = pm->pointcontents (point, pm->ps->clientNum );
04311                 if ( cont & MASK_WATER ) {
04312                         pm->waterlevel = 2;
04313                         point[2] = pm->ps->origin[2] + MINS_Z + sample2;
04314                         cont = pm->pointcontents (point, pm->ps->clientNum );
04315                         if ( cont & MASK_WATER ){
04316                                 pm->waterlevel = 3;
04317                         }
04318                 }
04319         }
04320 
04321 }
04322 
04323 qboolean PM_CheckDualForwardJumpDuck( void )
04324 {
04325         qboolean resized = qfalse;
04326         if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
04327         {
04328                 //dynamically reduce bounding box to let character sail over heads of enemies
04329                 if ( ( pm->ps->legsTimer >= 1450
04330                                 && PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 400 ) 
04331                         ||(pm->ps->legsTimer >= 400
04332                                 && PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 1100 ) )
04333                 {//in a part of the anim that we're pretty much sideways in, raise up the mins
04334                         pm->mins[2] = 0;
04335                         pm->ps->pm_flags |= PMF_FIX_MINS;
04336                         resized = qtrue;
04337                 }
04338         }
04339         return resized;
04340 }
04341 
04342 void PM_CheckFixMins( void )
04343 {
04344         if ( (pm->ps->pm_flags&PMF_FIX_MINS) )// pm->mins[2] > DEFAULT_MINS_2 )
04345         {//drop the mins back down
04346                 //do a trace to make sure it's okay
04347                 trace_t trace;
04348                 vec3_t end, curMins, curMaxs;
04349         
04350                 VectorSet( end, pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2]+MINS_Z ); 
04351                 VectorSet( curMins, pm->mins[0], pm->mins[1], 0 ); 
04352                 VectorSet( curMaxs, pm->maxs[0], pm->maxs[1], pm->ps->standheight ); 
04353 
04354                 pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
04355                 if ( !trace.allsolid && !trace.startsolid )
04356                 {//should never start in solid
04357                         if ( trace.fraction >= 1.0f )
04358                         {//all clear
04359                                 //drop the bottom of my bbox back down
04360                                 pm->mins[2] = MINS_Z;
04361                                 pm->ps->pm_flags &= ~PMF_FIX_MINS;
04362                         }
04363                         else
04364                         {//move me up so the bottom of my bbox will be where the trace ended, at least
04365                                 //need to trace up, too
04366                                 float updist = ((1.0f-trace.fraction) * -MINS_Z);
04367                                 end[2] = pm->ps->origin[2]+updist; 
04368                                 pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
04369                                 if ( !trace.allsolid && !trace.startsolid )
04370                                 {//should never start in solid
04371                                         if ( trace.fraction >= 1.0f )
04372                                         {//all clear
04373                                                 //move me up
04374                                                 pm->ps->origin[2] += updist;
04375                                                 //drop the bottom of my bbox back down
04376                                                 pm->mins[2] = MINS_Z;
04377                                                 pm->ps->pm_flags &= ~PMF_FIX_MINS;
04378                                         }
04379                                         else
04380                                         {//crap, no room to expand, so just crouch us
04381                                                 if ( pm->ps->legsAnim != BOTH_JUMPATTACK6
04382                                                         || pm->ps->legsTimer <= 200 )
04383                                                 {//at the end of the anim, and we can't leave ourselves like this
04384                                                         //so drop the maxs, put the mins back and move us up
04385                                                         pm->maxs[2] += MINS_Z;
04386                                                         pm->ps->origin[2] -= MINS_Z;
04387                                                         pm->mins[2] = MINS_Z;
04388                                                         //this way we'll be in a crouch when we're done
04389                                                         if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
04390                                                         {
04391                                                                 pm->ps->legsTimer = pm->ps->torsoTimer = 0;
04392                                                         }
04393                                                         pm->ps->pm_flags |= PMF_DUCKED;
04394                                                         //FIXME: do we need to set a crouch anim here?
04395                                                         pm->ps->pm_flags &= ~PMF_FIX_MINS;
04396                                                 }
04397                                         }
04398                                 }//crap, stuck
04399                         }
04400                 }//crap, stuck!
04401         }
04402 }
04403 
04404 /*
04405 ==============
04406 PM_CheckDuck
04407 
04408 Sets mins, maxs, and pm->ps->viewheight
04409 ==============
04410 */
04411 static void PM_CheckDuck (void)
04412 {
04413         trace_t trace;
04414 
04415         if ( pm->ps->m_iVehicleNum > 0 && pm->ps->m_iVehicleNum < ENTITYNUM_NONE )
04416         {//riding a vehicle or are a vehicle
04417                 //no ducking or rolling when on a vehicle
04418                 //right?  not even on ones that you just ride on top of?
04419                 pm->ps->pm_flags &= ~PMF_DUCKED;
04420                 pm->ps->pm_flags &= ~PMF_ROLLING;
04421                 //NOTE: we don't clear the pm->cmd.upmove here because 
04422                 //the vehicle code may need it later... but, for riders,
04423                 //it should have already been copied over to the vehicle, right?
04424 
04425                 if (pm->ps->clientNum >= MAX_CLIENTS)
04426                 {
04427                         return;
04428                 }
04429                 if (pm_entVeh && pm_entVeh->m_pVehicle &&
04430                         (pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
04431                          pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL))
04432                 {
04433                         trace_t solidTr;
04434 
04435                         pm->mins[0] = -16;
04436                         pm->mins[1] = -16;
04437                         pm->mins[2] = MINS_Z;
04438 
04439                         pm->maxs[0] = 16;
04440                         pm->maxs[1] = 16;
04441                         pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
04442                         pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
04443 
04444                         pm->trace (&solidTr, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->m_iVehicleNum, pm->tracemask);
04445                         if (solidTr.startsolid || solidTr.allsolid || solidTr.fraction != 1.0f)
04446                         { //whoops, can't fit here. Down to 0!
04447                                 VectorClear(pm->mins);
04448                                 VectorClear(pm->maxs);
04449 #ifdef QAGAME
04450                                 {
04451                                         gentity_t *me = &g_entities[pm->ps->clientNum];
04452                                         if (me->inuse && me->client)
04453                                         { //yeah, this is a really terrible hack.
04454                                                 me->client->solidHack = level.time + 200;
04455                                         }
04456                                 }
04457 #endif
04458                         }
04459                 }
04460         }
04461         else
04462         {
04463                 if (pm->ps->clientNum < MAX_CLIENTS)
04464                 {
04465                         pm->mins[0] = -15;
04466                         pm->mins[1] = -15;
04467 
04468                         pm->maxs[0] = 15;
04469                         pm->maxs[1] = 15;
04470                 }
04471 
04472                 if ( PM_CheckDualForwardJumpDuck() )
04473                 {//special anim resizing us
04474                 }
04475                 else
04476                 {
04477                         PM_CheckFixMins();
04478 
04479                         if ( !pm->mins[2] )
04480                         {
04481                                 pm->mins[2] = MINS_Z;
04482                         }
04483                 }
04484 
04485                 if (pm->ps->pm_type == PM_DEAD && pm->ps->clientNum < MAX_CLIENTS)
04486                 {
04487                         pm->maxs[2] = -8;
04488                         pm->ps->viewheight = DEAD_VIEWHEIGHT;
04489                         return;
04490                 }
04491 
04492                 if (BG_InRoll(pm->ps, pm->ps->legsAnim) && !BG_KickingAnim(pm->ps->legsAnim))
04493                 {
04494                         pm->maxs[2] = pm->ps->crouchheight; //CROUCH_MAXS_2;
04495                         pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
04496                         pm->ps->pm_flags &= ~PMF_DUCKED;
04497                         pm->ps->pm_flags |= PMF_ROLLING;
04498                         return;
04499                 }
04500                 else if (pm->ps->pm_flags & PMF_ROLLING)
04501                 {
04502                         // try to stand up
04503                         pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
04504                         pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
04505                         if (!trace.allsolid)
04506                                 pm->ps->pm_flags &= ~PMF_ROLLING;
04507                 }
04508                 else if (pm->cmd.upmove < 0 ||
04509                         pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
04510                         pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
04511                         pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
04512                 {       // duck
04513                         pm->ps->pm_flags |= PMF_DUCKED;
04514                 }
04515                 else
04516                 {       // stand up if possible 
04517                         if (pm->ps->pm_flags & PMF_DUCKED)
04518                         {
04519                                 // try to stand up
04520                                 pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
04521                                 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
04522                                 if (!trace.allsolid)
04523                                         pm->ps->pm_flags &= ~PMF_DUCKED;
04524                         }
04525                 }
04526         }
04527 
04528         if (pm->ps->pm_flags & PMF_DUCKED)
04529         {
04530                 pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
04531                 pm->ps->viewheight = CROUCH_VIEWHEIGHT;
04532         }
04533         else if (pm->ps->pm_flags & PMF_ROLLING)
04534         {
04535                 pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
04536                 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
04537         }
04538         else
04539         {
04540                 pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
04541                 pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
04542         }
04543 }
04544 
04545 
04546 
04547 //===================================================================
04548 
04549 
04550 
04551 /*
04552 ==============
04553 PM_Use
04554 
04555 Generates a use event
04556 ==============
04557 */
04558 #define USE_DELAY 2000
04559 
04560 void PM_Use( void ) 
04561 {
04562         if ( pm->ps->useTime > 0 )
04563                 pm->ps->useTime -= 100;//pm->cmd.msec;
04564 
04565         if ( pm->ps->useTime > 0 ) {
04566                 return;
04567         }
04568 
04569         if ( ! (pm->cmd.buttons & BUTTON_USE ) )
04570         {
04571                 pm->useEvent = 0;
04572                 pm->ps->useTime = 0;
04573                 return;
04574         }
04575 
04576         pm->useEvent = EV_USE;
04577         pm->ps->useTime = USE_DELAY;
04578 }
04579 
04580 qboolean PM_WalkingAnim( int anim )
04581 {
04582         switch ( anim )
04583         {
04584         case BOTH_WALK1:                                //# Normal walk
04585         case BOTH_WALK2:                                //# Normal walk with saber
04586         case BOTH_WALK_STAFF:                   //# Normal walk with staff
04587         case BOTH_WALK_DUAL:                    //# Normal walk with staff
04588         case BOTH_WALK5:                                //# Tavion taunting Kyle (cin 22)
04589         case BOTH_WALK6:                                //# Slow walk for Luke (cin 12)
04590         case BOTH_WALK7:                                //# Fast walk
04591         case BOTH_WALKBACK1:                    //# Walk1 backwards
04592         case BOTH_WALKBACK2:                    //# Walk2 backwards
04593         case BOTH_WALKBACK_STAFF:               //# Walk backwards with staff
04594         case BOTH_WALKBACK_DUAL:                //# Walk backwards with dual
04595                 return qtrue;
04596                 break;
04597         }
04598         return qfalse;
04599 }
04600 
04601 qboolean PM_RunningAnim( int anim )
04602 {
04603         switch ( (anim) )
04604         {
04605         case BOTH_RUN1:                 
04606         case BOTH_RUN2:                 
04607         case BOTH_RUN_STAFF:
04608         case BOTH_RUN_DUAL:
04609         case BOTH_RUNBACK1:                     
04610         case BOTH_RUNBACK2:                     
04611         case BOTH_RUNBACK_STAFF:                        
04612         case BOTH_RUNBACK_DUAL:
04613         case BOTH_RUN1START:                    //# Start into full run1
04614         case BOTH_RUN1STOP:                     //# Stop from full run1
04615         case BOTH_RUNSTRAFE_LEFT1:      //# Sidestep left: should loop
04616         case BOTH_RUNSTRAFE_RIGHT1:     //# Sidestep right: should loop
04617                 return qtrue;
04618                 break;
04619         }
04620         return qfalse;
04621 }
04622 
04623 qboolean PM_SwimmingAnim( int anim )
04624 {
04625         switch ( anim )
04626         {
04627         case BOTH_SWIM_IDLE1:           //# Swimming Idle 1
04628         case BOTH_SWIMFORWARD:          //# Swim forward loop
04629         case BOTH_SWIMBACKWARD:         //# Swim backward loop
04630                 return qtrue;
04631                 break;
04632         }
04633         return qfalse;
04634 }
04635 
04636 qboolean PM_RollingAnim( int anim )
04637 {
04638         switch ( anim )
04639         {
04640         case BOTH_ROLL_F:                       //# Roll forward
04641         case BOTH_ROLL_B:                       //# Roll backward
04642         case BOTH_ROLL_L:                       //# Roll left
04643         case BOTH_ROLL_R:                       //# Roll right
04644                 return qtrue;
04645                 break;
04646         }
04647         return qfalse;
04648 }
04649 
04650 void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles )
04651 {
04652         vec3_t  nvf, ovf, ovr, new_angles;
04653         float   pitch, mod, dot;
04654 
04655         VectorSet( angles, 0, yaw, 0 );
04656         AngleVectors( angles, ovf, ovr, NULL );
04657 
04658         vectoangles( slope, new_angles );
04659         pitch = new_angles[PITCH] + 90;
04660         new_angles[ROLL] = new_angles[PITCH] = 0;
04661 
04662         AngleVectors( new_angles, nvf, NULL, NULL );
04663 
04664         mod = DotProduct( nvf, ovr );
04665 
04666         if ( mod < 0 )
04667                 mod = -1;
04668         else
04669                 mod = 1;
04670 
04671         dot = DotProduct( nvf, ovf );
04672 
04673         angles[YAW] = 0;
04674         angles[PITCH] = dot * pitch;
04675         angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
04676 }
04677 
04678 void PM_FootSlopeTrace( float *pDiff, float *pInterval )
04679 {
04680         vec3_t  footLOrg, footROrg, footLBot, footRBot;
04681         vec3_t footLPoint, footRPoint;
04682         vec3_t footMins, footMaxs;
04683         vec3_t footLSlope, footRSlope;
04684 
04685         trace_t trace;
04686         float   diff, interval;
04687 
04688         mdxaBone_t      boltMatrix;
04689         vec3_t          G2Angles;
04690         
04691         VectorSet(G2Angles, 0, pm->ps->viewangles[YAW], 0);
04692 
04693         interval = 4;//?
04694 
04695         strap_G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_LFoot, 
04696                         &boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime, 
04697                                         NULL, pm->modelScale );
04698         footLPoint[0] = boltMatrix.matrix[0][3];
04699         footLPoint[1] = boltMatrix.matrix[1][3];
04700         footLPoint[2] = boltMatrix.matrix[2][3];
04701         
04702         strap_G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_RFoot, 
04703                                         &boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime, 
04704                                         NULL, pm->modelScale );
04705         footRPoint[0] = boltMatrix.matrix[0][3];
04706         footRPoint[1] = boltMatrix.matrix[1][3];
04707         footRPoint[2] = boltMatrix.matrix[2][3];
04708 
04709         //get these on the cgame and store it, save ourselves a ghoul2 construct skel call
04710         VectorCopy( footLPoint, footLOrg );
04711         VectorCopy( footRPoint, footROrg );
04712 
04713         //step 2: adjust foot tag z height to bottom of bbox+1
04714         footLOrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
04715         footROrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
04716         VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 );
04717         VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 );
04718 
04719         //step 3: trace down from each, find difference
04720         VectorSet( footMins, -3, -3, 0 );
04721         VectorSet( footMaxs, 3, 3, 1 );
04722 
04723         pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask );
04724         VectorCopy( trace.endpos, footLBot );
04725         VectorCopy( trace.plane.normal, footLSlope );
04726 
04727         pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask );
04728         VectorCopy( trace.endpos, footRBot );
04729         VectorCopy( trace.plane.normal, footRSlope );
04730 
04731         diff = footLBot[2] - footRBot[2];
04732 
04733         if ( pDiff != NULL )
04734         {
04735                 *pDiff = diff;
04736         }
04737         if ( pInterval != NULL )
04738         {
04739                 *pInterval = interval;
04740         }
04741 }
04742 
04743 qboolean BG_InSlopeAnim( int anim )
04744 {
04745         switch ( anim )
04746         {
04747         case LEGS_LEFTUP1:                      //# On a slope with left foot 4 higher than right
04748         case LEGS_LEFTUP2:                      //# On a slope with left foot 8 higher than right
04749         case LEGS_LEFTUP3:                      //# On a slope with left foot 12 higher than right
04750         case LEGS_LEFTUP4:                      //# On a slope with left foot 16 higher than right
04751         case LEGS_LEFTUP5:                      //# On a slope with left foot 20 higher than right
04752         case LEGS_RIGHTUP1:                     //# On a slope with RIGHT foot 4 higher than left
04753         case LEGS_RIGHTUP2:                     //# On a slope with RIGHT foot 8 higher than left
04754         case LEGS_RIGHTUP3:                     //# On a slope with RIGHT foot 12 higher than left
04755         case LEGS_RIGHTUP4:                     //# On a slope with RIGHT foot 16 higher than left
04756         case LEGS_RIGHTUP5:                     //# On a slope with RIGHT foot 20 higher than left
04757         case LEGS_S1_LUP1:
04758         case LEGS_S1_LUP2:
04759         case LEGS_S1_LUP3:
04760         case LEGS_S1_LUP4:
04761         case LEGS_S1_LUP5:
04762         case LEGS_S1_RUP1:
04763         case LEGS_S1_RUP2:
04764         case LEGS_S1_RUP3:
04765         case LEGS_S1_RUP4:
04766         case LEGS_S1_RUP5:
04767         case LEGS_S3_LUP1:
04768         case LEGS_S3_LUP2:
04769         case LEGS_S3_LUP3:
04770         case LEGS_S3_LUP4:
04771         case LEGS_S3_LUP5:
04772         case LEGS_S3_RUP1:
04773         case LEGS_S3_RUP2:
04774         case LEGS_S3_RUP3:
04775         case LEGS_S3_RUP4:
04776         case LEGS_S3_RUP5:
04777         case LEGS_S4_LUP1:
04778         case LEGS_S4_LUP2:
04779         case LEGS_S4_LUP3:
04780         case LEGS_S4_LUP4:
04781         case LEGS_S4_LUP5:
04782         case LEGS_S4_RUP1:
04783         case LEGS_S4_RUP2:
04784         case LEGS_S4_RUP3:
04785         case LEGS_S4_RUP4:
04786         case LEGS_S4_RUP5:
04787         case LEGS_S5_LUP1:
04788         case LEGS_S5_LUP2:
04789         case LEGS_S5_LUP3:
04790         case LEGS_S5_LUP4:
04791         case LEGS_S5_LUP5:
04792         case LEGS_S5_RUP1:
04793         case LEGS_S5_RUP2:
04794         case LEGS_S5_RUP3:
04795         case LEGS_S5_RUP4:
04796         case LEGS_S5_RUP5:
04797                 return qtrue;
04798                 break;
04799         }
04800         return qfalse;
04801 }
04802 
04803 #define SLOPE_RECALC_INT 100
04804 
04805 qboolean PM_AdjustStandAnimForSlope( void )
04806 {
04807         float   diff;
04808         float   interval;
04809         int             destAnim;
04810         int             legsAnim;
04811         #define SLOPERECALCVAR pm->ps->slopeRecalcTime //this is purely convenience
04812 
04813         if (!pm->ghoul2)
04814         { //probably just changed models and not quite in sync yet
04815                 return qfalse;
04816         }
04817 
04818         if ( pm->g2Bolts_LFoot == -1 || pm->g2Bolts_RFoot == -1 )
04819         {//need these bolts!
04820                 return qfalse;
04821         }
04822 
04823         //step 1: find the 2 foot tags
04824         PM_FootSlopeTrace( &diff, &interval );
04825 
04826         //step 4: based on difference, choose one of the left/right slope-match intervals
04827         if ( diff >= interval*5 )
04828         {
04829                 destAnim = LEGS_LEFTUP5;
04830         }
04831         else if ( diff >= interval*4 )
04832         {
04833                 destAnim = LEGS_LEFTUP4;
04834         }
04835         else if ( diff >= interval*3 )
04836         {
04837                 destAnim = LEGS_LEFTUP3;
04838         }
04839         else if ( diff >= interval*2 )
04840         {
04841                 destAnim = LEGS_LEFTUP2;
04842         }
04843         else if ( diff >= interval )
04844         {
04845                 destAnim = LEGS_LEFTUP1;
04846         }
04847         else if ( diff <= interval*-5 )
04848         {
04849                 destAnim = LEGS_RIGHTUP5;
04850         }
04851         else if ( diff <= interval*-4 )
04852         {
04853                 destAnim = LEGS_RIGHTUP4;
04854         }
04855         else if ( diff <= interval*-3 )
04856         {
04857                 destAnim = LEGS_RIGHTUP3;
04858         }
04859         else if ( diff <= interval*-2 )
04860         {
04861                 destAnim = LEGS_RIGHTUP2;
04862         }
04863         else if ( diff <= interval*-1 )
04864         {
04865                 destAnim = LEGS_RIGHTUP1;
04866         }
04867         else
04868         {
04869                 return qfalse;
04870         }
04871 
04872         legsAnim = pm->ps->legsAnim;
04873         //adjust for current legs anim
04874         switch ( legsAnim )
04875         {
04876         case BOTH_STAND1:
04877 
04878         case LEGS_S1_LUP1:
04879         case LEGS_S1_LUP2:
04880         case LEGS_S1_LUP3:
04881         case LEGS_S1_LUP4:
04882         case LEGS_S1_LUP5:
04883         case LEGS_S1_RUP1:
04884         case LEGS_S1_RUP2:
04885         case LEGS_S1_RUP3:
04886         case LEGS_S1_RUP4:
04887         case LEGS_S1_RUP5:
04888                 destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1);
04889                 break;
04890         case BOTH_STAND2:
04891         case BOTH_SABERFAST_STANCE:
04892         case BOTH_SABERSLOW_STANCE:
04893         case BOTH_CROUCH1IDLE:
04894         case BOTH_CROUCH1:
04895         case LEGS_LEFTUP1:                      //# On a slope with left foot 4 higher than right
04896         case LEGS_LEFTUP2:                      //# On a slope with left foot 8 higher than right
04897         case LEGS_LEFTUP3:                      //# On a slope with left foot 12 higher than right
04898         case LEGS_LEFTUP4:                      //# On a slope with left foot 16 higher than right
04899         case LEGS_LEFTUP5:                      //# On a slope with left foot 20 higher than right
04900         case LEGS_RIGHTUP1:                     //# On a slope with RIGHT foot 4 higher than left
04901         case LEGS_RIGHTUP2:                     //# On a slope with RIGHT foot 8 higher than left
04902         case LEGS_RIGHTUP3:                     //# On a slope with RIGHT foot 12 higher than left
04903         case LEGS_RIGHTUP4:                     //# On a slope with RIGHT foot 16 higher than left
04904         case LEGS_RIGHTUP5:                     //# On a slope with RIGHT foot 20 higher than left
04905                 //fine
04906                 break;
04907         case BOTH_STAND3:
04908         case LEGS_S3_LUP1:
04909         case LEGS_S3_LUP2:
04910         case LEGS_S3_LUP3:
04911         case LEGS_S3_LUP4:
04912         case LEGS_S3_LUP5:
04913         case LEGS_S3_RUP1:
04914         case LEGS_S3_RUP2:
04915         case LEGS_S3_RUP3:
04916         case LEGS_S3_RUP4:
04917         case LEGS_S3_RUP5:
04918                 destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1);
04919                 break;
04920         case BOTH_STAND4:
04921         case LEGS_S4_LUP1:
04922         case LEGS_S4_LUP2:
04923         case LEGS_S4_LUP3:
04924         case LEGS_S4_LUP4:
04925         case LEGS_S4_LUP5:
04926         case LEGS_S4_RUP1:
04927         case LEGS_S4_RUP2:
04928         case LEGS_S4_RUP3:
04929         case LEGS_S4_RUP4:
04930         case LEGS_S4_RUP5:
04931                 destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1);
04932                 break;
04933         case BOTH_STAND5:
04934         case LEGS_S5_LUP1:
04935         case LEGS_S5_LUP2:
04936         case LEGS_S5_LUP3:
04937         case LEGS_S5_LUP4:
04938         case LEGS_S5_LUP5:
04939         case LEGS_S5_RUP1:
04940         case LEGS_S5_RUP2:
04941         case LEGS_S5_RUP3:
04942         case LEGS_S5_RUP4:
04943         case LEGS_S5_RUP5:
04944                 destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1);
04945                 break;
04946         case BOTH_STAND6:
04947         default:
04948                 return qfalse;
04949                 break;
04950         }
04951 
04952         //step 5: based on the chosen interval and the current legsAnim, pick the correct anim
04953         //step 6: increment/decrement to the dest anim, not instant
04954         if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5)
04955                 || (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5)
04956                 || (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5)
04957                 || (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5)
04958                 || (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5) )
04959         {//already in left-side up
04960                 if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
04961                 {
04962                         legsAnim++;
04963                         SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
04964                 }
04965                 else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
04966                 {
04967                         legsAnim--;
04968                         SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
04969                 }
04970                 else //if (SLOPERECALCVAR < pm->cmd.serverTime)
04971                 {
04972                         legsAnim = destAnim;
04973                 }
04974 
04975                 destAnim = legsAnim;
04976         }
04977         else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5) 
04978                 || (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5)
04979                 || (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5)
04980                 || (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5)
04981                 || (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5) )
04982         {//already in right-side up
04983                 if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
04984                 {
04985                         legsAnim++;
04986                         SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
04987                 }
04988                 else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
04989                 {
04990                         legsAnim--;
04991                         SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
04992                 }
04993                 else //if (SLOPERECALCVAR < pm->cmd.serverTime)
04994                 {
04995                         legsAnim = destAnim;
04996                 }
04997                 
04998                 destAnim = legsAnim;
04999         }
05000         else
05001         {//in a stand of some sort?
05002                 switch ( legsAnim )
05003                 {
05004                 case BOTH_STAND1:
05005                 case TORSO_WEAPONREADY1:
05006                 case TORSO_WEAPONREADY2:
05007                 case TORSO_WEAPONREADY3:
05008                 case TORSO_WEAPONREADY10:
05009 
05010                         if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 )
05011                         {//going into left side up
05012                                 destAnim = LEGS_S1_LUP1;
05013                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05014                         }
05015                         else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 )
05016                         {//going into right side up
05017                                 destAnim = LEGS_S1_RUP1;
05018                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05019                         }
05020                         else
05021                         {//will never get here
05022                                 return qfalse;
05023                         }
05024                         break;
05025                 case BOTH_STAND2:
05026                 case BOTH_SABERFAST_STANCE:
05027                 case BOTH_SABERSLOW_STANCE:
05028                 case BOTH_CROUCH1IDLE:
05029                         if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
05030                         {//going into left side up
05031                                 destAnim = LEGS_LEFTUP1;
05032                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05033                         }
05034                         else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
05035                         {//going into right side up
05036                                 destAnim = LEGS_RIGHTUP1;
05037                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05038                         }
05039                         else
05040                         {//will never get here
05041                                 return qfalse;
05042                         }
05043                         break;
05044                 case BOTH_STAND3:
05045                         if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 )
05046                         {//going into left side up
05047                                 destAnim = LEGS_S3_LUP1;
05048                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05049                         }
05050                         else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 )
05051                         {//going into right side up
05052                                 destAnim = LEGS_S3_RUP1;
05053                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05054                         }
05055                         else
05056                         {//will never get here
05057                                 return qfalse;
05058                         }
05059                         break;
05060                 case BOTH_STAND4:
05061                         if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 )
05062                         {//going into left side up
05063                                 destAnim = LEGS_S4_LUP1;
05064                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05065                         }
05066                         else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 )
05067                         {//going into right side up
05068                                 destAnim = LEGS_S4_RUP1;
05069                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05070                         }
05071                         else
05072                         {//will never get here
05073                                 return qfalse;
05074                         }
05075                         break;
05076                 case BOTH_STAND5:
05077                         if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 )
05078                         {//going into left side up
05079                                 destAnim = LEGS_S5_LUP1;
05080                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05081                         }
05082                         else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 )
05083                         {//going into right side up
05084                                 destAnim = LEGS_S5_RUP1;
05085                                 SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
05086                         }
05087                         else
05088                         {//will never get here
05089                                 return qfalse;
05090                         }
05091                         break;
05092                 case BOTH_STAND6:
05093                 default:
05094                         return qfalse;
05095                         break;
05096                 }
05097         }
05098         //step 7: set the anim
05099         //PM_SetAnim( SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL, 100 );
05100         PM_ContinueLegsAnim(destAnim);
05101 
05102         return qtrue;
05103 }
05104 
05105 extern int WeaponReadyLegsAnim[WP_NUM_WEAPONS];
05106 
05107 //rww - slowly back out of slope leg anims, to prevent skipping between slope anims and general jittering
05108 int PM_LegsSlopeBackTransition(int desiredAnim)
05109 {
05110         int anim = pm->ps->legsAnim;
05111         int resultingAnim = desiredAnim;
05112 
05113         switch ( anim )
05114         {
05115         case LEGS_LEFTUP2:                      //# On a slope with left foot 8 higher than right
05116         case LEGS_LEFTUP3:                      //# On a slope with left foot 12 higher than right
05117         case LEGS_LEFTUP4:                      //# On a slope with left foot 16 higher than right
05118         case LEGS_LEFTUP5:                      //# On a slope with left foot 20 higher than right
05119         case LEGS_RIGHTUP2:                     //# On a slope with RIGHT foot 8 higher than left
05120         case LEGS_RIGHTUP3:                     //# On a slope with RIGHT foot 12 higher than left
05121         case LEGS_RIGHTUP4:                     //# On a slope with RIGHT foot 16 higher than left
05122         case LEGS_RIGHTUP5:                     //# On a slope with RIGHT foot 20 higher than left
05123         case LEGS_S1_LUP2:
05124         case LEGS_S1_LUP3:
05125         case LEGS_S1_LUP4:
05126         case LEGS_S1_LUP5:
05127         case LEGS_S1_RUP2:
05128         case LEGS_S1_RUP3:
05129         case LEGS_S1_RUP4:
05130         case LEGS_S1_RUP5:
05131         case LEGS_S3_LUP2:
05132         case LEGS_S3_LUP3:
05133         case LEGS_S3_LUP4:
05134         case LEGS_S3_LUP5:
05135         case LEGS_S3_RUP2:
05136         case LEGS_S3_RUP3:
05137         case LEGS_S3_RUP4:
05138         case LEGS_S3_RUP5:
05139         case LEGS_S4_LUP2:
05140         case LEGS_S4_LUP3:
05141         case LEGS_S4_LUP4:
05142         case LEGS_S4_LUP5:
05143         case LEGS_S4_RUP2:
05144         case LEGS_S4_RUP3:
05145         case LEGS_S4_RUP4:
05146         case LEGS_S4_RUP5:
05147         case LEGS_S5_LUP2:
05148         case LEGS_S5_LUP3:
05149         case LEGS_S5_LUP4:
05150         case LEGS_S5_LUP5:
05151         case LEGS_S5_RUP2:
05152         case LEGS_S5_RUP3:
05153         case LEGS_S5_RUP4:
05154         case LEGS_S5_RUP5:
05155                 if (pm->ps->slopeRecalcTime < pm->cmd.serverTime)
05156                 {
05157                         resultingAnim = anim-1;
05158                         pm->ps->slopeRecalcTime = pm->cmd.serverTime + 8;//SLOPE_RECALC_INT;
05159                 }
05160                 else
05161                 {
05162                         resultingAnim = anim;
05163                 }
05164                 VectorClear(pm->ps->velocity);
05165                 break;
05166         }
05167 
05168         return resultingAnim;
05169 }
05170 
05171 /*
05172 ===============
05173 PM_Footsteps
05174 ===============
05175 */
05176 static void PM_Footsteps( void ) {
05177         float           bobmove;
05178         int                     old;
05179         qboolean        footstep;
05180         int                     setAnimFlags = 0;
05181 
05182         if ( (PM_InSaberAnim( (pm->ps->legsAnim) ) && !BG_SpinningSaberAnim( (pm->ps->legsAnim) )) 
05183                 || (pm->ps->legsAnim) == BOTH_STAND1 
05184                 || (pm->ps->legsAnim) == BOTH_STAND1TO2 
05185                 || (pm->ps->legsAnim) == BOTH_STAND2TO1 
05186                 || (pm->ps->legsAnim) == BOTH_STAND2 
05187                 || (pm->ps->legsAnim) == BOTH_SABERFAST_STANCE
05188                 || (pm->ps->legsAnim) == BOTH_SABERSLOW_STANCE
05189                 || (pm->ps->legsAnim) == BOTH_BUTTON_HOLD
05190                 || (pm->ps->legsAnim) == BOTH_BUTTON_RELEASE
05191                 || PM_LandingAnim( (pm->ps->legsAnim) ) 
05192                 || PM_PainAnim( (pm->ps->legsAnim) ))
05193         {//legs are in a saber anim, and not spinning, be sure to override it
05194                 setAnimFlags |= SETANIM_FLAG_OVERRIDE;
05195         }
05196 
05197         //
05198         // calculate speed and cycle to be used for
05199         // all cyclic walking effects
05200         //
05201         pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
05202                 +  pm->ps->velocity[1] * pm->ps->velocity[1] );
05203 
05204         if (pm->ps->saberMove == LS_SPINATTACK)
05205         {
05206                 PM_ContinueLegsAnim( pm->ps->torsoAnim );
05207         }
05208         else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
05209 
05210                 // airborne leaves position in cycle intact, but doesn't advance
05211                 if ( pm->waterlevel > 1 )
05212                 {
05213                         if (pm->xyspeed > 60)
05214                         {
05215                                 PM_ContinueLegsAnim( BOTH_SWIMFORWARD );
05216                         }
05217                         else
05218                         {
05219                                 PM_ContinueLegsAnim( BOTH_SWIM_IDLE1 );
05220                         }
05221                 }
05222                 return;
05223         }
05224         // if not trying to move
05225         else if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
05226                 if (  pm->xyspeed < 5 ) {
05227                         pm->ps->bobCycle = 0;   // start at beginning of cycle again
05228                         if ( pm->ps->clientNum >= MAX_CLIENTS &&
05229                                 pm_entSelf &&
05230                                 pm_entSelf->s.NPC_class == CLASS_RANCOR )
05231                         {
05232                                 if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
05233                                 {//holding someone
05234                                         PM_ContinueLegsAnim( BOTH_STAND4 );
05235                                         //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
05236                                 }
05237                                 else if ( (pm->ps->eFlags2&EF2_ALERTED) )
05238                                 {//have an enemy or have had one since we spawned
05239                                         PM_ContinueLegsAnim( BOTH_STAND2 );
05240                                         //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
05241                                 }
05242                                 else
05243                                 {//just stand there
05244                                         PM_ContinueLegsAnim( BOTH_STAND1 );
05245                                         //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
05246                                 }
05247                         }
05248                         else if ( pm->ps->clientNum >= MAX_CLIENTS &&
05249                                 pm_entSelf &&
05250                                 pm_entSelf->s.NPC_class == CLASS_WAMPA )
05251                         {
05252                                 if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
05253                                 {//holding a victim
05254                                         PM_ContinueLegsAnim( BOTH_STAND2 );
05255                                         //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
05256                                 }
05257                                 else
05258                                 {//not holding a victim
05259                                         PM_ContinueLegsAnim( BOTH_STAND1 );
05260                                         //PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
05261                                 }
05262                         }
05263                         else if ( (pm->ps->pm_flags & PMF_DUCKED) || (pm->ps->pm_flags & PMF_ROLLING) ) {
05264                                 if ((pm->ps->legsAnim) != BOTH_CROUCH1IDLE)
05265                                 {
05266                                         PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1IDLE, setAnimFlags, 100);
05267                                 }
05268                                 else
05269                                 {
05270                                         PM_ContinueLegsAnim( BOTH_CROUCH1IDLE );
05271                                 }
05272                         } else {
05273                                 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
05274                                 {
05276                                         //yeah.. the anim has a valid pose for the legs, it uses it (you can't move while using disruptor)
05277                                         PM_ContinueLegsAnim( TORSO_WEAPONREADY4 );
05278                                 }
05279                                 else
05280                                 {
05281                                         if (pm->ps->weapon == WP_SABER && BG_SabersOff( pm->ps ) )
05282                                         {
05283                                                 if (!PM_AdjustStandAnimForSlope())
05284                                                 {
05285                                                         //PM_ContinueLegsAnim( BOTH_STAND1 );
05286                                                         PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(BOTH_STAND1));
05287                                                 }
05288                                         }
05289                                         else
05290                                         {
05291                                                 if (pm->ps->weapon != WP_SABER || !PM_AdjustStandAnimForSlope())
05292                                                 {
05293                                                         if (pm->ps->weapon == WP_SABER)
05294                                                         {
05295                                                                 PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(PM_GetSaberStance()));
05296                                                         }
05297                                                         else
05298                                                         {
05299                                                                 PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(WeaponReadyLegsAnim[pm->ps->weapon]));
05300                                                         }
05301                                                 }
05302                                         }
05303                                 }
05304                         }
05305                 }
05306                 return;
05307         }
05308         
05309 
05310         footstep = qfalse;
05311 
05312         if (pm->ps->saberMove == LS_SPINATTACK)
05313         {
05314                 bobmove = 0.2f;
05315                 PM_ContinueLegsAnim( pm->ps->torsoAnim );
05316         }
05317         else if ( pm->ps->pm_flags & PMF_DUCKED )
05318         {
05319                 int rolled = 0;
05320 
05321                 bobmove = 0.5;  // ducked characters bob much faster
05322 
05323                 if ( ( (PM_RunningAnim( pm->ps->legsAnim )&&VectorLengthSquared(pm->ps->velocity)>=40000/*200*200*/) || PM_CanRollFromSoulCal( pm->ps ) ) &&
05324                         !BG_InRoll(pm->ps, pm->ps->legsAnim) )
05325                 {//roll!
05326                         rolled = PM_TryRoll();
05327                 }
05328                 if ( !rolled )
05329                 { //if the roll failed or didn't attempt, do standard crouching anim stuff.
05330                         if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
05331                                 if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
05332                                 {
05333                                         PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags, 100);
05334                                 }
05335                                 else
05336                                 {
05337                                         PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
05338                                 }
05339                         }
05340                         else {
05341                                 if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
05342                                 {
05343                                         PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags, 100);
05344                                 }
05345                                 else
05346                                 {
05347                                         PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
05348                                 }
05349                         }
05350                 }
05351                 else
05352                 { //otherwise send us into the roll
05353                         pm->ps->legsTimer = 0;
05354                         pm->ps->legsAnim = 0;
05355                         PM_SetAnim(SETANIM_BOTH,rolled,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
05356                         PM_AddEventWithParm( EV_ROLL, 0 );
05357                         pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
05358                         pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
05359                         pm->ps->pm_flags &= ~PMF_DUCKED;
05360                         pm->ps->pm_flags |= PMF_ROLLING;
05361                 }
05362         }
05363         else if ((pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
05364                 !PM_InRollComplete(pm->ps, pm->ps->legsAnim))
05365         {
05366                 bobmove = 0.5;  // ducked characters bob much faster
05367 
05368                 if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
05369                 {
05370                         if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
05371                         {
05372                                 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags, 100);
05373                         }
05374                         else
05375                         {
05376                                 PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
05377                         }
05378                 }
05379                 else
05380                 {
05381                         if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
05382                         {
05383                                 PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags, 100);
05384                         }
05385                         else
05386                         {
05387                                 PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
05388                         }
05389                 }
05390         }
05391         else
05392         {
05393                 int desiredAnim = -1;
05394 
05395                 if ((pm->ps->legsAnim == BOTH_FORCELAND1 ||
05396                         pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
05397                         pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
05398                         pm->ps->legsAnim == BOTH_FORCELANDLEFT1) &&
05399                         pm->ps->legsTimer > 0)
05400                 { //let it finish first
05401                         bobmove = 0.2f;
05402                 }
05403                 else if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
05404                 {//running
05405                         bobmove = 0.4f; // faster speeds bob faster
05406                         if ( pm->ps->clientNum >= MAX_CLIENTS &&
05407                                 pm_entSelf &&
05408                                 pm_entSelf->s.NPC_class == CLASS_WAMPA )
05409                         {
05410                                 if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
05411                                 {//full on run, on all fours
05412                                         desiredAnim = BOTH_RUN1;
05413                                 }
05414                                 else
05415                                 {//regular, upright run
05416                                         desiredAnim = BOTH_RUN2;
05417                                 }
05418                         }
05419                         else if ( pm->ps->clientNum >= MAX_CLIENTS &&
05420                                 pm_entSelf &&
05421                                 pm_entSelf->s.NPC_class == CLASS_RANCOR )
05422                         {//no run anims
05423                                 if ( (pm->ps->pm_flags&PMF_BACKWARDS_RUN) )
05424                                 {
05425                                         desiredAnim = BOTH_WALKBACK1;
05426                                 }
05427                                 else
05428                                 {
05429                                         desiredAnim = BOTH_WALK1;
05430                                 }
05431                         }
05432                         else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
05433                         {
05434                                 switch (pm->ps->fd.saberAnimLevel)
05435                                 {
05436                                 case SS_STAFF:
05437                                         if ( pm->ps->saberHolstered > 1 ) 
05438                                         {//saber off
05439                                                 desiredAnim = BOTH_RUNBACK1;
05440                                         }
05441                                         else
05442                                         {
05443                                                 //desiredAnim = BOTH_RUNBACK_STAFF;
05444                                                 //hmm.. stuff runback anim is pretty messed up for some reason.
05445                                                 desiredAnim = BOTH_RUNBACK2;
05446                                         }
05447                                         break;
05448                                 case SS_DUAL:
05449                                         if ( pm->ps->saberHolstered > 1 ) 
05450                                         {//sabers off
05451                                                 desiredAnim = BOTH_RUNBACK1;
05452                                         }
05453                                         else
05454                                         {
05455                                                 //desiredAnim = BOTH_RUNBACK_DUAL;
05456                                                 //and so is the dual
05457                                                 desiredAnim = BOTH_RUNBACK2;
05458                                         }
05459                                         break;
05460                                 default:
05461                                         if ( pm->ps->saberHolstered ) 
05462                                         {//saber off
05463                                                 desiredAnim = BOTH_RUNBACK1;
05464                                         }
05465                                         else
05466                                         {
05467                                                 desiredAnim = BOTH_RUNBACK2;
05468                                         }
05469                                         break;
05470                                 }
05471                         }
05472                         else
05473                         {
05474                                 switch (pm->ps->fd.saberAnimLevel)
05475                                 {
05476                                 case SS_STAFF:
05477                                         if ( pm->ps->saberHolstered > 1 )
05478                                         {//blades off
05479                                                 desiredAnim = BOTH_RUN1;
05480                                         }
05481                                         else if ( pm->ps->saberHolstered == 1 )
05482                                         {//1 blade on
05483                                                 desiredAnim = BOTH_RUN2;
05484                                         }
05485                                         else
05486                                         {
05487                                                 if (pm->ps->fd.forcePowersActive & (1<<FP_SPEED))
05488                                                 {
05489                                                         desiredAnim = BOTH_RUN1;
05490                                                 }
05491                                                 else
05492                                                 {
05493                                                         desiredAnim = BOTH_RUN_STAFF;
05494                                                 }
05495                                         }
05496                                         break;
05497                                 case SS_DUAL:
05498                                         if ( pm->ps->saberHolstered > 1 )
05499                                         {//blades off
05500                                                 desiredAnim = BOTH_RUN1;
05501                                         }
05502                                         else if ( pm->ps->saberHolstered == 1 )
05503                                         {//1 saber on
05504                                                 desiredAnim = BOTH_RUN2;
05505                                         }
05506                                         else
05507                                         {
05508                                                 desiredAnim = BOTH_RUN_DUAL;
05509                                         }
05510                                         break;
05511                                 default:
05512                                         if ( pm->ps->saberHolstered )
05513                                         {//saber off
05514                                                 desiredAnim = BOTH_RUN1;
05515                                         }
05516                                         else
05517                                         {
05518                                                 desiredAnim = BOTH_RUN2;
05519                                         }
05520                                         break;
05521                                 }
05522                         }
05523                         footstep = qtrue;
05524                 }
05525                 else
05526                 {
05527                         bobmove = 0.2f; // walking bobs slow
05528                         if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
05529                         {
05530                                 switch (pm->ps->fd.saberAnimLevel)
05531                                 {
05532                                 case SS_STAFF:
05533                                         if ( pm->ps->saberHolstered > 1 )
05534                                         {
05535                                                 desiredAnim = BOTH_WALKBACK1;
05536                                         }
05537                                         else if ( pm->ps->saberHolstered )
05538                                         {
05539                                                 desiredAnim = BOTH_WALKBACK2;
05540                                         }
05541                                         else
05542                                         {
05543                                                 desiredAnim = BOTH_WALKBACK_STAFF;
05544                                         }
05545                                         break;
05546                                 case SS_DUAL:
05547                                         if ( pm->ps->saberHolstered > 1 )
05548                                         {
05549                                                 desiredAnim = BOTH_WALKBACK1;
05550                                         }
05551                                         else if ( pm->ps->saberHolstered )
05552                                         {
05553                                                 desiredAnim = BOTH_WALKBACK2;
05554                                         }
05555                                         else
05556                                         {
05557                                                 desiredAnim = BOTH_WALKBACK_DUAL;
05558                                         }
05559                                         break;
05560                                 default:
05561                                         if ( pm->ps->saberHolstered )
05562                                         {
05563                                                 desiredAnim = BOTH_WALKBACK1;
05564                                         }
05565                                         else
05566                                         {
05567                                                 desiredAnim = BOTH_WALKBACK2;
05568                                         }
05569                                         break;
05570                                 }
05571                         }
05572                         else
05573                         {
05574                                 if ( pm->ps->weapon == WP_MELEE )
05575                                 {
05576                                         desiredAnim = BOTH_WALK1;
05577                                 }
05578                                 else if ( BG_SabersOff( pm->ps ) )
05579                                 {
05580                                         desiredAnim = BOTH_WALK1;
05581                                 }
05582                                 else
05583                                 {
05584                                         switch (pm->ps->fd.saberAnimLevel)
05585                                         {
05586                                         case SS_STAFF:
05587                                                 if ( pm->ps->saberHolstered > 1 )
05588                                                 {
05589                                                         desiredAnim = BOTH_WALK1;
05590                                                 }
05591                                                 else if ( pm->ps->saberHolstered )
05592                                                 {
05593                                                         desiredAnim = BOTH_WALK2;
05594                                                 }
05595                                                 else
05596                                                 {
05597                                                         desiredAnim = BOTH_WALK_STAFF;
05598                                                 }
05599                                                 break;
05600                                         case SS_DUAL:
05601                                                 if ( pm->ps->saberHolstered > 1 )
05602                                                 {
05603                                                         desiredAnim = BOTH_WALK1;
05604                                                 }
05605                                                 else if ( pm->ps->saberHolstered )
05606                                                 {
05607                                                         desiredAnim = BOTH_WALK2;
05608                                                 }
05609                                                 else
05610                                                 {
05611                                                         desiredAnim = BOTH_WALK_DUAL;
05612                                                 }
05613                                                 break;
05614                                         default:
05615                                                 if ( pm->ps->saberHolstered )
05616                                                 {
05617                                                         desiredAnim = BOTH_WALK1;
05618                                                 }
05619                                                 else
05620                                                 {
05621                                                         desiredAnim = BOTH_WALK2;
05622                                                 }
05623                                                 break;
05624                                         }
05625                                 }
05626                         }
05627                 }
05628 
05629                 if (desiredAnim != -1)
05630                 {
05631                         int ires = PM_LegsSlopeBackTransition(desiredAnim);
05632 
05633                         if ((pm->ps->legsAnim) != desiredAnim && ires == desiredAnim)
05634                         {
05635                                 PM_SetAnim(SETANIM_LEGS, desiredAnim, setAnimFlags, 100);
05636                         }
05637                         else
05638                         {
05639                                 PM_ContinueLegsAnim(ires);
05640                         }
05641                 }
05642         }
05643 
05644         // check for footstep / splash sounds
05645         old = pm->ps->bobCycle;
05646         pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
05647 
05648         // if we just crossed a cycle boundary, play an apropriate footstep event
05649         if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
05650         {
05651                 pm->ps->footstepTime = pm->cmd.serverTime + 300;
05652                 if ( pm->waterlevel == 1 ) {
05653                         // splashing
05654                         PM_AddEvent( EV_FOOTSPLASH );
05655                 } else if ( pm->waterlevel == 2 ) {
05656                         // wading / swimming at surface
05657                         PM_AddEvent( EV_SWIM );
05658                 } else if ( pm->waterlevel == 3 ) {
05659                         // no sound when completely underwater
05660                 }
05661         }
05662 }
05663 
05664 /*
05665 ==============
05666 PM_WaterEvents
05667 
05668 Generate sound events for entering and leaving water
05669 ==============
05670 */
05671 static void PM_WaterEvents( void ) {            // FIXME?
05672 #ifdef QAGAME
05673         qboolean impact_splash = qfalse;
05674 #endif
05675         //
05676         // if just entered a water volume, play a sound
05677         //
05678         if (!pml.previous_waterlevel && pm->waterlevel) {
05679 #ifdef QAGAME
05680                 if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
05681                 {
05682                         impact_splash = qtrue;
05683                 }
05684 #endif
05685                 PM_AddEvent( EV_WATER_TOUCH );
05686         }
05687 
05688         //
05689         // if just completely exited a water volume, play a sound
05690         //
05691         if (pml.previous_waterlevel && !pm->waterlevel) {
05692 #ifdef QAGAME
05693                 if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
05694                 {
05695                         impact_splash = qtrue;
05696                 }
05697 #endif
05698                 PM_AddEvent( EV_WATER_LEAVE );
05699         }
05700 
05701 #ifdef QAGAME
05702         if ( impact_splash )
05703         {
05704                 //play the splash effect
05705                 trace_t tr;
05706                 vec3_t  start, end;
05707 
05708 
05709                 VectorCopy( pm->ps->origin, start );
05710                 VectorCopy( pm->ps->origin, end );
05711 
05712                 // FIXME: set start and end better
05713                 start[2] += 10;
05714                 end[2] -= 40;
05715 
05716                 pm->trace( &tr, start, vec3_origin, vec3_origin, end, pm->ps->clientNum, MASK_WATER );
05717 
05718                 if ( tr.fraction < 1.0f )
05719                 {
05720                         if ( (tr.contents&CONTENTS_LAVA) )
05721                         {
05722                                 G_PlayEffect( EFFECT_LAVA_SPLASH, tr.endpos, tr.plane.normal );
05723                         }
05724                         else if ( (tr.contents&CONTENTS_SLIME) )
05725                         {
05726                                 G_PlayEffect( EFFECT_ACID_SPLASH, tr.endpos, tr.plane.normal );
05727                         }
05728                         else //must be water
05729                         {
05730                                 G_PlayEffect( EFFECT_WATER_SPLASH, tr.endpos, tr.plane.normal );
05731                         }
05732                 }
05733         }
05734 #endif
05735 
05736         //
05737         // check for head just going under water
05738         //
05739         if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
05740                 PM_AddEvent( EV_WATER_UNDER );
05741         }
05742 
05743         //
05744         // check for head just coming out of water
05745         //
05746         if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
05747                 PM_AddEvent( EV_WATER_CLEAR );
05748         }
05749 }
05750 
05751 void BG_ClearRocketLock( playerState_t *ps )
05752 {
05753         if ( ps )
05754         {
05755                 ps->rocketLockIndex = ENTITYNUM_NONE;
05756                 ps->rocketLastValidTime = 0;
05757                 ps->rocketLockTime = -1;
05758                 ps->rocketTargetTime = 0;
05759         }
05760 }
05761 
05762 /*
05763 ===============
05764 PM_BeginWeaponChange
05765 ===============
05766 */
05767 void PM_BeginWeaponChange( int weapon ) {
05768         if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
05769                 return;
05770         }
05771 
05772         if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
05773                 return;
05774         }
05775         
05776         if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
05777                 return;
05778         }
05779 
05780         // turn of any kind of zooming when weapon switching.
05781         if (pm->ps->zoomMode)
05782         {
05783                 pm->ps->zoomMode = 0;
05784                 pm->ps->zoomTime = pm->ps->commandTime;
05785         }
05786 
05787         PM_AddEventWithParm( EV_CHANGE_WEAPON, weapon );
05788         pm->ps->weaponstate = WEAPON_DROPPING;
05789         pm->ps->weaponTime += 200;
05790         //PM_StartTorsoAnim( TORSO_DROPWEAP1 );
05791         PM_SetAnim(SETANIM_TORSO, TORSO_DROPWEAP1, SETANIM_FLAG_OVERRIDE, 0);
05792 
05793         BG_ClearRocketLock( pm->ps );
05794 }
05795 
05796 
05797 /*
05798 ===============
05799 PM_FinishWeaponChange
05800 ===============
05801 */
05802 void PM_FinishWeaponChange( void ) {
05803         int             weapon;
05804 
05805         weapon = pm->cmd.weapon;
05806         if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
05807                 weapon = WP_NONE;
05808         }
05809 
05810         if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
05811                 weapon = WP_NONE;
05812         }
05813 
05814         if (weapon == WP_SABER)
05815         {
05816                 PM_SetSaberMove(LS_DRAW);
05817         }
05818         else
05819         {
05820                 //PM_StartTorsoAnim( TORSO_RAISEWEAP1);
05821                 PM_SetAnim(SETANIM_TORSO, TORSO_RAISEWEAP1, SETANIM_FLAG_OVERRIDE, 0);
05822         }
05823         pm->ps->weapon = weapon;
05824         pm->ps->weaponstate = WEAPON_RAISING;
05825         pm->ps->weaponTime += 250;
05826 }
05827 
05828 #ifdef QAGAME
05829 extern void WP_GetVehicleCamPos( gentity_t *ent, gentity_t *pilot, vec3_t camPos );
05830 #else
05831 extern void CG_GetVehicleCamPos( vec3_t camPos );
05832 #endif
05833 #define MAX_XHAIR_DIST_ACCURACY 20000.0f
05834 int BG_VehTraceFromCamPos( trace_t *camTrace, bgEntity_t *bgEnt, const vec3_t entOrg, const vec3_t shotStart, const vec3_t end, vec3_t newEnd, vec3_t shotDir, float bestDist )
05835 {
05836         //NOTE: this MUST stay up to date with the method used in CG_ScanForCrosshairEntity (where it checks the doExtraVehTraceFromViewPos bool)
05837         vec3_t  viewDir2End, extraEnd, camPos;
05838         float   minAutoAimDist;
05839 
05840 #ifdef QAGAME
05841         WP_GetVehicleCamPos( (gentity_t *)bgEnt, (gentity_t *)bgEnt->m_pVehicle->m_pPilot, camPos );
05842 #else
05843         CG_GetVehicleCamPos( camPos );
05844 #endif
05845         
05846         minAutoAimDist = Distance( entOrg, camPos ) + (bgEnt->m_pVehicle->m_pVehicleInfo->length/2.0f) + 200.0f;
05847 
05848         VectorCopy( end, newEnd );
05849         VectorSubtract( end, camPos, viewDir2End );
05850         VectorNormalize( viewDir2End );
05851         VectorMA( camPos, MAX_XHAIR_DIST_ACCURACY, viewDir2End, extraEnd );
05852 
05853 #ifdef QAGAME
05854         trap_Trace( camTrace, camPos, vec3_origin, vec3_origin, extraEnd, 
05855                 bgEnt->s.number, CONTENTS_SOLID|CONTENTS_BODY );
05856 #else
05857         pm->trace( camTrace, camPos, vec3_origin, vec3_origin, extraEnd, 
05858                 bgEnt->s.number, CONTENTS_SOLID|CONTENTS_BODY );
05859 #endif
05860 
05861         if ( !camTrace->allsolid
05862                 && !camTrace->startsolid
05863                 && camTrace->fraction < 1.0f
05864                 && (camTrace->fraction*MAX_XHAIR_DIST_ACCURACY) > minAutoAimDist 
05865                 && ((camTrace->fraction*MAX_XHAIR_DIST_ACCURACY)-Distance( entOrg, camPos )) < bestDist )
05866         {//this trace hit *something* that's closer than the thing the main trace hit, so use this result instead
05867                 VectorCopy( camTrace->endpos, newEnd );
05868                 VectorSubtract( newEnd, shotStart, shotDir );
05869                 VectorNormalize( shotDir );
05870                 return (camTrace->entityNum+1);
05871         }
05872         return 0;
05873 }
05874 
05875 void PM_RocketLock( float lockDist, qboolean vehicleLock )
05876 {
05877         // Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
05878         //      implement our alt-fire locking stuff
05879         vec3_t          ang;
05880         trace_t         tr;
05881         
05882         vec3_t muzzleOffPoint, muzzlePoint, forward, right, up;
05883 
05884         if ( vehicleLock )
05885         {
05886                 AngleVectors( pm->ps->viewangles, forward, right, up );
05887                 VectorCopy( pm->ps->origin, muzzlePoint );
05888                 VectorMA( muzzlePoint, lockDist, forward, ang );
05889         }
05890         else
05891         {
05892                 AngleVectors( pm->ps->viewangles, forward, right, up );
05893 
05894                 AngleVectors(pm->ps->viewangles, ang, NULL, NULL);
05895 
05896                 VectorCopy( pm->ps->origin, muzzlePoint );
05897                 VectorCopy(WP_MuzzlePoint[WP_ROCKET_LAUNCHER], muzzleOffPoint);
05898 
05899                 VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
05900                 VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
05901                 muzzlePoint[2] += pm->ps->viewheight + muzzleOffPoint[2];
05902                 ang[0] = muzzlePoint[0] + ang[0]*lockDist;
05903                 ang[1] = muzzlePoint[1] + ang[1]*lockDist;
05904                 ang[2] = muzzlePoint[2] + ang[2]*lockDist;
05905         }
05906 
05907 
05908         pm->trace(&tr, muzzlePoint, NULL, NULL, ang, pm->ps->clientNum, MASK_PLAYERSOLID);
05909 
05910         if ( vehicleLock )
05911         {//vehicles also do a trace from the camera point if the main one misses
05912                 if ( tr.fraction >= 1.0f )
05913                 {
05914                         trace_t camTrace;
05915                         vec3_t newEnd, shotDir;
05916                         if ( BG_VehTraceFromCamPos( &camTrace, PM_BGEntForNum(pm->ps->clientNum), pm->ps->origin, muzzlePoint, tr.endpos, newEnd, shotDir, (tr.fraction*lockDist) ) )
05917                         {
05918                                 memcpy( &tr, &camTrace, sizeof(tr) );
05919                         }
05920                 }
05921         }
05922 
05923         if (tr.fraction != 1 && tr.entityNum < ENTITYNUM_NONE && tr.entityNum != pm->ps->clientNum)
05924         {
05925                 bgEntity_t *bgEnt = PM_BGEntForNum(tr.entityNum);
05926                 if ( bgEnt && (bgEnt->s.powerups&PW_CLOAKED) )
05927                 {
05928                         pm->ps->rocketLockIndex = ENTITYNUM_NONE;
05929                         pm->ps->rocketLockTime = 0;
05930                 }
05931                 else if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC))
05932                 {
05933                         if (pm->ps->rocketLockIndex == ENTITYNUM_NONE)
05934                         {
05935                                 pm->ps->rocketLockIndex = tr.entityNum;
05936                                 pm->ps->rocketLockTime = pm->cmd.serverTime;
05937                         }
05938                         else if (pm->ps->rocketLockIndex != tr.entityNum && pm->ps->rocketTargetTime < pm->cmd.serverTime)
05939                         {
05940                                 pm->ps->rocketLockIndex = tr.entityNum;
05941                                 pm->ps->rocketLockTime = pm->cmd.serverTime;
05942                         }
05943                         else if (pm->ps->rocketLockIndex == tr.entityNum)
05944                         {
05945                                 if (pm->ps->rocketLockTime == -1)
05946                                 {
05947                                         pm->ps->rocketLockTime = pm->ps->rocketLastValidTime;
05948                                 }
05949                         }
05950 
05951                         if (pm->ps->rocketLockIndex == tr.entityNum)
05952                         {
05953                                 pm->ps->rocketTargetTime = pm->cmd.serverTime + 500;
05954                         }
05955                 }
05956                 else if (!vehicleLock)
05957                 {
05958                         if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
05959                         {
05960                                 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
05961                                 pm->ps->rocketLockTime = 0;
05962                         }
05963                 }
05964         }
05965         else if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
05966         {
05967                 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
05968                 pm->ps->rocketLockTime = 0;
05969         }
05970         else
05971         {
05972                 if (pm->ps->rocketLockTime != -1)
05973                 {
05974                         pm->ps->rocketLastValidTime = pm->ps->rocketLockTime;
05975                 }
05976                 pm->ps->rocketLockTime = -1;
05977         }
05978 }
05979 
05980 //---------------------------------------
05981 static qboolean PM_DoChargedWeapons( qboolean vehicleRocketLock, bgEntity_t *veh )
05982 //---------------------------------------
05983 {
05984         qboolean        charging = qfalse,
05985                                 altFire = qfalse;
05986 
05987         if ( vehicleRocketLock )
05988         {
05989                 if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
05990                 {//actually charging
05991                         if ( veh 
05992                                 && veh->m_pVehicle )
05993                         {//just make sure we have this veh info
05994                                 if ( ( (pm->cmd.buttons&BUTTON_ATTACK)
05995                                                 &&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
05996                                                 &&pm->ps->ammo[0]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
05997                                                 || 
05998                                         ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)
05999                                                 &&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming
06000                                                 &&pm->ps->ammo[1]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot ) )
06001                                 {//pressing the appropriate fire button for the lock-on/charging weapon
06002                                         PM_RocketLock(16384, qtrue);
06003                                         charging = qtrue;
06004                                 }
06005                                 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06006                                 {
06007                                         altFire = qtrue;
06008                                 }
06009                         }
06010                 }
06011                 //else, let go and should fire now
06012         }
06013         else
06014         {
06015                 // If you want your weapon to be a charging weapon, just set this bit up
06016                 switch( pm->ps->weapon )
06017                 {
06018                 //------------------
06019                 case WP_BRYAR_PISTOL:
06020 
06021                         // alt-fire charges the weapon
06022                         //if ( pm->gametype == GT_SIEGE )
06023                         if (1)
06024                         {
06025                                 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06026                                 {
06027                                         charging = qtrue;
06028                                         altFire = qtrue;
06029                                 }
06030                         }
06031                         break;
06032 
06033                 case WP_CONCUSSION:
06034                         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06035                         {
06036                                 altFire = qtrue;
06037                         }
06038                         break;
06039 
06040                 case WP_BRYAR_OLD:
06041 
06042                         // alt-fire charges the weapon
06043                         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06044                         {
06045                                 charging = qtrue;
06046                                 altFire = qtrue;
06047                         }
06048                         break;
06049                 
06050                 //------------------
06051                 case WP_BOWCASTER:
06052 
06053                         // primary fire charges the weapon
06054                         if ( pm->cmd.buttons & BUTTON_ATTACK )
06055                         {
06056                                 charging = qtrue;
06057                         }
06058                         break;
06059                 
06060                 //------------------
06061                 case WP_ROCKET_LAUNCHER:
06062                         if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) 
06063                                 && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] >= weaponData[pm->ps->weapon].altEnergyPerShot )
06064                         {
06065                                 PM_RocketLock(2048,qfalse);
06066                                 charging = qtrue;
06067                                 altFire = qtrue;
06068                         }
06069                         break;
06070 
06071                 //------------------
06072                 case WP_THERMAL:
06073 
06074                         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06075                         {
06076                                 altFire = qtrue; // override default of not being an alt-fire
06077                                 charging = qtrue;
06078                         }
06079                         else if ( pm->cmd.buttons & BUTTON_ATTACK )
06080                         {
06081                                 charging = qtrue;
06082                         }
06083                         break;
06084 
06085                 case WP_DEMP2:
06086                         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06087                         {
06088                                 altFire = qtrue; // override default of not being an alt-fire
06089                                 charging = qtrue;
06090                         }
06091                         break;
06092 
06093                 case WP_DISRUPTOR:
06094                         if ((pm->cmd.buttons & BUTTON_ATTACK) &&
06095                                 pm->ps->zoomMode == 1 &&
06096                                 pm->ps->zoomLocked)
06097                         {
06098                                 if (!pm->cmd.forwardmove &&
06099                                         !pm->cmd.rightmove &&
06100                                         pm->cmd.upmove <= 0)
06101                                 {
06102                                         charging = qtrue;
06103                                         altFire = qtrue;
06104                                 }
06105                                 else
06106                                 {
06107                                         charging = qfalse;
06108                                         altFire = qfalse;
06109                                 }
06110                         }
06111 
06112                         if (pm->ps->zoomMode != 1 &&
06113                                 pm->ps->weaponstate == WEAPON_CHARGING_ALT)
06114                         {
06115                                 pm->ps->weaponstate = WEAPON_READY;
06116                                 charging = qfalse;
06117                                 altFire = qfalse;
06118                         }
06119 
06120                 } // end switch
06121         }
06122 
06123         // set up the appropriate weapon state based on the button that's down.  
06124         //      Note that we ALWAYS return if charging is set ( meaning the buttons are still down )
06125         if ( charging )
06126         {
06127                 if ( altFire )
06128                 {
06129                         if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT )
06130                         {
06131                                 // charge isn't started, so do it now
06132                                 pm->ps->weaponstate = WEAPON_CHARGING_ALT;
06133                                 pm->ps->weaponChargeTime = pm->cmd.serverTime;
06134                                 pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
06135 
06136 #ifdef _DEBUG
06137                                 Com_Printf("Starting charge\n");
06138 #endif
06139                                 assert(pm->ps->weapon > WP_NONE);
06140                                 BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE_ALT, pm->ps->weapon, pm->ps);
06141                         }
06142 
06143                         if ( vehicleRocketLock )
06144                         {//check vehicle ammo
06145                                 if ( veh && pm->ps->ammo[1] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot )
06146                                 {
06147                                         pm->ps->weaponstate = WEAPON_CHARGING_ALT;
06148                                         goto rest;
06149                                 }
06150                         }
06151                         else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].altChargeSub+weaponData[pm->ps->weapon].altEnergyPerShot))
06152                         {
06153                                 pm->ps->weaponstate = WEAPON_CHARGING_ALT;
06154 
06155                                 goto rest;
06156                         }
06157                         else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].altMaxCharge)
06158                         {
06159                                 if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
06160                                 {
06161                                         pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].altChargeSub;
06162                                         pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
06163                                 }
06164                         }
06165                 }
06166                 else
06167                 {
06168                         if ( pm->ps->weaponstate != WEAPON_CHARGING )
06169                         {
06170                                 // charge isn't started, so do it now
06171                                 pm->ps->weaponstate = WEAPON_CHARGING;
06172                                 pm->ps->weaponChargeTime = pm->cmd.serverTime;
06173                                 pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
06174 
06175 #ifdef _DEBUG
06176                                 Com_Printf("Starting charge\n");
06177 #endif
06178                                 BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE, pm->ps->weapon, pm->ps);
06179                         }
06180 
06181                         if ( vehicleRocketLock )
06182                         {
06183                                 if ( veh && pm->ps->ammo[0] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
06184                                 {//check vehicle ammo
06185                                         pm->ps->weaponstate = WEAPON_CHARGING;
06186                                         goto rest;
06187                                 }
06188                         }
06189                         else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].chargeSub+weaponData[pm->ps->weapon].energyPerShot))
06190                         {
06191                                 pm->ps->weaponstate = WEAPON_CHARGING;
06192 
06193                                 goto rest;
06194                         }
06195                         else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].maxCharge)
06196                         {
06197                                 if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
06198                                 {
06199                                         pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].chargeSub;
06200                                         pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
06201                                 }
06202                         }
06203                 }
06204 
06205                 return qtrue; // short-circuit rest of weapon code
06206         }
06207 rest:
06208         // Only charging weapons should be able to set these states...so....
06209         //      let's see which fire mode we need to set up now that the buttons are up
06210         if ( pm->ps->weaponstate == WEAPON_CHARGING )
06211         {
06212                 // weapon has a charge, so let us do an attack
06213 #ifdef _DEBUG
06214                 Com_Printf("Firing.  Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
06215 #endif
06216 
06217                 // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
06218                 pm->cmd.buttons |= BUTTON_ATTACK;
06219                 pm->ps->eFlags |= EF_FIRING;
06220         }
06221         else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
06222         {
06223                 // weapon has a charge, so let us do an alt-attack
06224 #ifdef _DEBUG
06225                 Com_Printf("Firing.  Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
06226 #endif
06227 
06228                 // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
06229                 pm->cmd.buttons |= BUTTON_ALT_ATTACK;
06230                 pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING);
06231         }
06232 
06233         return qfalse; // continue with the rest of the weapon code
06234 }
06235 
06236 
06237 #define BOWCASTER_CHARGE_UNIT   200.0f  // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
06238 #define BRYAR_CHARGE_UNIT               200.0f  // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
06239 
06240 int PM_ItemUsable(playerState_t *ps, int forcedUse)
06241 {
06242         vec3_t fwd, fwdorg, dest, pos;
06243         vec3_t yawonly;
06244         vec3_t mins, maxs;
06245         vec3_t trtest;
06246         trace_t tr;
06247 
06248         if (ps->m_iVehicleNum)
06249         {
06250                 return 0;
06251         }
06252 
06253         if (ps->pm_flags & PMF_USE_ITEM_HELD)
06254         { //force to let go first
06255                 return 0;
06256         }
06257 
06258         if (ps->duelInProgress)
06259         { //not allowed to use holdables while in a private duel.
06260                 return 0;
06261         }
06262 
06263         if (!forcedUse)
06264         {
06265                 forcedUse = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
06266         }
06267 
06268         if (!BG_IsItemSelectable(ps, forcedUse))
06269         {
06270                 return 0;
06271         }
06272 
06273         switch (forcedUse)
06274         {
06275         case HI_MEDPAC:
06276         case HI_MEDPAC_BIG:
06277                 if (ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH])
06278                 {
06279                         return 0;
06280                 }
06281                 if (ps->stats[STAT_HEALTH] <= 0 ||
06282                         (ps->eFlags & EF_DEAD))
06283                 {
06284                         return 0;
06285                 }
06286 
06287                 return 1;
06288         case HI_SEEKER:
06289                 if (ps->eFlags & EF_SEEKERDRONE)
06290                 {
06291                         PM_AddEventWithParm(EV_ITEMUSEFAIL, SEEKER_ALREADYDEPLOYED);
06292                         return 0;
06293                 }
06294 
06295                 return 1;
06296         case HI_SENTRY_GUN:
06297                 if (ps->fd.sentryDeployed)
06298                 {
06299                         PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_ALREADYPLACED);
06300                         return 0;
06301                 }
06302 
06303                 yawonly[ROLL] = 0;
06304                 yawonly[PITCH] = 0;
06305                 yawonly[YAW] = ps->viewangles[YAW];
06306 
06307                 VectorSet( mins, -8, -8, 0 );
06308                 VectorSet( maxs, 8, 8, 24 );
06309 
06310                 AngleVectors(yawonly, fwd, NULL, NULL);
06311 
06312                 fwdorg[0] = ps->origin[0] + fwd[0]*64;
06313                 fwdorg[1] = ps->origin[1] + fwd[1]*64;
06314                 fwdorg[2] = ps->origin[2] + fwd[2]*64;
06315 
06316                 trtest[0] = fwdorg[0] + fwd[0]*16;
06317                 trtest[1] = fwdorg[1] + fwd[1]*16;
06318                 trtest[2] = fwdorg[2] + fwd[2]*16;
06319 
06320                 pm->trace(&tr, ps->origin, mins, maxs, trtest, ps->clientNum, MASK_PLAYERSOLID);
06321 
06322                 if ((tr.fraction != 1 && tr.entityNum != ps->clientNum) || tr.startsolid || tr.allsolid)
06323                 {
06324                         PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_NOROOM);
06325                         return 0;
06326                 }
06327 
06328                 return 1;
06329         case HI_SHIELD:
06330                 mins[0] = -8;
06331                 mins[1] = -8;
06332                 mins[2] = 0;
06333 
06334                 maxs[0] = 8;
06335                 maxs[1] = 8;
06336                 maxs[2] = 8;
06337 
06338                 AngleVectors (ps->viewangles, fwd, NULL, NULL);
06339                 fwd[2] = 0;
06340                 VectorMA(ps->origin, 64, fwd, dest);
06341                 pm->trace(&tr, ps->origin, mins, maxs, dest, ps->clientNum, MASK_SHOT );
06342                 if (tr.fraction > 0.9 && !tr.startsolid && !tr.allsolid)
06343                 {
06344                         VectorCopy(tr.endpos, pos);
06345                         VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
06346                         pm->trace( &tr, pos, mins, maxs, dest, ps->clientNum, MASK_SOLID );
06347                         if ( !tr.startsolid && !tr.allsolid )
06348                         {
06349                                 return 1;
06350                         }
06351                 }
06352                 PM_AddEventWithParm(EV_ITEMUSEFAIL, SHIELD_NOROOM);
06353                 return 0;
06354         case HI_JETPACK: //check for stuff here?
06355                 return 1;
06356         case HI_HEALTHDISP:
06357                 return 1;
06358         case HI_AMMODISP:
06359                 return 1;
06360         case HI_EWEB:
06361                 return 1;
06362         case HI_CLOAK: //check for stuff here?
06363                 return 1;
06364         default:
06365                 return 1;
06366         }
06367 }
06368 
06369 //cheesy vehicle weapon hackery
06370 qboolean PM_CanSetWeaponAnims(void)
06371 {
06372     if (pm->ps->m_iVehicleNum)
06373         {
06374                 return qfalse;
06375         }
06376 
06377         return qtrue;
06378 }
06379 
06380 //perform player anim overrides while on vehicle.
06381 extern int PM_irand_timesync(int val1, int val2);
06382 void PM_VehicleWeaponAnimate(void)
06383 {
06384         bgEntity_t *veh = pm_entVeh;
06385         Vehicle_t *pVeh;
06386         int iFlags = 0, iBlend = 0, Anim = -1;
06387 
06388         if (!veh ||
06389                 !veh->m_pVehicle ||
06390                 !veh->m_pVehicle->m_pPilot ||
06391                 !veh->m_pVehicle->m_pPilot->playerState ||
06392                 pm->ps->clientNum != veh->m_pVehicle->m_pPilot->playerState->clientNum)
06393         { //make sure the vehicle exists, and its pilot is this player
06394                 return;
06395         }
06396 
06397         pVeh = veh->m_pVehicle;
06398 
06399         if (pVeh->m_pVehicleInfo->type == VH_WALKER ||
06400                 pVeh->m_pVehicleInfo->type == VH_FIGHTER)
06401         { //slightly hacky I guess, but whatever.
06402                 return;
06403         }
06404 backAgain:
06405         // If they're firing, play the right fire animation.
06406         if ( pm->cmd.buttons & ( BUTTON_ATTACK | BUTTON_ALT_ATTACK ) )
06407         {
06408                 iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
06409                 iBlend = 200;
06410 
06411                 switch ( pm->ps->weapon )
06412                 {
06413                         case WP_SABER:
06414                                 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
06415                                 { //don't do anything.. I guess.
06416                                         pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
06417                                         goto backAgain;
06418                                 }
06419                                 // If we're already in an attack animation, leave (let it continue).
06420                                 if (pm->ps->torsoTimer <= 0)
06421                                 { //we'll be starting a new attack
06422                                         PM_AddEvent(EV_SABER_ATTACK);
06423                                 }
06424 
06425                                 //just set it to something so we have a proper trail. This is a stupid
06426                                 //hack (much like the rest of this function)
06427                                 pm->ps->saberMove = LS_R_TL2BR;
06428 
06429                                 if ( pm->ps->torsoTimer > 0 && (pm->ps->torsoAnim == BOTH_VS_ATR_S ||
06430                                                 pm->ps->torsoAnim == BOTH_VS_ATL_S) )
06431                                 {
06432                                         /*
06433                                         //FIXME: no need to even call the PM_SetAnim at all in this case
06434                                         Anim = (animNumber_t)pm->ps->torsoAnim;
06435                                         iFlags = SETANIM_FLAG_NORMAL;
06436                                         break;
06437                                         */
06438                                         return;
06439                                 }
06440 
06441                                 // Start the attack.
06442                                 if ( pm->cmd.rightmove > 0 )    //right side attack
06443                                 {
06444                                         Anim = BOTH_VS_ATR_S;
06445                                 }
06446                                 else if ( pm->cmd.rightmove < 0 )       //left-side attack
06447                                 {
06448                                         Anim = BOTH_VS_ATL_S;
06449                                 }
06450                                 else    //random
06451                                 {
06452                                         //FIXME: alternate back and forth or auto-aim?
06453                                         //if ( !Q_irand( 0, 1 ) )
06454                                         if (!PM_irand_timesync(0, 1))
06455                                         {
06456                                                 Anim = BOTH_VS_ATR_S;
06457                                         }
06458                                         else
06459                                         {
06460                                                 Anim = BOTH_VS_ATL_S;
06461                                         }
06462                                 }
06463 
06464                                 if (pm->ps->torsoTimer <= 0)
06465                                 { //restart the anim if we are already in it (and finished)
06466                                         iFlags |= SETANIM_FLAG_RESTART;
06467                                 }
06468                                 break;
06469 
06470                         case WP_BLASTER:
06471                                 // Override the shoot anim.
06472                                 if ( pm->ps->torsoAnim == BOTH_ATTACK3 )
06473                                 {
06474                                         if ( pm->cmd.rightmove > 0 )                    //right side attack
06475                                         {
06476                                                 Anim = BOTH_VS_ATR_G;
06477                                         }
06478                                         else if ( pm->cmd.rightmove < 0 )       //left side
06479                                         {
06480                                                 Anim = BOTH_VS_ATL_G;
06481                                         }
06482                                         else    //frontal
06483                                         {
06484                                                 Anim = BOTH_VS_ATF_G;
06485                                         }
06486                                 }
06487                                 break;
06488 
06489                         default:
06490                                 Anim = BOTH_VS_IDLE;
06491                                 break;
06492                 }
06493         }
06494         else if (veh->playerState && veh->playerState->speed < 0 &&
06495                 pVeh->m_pVehicleInfo->type == VH_ANIMAL)
06496         { //tauntaun is going backwards
06497                 Anim = BOTH_VT_WALK_REV;
06498                 iBlend = 600;
06499         }
06500         else if (veh->playerState && veh->playerState->speed < 0 &&
06501                 pVeh->m_pVehicleInfo->type == VH_SPEEDER)
06502         { //speeder is going backwards
06503                 Anim = BOTH_VS_REV;
06504                 iBlend = 600;
06505         }
06506         // They're not firing so play the Idle for the weapon.
06507         else
06508         {
06509                 iFlags = SETANIM_FLAG_NORMAL;
06510 
06511                 switch ( pm->ps->weapon )
06512                 {
06513                         case WP_SABER:
06514                                 if ( BG_SabersOff( pm->ps ) )
06515                                 { //saber holstered, normal idle
06516                                         Anim = BOTH_VS_IDLE;
06517                                 }
06518                                 // In the Air.
06519                                 //else if ( pVeh->m_ulFlags & VEH_FLYING )
06520                                 else if (0)
06521                                 {
06522                                         iBlend = 800;
06523                                         Anim = BOTH_VS_AIR_G;
06524                                         iFlags = SETANIM_FLAG_OVERRIDE;
06525                                 }
06526                                 // Crashing.
06527                                 //else if ( pVeh->m_ulFlags & VEH_CRASHING )
06528                                 else if (0)
06529                                 {
06530                                         pVeh->m_ulFlags &= ~VEH_CRASHING;       // Remove the flag, we are doing the animation.
06531                                         iBlend = 800;
06532                                         Anim = BOTH_VS_LAND_SR;
06533                                         iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
06534                                 }
06535                                 else
06536                                 {
06537                                         Anim = BOTH_VS_IDLE_SR;
06538                                 }
06539                                 break;
06540 
06541                         case WP_BLASTER:
06542                                 // In the Air.
06543                                 //if ( pVeh->m_ulFlags & VEH_FLYING )
06544                                 if (0)
06545                                 {
06546                                         iBlend = 800;
06547                                         Anim = BOTH_VS_AIR_G;
06548                                         iFlags = SETANIM_FLAG_OVERRIDE;
06549                                 }
06550                                 // Crashing.
06551                                 //else if ( pVeh->m_ulFlags & VEH_CRASHING )
06552                                 else if (0)
06553                                 {
06554                                         pVeh->m_ulFlags &= ~VEH_CRASHING;       // Remove the flag, we are doing the animation.
06555                                         iBlend = 800;
06556                                         Anim = BOTH_VS_LAND_G;
06557                                         iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
06558                                 }
06559                                 else
06560                                 {
06561                                         Anim = BOTH_VS_IDLE_G;
06562                                 }
06563                                 break;
06564 
06565                         default:
06566                                 Anim = BOTH_VS_IDLE;
06567                                 break;
06568                 }
06569         }
06570 
06571         if (Anim != -1)
06572         { //override it
06573                 if (pVeh->m_pVehicleInfo->type == VH_ANIMAL)
06574                 { //agh.. remap anims for the tauntaun
06575                         switch (Anim)
06576                         {
06577                         case BOTH_VS_IDLE:
06578                                 if (veh->playerState && veh->playerState->speed > 0)
06579                                 {
06580                                         if (veh->playerState->speed > pVeh->m_pVehicleInfo->speedMax)
06581                                         { //turbo
06582                                                 Anim = BOTH_VT_TURBO;
06583                                         }
06584                                         else
06585                                         {
06586                                                 Anim = BOTH_VT_RUN_FWD;
06587                                         }
06588                                 }
06589                                 else
06590                                 {
06591                                         Anim = BOTH_VT_IDLE;
06592                                 }
06593                                 break;
06594                         case BOTH_VS_ATR_S:
06595                                 Anim = BOTH_VT_ATR_S;
06596                                 break;
06597                         case BOTH_VS_ATL_S:
06598                                 Anim = BOTH_VT_ATL_S;
06599                                 break;
06600                         case BOTH_VS_ATR_G:
06601                 Anim = BOTH_VT_ATR_G;
06602                                 break;
06603                         case BOTH_VS_ATL_G:
06604                                 Anim = BOTH_VT_ATL_G;
06605                                 break;
06606                         case BOTH_VS_ATF_G:
06607                                 Anim = BOTH_VT_ATF_G;
06608                                 break;
06609                         case BOTH_VS_IDLE_SL:
06610                                 Anim = BOTH_VT_IDLE_S;
06611                                 break;
06612                         case BOTH_VS_IDLE_SR:
06613                                 Anim = BOTH_VT_IDLE_S;
06614                                 break;
06615                         case BOTH_VS_IDLE_G:
06616                                 Anim = BOTH_VT_IDLE_G;
06617                                 break;
06618 
06619                         //should not happen for tauntaun:
06620                         case BOTH_VS_AIR_G:
06621                         case BOTH_VS_LAND_SL:
06622                         case BOTH_VS_LAND_SR:
06623                         case BOTH_VS_LAND_G:
06624                                 return;
06625                         default:
06626                                 break;
06627                         }
06628                 }
06629 
06630                 PM_SetAnim(SETANIM_BOTH, Anim, iFlags, iBlend);
06631         }
06632 }
06633 
06634 /*
06635 ==============
06636 PM_Weapon
06637 
06638 Generates weapon events and modifes the weapon counter
06639 ==============
06640 */
06641 extern int PM_KickMoveForConditions(void);
06642 static void PM_Weapon( void )
06643 {
06644         int             addTime;
06645         int amount;
06646         int             killAfterItem = 0;
06647         bgEntity_t *veh = NULL;
06648         qboolean vehicleRocketLock = qfalse;
06649 
06650 #ifdef QAGAME
06651         if (pm->ps->clientNum >= MAX_CLIENTS &&
06652                 pm->ps->weapon == WP_NONE &&
06653                 pm->cmd.weapon == WP_NONE &&
06654                 pm_entSelf)
06655         { //npc with no weapon
06656                 gentity_t *gent = (gentity_t *)pm_entSelf;
06657                 if (gent->inuse && gent->client &&
06658                         !gent->localAnimIndex)
06659                 { //humanoid
06660                         pm->ps->torsoAnim = pm->ps->legsAnim;
06661                         pm->ps->torsoTimer = pm->ps->legsTimer;
06662                         return;
06663                 }
06664         }
06665 #endif
06666 
06667         if (!pm->ps->emplacedIndex &&
06668                 pm->ps->weapon == WP_EMPLACED_GUN)
06669         { //oh no!
06670                 int i = 0;
06671                 int weap = -1;
06672 
06673                 while (i < WP_NUM_WEAPONS)
06674                 {
06675                         if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) && i != WP_NONE)
06676                         { //this one's good
06677                                 weap = i;
06678                                 break;
06679                         }
06680                         i++;
06681                 }
06682 
06683                 if (weap != -1)
06684                 {
06685                         pm->cmd.weapon = weap;
06686                         pm->ps->weapon = weap;
06687                         return;
06688                 }
06689         }
06690 
06691         if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
06692                 &&pm->ps->m_iVehicleNum)
06693         { //riding a vehicle
06694                 veh = pm_entVeh;
06695                 if ( veh &&
06696                         (veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) )
06697                 {//riding a walker/fighter
06698                         //keep saber off, do no weapon stuff at all!
06699                         pm->ps->saberHolstered = 2;
06700 #ifdef QAGAME
06701                         pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
06702 #else
06703                         if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
06704                                 ||  g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
06705                         {//our vehicle uses a rocket launcher, so do the normal checks
06706                                 vehicleRocketLock = qtrue;
06707                                 pm->cmd.buttons &= ~BUTTON_ATTACK;
06708                         }
06709                         else
06710                         {
06711                                 pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
06712                         }
06713 #endif
06714                 }
06715         }
06716 
06717         if (pm->ps->weapon != WP_DISRUPTOR //not using disruptor
06718                 && pm->ps->weapon != WP_ROCKET_LAUNCHER//not using rocket launcher
06719                 && pm->ps->weapon != WP_THERMAL//not using thermals
06720                 && !pm->ps->m_iVehicleNum )//not a vehicle or in a vehicle
06721         { //check for exceeding max charge time if not using disruptor or rocket launcher or thermals
06722                 if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
06723                 {
06724                         int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
06725 
06726                         if (timeDif > MAX_WEAPON_CHARGE_TIME)
06727                         {
06728                                 pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
06729                         }
06730                 }
06731 
06732                 if ( pm->ps->weaponstate == WEAPON_CHARGING )
06733                 {
06734                         int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
06735 
06736                         if (timeDif > MAX_WEAPON_CHARGE_TIME)
06737                         {
06738                                 pm->cmd.buttons &= ~BUTTON_ATTACK;
06739                         }
06740                 }
06741         }
06742 
06743         if (pm->ps->forceHandExtend == HANDEXTEND_WEAPONREADY &&
06744                 PM_CanSetWeaponAnims())
06745         { //reset into weapon stance
06746                 if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
06747                 { //saber handles its own anims
06748                         if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
06749                         {
06750                                 //PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
06751                                 PM_StartTorsoAnim( TORSO_RAISEWEAP1);
06752                         }
06753                         else
06754                         {
06755                                 if (pm->ps->weapon == WP_EMPLACED_GUN)
06756                                 {
06757                                         PM_StartTorsoAnim( BOTH_GUNSIT1 );
06758                                 }
06759                                 else
06760                                 {
06761                                         //PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
06762                                         PM_StartTorsoAnim( TORSO_RAISEWEAP1);
06763                                 }
06764                         }
06765                 }
06766 
06767                 //we now go into a weapon raise anim after every force hand extend.
06768                 //this is so that my holster-view-weapon-when-hand-extend stuff works.
06769                 pm->ps->weaponstate = WEAPON_RAISING;
06770                 pm->ps->weaponTime += 250;
06771 
06772                 pm->ps->forceHandExtend = HANDEXTEND_NONE;
06773         }
06774         else if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
06775         { //nothing else should be allowed to happen during this time, including weapon fire
06776                 int desiredAnim = 0;
06777                 qboolean seperateOnTorso = qfalse;
06778                 qboolean playFullBody = qfalse;
06779                 int desiredOnTorso = 0;
06780 
06781                 switch(pm->ps->forceHandExtend)
06782                 {
06783                 case HANDEXTEND_FORCEPUSH:
06784                         desiredAnim = BOTH_FORCEPUSH;
06785                         break;
06786                 case HANDEXTEND_FORCEPULL:
06787                         desiredAnim = BOTH_FORCEPULL;
06788                         break;
06789                 case HANDEXTEND_FORCE_HOLD:
06790                         if ( (pm->ps->fd.forcePowersActive&(1<<FP_GRIP)) )
06791                         {//gripping
06792                                 desiredAnim = BOTH_FORCEGRIP_HOLD;
06793                         }
06794                         else if ( (pm->ps->fd.forcePowersActive&(1<<FP_LIGHTNING)) )
06795                         {//lightning
06796                                 if ( pm->ps->weapon == WP_MELEE
06797                                         && pm->ps->activeForcePass > FORCE_LEVEL_2 )
06798                                 {//2-handed lightning
06799                                         desiredAnim = BOTH_FORCE_2HANDEDLIGHTNING_HOLD;
06800                                 }
06801                                 else
06802                                 {
06803                                         desiredAnim = BOTH_FORCELIGHTNING_HOLD;
06804                                 }
06805                         }
06806                         else if ( (pm->ps->fd.forcePowersActive&(1<<FP_DRAIN)) )
06807                         {//draining
06808                                 desiredAnim = BOTH_FORCEGRIP_HOLD;
06809                         }
06810                         else
06811                         {//???
06812                                 desiredAnim = BOTH_FORCEGRIP_HOLD;
06813                         }
06814                         break;
06815                 case HANDEXTEND_SABERPULL:
06816                         desiredAnim = BOTH_SABERPULL;
06817                         break;
06818                 case HANDEXTEND_CHOKE:
06819                         desiredAnim = BOTH_CHOKE3; //left-handed choke
06820                         break;
06821                 case HANDEXTEND_DODGE:
06822                         desiredAnim = pm->ps->forceDodgeAnim;
06823                         break;
06824                 case HANDEXTEND_KNOCKDOWN:
06825                         if (pm->ps->forceDodgeAnim)
06826                         {
06827                                 if (pm->ps->forceDodgeAnim > 4)
06828                                 { //this means that we want to play a sepereate anim on the torso
06829                                         int originalDAnim = pm->ps->forceDodgeAnim-8; //-8 is the original legs anim
06830                                         if (originalDAnim == 2)
06831                                         {
06832                                                 desiredAnim = BOTH_FORCE_GETUP_B1;
06833                                         }
06834                                         else if (originalDAnim == 3)
06835                                         {
06836                                                 desiredAnim = BOTH_FORCE_GETUP_B3;
06837                                         }
06838                                         else
06839                                         {
06840                                                 desiredAnim = BOTH_GETUP1;
06841                                         }
06842 
06843                                         //now specify the torso anim
06844                                         seperateOnTorso = qtrue;
06845                                         desiredOnTorso = BOTH_FORCEPUSH;
06846                                 }
06847                                 else if (pm->ps->forceDodgeAnim == 2)
06848                                 {
06849                                         desiredAnim = BOTH_FORCE_GETUP_B1;
06850                                 }
06851                                 else if (pm->ps->forceDodgeAnim == 3)
06852                                 {
06853                                         desiredAnim = BOTH_FORCE_GETUP_B3;
06854                                 }
06855                                 else
06856                                 {
06857                                         desiredAnim = BOTH_GETUP1;
06858                                 }
06859                         }
06860                         else
06861                         {
06862                                 desiredAnim = BOTH_KNOCKDOWN1;
06863                         }
06864                         break;
06865                 case HANDEXTEND_DUELCHALLENGE:
06866                         desiredAnim = BOTH_ENGAGETAUNT;
06867                         break;
06868                 case HANDEXTEND_TAUNT:
06869                         desiredAnim = pm->ps->forceDodgeAnim;
06870                         if ( desiredAnim != BOTH_ENGAGETAUNT 
06871                                 && VectorCompare( pm->ps->velocity, vec3_origin )
06872                                 && pm->ps->groundEntityNum != ENTITYNUM_NONE ) 
06873                         {
06874                                 playFullBody = qtrue;
06875                         }
06876                         break;
06877                 case HANDEXTEND_PRETHROW:
06878                         desiredAnim = BOTH_A3_TL_BR;
06879                         playFullBody = qtrue;
06880                         break;
06881                 case HANDEXTEND_POSTTHROW:
06882                         desiredAnim = BOTH_D3_TL___;
06883                         playFullBody = qtrue;
06884                         break;
06885                 case HANDEXTEND_PRETHROWN:
06886                         desiredAnim = BOTH_KNEES1;
06887                         playFullBody = qtrue;
06888                         break;
06889                 case HANDEXTEND_POSTTHROWN:
06890                         if (pm->ps->forceDodgeAnim)
06891                         {
06892                                 desiredAnim = BOTH_FORCE_GETUP_F2;
06893                         }
06894                         else
06895                         {
06896                                 desiredAnim = BOTH_KNOCKDOWN5;
06897                         }
06898                         playFullBody = qtrue;
06899                         break;
06900                 case HANDEXTEND_DRAGGING:
06901                         desiredAnim = BOTH_B1_BL___;
06902                         break;
06903                 case HANDEXTEND_JEDITAUNT:
06904                         desiredAnim = BOTH_GESTURE1;
06905                         //playFullBody = qtrue;
06906                         break;
06907                         //Hmm... maybe use these, too?
06908                         //BOTH_FORCEHEAL_QUICK //quick heal (SP level 2 & 3)
06909                         //BOTH_MINDTRICK1 // wave (maybe for mind trick 2 & 3 - whole area, and for force seeing)
06910                         //BOTH_MINDTRICK2 // tap (maybe for mind trick 1 - one person)
06911                         //BOTH_FORCEGRIP_START //start grip
06912                         //BOTH_FORCEGRIP_HOLD //hold grip
06913                         //BOTH_FORCEGRIP_RELEASE //release grip
06914                         //BOTH_FORCELIGHTNING //quick lightning burst (level 1)
06915                         //BOTH_FORCELIGHTNING_START //start lightning
06916                         //BOTH_FORCELIGHTNING_HOLD //hold lightning
06917                         //BOTH_FORCELIGHTNING_RELEASE //release lightning
06918                 default:
06919                         desiredAnim = BOTH_FORCEPUSH;
06920                         break;
06921                 }
06922 
06923                 if (!seperateOnTorso)
06924                 { //of seperateOnTorso, handle it after setting the legs
06925                         PM_SetAnim(SETANIM_TORSO, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
06926                         pm->ps->torsoTimer = 1;
06927                 }
06928 
06929                 if (playFullBody)
06930                 { //sorry if all these exceptions are getting confusing. This one just means play on both legs and torso.
06931                         PM_SetAnim(SETANIM_BOTH, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
06932                         pm->ps->legsTimer = pm->ps->torsoTimer = 1;
06933                 }
06934                 else if (pm->ps->forceHandExtend == HANDEXTEND_DODGE || pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
06935                         (pm->ps->forceHandExtend == HANDEXTEND_CHOKE && pm->ps->groundEntityNum == ENTITYNUM_NONE) )
06936                 { //special case, play dodge anim on whole body, choke anim too if off ground
06937                         if (seperateOnTorso)
06938                         {
06939                                 PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
06940                                 pm->ps->legsTimer = 1;
06941 
06942                                 PM_SetAnim(SETANIM_TORSO, desiredOnTorso, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
06943                                 pm->ps->torsoTimer = 1;
06944                         }
06945                         else
06946                         {
06947                                 PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
06948                                 pm->ps->legsTimer = 1;
06949                         }
06950                 }
06951 
06952                 return;
06953         }
06954 
06955         if (BG_InSpecialJump(pm->ps->legsAnim) ||
06956                 BG_InRoll(pm->ps, pm->ps->legsAnim) ||
06957                 PM_InRollComplete(pm->ps, pm->ps->legsAnim))
06958         {
06959                 /*
06960                 if (pm->cmd.weapon != WP_MELEE &&
06961                         pm->ps->weapon != WP_MELEE &&
06962                         (pm->ps->stats[STAT_WEAPONS] & (1<<WP_SABER)))
06963                 { //it's alright also if we are melee
06964                         pm->cmd.weapon = WP_SABER;
06965                         pm->ps->weapon = WP_SABER;
06966                 }
06967                 */
06968                 if (pm->ps->weaponTime < pm->ps->legsTimer)
06969                 {
06970                         pm->ps->weaponTime = pm->ps->legsTimer;
06971                 }
06972         }
06973 
06974         if (pm->ps->duelInProgress)
06975         {
06976                 pm->cmd.weapon = WP_SABER;
06977                 pm->ps->weapon = WP_SABER;
06978 
06979                 if (pm->ps->duelTime >= pm->cmd.serverTime)
06980                 {
06981                         pm->cmd.upmove = 0;
06982                         pm->cmd.forwardmove = 0;
06983                         pm->cmd.rightmove = 0;
06984                 }
06985         }
06986 
06987         if (pm->ps->weapon == WP_SABER && pm->ps->saberMove != LS_READY && pm->ps->saberMove != LS_NONE)
06988         {
06989                 pm->cmd.weapon = WP_SABER; //don't allow switching out mid-attack
06990         }
06991 
06992         if (pm->ps->weapon == WP_SABER)
06993         {
06994                 //rww - we still need the item stuff, so we won't return immediately
06995                 PM_WeaponLightsaber();
06996                 killAfterItem = 1;
06997         }
06998         else if (pm->ps->weapon != WP_EMPLACED_GUN)
06999         {
07000                 pm->ps->saberHolstered = 0;
07001         }
07002 
07003         if (PM_CanSetWeaponAnims())
07004         {
07005                 if (pm->ps->weapon == WP_THERMAL ||
07006                         pm->ps->weapon == WP_TRIP_MINE ||
07007                         pm->ps->weapon == WP_DET_PACK)
07008                 {
07009                         if (pm->ps->weapon == WP_THERMAL)
07010                         {
07011                                 if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
07012                                         (pm->ps->weaponTime-200) <= 0)
07013                                 {
07014                                         PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
07015                                 }
07016                         }
07017                         else
07018                         {
07019                                 if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
07020                                         (pm->ps->weaponTime-700) <= 0)
07021                                 {
07022                                         PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
07023                                 }
07024                         }
07025                 }
07026         }
07027 
07028         // don't allow attack until all buttons are up
07029         if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
07030                 return;
07031         }
07032 
07033         // ignore if spectator
07034         if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
07035                         return;
07036         }
07037 
07038         // check for dead player
07039         if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
07040                 pm->ps->weapon = WP_NONE;
07041                 return;
07042         }
07043 
07044         // check for item using
07045         if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
07046                 if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
07047 
07048                         if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
07049                                 && pm->ps->m_iVehicleNum)
07050                         {//riding a vehicle, can't use holdable items, this button operates as the weapon link/unlink toggle
07051                                 return;
07052                         }
07053 
07054                         if (!pm->ps->stats[STAT_HOLDABLE_ITEM])
07055                         {
07056                                 return;
07057                         }
07058 
07059                         if (!PM_ItemUsable(pm->ps, 0))
07060                         {
07061                                 pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
07062                                 return;
07063                         }
07064                         else
07065                         {
07066                                 if (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag))
07067                                 {
07068                                         if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
07069                                                 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
07070                                                 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
07071                                                 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
07072                                                 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
07073                                                 bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
07074                                         { //never use up the binoculars or jetpack or dispensers or cloak or ...
07075                                                 pm->ps->stats[STAT_HOLDABLE_ITEMS] -= (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag);
07076                                         }
07077                                 }
07078                                 else
07079                                 {
07080                                         return; //this should not happen...
07081                                 }
07082 
07083                                 pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
07084                                 PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
07085 
07086                                 if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
07087                                         bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
07088                                         bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
07089                                         bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
07090                                         bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
07091                                         bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
07092                                 {
07093                                         pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
07094                                         BG_CycleInven(pm->ps, 1);
07095                                 }
07096                         }
07097                         return;
07098                 }
07099         } else {
07100                 pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
07101         }
07102 
07103         /*
07104         if (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
07105         { //we can't toggle zoom while using saber (for obvious reasons) so make sure it's always off
07106                 pm->ps->zoomMode = 0;
07107                 pm->ps->zoomFov = 0;
07108                 pm->ps->zoomLocked = qfalse;
07109                 pm->ps->zoomLockTime = 0;
07110         }
07111         */
07112 
07113         if (killAfterItem)
07114         {
07115                 return;
07116         }
07117 
07118         // make weapon function
07119         if ( pm->ps->weaponTime > 0 ) {
07120                 pm->ps->weaponTime -= pml.msec;
07121         }
07122 
07123         if (pm->ps->isJediMaster && pm->ps->emplacedIndex)
07124         {
07125                 pm->ps->emplacedIndex = 0;
07126                 pm->ps->saberHolstered = 0;
07127         }
07128 
07129         if (pm->ps->duelInProgress && pm->ps->emplacedIndex)
07130         {
07131                 pm->ps->emplacedIndex = 0;
07132                 pm->ps->saberHolstered = 0;
07133         }
07134 
07135         if (pm->ps->weapon == WP_EMPLACED_GUN && pm->ps->emplacedIndex)
07136         {
07137                 pm->cmd.weapon = WP_EMPLACED_GUN; //No switch for you!
07138                 PM_StartTorsoAnim( BOTH_GUNSIT1 );
07139         }
07140 
07141         if (pm->ps->isJediMaster || pm->ps->duelInProgress || pm->ps->trueJedi)
07142         {
07143                 pm->cmd.weapon = WP_SABER;
07144                 pm->ps->weapon = WP_SABER;
07145 
07146                 if (pm->ps->isJediMaster || pm->ps->trueJedi)
07147                 {
07148                         pm->ps->stats[STAT_WEAPONS] = (1 << WP_SABER);
07149                 }
07150         }
07151 
07152         amount = weaponData[pm->ps->weapon].energyPerShot;
07153 
07154         // take an ammo away if not infinite
07155         if ( pm->ps->weapon != WP_NONE &&
07156                 pm->ps->weapon == pm->cmd.weapon &&
07157                 (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) )
07158         {
07159                 if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
07160                 {
07161                         // enough energy to fire this weapon?
07162                         if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].energyPerShot &&
07163                                 pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].altEnergyPerShot) 
07164                         { //the weapon is out of ammo essentially because it cannot fire primary or secondary, so do the switch
07165                           //regardless of if the player is attacking or not
07166                                 PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
07167 
07168                                 if (pm->ps->weaponTime < 500)
07169                                 {
07170                                         pm->ps->weaponTime += 500;
07171                                 }
07172                                 return;
07173                         }
07174 
07175                         if (pm->ps->weapon == WP_DET_PACK && !pm->ps->hasDetPackPlanted && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < 1)
07176                         {
07177                                 PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
07178 
07179                                 if (pm->ps->weaponTime < 500)
07180                                 {
07181                                         pm->ps->weaponTime += 500;
07182                                 }
07183                                 return;
07184                         }
07185                 }
07186         }
07187 
07188         // check for weapon change
07189         // can't change if weapon is firing, but can change
07190         // again if lowering or raising
07191         if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
07192                 if ( pm->ps->weapon != pm->cmd.weapon ) {
07193                         PM_BeginWeaponChange( pm->cmd.weapon );
07194                 }
07195         }
07196 
07197         if ( pm->ps->weaponTime > 0 ) {
07198                 return;
07199         }
07200 
07201         if (pm->ps->weapon == WP_DISRUPTOR &&
07202                 pm->ps->zoomMode == 1)
07203         {
07204                 if (pm_cancelOutZoom)
07205                 {
07206                         pm->ps->zoomMode = 0;
07207                         pm->ps->zoomFov = 0;
07208                         pm->ps->zoomLocked = qfalse;
07209                         pm->ps->zoomLockTime = 0;
07210                         PM_AddEvent( EV_DISRUPTOR_ZOOMSOUND );
07211                         return;
07212                 }
07213 
07214                 if (pm->cmd.forwardmove ||
07215                         pm->cmd.rightmove ||
07216                         pm->cmd.upmove > 0)
07217                 {
07218                         return;
07219                 }
07220         }
07221 
07222         // change weapon if time
07223         if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
07224                 PM_FinishWeaponChange();
07225                 return;
07226         }
07227 
07228         if ( pm->ps->weaponstate == WEAPON_RAISING ) {
07229                 pm->ps->weaponstate = WEAPON_READY;
07230                 if (PM_CanSetWeaponAnims())
07231                 {
07232                         if ( pm->ps->weapon == WP_SABER )
07233                         {
07234                                 PM_StartTorsoAnim( PM_GetSaberStance() );
07235                         }
07236                         else if (pm->ps->weapon == WP_MELEE || PM_IsRocketTrooper())
07237                         {
07238                                 PM_StartTorsoAnim( pm->ps->legsAnim );
07239                         }
07240                         else
07241                         {
07242                                 if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
07243                                 {
07244                                         PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
07245                                 }
07246                                 else
07247                                 {
07248                                         if (pm->ps->weapon == WP_EMPLACED_GUN)
07249                                         {
07250                                                 PM_StartTorsoAnim( BOTH_GUNSIT1 );
07251                                         }
07252                                         else
07253                                         {
07254                                                 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
07255                                         }
07256                                 }
07257                         }
07258                 }
07259                 return;
07260         }
07261 
07262         if (PM_CanSetWeaponAnims() &&
07263                 !PM_IsRocketTrooper() &&
07264                 pm->ps->weaponstate == WEAPON_READY && pm->ps->weaponTime <= 0 &&
07265                 (pm->ps->weapon >= WP_BRYAR_PISTOL || pm->ps->weapon == WP_STUN_BATON) &&
07266                 pm->ps->torsoTimer <= 0 &&
07267                 (pm->ps->torsoAnim) != WeaponReadyAnim[pm->ps->weapon] &&
07268                 pm->ps->torsoAnim != TORSO_WEAPONIDLE3 &&
07269                 pm->ps->weapon != WP_EMPLACED_GUN)
07270         {
07271                 PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
07272         }
07273         else if (PM_CanSetWeaponAnims() &&
07274                 pm->ps->weapon == WP_MELEE)
07275         {
07276                 if (pm->ps->weaponTime <= 0 &&
07277                         pm->ps->forceHandExtend == HANDEXTEND_NONE)
07278                 {
07279                         int desTAnim = pm->ps->legsAnim;
07280 
07281                         if (desTAnim == BOTH_STAND1 ||
07282                                 desTAnim == BOTH_STAND2)
07283                         { //remap the standard standing anims for melee stance
07284                                 desTAnim = BOTH_STAND6;
07285                         }
07286 
07287                         if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
07288                         { //don't do this while holding attack
07289                                 if (pm->ps->torsoAnim != desTAnim)
07290                                 {
07291                                         PM_StartTorsoAnim( desTAnim );
07292                                 }
07293                         }
07294                 }
07295         }
07296         else if (PM_CanSetWeaponAnims() && PM_IsRocketTrooper())
07297         {
07298                 int desTAnim = pm->ps->legsAnim;
07299 
07300                 if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
07301                 { //don't do this while holding attack
07302                         if (pm->ps->torsoAnim != desTAnim)
07303                         {
07304                                 PM_StartTorsoAnim( desTAnim );
07305                         }
07306                 }
07307         }
07308 
07309         if (((pm->ps->torsoAnim) == TORSO_WEAPONREADY4 ||
07310                 (pm->ps->torsoAnim) == BOTH_ATTACK4) &&
07311                 (pm->ps->weapon != WP_DISRUPTOR || pm->ps->zoomMode != 1))
07312         {
07313                 if (pm->ps->weapon == WP_EMPLACED_GUN)
07314                 {
07315                         PM_StartTorsoAnim( BOTH_GUNSIT1 );
07316                 }
07317                 else if (PM_CanSetWeaponAnims())
07318                 {
07319                         PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
07320                 }
07321         }
07322         else if (((pm->ps->torsoAnim) != TORSO_WEAPONREADY4 &&
07323                 (pm->ps->torsoAnim) != BOTH_ATTACK4) &&
07324                 PM_CanSetWeaponAnims() &&
07325                 (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1))
07326         {
07327                 PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
07328         }
07329 
07330         if (pm->ps->clientNum >= MAX_CLIENTS &&
07331                 pm_entSelf &&
07332                 pm_entSelf->s.NPC_class == CLASS_VEHICLE)
07333         {//we are a vehicle
07334                 veh = pm_entSelf;
07335         }
07336         if ( veh 
07337                 && veh->m_pVehicle )
07338         {
07339                 if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
07340                         || g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
07341                 {//don't clear the rocket locking ever?
07342                         vehicleRocketLock = qtrue;
07343                 }
07344         }
07345 
07346         if ( !vehicleRocketLock )
07347         {
07348                 if (pm->ps->weapon != WP_ROCKET_LAUNCHER)
07349                 {
07350                         if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
07351                                 &&pm->ps->m_iVehicleNum)
07352                         {//riding a vehicle, the vehicle will tell me my rocketlock stuff...
07353                         }
07354                         else
07355                         {
07356                                 pm->ps->rocketLockIndex = ENTITYNUM_NONE;
07357                                 pm->ps->rocketLockTime = 0;
07358                                 pm->ps->rocketTargetTime = 0;
07359                         }
07360                 }
07361         }
07362 
07363         if ( PM_DoChargedWeapons(vehicleRocketLock, veh))
07364         {
07365                 // In some cases the charged weapon code may want us to short circuit the rest of the firing code
07366                 return;
07367         }
07368 
07369         // check for fire
07370         if ( ! (pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK))) 
07371         {
07372                 pm->ps->weaponTime = 0;
07373                 pm->ps->weaponstate = WEAPON_READY;
07374                 return;
07375         }
07376 
07377         if (pm->ps->weapon == WP_EMPLACED_GUN)
07378         {
07379                 addTime = weaponData[pm->ps->weapon].fireTime;
07380                 pm->ps->weaponTime += addTime;
07381                 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
07382                 {
07383                         PM_AddEvent( EV_ALT_FIRE );
07384                 }
07385                 else
07386                 {
07387                         PM_AddEvent( EV_FIRE_WEAPON );
07388                 }
07389                 return;
07390         }
07391         else if (pm->ps->m_iVehicleNum
07392                 && pm_entSelf->s.NPC_class==CLASS_VEHICLE)
07393         { //a vehicle NPC that has a pilot
07394                 pm->ps->weaponstate = WEAPON_FIRING;
07395                 pm->ps->weaponTime += 100;
07396 #ifdef QAGAME //hack, only do it game-side. vehicle weapons don't really need predicting I suppose.
07397                 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
07398                 {
07399                         G_CheapWeaponFire(pm->ps->clientNum, EV_ALT_FIRE);
07400                 }
07401                 else
07402                 {
07403                         G_CheapWeaponFire(pm->ps->clientNum, EV_FIRE_WEAPON);
07404                 }
07405 #endif
07406                 /*
07407                 addTime = weaponData[WP_EMPLACED_GUN].fireTime;
07408                 pm->ps->weaponTime += addTime;
07409                 if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
07410                 {
07411                         PM_AddEvent( EV_ALT_FIRE );
07412                 }
07413                 else
07414                 {
07415                         PM_AddEvent( EV_FIRE_WEAPON );
07416                 }
07417                 */
07418                 return;
07419         }
07420 
07421         if (pm->ps->weapon == WP_DISRUPTOR &&
07422                 (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
07423                 !pm->ps->zoomLocked)
07424         {
07425                 return;
07426         }
07427 
07428         if (pm->ps->weapon == WP_DISRUPTOR &&
07429                 (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
07430                 pm->ps->zoomMode == 2)
07431         { //can't use disruptor secondary while zoomed binoculars
07432                 return;
07433         }
07434 
07435         if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
07436         {
07437                 PM_StartTorsoAnim( BOTH_ATTACK4 );
07438         }
07439         else if (pm->ps->weapon == WP_MELEE)
07440         { //special anims for standard melee attacks
07441                 //Alternate between punches and use the anim length as weapon time.
07442                 if (!pm->ps->m_iVehicleNum)
07443                 { //if riding a vehicle don't do this stuff at all
07444                         if (pm->debugMelee &&
07445                                 (pm->cmd.buttons & BUTTON_ATTACK) &&
07446                                 (pm->cmd.buttons & BUTTON_ALT_ATTACK))
07447                         { //ok, grapple time
07448 #if 0 //eh, I want to try turning the saber off, but can't do that reliably for prediction..
07449                                 qboolean icandoit = qtrue;
07450                                 if (pm->ps->weaponTime > 0)
07451                                 { //weapon busy
07452                                         icandoit = qfalse;
07453                                 }
07454                                 if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
07455                                 { //force power or knockdown or something
07456                                         icandoit = qfalse;
07457                                 }
07458                                 if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE)
07459                                 {
07460                                         icandoit = qfalse;
07461                                 }
07462 
07463                                 if (icandoit)
07464                                 {
07465                                         //G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
07466                                         PM_SetAnim(SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
07467                                         if (pm->ps->torsoAnim == BOTH_KYLE_GRAB)
07468                                         { //providing the anim set succeeded..
07469                                                 pm->ps->torsoTimer += 500; //make the hand stick out a little longer than it normally would
07470                                                 if (pm->ps->legsAnim == pm->ps->torsoAnim)
07471                                                 {
07472                                                         pm->ps->legsTimer = pm->ps->torsoTimer;
07473                                                 }
07474                                                 pm->ps->weaponTime = pm->ps->torsoTimer;
07475                                                 return;
07476                                         }
07477                                 }
07478 #else
07479         #ifdef QAGAME
07480                                 if (pm_entSelf)
07481                                 {
07482                                         if (TryGrapple((gentity_t *)pm_entSelf))
07483                                         {
07484                                                 return;
07485                                         }
07486                                 }
07487         #else
07488                                 return;
07489         #endif
07490 #endif
07491                         }
07492                         else if (pm->debugMelee &&
07493                                 (pm->cmd.buttons & BUTTON_ALT_ATTACK))
07494                         { //kicks
07495                                 if (!BG_KickingAnim(pm->ps->torsoAnim) &&
07496                                         !BG_KickingAnim(pm->ps->legsAnim))
07497                                 {
07498                                         int kickMove = PM_KickMoveForConditions();
07499                                         if (kickMove == LS_HILT_BASH)
07500                                         { //yeah.. no hilt to bash with!
07501                                                 kickMove = LS_KICK_F;
07502                                         }
07503 
07504                                         if (kickMove != -1)
07505                                         {
07506                                                 if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
07507                                                 {//if in air, convert kick to an in-air kick
07508                                                         float gDist = PM_GroundDistance();
07509                                                         //let's only allow air kicks if a certain distance from the ground
07510                                                         //it's silly to be able to do them right as you land.
07511                                                         //also looks wrong to transition from a non-complete flip anim...
07512                                                         if ((!BG_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsTimer <= 0) &&
07513                                                                 gDist > 64.0f && //strict minimum
07514                                                                 gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
07515                                                                 )
07516                                                         {
07517                                                                 switch ( kickMove )
07518                                                                 {
07519                                                                 case LS_KICK_F:
07520                                                                         kickMove = LS_KICK_F_AIR;
07521                                                                         break;
07522                                                                 case LS_KICK_B:
07523                                                                         kickMove = LS_KICK_B_AIR;
07524                                                                         break;
07525                                                                 case LS_KICK_R:
07526                                                                         kickMove = LS_KICK_R_AIR;
07527                                                                         break;
07528                                                                 case LS_KICK_L:
07529                                                                         kickMove = LS_KICK_L_AIR;
07530                                                                         break;
07531                                                                 default: //oh well, can't do any other kick move while in-air
07532                                                                         kickMove = -1;
07533                                                                         break;
07534                                                                 }
07535                                                         }
07536                                                         else
07537                                                         { //off ground, but too close to ground
07538                                                                 kickMove = -1;
07539                                                         }
07540                                                 }
07541                                         }
07542 
07543                                         if (kickMove != -1)
07544                                         {
07545                                                 int kickAnim = saberMoveData[kickMove].animToUse;
07546 
07547                                                 if (kickAnim != -1)
07548                                                 {
07549                                                         PM_SetAnim(SETANIM_BOTH, kickAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
07550                                                         if (pm->ps->legsAnim == kickAnim)
07551                                                         {
07552                                                                 pm->ps->weaponTime = pm->ps->legsTimer;
07553                                                                 return;
07554                                                         }
07555                                                 }
07556                                         }
07557                                 }
07558 
07559                                 //if got here then no move to do so put torso into leg idle or whatever
07560                                 if (pm->ps->torsoAnim != pm->ps->legsAnim)
07561                                 {
07562                                         PM_SetAnim(SETANIM_BOTH, pm->ps->legsAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
07563                                 }
07564                                 pm->ps->weaponTime = 0;
07565                                 return;
07566                         }
07567                         else
07568                         { //just punch
07569                                 int desTAnim = BOTH_MELEE1;
07570                                 if (pm->ps->torsoAnim == BOTH_MELEE1)
07571                                 {
07572                                         desTAnim = BOTH_MELEE2;
07573                                 }
07574                                 PM_StartTorsoAnim( desTAnim );
07575 
07576                                 if (pm->ps->torsoAnim == desTAnim)
07577                                 {
07578                                         pm->ps->weaponTime = pm->ps->torsoTimer;
07579                                 }
07580                         }
07581                 }
07582         }
07583         else
07584         {
07585                 PM_StartTorsoAnim( WeaponAttackAnim[pm->ps->weapon] );
07586         }
07587 
07588         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
07589         {
07590                 amount = weaponData[pm->ps->weapon].altEnergyPerShot;
07591         }
07592         else
07593         {
07594                 amount = weaponData[pm->ps->weapon].energyPerShot;
07595         }
07596 
07597         pm->ps->weaponstate = WEAPON_FIRING;
07598 
07599         // take an ammo away if not infinite
07600         if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
07601         {
07602                 // enough energy to fire this weapon?
07603                 if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0) 
07604                 {
07605                         pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
07606                 }
07607                 else    // Not enough energy
07608                 {
07609                         // Switch weapons
07610                         if (pm->ps->weapon != WP_DET_PACK || !pm->ps->hasDetPackPlanted)
07611                         {
07612                                 PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
07613                                 if (pm->ps->weaponTime < 500)
07614                                 {
07615                                         pm->ps->weaponTime += 500;
07616                                 }
07617                         }
07618                         return;
07619                 }
07620         }
07621 
07622         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )      {
07623                 //if ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->gametype != GT_SIEGE )
07624                 if (0)
07625                 { //kind of a hack for now
07626                         PM_AddEvent( EV_FIRE_WEAPON );
07627                         addTime = weaponData[pm->ps->weapon].fireTime;
07628                 }
07629                 else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode != 1)
07630                 {
07631                         PM_AddEvent( EV_FIRE_WEAPON );
07632                         addTime = weaponData[pm->ps->weapon].fireTime;
07633                 }
07634                 else
07635                 {
07636                         if (pm->ps->weapon != WP_MELEE ||
07637                                 !pm->ps->m_iVehicleNum)
07638                         { //do not fire melee events at all when on vehicle
07639                                 PM_AddEvent( EV_ALT_FIRE );
07640                         }
07641                         addTime = weaponData[pm->ps->weapon].altFireTime;
07642                 }
07643         }
07644         else {
07645                 if (pm->ps->weapon != WP_MELEE ||
07646                         !pm->ps->m_iVehicleNum)
07647                 { //do not fire melee events at all when on vehicle
07648                         PM_AddEvent( EV_FIRE_WEAPON );
07649                 }
07650                 addTime = weaponData[pm->ps->weapon].fireTime;
07651                 if ( pm->gametype == GT_SIEGE && pm->ps->weapon == WP_DET_PACK )
07652                 {       // were far too spammy before?  So says Rick.
07653                         addTime *= 2;
07654                 }
07655         }
07656 
07657         /*
07658         if ( pm->ps->powerups[PW_HASTE] ) {
07659                 addTime /= 1.3;
07660         }
07661         */
07662 
07663         if (pm->ps->fd.forcePowersActive & (1 << FP_RAGE))
07664         {
07665                 addTime *= 0.75;
07666         }
07667         else if (pm->ps->fd.forceRageRecoveryTime > pm->cmd.serverTime)
07668         {
07669                 addTime *= 1.5;
07670         }
07671 
07672         pm->ps->weaponTime += addTime;
07673 }
07674 
07675 /*
07676 ================
07677 PM_Animate
07678 ================
07679 */
07680 
07681 static void PM_Animate( void ) {
07682         if ( pm->cmd.buttons & BUTTON_GESTURE ) {
07683                 if (pm->ps->m_iVehicleNum)
07684                 { //eh, fine, clear it
07685                         if (pm->ps->forceHandExtendTime < pm->cmd.serverTime)
07686                         {
07687                                 pm->ps->forceHandExtend = HANDEXTEND_NONE;
07688                         }
07689                 }
07690 
07691                 if ( pm->ps->torsoTimer < 1 && pm->ps->forceHandExtend == HANDEXTEND_NONE &&
07692                         pm->ps->legsTimer < 1 && pm->ps->weaponTime < 1 && pm->ps->saberLockTime < pm->cmd.serverTime) {
07693 
07694                         pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
07695 
07696                         //FIXME: random taunt anims?
07697                         pm->ps->forceDodgeAnim = BOTH_ENGAGETAUNT;
07698 
07699                         pm->ps->forceHandExtendTime = pm->cmd.serverTime + 1000;
07700                         
07701                         //pm->ps->weaponTime = 100;
07702 
07703                         PM_AddEvent( EV_TAUNT );
07704                 }
07705 #if 0
07706 // Here's an interesting bit.  The bots in TA used buttons to do additional gestures.
07707 // I ripped them out because I didn't want too many buttons given the fact that I was already adding some for JK2.
07708 // We can always add some back in if we want though.
07709         } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
07710                 if ( pm->ps->torsoTimer == 0 ) {
07711                         PM_StartTorsoAnim( TORSO_GETFLAG );
07712                         pm->ps->torsoTimer = 600;       //TIMER_GESTURE;
07713                 }
07714         } else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
07715                 if ( pm->ps->torsoTimer == 0 ) {
07716                         PM_StartTorsoAnim( TORSO_GUARDBASE );
07717                         pm->ps->torsoTimer = 600;       //TIMER_GESTURE;
07718                 }
07719         } else if ( pm->cmd.buttons & BUTTON_PATROL ) {
07720                 if ( pm->ps->torsoTimer == 0 ) {
07721                         PM_StartTorsoAnim( TORSO_PATROL );
07722                         pm->ps->torsoTimer = 600;       //TIMER_GESTURE;
07723                 }
07724         } else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
07725                 if ( pm->ps->torsoTimer == 0 ) {
07726                         PM_StartTorsoAnim( TORSO_FOLLOWME );
07727                         pm->ps->torsoTimer = 600;       //TIMER_GESTURE;
07728                 }
07729         } else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
07730                 if ( pm->ps->torsoTimer == 0 ) {
07731                         PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
07732                         pm->ps->torsoTimer = 600;       //TIMER_GESTURE;
07733                 }
07734         } else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
07735                 if ( pm->ps->torsoTimer == 0 ) {
07736                         PM_StartTorsoAnim( TORSO_NEGATIVE );
07737                         pm->ps->torsoTimer = 600;       //TIMER_GESTURE;
07738                 }
07739 #endif //
07740         }
07741 }
07742 
07743 
07744 /*
07745 ================
07746 PM_DropTimers
07747 ================
07748 */
07749 static void PM_DropTimers( void ) {
07750         // drop misc timing counter
07751         if ( pm->ps->pm_time ) {
07752                 if ( pml.msec >= pm->ps->pm_time ) {
07753                         pm->ps->pm_flags &= ~PMF_ALL_TIMES;
07754                         pm->ps->pm_time = 0;
07755                 } else {
07756                         pm->ps->pm_time -= pml.msec;
07757                 }
07758         }
07759 
07760         // drop animation counter
07761         if ( pm->ps->legsTimer > 0 ) {
07762                 pm->ps->legsTimer -= pml.msec;
07763                 if ( pm->ps->legsTimer < 0 ) {
07764                         pm->ps->legsTimer = 0;
07765                 }
07766         }
07767 
07768         if ( pm->ps->torsoTimer > 0 ) {
07769                 pm->ps->torsoTimer -= pml.msec;
07770                 if ( pm->ps->torsoTimer < 0 ) {
07771                         pm->ps->torsoTimer = 0;
07772                 }
07773         }
07774 }
07775 
07776 // Following function is stateless (at the moment). And hoisting it out
07777 // of the namespace here is easier than fixing all the places it's used,
07778 // which includes files that are also compiled in SP. We do need to make
07779 // sure we only get one copy in the linker, though.
07780 
07781 #include "../namespace_end.h"
07782 
07783 #if !defined(_XBOX) || defined(QAGAME)
07784 extern  vmCvar_t        bg_fighterAltControl;
07785 qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh )
07786 {
07787         if ( bg_fighterAltControl.integer
07788                 && ps->clientNum < MAX_CLIENTS //real client
07789                 && ps->m_iVehicleNum//in a vehicle
07790                 && pVeh //valid vehicle data pointer
07791                 && pVeh->m_pVehicleInfo//valid vehicle info
07792                 && pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
07793                 //FIXME: specify per vehicle instead of assuming true for all fighters
07794                 //FIXME: map/server setting?
07795         {//can roll and pitch without limitation!
07796                 return qtrue;
07797         }
07798         return qfalse;
07799 }
07800 #else
07801 extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
07802 #endif
07803 
07804 #include "../namespace_begin.h"
07805 
07806 /*
07807 ================
07808 PM_UpdateViewAngles
07809 
07810 This can be used as another entry point when only the viewangles
07811 are being updated isntead of a full move
07812 ================
07813 */
07814 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
07815         short           temp;
07816         int             i;
07817 
07818         if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
07819                 return;         // no view changes at all
07820         }
07821 
07822         if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
07823                 return;         // no view changes at all
07824         }
07825 
07826         // circularly clamp the angles with deltas
07827         for (i=0 ; i<3 ; i++) {
07828                 temp = cmd->angles[i] + ps->delta_angles[i];
07829 #ifdef VEH_CONTROL_SCHEME_4
07830                 if ( pm_entVeh 
07831                         && pm_entVeh->m_pVehicle
07832                         && pm_entVeh->m_pVehicle->m_pVehicleInfo
07833                         && pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER
07834                         && (cmd->serverTime-pm_entVeh->playerState->hyperSpaceTime) >= HYPERSPACE_TIME )
07835                 {//in a vehicle and not hyperspacing
07836                         if ( i == PITCH ) 
07837                         {
07838                                 int pitchClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[PITCH]+10.0f));
07839                                 // don't let the player look up or down more than 22.5 degrees
07840                                 if ( temp > pitchClamp ) 
07841                                 {
07842                                         ps->delta_angles[i] = pitchClamp - cmd->angles[i];
07843                                         temp = pitchClamp;
07844                                 } 
07845                                 else if ( temp < -pitchClamp ) 
07846                                 {
07847                                         ps->delta_angles[i] = -pitchClamp - cmd->angles[i];
07848                                         temp = -pitchClamp;
07849                                 }
07850                         }
07851                         if ( i == YAW ) 
07852                         {
07853                                 int yawClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[YAW]+10.0f));
07854                                 // don't let the player look left or right more than 22.5 degrees
07855                                 if ( temp > yawClamp ) 
07856                                 {
07857                                         ps->delta_angles[i] = yawClamp - cmd->angles[i];
07858                                         temp = yawClamp;
07859                                 }
07860                                 else if ( temp < -yawClamp ) 
07861                                 {
07862                                         ps->delta_angles[i] = -yawClamp - cmd->angles[i];
07863                                         temp = -yawClamp;
07864                                 }
07865                         }
07866                 }
07867 #else //VEH_CONTROL_SCHEME_4
07868                 if ( pm_entVeh && BG_UnrestrainedPitchRoll( ps, pm_entVeh->m_pVehicle ) )
07869                 {//in a fighter
07870                         /*
07871                         if ( i == ROLL )
07872                         {//get roll from vehicle
07873                                 ps->viewangles[ROLL] = pm_entVeh->playerState->viewangles[ROLL];//->m_pVehicle->m_vOrientation[ROLL];
07874                                 continue;
07875 
07876                         }
07877                         */
07878                 }
07879 #endif // VEH_CONTROL_SCHEME_4
07880                 else
07881                 {
07882                         if ( i == PITCH ) {
07883                                 // don't let the player look up or down more than 90 degrees
07884                                 if ( temp > 16000 ) {
07885                                         ps->delta_angles[i] = 16000 - cmd->angles[i];
07886                                         temp = 16000;
07887                                 } else if ( temp < -16000 ) {
07888                                         ps->delta_angles[i] = -16000 - cmd->angles[i];
07889                                         temp = -16000;
07890                                 }
07891                         }
07892                 }
07893                 ps->viewangles[i] = SHORT2ANGLE(temp);
07894         }
07895 }
07896 
07897 /*
07898 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
07899         short           temp;
07900         int             i;
07901         float   rootPitch = 0, pitchMin=-90, pitchMax=90, yawMin=0, yawMax=0, lockedYawValue = 0;       //just to shut up warnings
07902         qboolean        lockedYaw = qfalse, clamped = qfalse;
07903         bgEntity_t *vehEnt = NULL;
07904 
07905         if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
07906                 return;         // no view changes at all
07907         }
07908 
07909         if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
07910                 return;         // no view changes at all
07911         }
07912 
07913         // If we're a vehicle, or we're riding a vehicle...?
07914         if ( ps->m_iVehicleNum )
07915         {
07916                 if ( ps->clientNum < MAX_CLIENTS )
07917                 { //player riding vehicle
07918                         vehEnt = PM_BGEntForNum(ps->m_iVehicleNum);
07919                 }
07920                 else
07921                 { //vehicle with player pilot
07922                         vehEnt = PM_BGEntForNum(ps->clientNum);
07923                 }
07924                 if ( vehEnt )
07925                 {//there is a vehicle
07926                         Vehicle_t *pVeh = vehEnt->m_pVehicle;
07927                         if ( pVeh && pVeh->m_pVehicleInfo )
07928                         {
07929                                 // There is a vehicle...
07930                                 if ( pVeh->m_pVehicleInfo->type != VH_ANIMAL )
07931                                 {//animals just turn normally, no clamping
07932                                         if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
07933                                         {
07934                                                 rootPitch = pVeh->m_vOrientation[PITCH];//gent->owner->client->ps.vehicleAngles[PITCH];//???  what if goes over 90 when add the min/max?
07935                                                 if ( pVeh->m_pVehicleInfo->pitchLimit == -1 )
07936                                                 {
07937                                                         pitchMax = 180;
07938                                                 }
07939                                                 else
07940                                                 {
07941                                                         pitchMax = pVeh->m_pVehicleInfo->pitchLimit;
07942                                                 }
07943                                                 pitchMin = -pitchMax;
07944                                         }
07945                                         else
07946                                         {
07947                                                 lockedYawValue = 0;//gent->owner->client->ps.vehicleAngles[YAW];
07948                                                 lockedYaw = qtrue;
07949                                                 yawMax = pVeh->m_pVehicleInfo->lookYaw;
07950                                                 yawMin = -yawMax;
07951                                                 rootPitch = 0;//gent->owner->client->ps.vehicleAngles[PITCH];//???  what if goes over 90 when add the min/max?
07952                                                 pitchMax = pVeh->m_pVehicleInfo->lookPitch;
07953                                                 pitchMin = -pitchMax;
07954                                         }
07955                                 }
07956                         }
07957                 }
07958         }
07959         if ( 1 )
07960         {
07961                 const short pitchClampMin = ANGLE2SHORT(rootPitch+pitchMin);
07962                 const short pitchClampMax = ANGLE2SHORT(rootPitch+pitchMax);
07963                 const short yawClampMin = ANGLE2SHORT(lockedYawValue+yawMin);
07964                 const short yawClampMax = ANGLE2SHORT(lockedYawValue+yawMax);
07965                 for (i=0 ; i<3 ; i++) 
07966                 {
07967                         temp = cmd->angles[i] + ps->delta_angles[i];
07968                         if ( i == PITCH ) 
07969                         {
07970                                 //FIXME get this limit from the NPCs stats?
07971                                 // don't let the player look up or down more than 90 degrees
07972                                 if ( temp > pitchClampMax ) 
07973                                 {
07974                                         ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff;        //& clamp to short
07975                                         temp = pitchClampMax;
07976                                         clamped = qtrue;
07977                                 } 
07978                                 else if ( temp < pitchClampMin ) 
07979                                 {
07980                                         ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff;        //& clamp to short
07981                                         temp = pitchClampMin;
07982                                         clamped = qtrue;
07983                                 }
07984                         }
07985                         if ( i == YAW && lockedYaw ) 
07986                         {
07987                                 //FIXME get this limit from the NPCs stats?
07988                                 // don't let the player look up or down more than 90 degrees
07989                                 if ( temp > yawClampMax ) 
07990                                 {
07991                                         ps->delta_angles[i] = (yawClampMax - cmd->angles[i]) & 0xffff;  //& clamp to short
07992                                         temp = yawClampMax;
07993                                         clamped = qtrue;
07994                                 } 
07995                                 else if ( temp < yawClampMin ) 
07996                                 {
07997                                         ps->delta_angles[i] = (yawClampMin - cmd->angles[i]) & 0xffff;  //& clamp to short
07998                                         temp = yawClampMin;
07999                                         clamped = qtrue;
08000                                 }
08001                                 ps->viewangles[i] = SHORT2ANGLE(temp);
08002                         }
08003                         else
08004                         {
08005                                 ps->viewangles[i] = SHORT2ANGLE(temp);
08006                         }
08007                 }
08008         }
08009         else
08010         {
08011                 // circularly clamp the angles with deltas
08012                 for (i=0 ; i<3 ; i++) {
08013                         temp = cmd->angles[i] + ps->delta_angles[i];
08014                         if ( i == PITCH ) {
08015                                 // don't let the player look up or down more than 90 degrees
08016                                 if ( temp > 16000 ) {
08017                                         ps->delta_angles[i] = 16000 - cmd->angles[i];
08018                                         temp = 16000;
08019                                 } else if ( temp < -16000 ) {
08020                                         ps->delta_angles[i] = -16000 - cmd->angles[i];
08021                                         temp = -16000;
08022                                 }
08023                         }
08024                         ps->viewangles[i] = SHORT2ANGLE(temp);
08025                 }
08026         }
08027 
08028 }
08029 */
08030 
08031 //-------------------------------------------
08032 void PM_AdjustAttackStates( pmove_t *pm )
08033 //-------------------------------------------
08034 {
08035         int amount;
08036 
08037         if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
08038                 &&pm->ps->m_iVehicleNum)
08039         { //riding a vehicle
08040                 bgEntity_t *veh = pm_entVeh;
08041                 if ( veh &&
08042                         (veh->m_pVehicle && (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)) )
08043                 {//riding a walker/fighter
08044                         //not firing, ever
08045                         pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
08046                         return;
08047                 }
08048         }
08049         // get ammo usage
08050         if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
08051         {
08052                 amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot;
08053         }
08054         else
08055         {
08056                 amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot;
08057         }
08058 
08059         // disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player?
08060         if ( pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_READY )
08061         {
08062                 if ( !(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) /*&&
08063                         pm->cmd.upmove <= 0 && !pm->cmd.forwardmove && !pm->cmd.rightmove*/)
08064                 {
08065                         // We just pressed the alt-fire key
08066                         if ( !pm->ps->zoomMode && pm->ps->pm_type != PM_DEAD )
08067                         {
08068                                 // not already zooming, so do it now
08069                                 pm->ps->zoomMode = 1;
08070                                 pm->ps->zoomLocked = qfalse;
08071                                 pm->ps->zoomFov = 80.0f;//cg_fov.value;
08072                                 pm->ps->zoomLockTime = pm->cmd.serverTime + 50;
08073                                 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
08074                         }
08075                         else if (pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
08076                         { //check for == 1 so we can't turn binoculars off with disruptor alt fire
08077                                 // already zooming, so must be wanting to turn it off
08078                                 pm->ps->zoomMode = 0;
08079                                 pm->ps->zoomTime = pm->ps->commandTime;
08080                                 pm->ps->zoomLocked = qfalse;
08081                                 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
08082                                 pm->ps->weaponTime = 1000;
08083                         }
08084                 }
08085                 else if ( !(pm->cmd.buttons & BUTTON_ALT_ATTACK ) && pm->ps->zoomLockTime < pm->cmd.serverTime)
08086                 {
08087                         // Not pressing zoom any more
08088                         if ( pm->ps->zoomMode )
08089                         {
08090                                 if (pm->ps->zoomMode == 1 && !pm->ps->zoomLocked)
08091                                 { //approximate what level the client should be zoomed at based on how long zoom was held
08092                                         pm->ps->zoomFov = ((pm->cmd.serverTime+50) - pm->ps->zoomLockTime) * 0.035f;
08093                                         if (pm->ps->zoomFov > 50)
08094                                         {
08095                                                 pm->ps->zoomFov = 50;
08096                                         }
08097                                         if (pm->ps->zoomFov < 1)
08098                                         {
08099                                                 pm->ps->zoomFov = 1;
08100                                         }
08101                                 }
08102                                 // were zooming in, so now lock the zoom
08103                                 pm->ps->zoomLocked = qtrue;
08104                         }
08105                 }
08106                 //This seemed like a good idea, but apparently it confuses people. So disabled for now.
08107                 /*
08108                 else if (!(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
08109                         (pm->cmd.upmove > 0 || pm->cmd.forwardmove || pm->cmd.rightmove))
08110                 { //if you try to zoom while moving, just convert it into a primary attack
08111                         pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
08112                         pm->cmd.buttons |= BUTTON_ATTACK;
08113                 }
08114                 */
08115 
08116                 /*
08117                 if (pm->cmd.upmove > 0 || pm->cmd.forwardmove || pm->cmd.rightmove)
08118                 {
08119                         if (pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
08120                         { //check for == 1 so we can't turn binoculars off with disruptor alt fire
08121                                 pm->ps->zoomMode = 0;
08122                                 pm->ps->zoomTime = pm->ps->commandTime;
08123                                 pm->ps->zoomLocked = qfalse;
08124                                 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
08125                         }
08126                 }
08127                 */
08128 
08129                 if ( pm->cmd.buttons & BUTTON_ATTACK )
08130                 {
08131                         // If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll
08132                         //      just use whatever ammo was selected from above
08133                         if ( pm->ps->zoomMode )
08134                         {
08135                                 amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - 
08136                                                         weaponData[pm->ps->weapon].altEnergyPerShot;
08137                         }
08138                 }
08139                 else
08140                 {
08141                         // alt-fire button pressing doesn't use any ammo
08142                         amount = 0;
08143                 }
08144         }
08145         /*
08146         else if (pm->ps->weapon == WP_DISRUPTOR) //still perform certain checks, even if the weapon is not ready
08147         {
08148                 if (pm->cmd.upmove > 0 || pm->cmd.forwardmove || pm->cmd.rightmove)
08149                 {
08150                         if (pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
08151                         { //check for == 1 so we can't turn binoculars off with disruptor alt fire
08152                                 pm->ps->zoomMode = 0;
08153                                 pm->ps->zoomTime = pm->ps->commandTime;
08154                                 pm->ps->zoomLocked = qfalse;
08155                                 PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
08156                         }
08157                 }
08158         }
08159         */
08160 
08161         // set the firing flag for continuous beam weapons, saber will fire even if out of ammo
08162         if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && 
08163                         pm->ps->pm_type != PM_INTERMISSION && 
08164                         ( pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) && 
08165                         ( amount >= 0 || pm->ps->weapon == WP_SABER ))
08166         {
08167                 if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
08168                 {
08169                         pm->ps->eFlags |= EF_ALT_FIRING;
08170                 }
08171                 else
08172                 {
08173                         pm->ps->eFlags &= ~EF_ALT_FIRING;
08174                 }
08175 
08176                 // This flag should always get set, even when alt-firing
08177                 pm->ps->eFlags |= EF_FIRING;
08178         } 
08179         else 
08180         {
08181                 // Clear 'em out
08182                 pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
08183         }
08184 
08185         // disruptor should convert a main fire to an alt-fire if the gun is currently zoomed
08186         if ( pm->ps->weapon == WP_DISRUPTOR)
08187         {
08188                 if ( pm->cmd.buttons & BUTTON_ATTACK && pm->ps->zoomMode == 1 && pm->ps->zoomLocked)
08189                 {
08190                         // converting the main fire to an alt-fire
08191                         pm->cmd.buttons |= BUTTON_ALT_ATTACK;
08192                         pm->ps->eFlags |= EF_ALT_FIRING;
08193                 }
08194                 else if ( pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->ps->zoomMode == 1 && pm->ps->zoomLocked)
08195                 {
08196                         pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
08197                         pm->ps->eFlags &= ~EF_ALT_FIRING;
08198                 }
08199         }
08200 }
08201 
08202 void BG_CmdForRoll( playerState_t *ps, int anim, usercmd_t *pCmd )
08203 {
08204         switch ( (anim) )
08205         {
08206         case BOTH_ROLL_F:
08207                 pCmd->forwardmove = 127;
08208                 pCmd->rightmove = 0;
08209                 break;
08210         case BOTH_ROLL_B:
08211                 pCmd->forwardmove = -127;
08212                 pCmd->rightmove = 0;
08213                 break;
08214         case BOTH_ROLL_R:
08215                 pCmd->forwardmove = 0;
08216                 pCmd->rightmove = 127;
08217                 break;
08218         case BOTH_ROLL_L:
08219                 pCmd->forwardmove = 0;
08220                 pCmd->rightmove = -127;
08221                 break;
08222         case BOTH_GETUP_BROLL_R:
08223                 pCmd->forwardmove = 0;
08224                 pCmd->rightmove = 48;
08225                 //NOTE: speed is 400
08226                 break;
08227 
08228         case BOTH_GETUP_FROLL_R:
08229                 if ( ps->legsTimer <= 250 )
08230                 {//end of anim
08231                         pCmd->forwardmove = pCmd->rightmove = 0;
08232                 }
08233                 else
08234                 {
08235                         pCmd->forwardmove = 0;
08236                         pCmd->rightmove = 48;
08237                         //NOTE: speed is 400
08238                 }
08239                 break;
08240 
08241         case BOTH_GETUP_BROLL_L:
08242                 pCmd->forwardmove = 0;
08243                 pCmd->rightmove = -48;
08244                 //NOTE: speed is 400
08245                 break;
08246 
08247         case BOTH_GETUP_FROLL_L:
08248                 if ( ps->legsTimer <= 250 )
08249                 {//end of anim
08250                         pCmd->forwardmove = pCmd->rightmove = 0;
08251                 }
08252                 else
08253                 {
08254                         pCmd->forwardmove = 0;
08255                         pCmd->rightmove = -48;
08256                         //NOTE: speed is 400
08257                 }
08258                 break;
08259 
08260         case BOTH_GETUP_BROLL_B:
08261                 if ( ps->torsoTimer <= 250 )
08262                 {//end of anim
08263                         pCmd->forwardmove = pCmd->rightmove = 0;
08264                 }
08265                 else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 350 )
08266                 {//beginning of anim
08267                         pCmd->forwardmove = pCmd->rightmove = 0;
08268                 }
08269                 else
08270                 {
08271                         //FIXME: ramp down over length of anim
08272                         pCmd->forwardmove = -64;
08273                         pCmd->rightmove = 0;
08274                         //NOTE: speed is 400
08275                 }
08276                 break;
08277 
08278         case BOTH_GETUP_FROLL_B:
08279                 if ( ps->torsoTimer <= 100 )
08280                 {//end of anim
08281                         pCmd->forwardmove = pCmd->rightmove = 0;
08282                 }
08283                 else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 200 )
08284                 {//beginning of anim
08285                         pCmd->forwardmove = pCmd->rightmove = 0;
08286                 }
08287                 else
08288                 {
08289                         //FIXME: ramp down over length of anim
08290                         pCmd->forwardmove = -64;
08291                         pCmd->rightmove = 0;
08292                         //NOTE: speed is 400
08293                 }
08294                 break;
08295 
08296         case BOTH_GETUP_BROLL_F:
08297                 if ( ps->torsoTimer <= 550 )
08298                 {//end of anim
08299                         pCmd->forwardmove = pCmd->rightmove = 0;
08300                 }
08301                 else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 150 )
08302                 {//beginning of anim
08303                         pCmd->forwardmove = pCmd->rightmove = 0;
08304                 }
08305                 else
08306                 {
08307                         pCmd->forwardmove = 64;
08308                         pCmd->rightmove = 0;
08309                         //NOTE: speed is 400
08310                 }
08311                 break;
08312 
08313         case BOTH_GETUP_FROLL_F:
08314                 if ( ps->torsoTimer <= 100 )
08315                 {//end of anim
08316                         pCmd->forwardmove = pCmd->rightmove = 0;
08317                 }
08318                 else
08319                 {
08320                         //FIXME: ramp down over length of anim
08321                         pCmd->forwardmove = 64;
08322                         pCmd->rightmove = 0;
08323                         //NOTE: speed is 400
08324                 }
08325                 break;
08326         }
08327         pCmd->upmove = 0;
08328 }
08329 
08330 qboolean PM_SaberInTransition( int move );
08331 
08332 void BG_AdjustClientSpeed(playerState_t *ps, usercmd_t *cmd, int svTime)
08333 {
08334         saberInfo_t     *saber;
08335 
08336         if (ps->clientNum >= MAX_CLIENTS)
08337         {
08338                 bgEntity_t *bgEnt = pm_entSelf;
08339 
08340                 if (bgEnt && bgEnt->s.NPC_class == CLASS_VEHICLE)
08341                 { //vehicles manage their own speed
08342                         return;
08343                 }
08344         }
08345 
08346         //For prediction, always reset speed back to the last known server base speed
08347         //If we didn't do this, under lag we'd eventually dwindle speed down to 0 even though
08348         //that would not be the correct predicted value.
08349         ps->speed = ps->basespeed;
08350 
08351         if (ps->forceHandExtend == HANDEXTEND_DODGE)
08352         {
08353                 ps->speed = 0;
08354         }
08355 
08356         if (ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
08357                 ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
08358                 ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
08359         {
08360                 ps->speed = 0;
08361         }
08362 
08363 
08364         if ( cmd->forwardmove < 0 && !(cmd->buttons&BUTTON_WALKING) && pm->ps->groundEntityNum != ENTITYNUM_NONE )
08365         {//running backwards is slower than running forwards (like SP)
08366                 ps->speed *= 0.75;
08367         }
08368 
08369         if (ps->fd.forcePowersActive & (1 << FP_GRIP))
08370         {
08371                 ps->speed *= 0.4;
08372         }
08373 
08374         if (ps->fd.forcePowersActive & (1 << FP_SPEED))
08375         {
08376                 ps->speed *= 1.7;
08377         }
08378         else if (ps->fd.forcePowersActive & (1 << FP_RAGE))
08379         {
08380                 ps->speed *= 1.3;
08381         }
08382         else if (ps->fd.forceRageRecoveryTime > svTime)
08383         {
08384                 ps->speed *= 0.75;
08385         }
08386 
08387         if (pm->ps->weapon == WP_DISRUPTOR &&
08388                 pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
08389         {
08390                 ps->speed *= 0.5f;
08391         }
08392 
08393         if (ps->fd.forceGripCripple)
08394         {
08395                 if (ps->fd.forcePowersActive & (1 << FP_RAGE))
08396                 {
08397                         ps->speed *= 0.9;
08398                 }
08399                 else if (ps->fd.forcePowersActive & (1 << FP_SPEED))
08400                 { //force speed will help us escape
08401                         ps->speed *= 0.8;
08402                 }
08403                 else
08404                 {
08405                         ps->speed *= 0.2;
08406                 }
08407         }
08408 
08409         if ( BG_SaberInAttack( ps->saberMove ) && cmd->forwardmove < 0 )
08410         {//if running backwards while attacking, don't run as fast.
08411                 switch( ps->fd.saberAnimLevel )
08412                 {
08413                 case FORCE_LEVEL_1:
08414                         ps->speed *= 0.75f;
08415                         break;
08416                 case FORCE_LEVEL_2:
08417                 case SS_DUAL:
08418                 case SS_STAFF:
08419                         ps->speed *= 0.60f;
08420                         break;
08421                 case FORCE_LEVEL_3:
08422                         ps->speed *= 0.45f;
08423                         break;
08424                 default:
08425                         break;
08426                 }
08427         }
08428         else if ( BG_SpinningSaberAnim( ps->legsAnim ) )
08429         {
08430                 if (ps->fd.saberAnimLevel == FORCE_LEVEL_3)
08431                 {
08432                         ps->speed *= 0.3f;
08433                 }
08434                 else
08435                 {
08436                         ps->speed *= 0.5f;
08437                 }
08438         }
08439         else if ( ps->weapon == WP_SABER && BG_SaberInAttack( ps->saberMove ) )
08440         {//if attacking with saber while running, drop your speed
08441                 switch( ps->fd.saberAnimLevel )
08442                 {
08443                 case FORCE_LEVEL_2:
08444                 case SS_DUAL:
08445                 case SS_STAFF:
08446                         ps->speed *= 0.85f;
08447                         break;
08448                 case FORCE_LEVEL_3:
08449                         ps->speed *= 0.55f;
08450                         break;
08451                 default:
08452                         break;
08453                 }
08454         }
08455         else if (ps->weapon == WP_SABER && ps->fd.saberAnimLevel == FORCE_LEVEL_3 &&
08456                 PM_SaberInTransition(ps->saberMove))
08457         { //Now, we want to even slow down in transitions for level 3 (since it has chains and stuff now)
08458                 if (cmd->forwardmove < 0)
08459                 {
08460                         ps->speed *= 0.4f;
08461                 }
08462                 else
08463                 {
08464                         ps->speed *= 0.6f;
08465                 }
08466         }
08467 
08468         if ( BG_InRoll( ps, ps->legsAnim ) && ps->speed > 50 )
08469         { //can't roll unless you're able to move normally
08470                 if ((ps->legsAnim) == BOTH_ROLL_B)
08471                 { //backwards roll is pretty fast, should also be slower
08472                         if (ps->legsTimer > 800)
08473                         {
08474                                 ps->speed = ps->legsTimer/2.5;
08475                         }
08476                         else
08477                         {
08478                                 ps->speed = ps->legsTimer/6.0;//450;
08479                         }
08480                 }
08481                 else
08482                 {
08483                         if (ps->legsTimer > 800)
08484                         {
08485                                 ps->speed = ps->legsTimer/1.5;//450;
08486                         }
08487                         else
08488                         {
08489                                 ps->speed = ps->legsTimer/5.0;//450;
08490                         }
08491                 }
08492                 if (ps->speed > 600)
08493                 {
08494                         ps->speed = 600;
08495                 }
08496                 //Automatically slow down as the roll ends.
08497         }
08498 
08499         saber = BG_MySaber( ps->clientNum, 0 );
08500         if ( saber 
08501                 && saber->moveSpeedScale != 1.0f )
08502         {
08503                 ps->speed *= saber->moveSpeedScale;
08504         }
08505         saber = BG_MySaber( ps->clientNum, 1 );
08506         if ( saber 
08507                 && saber->moveSpeedScale != 1.0f )
08508         {
08509                 ps->speed *= saber->moveSpeedScale;
08510         }
08511 }
08512 
08513 qboolean BG_InRollAnim( entityState_t *cent )
08514 {
08515         switch ( (cent->legsAnim) )
08516         {
08517         case BOTH_ROLL_F:
08518         case BOTH_ROLL_B:
08519         case BOTH_ROLL_R:
08520         case BOTH_ROLL_L:
08521                 return qtrue;
08522         }
08523         return qfalse;
08524 }
08525 
08526 qboolean BG_InKnockDown( int anim )
08527 {
08528         switch ( (anim) )
08529         {
08530         case BOTH_KNOCKDOWN1:
08531         case BOTH_KNOCKDOWN2:
08532         case BOTH_KNOCKDOWN3:
08533         case BOTH_KNOCKDOWN4:
08534         case BOTH_KNOCKDOWN5:
08535                 return qtrue;
08536                 break;
08537         case BOTH_GETUP1:
08538         case BOTH_GETUP2:
08539         case BOTH_GETUP3:
08540         case BOTH_GETUP4:
08541         case BOTH_GETUP5:
08542         case BOTH_FORCE_GETUP_F1:
08543         case BOTH_FORCE_GETUP_F2:
08544         case BOTH_FORCE_GETUP_B1:
08545         case BOTH_FORCE_GETUP_B2:
08546         case BOTH_FORCE_GETUP_B3:
08547         case BOTH_FORCE_GETUP_B4:
08548         case BOTH_FORCE_GETUP_B5:
08549         case BOTH_GETUP_BROLL_B:
08550         case BOTH_GETUP_BROLL_F:
08551         case BOTH_GETUP_BROLL_L:
08552         case BOTH_GETUP_BROLL_R:
08553         case BOTH_GETUP_FROLL_B:
08554         case BOTH_GETUP_FROLL_F:
08555         case BOTH_GETUP_FROLL_L:
08556         case BOTH_GETUP_FROLL_R:
08557                 return qtrue;
08558                 break;
08559         }
08560         return qfalse;
08561 }
08562 
08563 qboolean BG_InRollES( entityState_t *ps, int anim )
08564 {
08565         switch ( (anim) )
08566         {
08567         case BOTH_ROLL_F:
08568         case BOTH_ROLL_B:
08569         case BOTH_ROLL_R:
08570         case BOTH_ROLL_L:
08571                 return qtrue;
08572                 break;
08573         }
08574         return qfalse;
08575 }
08576 
08577 void BG_IK_MoveArm(void *ghoul2, int lHandBolt, int time, entityState_t *ent, int basePose, vec3_t desiredPos, qboolean *ikInProgress,
08578                                          vec3_t origin, vec3_t angles, vec3_t scale, int blendTime, qboolean forceHalt)
08579 {
08580         mdxaBone_t lHandMatrix;
08581         vec3_t lHand;
08582         vec3_t torg;
08583         float distToDest;
08584 
08585         if (!ghoul2)
08586         {
08587                 return;
08588         }
08589 
08590         assert(bgHumanoidAnimations[basePose].firstFrame > 0);
08591 
08592         if (!*ikInProgress && !forceHalt)
08593         {
08594                 int baseposeAnim = basePose;
08595                 sharedSetBoneIKStateParams_t ikP;
08596 
08597                 //restrict the shoulder joint
08598                 //VectorSet(ikP.pcjMins,-50.0f,-80.0f,-15.0f);
08599                 //VectorSet(ikP.pcjMaxs,15.0f,40.0f,15.0f);
08600 
08601                 //for now, leaving it unrestricted, but restricting elbo