codemp/game/g_active.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 
00004 #include "g_local.h"
00005 #include "bg_saga.h"
00006 
00007 extern void Jedi_Cloak( gentity_t *self );
00008 extern void Jedi_Decloak( gentity_t *self );
00009 
00010 #include "../namespace_begin.h"
00011 qboolean PM_SaberInTransition( int move );
00012 qboolean PM_SaberInStart( int move );
00013 qboolean PM_SaberInReturn( int move );
00014 qboolean WP_SaberStyleValidForSaber( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int saberAnimLevel );
00015 #include "../namespace_end.h"
00016 qboolean saberCheckKnockdown_DuelLoss(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other);
00017 
00018 extern vmCvar_t g_saberLockRandomNess;
00019 
00020 void P_SetTwitchInfo(gclient_t  *client)
00021 {
00022         client->ps.painTime = level.time;
00023         client->ps.painDirection ^= 1;
00024 }
00025 
00026 /*
00027 ===============
00028 G_DamageFeedback
00029 
00030 Called just before a snapshot is sent to the given player.
00031 Totals up all damage and generates both the player_state_t
00032 damage values to that client for pain blends and kicks, and
00033 global pain sound events for all clients.
00034 ===============
00035 */
00036 void P_DamageFeedback( gentity_t *player ) {
00037         gclient_t       *client;
00038         float   count;
00039         vec3_t  angles;
00040 
00041         client = player->client;
00042         if ( client->ps.pm_type == PM_DEAD ) {
00043                 return;
00044         }
00045 
00046         // total points of damage shot at the player this frame
00047         count = client->damage_blood + client->damage_armor;
00048         if ( count == 0 ) {
00049                 return;         // didn't take any damage
00050         }
00051 
00052         if ( count > 255 ) {
00053                 count = 255;
00054         }
00055 
00056         // send the information to the client
00057 
00058         // world damage (falling, slime, etc) uses a special code
00059         // to make the blend blob centered instead of positional
00060         if ( client->damage_fromWorld ) {
00061                 client->ps.damagePitch = 255;
00062                 client->ps.damageYaw = 255;
00063 
00064                 client->damage_fromWorld = qfalse;
00065         } else {
00066                 vectoangles( client->damage_from, angles );
00067                 client->ps.damagePitch = angles[PITCH]/360.0 * 256;
00068                 client->ps.damageYaw = angles[YAW]/360.0 * 256;
00069 
00070                 //cap them since we can't send negative values in here across the net
00071                 if (client->ps.damagePitch < 0)
00072                 {
00073                         client->ps.damagePitch = 0;
00074                 }
00075                 if (client->ps.damageYaw < 0)
00076                 {
00077                         client->ps.damageYaw = 0;
00078                 }
00079         }
00080 
00081         // play an apropriate pain sound
00082         if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && !(player->s.eFlags & EF_DEAD) ) {
00083 
00084                 // don't do more than two pain sounds a second
00085                 // nmckenzie: also don't make him loud and whiny if he's only getting nicked.
00086                 if ( level.time - client->ps.painTime < 500 || count < 10) {
00087                         return;
00088                 }
00089                 P_SetTwitchInfo(client);
00090                 player->pain_debounce_time = level.time + 700;
00091                 
00092                 G_AddEvent( player, EV_PAIN, player->health );
00093                 client->ps.damageEvent++;
00094 
00095                 if (client->damage_armor && !client->damage_blood)
00096                 {
00097                         client->ps.damageType = 1; //pure shields
00098                 }
00099                 else if (client->damage_armor)
00100                 {
00101                         client->ps.damageType = 2; //shields and health
00102                 }
00103                 else
00104                 {
00105                         client->ps.damageType = 0; //pure health
00106                 }
00107         }
00108 
00109 
00110         client->ps.damageCount = count;
00111 
00112         //
00113         // clear totals
00114         //
00115         client->damage_blood = 0;
00116         client->damage_armor = 0;
00117         client->damage_knockback = 0;
00118 }
00119 
00120 
00121 
00122 /*
00123 =============
00124 P_WorldEffects
00125 
00126 Check for lava / slime contents and drowning
00127 =============
00128 */
00129 void P_WorldEffects( gentity_t *ent ) {
00130         qboolean        envirosuit;
00131         int                     waterlevel;
00132 
00133         if ( ent->client->noclip ) {
00134                 ent->client->airOutTime = level.time + 12000;   // don't need air
00135                 return;
00136         }
00137 
00138         waterlevel = ent->waterlevel;
00139 
00140         envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time;
00141 
00142         //
00143         // check for drowning
00144         //
00145         if ( waterlevel == 3 ) {
00146                 // envirosuit give air
00147                 if ( envirosuit ) {
00148                         ent->client->airOutTime = level.time + 10000;
00149                 }
00150 
00151                 // if out of air, start drowning
00152                 if ( ent->client->airOutTime < level.time) {
00153                         // drown!
00154                         ent->client->airOutTime += 1000;
00155                         if ( ent->health > 0 ) {
00156                                 // take more damage the longer underwater
00157                                 ent->damage += 2;
00158                                 if (ent->damage > 15)
00159                                         ent->damage = 15;
00160 
00161                                 // play a gurp sound instead of a normal pain sound
00162                                 if (ent->health <= ent->damage) {
00163                                         G_Sound(ent, CHAN_VOICE, G_SoundIndex(/*"*drown.wav"*/"sound/player/gurp1.wav"));
00164                                 } else if (rand()&1) {
00165                                         G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp1.wav"));
00166                                 } else {
00167                                         G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp2.wav"));
00168                                 }
00169 
00170                                 // don't play a normal pain sound
00171                                 ent->pain_debounce_time = level.time + 200;
00172 
00173                                 G_Damage (ent, NULL, NULL, NULL, NULL, 
00174                                         ent->damage, DAMAGE_NO_ARMOR, MOD_WATER);
00175                         }
00176                 }
00177         } else {
00178                 ent->client->airOutTime = level.time + 12000;
00179                 ent->damage = 2;
00180         }
00181 
00182         //
00183         // check for sizzle damage (move to pmove?)
00184         //
00185         if (waterlevel && 
00186                 (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
00187                 if (ent->health > 0
00188                         && ent->pain_debounce_time <= level.time        ) {
00189 
00190                         if ( envirosuit ) {
00191                                 G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 );
00192                         } else {
00193                                 if (ent->watertype & CONTENTS_LAVA) {
00194                                         G_Damage (ent, NULL, NULL, NULL, NULL, 
00195                                                 30*waterlevel, 0, MOD_LAVA);
00196                                 }
00197 
00198                                 if (ent->watertype & CONTENTS_SLIME) {
00199                                         G_Damage (ent, NULL, NULL, NULL, NULL, 
00200                                                 10*waterlevel, 0, MOD_SLIME);
00201                                 }
00202                         }
00203                 }
00204         }
00205 }
00206 
00207 
00208 
00209 
00210 
00211 //==============================================================
00212 extern void G_ApplyKnockback( gentity_t *targ, vec3_t newDir, float knockback );
00213 void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf )
00214 {
00215         float magnitude, my_mass;
00216         vec3_t  velocity;
00217         int cont;
00218         qboolean easyBreakBrush = qtrue;
00219 
00220         if( self->client )
00221         {
00222                 VectorCopy( self->client->ps.velocity, velocity );
00223                 if( !self->mass )
00224                 {
00225                         my_mass = 10;
00226                 }
00227                 else
00228                 {
00229                         my_mass = self->mass;
00230                 }
00231         }
00232         else 
00233         {
00234                 VectorCopy( self->s.pos.trDelta, velocity );
00235                 if ( self->s.pos.trType == TR_GRAVITY )
00236                 {
00237                         velocity[2] -= 0.25f * g_gravity.value;
00238                 }
00239                 if( !self->mass )
00240                 {
00241                         my_mass = 1;
00242                 }
00243                 else if ( self->mass <= 10 )
00244                 {
00245                         my_mass = 10;
00246                 }
00247                 else
00248                 {
00249                         my_mass = self->mass;
00250                 }
00251         }
00252 
00253         magnitude = VectorLength( velocity ) * my_mass / 10;
00254 
00255         /*
00256         if(pointcontents(self.absmax)==CONTENT_WATER)//FIXME: or other watertypes
00257                 magnitude/=3;                                                   //water absorbs 2/3 velocity
00258 
00259         if(self.classname=="barrel"&&self.aflag)//rolling barrels are made for impacts!
00260                 magnitude*=3;
00261 
00262         if(self.frozen>0&&magnitude<300&&self.flags&FL_ONGROUND&&loser==world&&self.velocity_z<-20&&self.last_onground+0.3<time)
00263                 magnitude=300;
00264         */
00265         if ( other->material == MAT_GLASS 
00266                 || other->material == MAT_GLASS_METAL 
00267                 || other->material == MAT_GRATE1
00268                 || ((other->flags&FL_BBRUSH)&&(other->spawnflags&8/*THIN*/))
00269                 || (other->r.svFlags&SVF_GLASS_BRUSH) )
00270         {
00271                 easyBreakBrush = qtrue;
00272         }
00273 
00274         if ( !self->client || self->client->ps.lastOnGround+300<level.time || ( self->client->ps.lastOnGround+100 < level.time && easyBreakBrush ) )
00275         {
00276                 vec3_t dir1, dir2;
00277                 float force = 0, dot;
00278 
00279                 if ( easyBreakBrush )
00280                         magnitude *= 2;
00281 
00282                 //damage them
00283                 if ( magnitude >= 100 && other->s.number < ENTITYNUM_WORLD )
00284                 {
00285                         VectorCopy( velocity, dir1 );
00286                         VectorNormalize( dir1 );
00287                         if( VectorCompare( other->r.currentOrigin, vec3_origin ) )
00288                         {//a brush with no origin
00289                                 VectorCopy ( dir1, dir2 );
00290                         }
00291                         else
00292                         {
00293                                 VectorSubtract( other->r.currentOrigin, self->r.currentOrigin, dir2 );
00294                                 VectorNormalize( dir2 );
00295                         }
00296 
00297                         dot = DotProduct( dir1, dir2 );
00298 
00299                         if ( dot >= 0.2 )
00300                         {
00301                                 force = dot;
00302                         }
00303                         else
00304                         {
00305                                 force = 0;
00306                         }
00307 
00308                         force *= (magnitude/50);
00309 
00310                         cont = trap_PointContents( other->r.absmax, other->s.number );
00311                         if( (cont&CONTENTS_WATER) )//|| (self.classname=="barrel"&&self.aflag))//FIXME: or other watertypes
00312                         {
00313                                 force /= 3;                                                     //water absorbs 2/3 velocity
00314                         }
00315 
00316                         /*
00317                         if(self.frozen>0&&force>10)
00318                                 force=10;
00319                         */
00320 
00321                         if( ( force >= 1 && other->s.number != 0 ) || force >= 10)
00322                         {
00323         /*                      
00324                                 dprint("Damage other (");
00325                                 dprint(loser.classname);
00326                                 dprint("): ");
00327                                 dprint(ftos(force));
00328                                 dprint("\n");
00329         */
00330                                 if ( other->r.svFlags & SVF_GLASS_BRUSH )
00331                                 {
00332                                         other->splashRadius = (float)(self->r.maxs[0] - self->r.mins[0])/4.0f;
00333                                 }
00334                                 if ( other->takedamage )
00335                                 {
00336                                         G_Damage( other, self, self, velocity, self->r.currentOrigin, force, DAMAGE_NO_ARMOR, MOD_CRUSH);//FIXME: MOD_IMPACT
00337                                 }
00338                                 else
00339                                 {
00340                                         G_ApplyKnockback( other, dir2, force );
00341                                 }
00342                         }
00343                 }
00344 
00345                 if ( damageSelf && self->takedamage )
00346                 {
00347                         //Now damage me
00348                         //FIXME: more lenient falling damage, especially for when driving a vehicle
00349                         if ( self->client && self->client->ps.fd.forceJumpZStart )
00350                         {//we were force-jumping
00351                                 if ( self->r.currentOrigin[2] >= self->client->ps.fd.forceJumpZStart )
00352                                 {//we landed at same height or higher than we landed
00353                                         magnitude = 0;
00354                                 }
00355                                 else
00356                                 {//FIXME: take off some of it, at least?
00357                                         magnitude = (self->client->ps.fd.forceJumpZStart-self->r.currentOrigin[2])/3;
00358                                 }
00359                         }
00360                         //if(self.classname!="monster_mezzoman"&&self.netname!="spider")//Cats always land on their feet
00361                                 if( ( magnitude >= 100 + self->health && self->s.number != 0 && self->s.weapon != WP_SABER ) || ( magnitude >= 700 ) )//&& self.safe_time < level.time ))//health here is used to simulate structural integrity
00362                                 {
00363                                         if ( (self->s.weapon == WP_SABER || self->s.number == 0) && self->client && self->client->ps.groundEntityNum < ENTITYNUM_NONE && magnitude < 1000 )
00364                                         {//players and jedi take less impact damage
00365                                                 //allow for some lenience on high falls
00366                                                 magnitude /= 2;
00367                                                 /*
00368                                                 if ( self.absorb_time >= time )//crouching on impact absorbs 1/2 the damage
00369                                                 {
00370                                                         magnitude/=2;
00371                                                 }
00372                                                 */
00373                                         }
00374                                         magnitude /= 40;
00375                                         magnitude = magnitude - force/2;//If damage other, subtract half of that damage off of own injury
00376                                         if ( magnitude >= 1 )
00377                                         {
00378                 //FIXME: Put in a thingtype impact sound function
00379                 /*                                      
00380                                                 dprint("Damage self (");
00381                                                 dprint(self.classname);
00382                                                 dprint("): ");
00383                                                 dprint(ftos(magnitude));
00384                                                 dprint("\n");
00385                 */
00386                                                 /*
00387                                                 if ( self.classname=="player_sheep "&& self.flags&FL_ONGROUND && self.velocity_z > -50 )
00388                                                         return;
00389                                                 */
00390                                                 G_Damage( self, NULL, NULL, NULL, self->r.currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT
00391                                         }
00392                                 }
00393                 }
00394 
00395                 //FIXME: slow my velocity some?
00396 
00397                 // NOTENOTE We don't use lastimpact as of yet
00398 //              self->lastImpact = level.time;
00399 
00400                 /*
00401                 if(self.flags&FL_ONGROUND)
00402                         self.last_onground=time;
00403                 */
00404         }
00405 }
00406 
00407 void Client_CheckImpactBBrush( gentity_t *self, gentity_t *other )
00408 {
00409         if ( !other || !other->inuse )
00410         {
00411                 return;
00412         }
00413         if (!self || !self->inuse || !self->client ||
00414                 self->client->tempSpectate >= level.time ||
00415                 self->client->sess.sessionTeam == TEAM_SPECTATOR)
00416         { //hmm.. let's not let spectators ram into breakables.
00417                 return;
00418         }
00419 
00420         /*
00421         if (BG_InSpecialJump(self->client->ps.legsAnim))
00422         { //don't do this either, qa says it creates "balance issues"
00423                 return;
00424         }
00425         */
00426 
00427         if ( other->material == MAT_GLASS 
00428                 || other->material == MAT_GLASS_METAL 
00429                 || other->material == MAT_GRATE1
00430                 || ((other->flags&FL_BBRUSH)&&(other->spawnflags&8/*THIN*/))
00431                 || ((other->flags&FL_BBRUSH)&&(other->health<=10))
00432                 || (other->r.svFlags&SVF_GLASS_BRUSH) )
00433         {//clients only do impact damage against easy-break breakables
00434                 DoImpact( self, other, qfalse );
00435         }
00436 }
00437 
00438 
00439 /*
00440 ===============
00441 G_SetClientSound
00442 ===============
00443 */
00444 void G_SetClientSound( gentity_t *ent ) {
00445         if (ent->client && ent->client->isHacking)
00446         { //loop hacking sound
00447                 ent->client->ps.loopSound = level.snd_hack;
00448                 ent->s.loopIsSoundset = qfalse;
00449         }
00450         else if (ent->client && ent->client->isMedHealed > level.time)
00451         { //loop healing sound
00452                 ent->client->ps.loopSound = level.snd_medHealed;
00453                 ent->s.loopIsSoundset = qfalse;
00454         }
00455         else if (ent->client && ent->client->isMedSupplied > level.time)
00456         { //loop supplying sound
00457                 ent->client->ps.loopSound = level.snd_medSupplied;
00458                 ent->s.loopIsSoundset = qfalse;
00459         }
00460         else if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
00461                 ent->client->ps.loopSound = level.snd_fry;
00462                 ent->s.loopIsSoundset = qfalse;
00463         } else {
00464                 ent->client->ps.loopSound = 0;
00465                 ent->s.loopIsSoundset = qfalse;
00466         }
00467 }
00468 
00469 
00470 
00471 //==============================================================
00472 
00473 /*
00474 ==============
00475 ClientImpacts
00476 ==============
00477 */
00478 void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
00479         int             i, j;
00480         trace_t trace;
00481         gentity_t       *other;
00482 
00483         memset( &trace, 0, sizeof( trace ) );
00484         for (i=0 ; i<pm->numtouch ; i++) {
00485                 for (j=0 ; j<i ; j++) {
00486                         if (pm->touchents[j] == pm->touchents[i] ) {
00487                                 break;
00488                         }
00489                 }
00490                 if (j != i) {
00491                         continue;       // duplicated
00492                 }
00493                 other = &g_entities[ pm->touchents[i] ];
00494 
00495                 if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
00496                         ent->touch( ent, other, &trace );
00497                 }
00498 
00499                 if ( !other->touch ) {
00500                         continue;
00501                 }
00502 
00503                 other->touch( other, ent, &trace );
00504         }
00505 
00506 }
00507 
00508 /*
00509 ============
00510 G_TouchTriggers
00511 
00512 Find all trigger entities that ent's current position touches.
00513 Spectators will only interact with teleporters.
00514 ============
00515 */
00516 void    G_TouchTriggers( gentity_t *ent ) {
00517         int                     i, num;
00518         int                     touch[MAX_GENTITIES];
00519         gentity_t       *hit;
00520         trace_t         trace;
00521         vec3_t          mins, maxs;
00522         static vec3_t   range = { 40, 40, 52 };
00523 
00524         if ( !ent->client ) {
00525                 return;
00526         }
00527 
00528         // dead clients don't activate triggers!
00529         if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) {
00530                 return;
00531         }
00532 
00533         VectorSubtract( ent->client->ps.origin, range, mins );
00534         VectorAdd( ent->client->ps.origin, range, maxs );
00535 
00536         num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
00537 
00538         // can't use ent->r.absmin, because that has a one unit pad
00539         VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
00540         VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
00541 
00542         for ( i=0 ; i<num ; i++ ) {
00543                 hit = &g_entities[touch[i]];
00544 
00545                 if ( !hit->touch && !ent->touch ) {
00546                         continue;
00547                 }
00548                 if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) {
00549                         continue;
00550                 }
00551 
00552                 // ignore most entities if a spectator
00553                 if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
00554                         if ( hit->s.eType != ET_TELEPORT_TRIGGER &&
00555                                 // this is ugly but adding a new ET_? type will
00556                                 // most likely cause network incompatibilities
00557                                 hit->touch != Touch_DoorTrigger) {
00558                                 continue;
00559                         }
00560                 }
00561 
00562                 // use seperate code for determining if an item is picked up
00563                 // so you don't have to actually contact its bounding box
00564                 if ( hit->s.eType == ET_ITEM ) {
00565                         if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
00566                                 continue;
00567                         }
00568                 } else {
00569                         if ( !trap_EntityContact( mins, maxs, hit ) ) {
00570                                 continue;
00571                         }
00572                 }
00573 
00574                 memset( &trace, 0, sizeof(trace) );
00575 
00576                 if ( hit->touch ) {
00577                         hit->touch (hit, ent, &trace);
00578                 }
00579 
00580                 if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
00581                         ent->touch( ent, hit, &trace );
00582                 }
00583         }
00584 
00585         // if we didn't touch a jump pad this pmove frame
00586         if ( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) {
00587                 ent->client->ps.jumppad_frame = 0;
00588                 ent->client->ps.jumppad_ent = 0;
00589         }
00590 }
00591 
00592 
00593 /*
00594 ============
00595 G_MoverTouchTriggers
00596 
00597 Find all trigger entities that ent's current position touches.
00598 Spectators will only interact with teleporters.
00599 ============
00600 */
00601 void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg ) 
00602 {
00603         int                     i, num;
00604         float           step, stepSize, dist;
00605         int                     touch[MAX_GENTITIES];
00606         gentity_t       *hit;
00607         trace_t         trace;
00608         vec3_t          mins, maxs, dir, size, checkSpot;
00609         const vec3_t    range = { 40, 40, 52 };
00610 
00611         // non-moving movers don't hit triggers!
00612         if ( !VectorLengthSquared( ent->s.pos.trDelta ) ) 
00613         {
00614                 return;
00615         }
00616 
00617         VectorSubtract( ent->r.mins, ent->r.maxs, size );
00618         stepSize = VectorLength( size );
00619         if ( stepSize < 1 )
00620         {
00621                 stepSize = 1;
00622         }
00623 
00624         VectorSubtract( ent->r.currentOrigin, oldOrg, dir );
00625         dist = VectorNormalize( dir );
00626         for ( step = 0; step <= dist; step += stepSize )
00627         {
00628                 VectorMA( ent->r.currentOrigin, step, dir, checkSpot );
00629                 VectorSubtract( checkSpot, range, mins );
00630                 VectorAdd( checkSpot, range, maxs );
00631 
00632                 num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
00633 
00634                 // can't use ent->r.absmin, because that has a one unit pad
00635                 VectorAdd( checkSpot, ent->r.mins, mins );
00636                 VectorAdd( checkSpot, ent->r.maxs, maxs );
00637 
00638                 for ( i=0 ; i<num ; i++ ) 
00639                 {
00640                         hit = &g_entities[touch[i]];
00641 
00642                         if ( hit->s.eType != ET_PUSH_TRIGGER )
00643                         {
00644                                 continue;
00645                         }
00646 
00647                         if ( hit->touch == NULL ) 
00648                         {
00649                                 continue;
00650                         }
00651 
00652                         if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) 
00653                         {
00654                                 continue;
00655                         }
00656 
00657 
00658                         if ( !trap_EntityContact( mins, maxs, hit ) ) 
00659                         {
00660                                 continue;
00661                         }
00662 
00663                         memset( &trace, 0, sizeof(trace) );
00664 
00665                         if ( hit->touch != NULL ) 
00666                         {
00667                                 hit->touch(hit, ent, &trace);
00668                         }
00669                 }
00670         }
00671 }
00672 
00673 /*
00674 =================
00675 SpectatorThink
00676 =================
00677 */
00678 void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) {
00679         pmove_t pm;
00680         gclient_t       *client;
00681 
00682         client = ent->client;
00683 
00684         if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) {
00685                 client->ps.pm_type = PM_SPECTATOR;
00686                 client->ps.speed = 400; // faster than normal
00687                 client->ps.basespeed = 400;
00688 
00689                 //hmm, shouldn't have an anim if you're a spectator, make sure
00690                 //it gets cleared.
00691                 client->ps.legsAnim = 0;
00692                 client->ps.legsTimer = 0;
00693                 client->ps.torsoAnim = 0;
00694                 client->ps.torsoTimer = 0;
00695 
00696                 // set up for pmove
00697                 memset (&pm, 0, sizeof(pm));
00698                 pm.ps = &client->ps;
00699                 pm.cmd = *ucmd;
00700                 pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;       // spectators can fly through bodies
00701                 pm.trace = trap_Trace;
00702                 pm.pointcontents = trap_PointContents;
00703 
00704                 pm.noSpecMove = g_noSpecMove.integer;
00705 
00706                 pm.animations = NULL;
00707                 pm.nonHumanoid = qfalse;
00708 
00709                 //Set up bg entity data
00710                 pm.baseEnt = (bgEntity_t *)g_entities;
00711                 pm.entSize = sizeof(gentity_t);
00712 
00713                 // perform a pmove
00714                 Pmove (&pm);
00715                 // save results of pmove
00716                 VectorCopy( client->ps.origin, ent->s.origin );
00717 
00718                 if (ent->client->tempSpectate < level.time)
00719                 {
00720                         G_TouchTriggers( ent );
00721                 }
00722                 trap_UnlinkEntity( ent );
00723         }
00724 
00725         client->oldbuttons = client->buttons;
00726         client->buttons = ucmd->buttons;
00727 
00728         if (client->tempSpectate < level.time)
00729         {
00730                 // attack button cycles through spectators
00731                 if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) {
00732                         Cmd_FollowCycle_f( ent, 1 );
00733                 }
00734 
00735                 if (client->sess.spectatorState == SPECTATOR_FOLLOW && (ucmd->upmove > 0))
00736                 { //jump now removes you from follow mode
00737                         StopFollowing(ent);
00738                 }
00739         }
00740 }
00741 
00742 
00743 
00744 /*
00745 =================
00746 ClientInactivityTimer
00747 
00748 Returns qfalse if the client is dropped
00749 =================
00750 */
00751 qboolean ClientInactivityTimer( gclient_t *client ) {
00752         if ( ! g_inactivity.integer ) {
00753                 // give everyone some time, so if the operator sets g_inactivity during
00754                 // gameplay, everyone isn't kicked
00755                 client->inactivityTime = level.time + 60 * 1000;
00756                 client->inactivityWarning = qfalse;
00757         } else if ( client->pers.cmd.forwardmove || 
00758                 client->pers.cmd.rightmove || 
00759                 client->pers.cmd.upmove ||
00760                 (client->pers.cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) ) {
00761                 client->inactivityTime = level.time + g_inactivity.integer * 1000;
00762                 client->inactivityWarning = qfalse;
00763         } else if ( !client->pers.localClient ) {
00764                 if ( level.time > client->inactivityTime ) {
00765                         trap_DropClient( client - level.clients, "Dropped due to inactivity" );
00766                         return qfalse;
00767                 }
00768                 if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) {
00769                         client->inactivityWarning = qtrue;
00770                         trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
00771                 }
00772         }
00773         return qtrue;
00774 }
00775 
00776 /*
00777 ==================
00778 ClientTimerActions
00779 
00780 Actions that happen once a second
00781 ==================
00782 */
00783 void ClientTimerActions( gentity_t *ent, int msec ) {
00784         gclient_t       *client;
00785 
00786         client = ent->client;
00787         client->timeResidual += msec;
00788 
00789         while ( client->timeResidual >= 1000 ) 
00790         {
00791                 client->timeResidual -= 1000;
00792 
00793                 // count down health when over max
00794                 if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) {
00795                         ent->health--;
00796                 }
00797 
00798                 // count down armor when over max
00799                 if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] ) {
00800                         client->ps.stats[STAT_ARMOR]--;
00801                 }
00802         }
00803 }
00804 
00805 /*
00806 ====================
00807 ClientIntermissionThink
00808 ====================
00809 */
00810 void ClientIntermissionThink( gclient_t *client ) {
00811         client->ps.eFlags &= ~EF_TALK;
00812         client->ps.eFlags &= ~EF_FIRING;
00813 
00814         // the level will exit when everyone wants to or after timeouts
00815 
00816         // swap and latch button actions
00817         client->oldbuttons = client->buttons;
00818         client->buttons = client->pers.cmd.buttons;
00819         if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) {
00820                 // this used to be an ^1 but once a player says ready, it should stick
00821                 client->readyToExit = 1;
00822         }
00823 }
00824 
00825 extern void NPC_SetAnim(gentity_t       *ent,int setAnimParts,int anim,int setAnimFlags);
00826 void G_VehicleAttachDroidUnit( gentity_t *vehEnt )
00827 {
00828         if ( vehEnt && vehEnt->m_pVehicle && vehEnt->m_pVehicle->m_pDroidUnit != NULL )
00829         {
00830                 gentity_t *droidEnt = (gentity_t *)vehEnt->m_pVehicle->m_pDroidUnit;
00831                 mdxaBone_t boltMatrix;
00832                 vec3_t  fwd;
00833 
00834                 trap_G2API_GetBoltMatrix(vehEnt->ghoul2, 0, vehEnt->m_pVehicle->m_iDroidUnitTag, &boltMatrix, vehEnt->r.currentAngles, vehEnt->r.currentOrigin, level.time,
00835                         NULL, vehEnt->modelScale);
00836                 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, droidEnt->r.currentOrigin);
00837                 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, fwd);
00838                 vectoangles( fwd, droidEnt->r.currentAngles );
00839                 
00840                 if ( droidEnt->client )
00841                 {
00842                         VectorCopy( droidEnt->r.currentAngles, droidEnt->client->ps.viewangles );
00843                         VectorCopy( droidEnt->r.currentOrigin, droidEnt->client->ps.origin );
00844                 }
00845 
00846                 G_SetOrigin( droidEnt, droidEnt->r.currentOrigin );
00847                 trap_LinkEntity( droidEnt );
00848                 
00849                 if ( droidEnt->NPC )
00850                 {
00851                         NPC_SetAnim( droidEnt, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00852                 }
00853         }
00854 }
00855 
00856 //called gameside only from pmove code (convenience)
00857 void G_CheapWeaponFire(int entNum, int ev)
00858 {
00859         gentity_t *ent = &g_entities[entNum];
00860         
00861         if (!ent->inuse || !ent->client)
00862         {
00863                 return;
00864         }
00865 
00866         switch (ev)
00867         {
00868                 case EV_FIRE_WEAPON:
00869                         if (ent->m_pVehicle && ent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER &&
00870                                 ent->client && ent->client->ps.m_iVehicleNum)
00871                         { //a speeder with a pilot
00872                                 gentity_t *rider = &g_entities[ent->client->ps.m_iVehicleNum-1];
00873                                 if (rider->inuse && rider->client)
00874                                 { //pilot is valid...
00875                     if (rider->client->ps.weapon != WP_MELEE &&
00876                                                 (rider->client->ps.weapon != WP_SABER || !rider->client->ps.saberHolstered))
00877                                         { //can only attack on speeder when using melee or when saber is holstered
00878                                                 break;
00879                                         }
00880                                 }
00881                         }
00882 
00883                         FireWeapon( ent, qfalse );
00884                         ent->client->dangerTime = level.time;
00885                         ent->client->ps.eFlags &= ~EF_INVULNERABLE;
00886                         ent->client->invulnerableTimer = 0;
00887                         break;
00888                 case EV_ALT_FIRE:
00889                         FireWeapon( ent, qtrue );
00890                         ent->client->dangerTime = level.time;
00891                         ent->client->ps.eFlags &= ~EF_INVULNERABLE;
00892                         ent->client->invulnerableTimer = 0;
00893                         break;
00894         }
00895 }
00896 
00897 /*
00898 ================
00899 ClientEvents
00900 
00901 Events will be passed on to the clients for presentation,
00902 but any server game effects are handled here
00903 ================
00904 */
00905 #include "../namespace_begin.h"
00906 qboolean BG_InKnockDownOnly( int anim );
00907 #include "../namespace_end.h"
00908 
00909 void ClientEvents( gentity_t *ent, int oldEventSequence ) {
00910         int             i;//, j;
00911         int             event;
00912         gclient_t *client;
00913         int             damage;
00914         vec3_t  dir;
00915 //      vec3_t  origin, angles;
00916 //      qboolean        fired;
00917 //      gitem_t *item;
00918 //      gentity_t *drop;
00919 
00920         client = ent->client;
00921 
00922         if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) {
00923                 oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
00924         }
00925         for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) {
00926                 event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];
00927 
00928                 switch ( event ) {
00929                 case EV_FALL:
00930                 case EV_ROLL:
00931                         {
00932                                 int delta = client->ps.eventParms[ i & (MAX_PS_EVENTS-1) ];
00933                                 qboolean knockDownage = qfalse;
00934 
00935                                 if (ent->client && ent->client->ps.fallingToDeath)
00936                                 {
00937                                         break;
00938                                 }
00939 
00940                                 if ( ent->s.eType != ET_PLAYER )
00941                                 {
00942                                         break;          // not in the player model
00943                                 }
00944                                 
00945                                 if ( g_dmflags.integer & DF_NO_FALLING )
00946                                 {
00947                                         break;
00948                                 }
00949 
00950                                 if (BG_InKnockDownOnly(ent->client->ps.legsAnim))
00951                                 {
00952                                         if (delta <= 14)
00953                                         {
00954                                                 break;
00955                                         }
00956                                         knockDownage = qtrue;
00957                                 }
00958                                 else
00959                                 {
00960                                         if (delta <= 44)
00961                                         {
00962                                                 break;
00963                                         }
00964                                 }
00965 
00966                                 if (knockDownage)
00967                                 {
00968                                         damage = delta*1; //you suffer for falling unprepared. A lot. Makes throws and things useful, and more realistic I suppose.
00969                                 }
00970                                 else
00971                                 {
00972                                         if (g_gametype.integer == GT_SIEGE &&
00973                                                 delta > 60)
00974                                         { //longer falls hurt more
00975                                                 damage = delta*1; //good enough for now, I guess
00976                                         }
00977                                         else
00978                                         {
00979                                                 damage = delta*0.16; //good enough for now, I guess
00980                                         }
00981                                 }
00982 
00983                                 VectorSet (dir, 0, 0, 1);
00984                                 ent->pain_debounce_time = level.time + 200;     // no normal pain sound
00985                                 G_Damage (ent, NULL, NULL, NULL, NULL, damage, DAMAGE_NO_ARMOR, MOD_FALLING);
00986 
00987                                 if (ent->health < 1)
00988                                 {
00989                                         G_Sound(ent, CHAN_AUTO, G_SoundIndex( "sound/player/fallsplat.wav" ));
00990                                 }
00991                         }
00992                         break;
00993                 case EV_FIRE_WEAPON:
00994                         FireWeapon( ent, qfalse );
00995                         ent->client->dangerTime = level.time;
00996                         ent->client->ps.eFlags &= ~EF_INVULNERABLE;
00997                         ent->client->invulnerableTimer = 0;
00998                         break;
00999 
01000                 case EV_ALT_FIRE:
01001                         FireWeapon( ent, qtrue );
01002                         ent->client->dangerTime = level.time;
01003                         ent->client->ps.eFlags &= ~EF_INVULNERABLE;
01004                         ent->client->invulnerableTimer = 0;
01005                         break;
01006 
01007                 case EV_SABER_ATTACK:
01008                         ent->client->dangerTime = level.time;
01009                         ent->client->ps.eFlags &= ~EF_INVULNERABLE;
01010                         ent->client->invulnerableTimer = 0;
01011                         break;
01012 
01013                 //rww - Note that these must be in the same order (ITEM#-wise) as they are in holdable_t
01014                 case EV_USE_ITEM1: //seeker droid
01015                         ItemUse_Seeker(ent);
01016                         break;
01017                 case EV_USE_ITEM2: //shield
01018                         ItemUse_Shield(ent);
01019                         break;
01020                 case EV_USE_ITEM3: //medpack
01021                         ItemUse_MedPack(ent);
01022                         break;
01023                 case EV_USE_ITEM4: //big medpack
01024                         ItemUse_MedPack_Big(ent);
01025                         break;
01026                 case EV_USE_ITEM5: //binoculars
01027                         ItemUse_Binoculars(ent);
01028                         break;
01029                 case EV_USE_ITEM6: //sentry gun
01030                         ItemUse_Sentry(ent);
01031                         break;
01032                 case EV_USE_ITEM7: //jetpack
01033                         ItemUse_Jetpack(ent);
01034                         break;
01035                 case EV_USE_ITEM8: //health disp
01036                         //ItemUse_UseDisp(ent, HI_HEALTHDISP);
01037                         break;
01038                 case EV_USE_ITEM9: //ammo disp
01039                         //ItemUse_UseDisp(ent, HI_AMMODISP);
01040                         break;
01041                 case EV_USE_ITEM10: //eweb
01042                         ItemUse_UseEWeb(ent);
01043                         break;
01044                 case EV_USE_ITEM11: //cloak
01045                         ItemUse_UseCloak(ent);
01046                         break;
01047                 default:
01048                         break;
01049                 }
01050         }
01051 
01052 }
01053 
01054 /*
01055 ==============
01056 SendPendingPredictableEvents
01057 ==============
01058 */
01059 void SendPendingPredictableEvents( playerState_t *ps ) {
01060         gentity_t *t;
01061         int event, seq;
01062         int extEvent, number;
01063 
01064         // if there are still events pending
01065         if ( ps->entityEventSequence < ps->eventSequence ) {
01066                 // create a temporary entity for this event which is sent to everyone
01067                 // except the client who generated the event
01068                 seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
01069                 event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
01070                 // set external event to zero before calling BG_PlayerStateToEntityState
01071                 extEvent = ps->externalEvent;
01072                 ps->externalEvent = 0;
01073                 // create temporary entity for event
01074                 t = G_TempEntity( ps->origin, event );
01075                 number = t->s.number;
01076                 BG_PlayerStateToEntityState( ps, &t->s, qtrue );
01077                 t->s.number = number;
01078                 t->s.eType = ET_EVENTS + event;
01079                 t->s.eFlags |= EF_PLAYER_EVENT;
01080                 t->s.otherEntityNum = ps->clientNum;
01081                 // send to everyone except the client who generated the event
01082                 t->r.svFlags |= SVF_NOTSINGLECLIENT;
01083                 t->r.singleClient = ps->clientNum;
01084                 // set back external event
01085                 ps->externalEvent = extEvent;
01086         }
01087 }
01088 
01089 /*
01090 ==================
01091 G_UpdateClientBroadcasts
01092 
01093 Determines whether this client should be broadcast to any other clients.  
01094 A client is broadcast when another client is using force sight or is
01095 ==================
01096 */
01097 #define MAX_JEDIMASTER_DISTANCE 2500
01098 #define MAX_JEDIMASTER_FOV              100
01099 
01100 #define MAX_SIGHT_DISTANCE              1500
01101 #define MAX_SIGHT_FOV                   100
01102 
01103 static void G_UpdateForceSightBroadcasts ( gentity_t *self )
01104 {
01105         int i;
01106 
01107         // Any clients with force sight on should see this client
01108         for ( i = 0; i < level.numConnectedClients; i ++ )
01109         {
01110                 gentity_t *ent = &g_entities[level.sortedClients[i]];
01111                 float     dist;
01112                 vec3_t    angles;
01113         
01114                 if ( ent == self )
01115                 {
01116                         continue;
01117                 }
01118 
01119                 // Not using force sight so we shouldnt broadcast to this one
01120                 if ( !(ent->client->ps.fd.forcePowersActive & (1<<FP_SEE) ) )
01121                 {
01122                         continue;
01123                 }
01124 
01125                 VectorSubtract( self->client->ps.origin, ent->client->ps.origin, angles );
01126                 dist = VectorLengthSquared ( angles );
01127                 vectoangles ( angles, angles );
01128 
01129                 // Too far away then just forget it
01130                 if ( dist > MAX_SIGHT_DISTANCE * MAX_SIGHT_DISTANCE )
01131                 {
01132                         continue;
01133                 }
01134                 
01135                 // If not within the field of view then forget it
01136                 if ( !InFieldOfVision ( ent->client->ps.viewangles, MAX_SIGHT_FOV, angles ) )
01137                 {
01138                         break;
01139                 }
01140 
01141                 // Turn on the broadcast bit for the master and since there is only one
01142                 // master we are done
01143                 self->r.broadcastClients[ent->s.clientNum/32] |= (1 << (ent->s.clientNum%32));
01144         
01145                 break;
01146         }
01147 }
01148 
01149 static void G_UpdateJediMasterBroadcasts ( gentity_t *self )
01150 {
01151         int i;
01152 
01153         // Not jedi master mode then nothing to do
01154         if ( g_gametype.integer != GT_JEDIMASTER )
01155         {
01156                 return;
01157         }
01158 
01159         // This client isnt the jedi master so it shouldnt broadcast
01160         if ( !self->client->ps.isJediMaster )
01161         {
01162                 return;
01163         }
01164 
01165         // Broadcast ourself to all clients within range
01166         for ( i = 0; i < level.numConnectedClients; i ++ )
01167         {
01168                 gentity_t *ent = &g_entities[level.sortedClients[i]];
01169                 float     dist;
01170                 vec3_t    angles;
01171 
01172                 if ( ent == self )
01173                 {
01174                         continue;
01175                 }
01176 
01177                 VectorSubtract( self->client->ps.origin, ent->client->ps.origin, angles );
01178                 dist = VectorLengthSquared ( angles );
01179                 vectoangles ( angles, angles );
01180 
01181                 // Too far away then just forget it
01182                 if ( dist > MAX_JEDIMASTER_DISTANCE * MAX_JEDIMASTER_DISTANCE )
01183                 {
01184                         continue;
01185                 }
01186                 
01187                 // If not within the field of view then forget it
01188                 if ( !InFieldOfVision ( ent->client->ps.viewangles, MAX_JEDIMASTER_FOV, angles ) )
01189                 {
01190                         continue;
01191                 }
01192 
01193                 // Turn on the broadcast bit for the master and since there is only one
01194                 // master we are done
01195                 self->r.broadcastClients[ent->s.clientNum/32] |= (1 << (ent->s.clientNum%32));
01196         }
01197 }
01198 
01199 void G_UpdateClientBroadcasts ( gentity_t *self )
01200 {
01201         // Clear all the broadcast bits for this client
01202         memset ( self->r.broadcastClients, 0, sizeof ( self->r.broadcastClients ) );
01203 
01204         // The jedi master is broadcast to everyone in range
01205         G_UpdateJediMasterBroadcasts ( self );
01206 
01207         // Anyone with force sight on should see this client
01208         G_UpdateForceSightBroadcasts ( self );
01209 }
01210 
01211 void G_AddPushVecToUcmd( gentity_t *self, usercmd_t *ucmd )
01212 {
01213         vec3_t  forward, right, moveDir;
01214         float   pushSpeed, fMove, rMove;
01215 
01216         if ( !self->client )
01217         {
01218                 return;
01219         }
01220         pushSpeed = VectorLengthSquared(self->client->pushVec);
01221         if(!pushSpeed)
01222         {//not being pushed
01223                 return;
01224         }
01225 
01226         AngleVectors(self->client->ps.viewangles, forward, right, NULL);
01227         VectorScale(forward, ucmd->forwardmove/127.0f * self->client->ps.speed, moveDir);
01228         VectorMA(moveDir, ucmd->rightmove/127.0f * self->client->ps.speed, right, moveDir);
01229         //moveDir is now our intended move velocity
01230 
01231         VectorAdd(moveDir, self->client->pushVec, moveDir);
01232         self->client->ps.speed = VectorNormalize(moveDir);
01233         //moveDir is now our intended move velocity plus our push Vector
01234 
01235         fMove = 127.0 * DotProduct(forward, moveDir);
01236         rMove = 127.0 * DotProduct(right, moveDir);
01237         ucmd->forwardmove = floor(fMove);//If in the same dir , will be positive
01238         ucmd->rightmove = floor(rMove);//If in the same dir , will be positive
01239 
01240         if ( self->client->pushVecTime < level.time )
01241         {
01242                 VectorClear( self->client->pushVec );
01243         }
01244 }
01245 
01246 qboolean G_StandingAnim( int anim )
01247 {//NOTE: does not check idles or special (cinematic) stands
01248         switch ( anim )
01249         {
01250         case BOTH_STAND1:
01251         case BOTH_STAND2:
01252         case BOTH_STAND3:
01253         case BOTH_STAND4:
01254                 return qtrue;
01255                 break;
01256         }
01257         return qfalse;
01258 }
01259 
01260 qboolean G_ActionButtonPressed(int buttons)
01261 {
01262         if (buttons & BUTTON_ATTACK)
01263         {
01264                 return qtrue;
01265         }
01266         else if (buttons & BUTTON_USE_HOLDABLE)
01267         {
01268                 return qtrue;
01269         }
01270         else if (buttons & BUTTON_GESTURE)
01271         {
01272                 return qtrue;
01273         }
01274         else if (buttons & BUTTON_USE)
01275         {
01276                 return qtrue;
01277         }
01278         else if (buttons & BUTTON_FORCEGRIP)
01279         {
01280                 return qtrue;
01281         }
01282         else if (buttons & BUTTON_ALT_ATTACK)
01283         {
01284                 return qtrue;
01285         }
01286         else if (buttons & BUTTON_FORCEPOWER)
01287         {
01288                 return qtrue;
01289         }
01290         else if (buttons & BUTTON_FORCE_LIGHTNING)
01291         {
01292                 return qtrue;
01293         }
01294         else if (buttons & BUTTON_FORCE_DRAIN)
01295         {
01296                 return qtrue;
01297         }
01298 
01299         return qfalse;
01300 }
01301 
01302 void G_CheckClientIdle( gentity_t *ent, usercmd_t *ucmd ) 
01303 {
01304         vec3_t viewChange;
01305         qboolean actionPressed;
01306         int buttons;
01307 
01308         if ( !ent || !ent->client || ent->health <= 0 || ent->client->ps.stats[STAT_HEALTH] <= 0 ||
01309                 ent->client->sess.sessionTeam == TEAM_SPECTATOR || (ent->client->ps.pm_flags & PMF_FOLLOW))
01310         {
01311                 return;
01312         }
01313 
01314         buttons = ucmd->buttons;
01315 
01316         if (ent->r.svFlags & SVF_BOT)
01317         { //they press use all the time..
01318                 buttons &= ~BUTTON_USE;
01319         }
01320         actionPressed = G_ActionButtonPressed(buttons);
01321 
01322         VectorSubtract(ent->client->ps.viewangles, ent->client->idleViewAngles, viewChange);
01323         if ( !VectorCompare( vec3_origin, ent->client->ps.velocity ) 
01324                 || actionPressed || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove 
01325                 || !G_StandingAnim( ent->client->ps.legsAnim ) 
01326                 || (ent->health+ent->client->ps.stats[STAT_ARMOR]) != ent->client->idleHealth
01327                 || VectorLength(viewChange) > 10
01328                 || ent->client->ps.legsTimer > 0
01329                 || ent->client->ps.torsoTimer > 0
01330                 || ent->client->ps.weaponTime > 0
01331                 || ent->client->ps.weaponstate == WEAPON_CHARGING
01332                 || ent->client->ps.weaponstate == WEAPON_CHARGING_ALT
01333                 || ent->client->ps.zoomMode
01334                 || (ent->client->ps.weaponstate != WEAPON_READY && ent->client->ps.weapon != WP_SABER)
01335                 || ent->client->ps.forceHandExtend != HANDEXTEND_NONE
01336                 || ent->client->ps.saberBlocked != BLOCKED_NONE
01337                 || ent->client->ps.saberBlocking >= level.time
01338                 || ent->client->ps.weapon == WP_MELEE
01339                 || (ent->client->ps.weapon != ent->client->pers.cmd.weapon && ent->s.eType != ET_NPC))
01340         {//FIXME: also check for turning?
01341                 qboolean brokeOut = qfalse;
01342 
01343                 if ( !VectorCompare( vec3_origin, ent->client->ps.velocity ) 
01344                         || actionPressed || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove 
01345                         || (ent->health+ent->client->ps.stats[STAT_ARMOR]) != ent->client->idleHealth
01346                         || ent->client->ps.zoomMode
01347                         || (ent->client->ps.weaponstate != WEAPON_READY && ent->client->ps.weapon != WP_SABER)
01348                         || (ent->client->ps.weaponTime > 0 && ent->client->ps.weapon == WP_SABER)
01349                         || ent->client->ps.weaponstate == WEAPON_CHARGING
01350                         || ent->client->ps.weaponstate == WEAPON_CHARGING_ALT
01351                         || ent->client->ps.forceHandExtend != HANDEXTEND_NONE
01352                         || ent->client->ps.saberBlocked != BLOCKED_NONE
01353                         || ent->client->ps.saberBlocking >= level.time
01354                         || ent->client->ps.weapon == WP_MELEE
01355                         || (ent->client->ps.weapon != ent->client->pers.cmd.weapon && ent->s.eType != ET_NPC))
01356                 {
01357                         //if in an idle, break out
01358                         switch ( ent->client->ps.legsAnim )
01359                         {
01360                         case BOTH_STAND1IDLE1:
01361                         case BOTH_STAND2IDLE1:
01362                         case BOTH_STAND2IDLE2:
01363                         case BOTH_STAND3IDLE1:
01364                         case BOTH_STAND5IDLE1:
01365                                 ent->client->ps.legsTimer = 0;
01366                                 brokeOut = qtrue;
01367                                 break;
01368                         }
01369                         switch ( ent->client->ps.torsoAnim )
01370                         {
01371                         case BOTH_STAND1IDLE1:
01372                         case BOTH_STAND2IDLE1:
01373                         case BOTH_STAND2IDLE2:
01374                         case BOTH_STAND3IDLE1:
01375                         case BOTH_STAND5IDLE1:
01376                                 ent->client->ps.torsoTimer = 0;
01377                                 ent->client->ps.weaponTime = 0;
01378                                 ent->client->ps.saberMove = LS_READY;
01379                                 brokeOut = qtrue;
01380                                 break;
01381                         }
01382                 }
01383                 //
01384                 ent->client->idleHealth = (ent->health+ent->client->ps.stats[STAT_ARMOR]);
01385                 VectorCopy(ent->client->ps.viewangles, ent->client->idleViewAngles);
01386                 if ( ent->client->idleTime < level.time )
01387                 {
01388                         ent->client->idleTime = level.time;
01389                 }
01390 
01391                 if (brokeOut &&
01392                         (ent->client->ps.weaponstate == WEAPON_CHARGING || ent->client->ps.weaponstate == WEAPON_CHARGING_ALT))
01393                 {
01394                         ent->client->ps.torsoAnim = TORSO_RAISEWEAP1;
01395                 }
01396         }
01397         else if ( level.time - ent->client->idleTime > 5000 )
01398         {//been idle for 5 seconds
01399                 int     idleAnim = -1;
01400                 switch ( ent->client->ps.legsAnim )
01401                 {
01402                 case BOTH_STAND1:
01403                         idleAnim = BOTH_STAND1IDLE1;
01404                         break;
01405                 case BOTH_STAND2:
01406                         idleAnim = BOTH_STAND2IDLE1;//Q_irand(BOTH_STAND2IDLE1,BOTH_STAND2IDLE2);
01407                         break;
01408                 case BOTH_STAND3:
01409                         idleAnim = BOTH_STAND3IDLE1;
01410                         break;
01411                 case BOTH_STAND5:
01412                         idleAnim = BOTH_STAND5IDLE1;
01413                         break;
01414                 }
01415 
01416                 if (idleAnim == BOTH_STAND2IDLE1 && Q_irand(1, 10) <= 5)
01417                 {
01418                         idleAnim = BOTH_STAND2IDLE2;
01419                 }
01420 
01421                 if ( idleAnim != -1 && /*PM_HasAnimation( ent, idleAnim )*/idleAnim > 0 && idleAnim < MAX_ANIMATIONS )
01422                 {
01423                         G_SetAnim(ent, ucmd, SETANIM_BOTH, idleAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01424 
01425                         //don't idle again after this anim for a while
01426                         //ent->client->idleTime = level.time + PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)idleAnim ) + Q_irand( 0, 2000 );
01427                         ent->client->idleTime = level.time + ent->client->ps.legsTimer + Q_irand( 0, 2000 );
01428                 }
01429         }
01430 }
01431 
01432 void NPC_Accelerate( gentity_t *ent, qboolean fullWalkAcc, qboolean fullRunAcc )
01433 {
01434         if ( !ent->client || !ent->NPC )
01435         {
01436                 return;
01437         }
01438 
01439         if ( !ent->NPC->stats.acceleration )
01440         {//No acceleration means just start and stop
01441                 ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
01442         }
01443         //FIXME:  in cinematics always accel/decel?
01444         else if ( ent->NPC->desiredSpeed <= ent->NPC->stats.walkSpeed )
01445         {//Only accelerate if at walkSpeeds
01446                 if ( ent->NPC->desiredSpeed > ent->NPC->currentSpeed + ent->NPC->stats.acceleration )
01447                 {
01448                         //ent->client->ps.friction = 0;
01449                         ent->NPC->currentSpeed += ent->NPC->stats.acceleration;
01450                 }
01451                 else if ( ent->NPC->desiredSpeed > ent->NPC->currentSpeed )
01452                 {
01453                         //ent->client->ps.friction = 0;
01454                         ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
01455                 }
01456                 else if ( fullWalkAcc && ent->NPC->desiredSpeed < ent->NPC->currentSpeed - ent->NPC->stats.acceleration )
01457                 {//decelerate even when walking
01458                         ent->NPC->currentSpeed -= ent->NPC->stats.acceleration;
01459                 }
01460                 else if ( ent->NPC->desiredSpeed < ent->NPC->currentSpeed )
01461                 {//stop on a dime
01462                         ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
01463                 }
01464         }
01465         else//  if ( ent->NPC->desiredSpeed > ent->NPC->stats.walkSpeed )
01466         {//Only decelerate if at runSpeeds
01467                 if ( fullRunAcc && ent->NPC->desiredSpeed > ent->NPC->currentSpeed + ent->NPC->stats.acceleration )
01468                 {//Accelerate to runspeed
01469                         //ent->client->ps.friction = 0;
01470                         ent->NPC->currentSpeed += ent->NPC->stats.acceleration;
01471                 }
01472                 else if ( ent->NPC->desiredSpeed > ent->NPC->currentSpeed )
01473                 {//accelerate instantly
01474                         //ent->client->ps.friction = 0;
01475                         ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
01476                 }
01477                 else if ( fullRunAcc && ent->NPC->desiredSpeed < ent->NPC->currentSpeed - ent->NPC->stats.acceleration )
01478                 {
01479                         ent->NPC->currentSpeed -= ent->NPC->stats.acceleration;
01480                 }
01481                 else if ( ent->NPC->desiredSpeed < ent->NPC->currentSpeed )
01482                 {
01483                         ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
01484                 }
01485         }
01486 }
01487 
01488 /*
01489 -------------------------
01490 NPC_GetWalkSpeed
01491 -------------------------
01492 */
01493 
01494 static int NPC_GetWalkSpeed( gentity_t *ent )
01495 {
01496         int     walkSpeed = 0;
01497 
01498         if ( ( ent->client == NULL ) || ( ent->NPC == NULL ) )
01499                 return 0;
01500 
01501         switch ( ent->client->playerTeam )
01502         {
01503         case NPCTEAM_PLAYER:    //To shutup compiler, will add entries later (this is stub code)
01504         default:
01505                 walkSpeed = ent->NPC->stats.walkSpeed;
01506                 break;
01507         }
01508 
01509         return walkSpeed;
01510 }
01511 
01512 /*
01513 -------------------------
01514 NPC_GetRunSpeed
01515 -------------------------
01516 */
01517 static int NPC_GetRunSpeed( gentity_t *ent )
01518 {
01519         int     runSpeed = 0;
01520 
01521         if ( ( ent->client == NULL ) || ( ent->NPC == NULL ) )
01522                 return 0;
01523 /*
01524         switch ( ent->client->playerTeam )
01525         {
01526         case TEAM_BORG:
01527                 runSpeed = ent->NPC->stats.runSpeed;
01528                 runSpeed += BORG_RUN_INCR * (g_spskill->integer%3);
01529                 break;
01530 
01531         case TEAM_8472:
01532                 runSpeed = ent->NPC->stats.runSpeed;
01533                 runSpeed += SPECIES_RUN_INCR * (g_spskill->integer%3);
01534                 break;
01535 
01536         case TEAM_STASIS:
01537                 runSpeed = ent->NPC->stats.runSpeed;
01538                 runSpeed += STASIS_RUN_INCR * (g_spskill->integer%3);
01539                 break;
01540 
01541         case TEAM_BOTS:
01542                 runSpeed = ent->NPC->stats.runSpeed;
01543                 break;
01544 
01545         default:
01546                 runSpeed = ent->NPC->stats.runSpeed;
01547                 break;
01548         }
01549 */
01550         // team no longer indicates species/race.  Use NPC_class to adjust speed for specific npc types
01551         switch( ent->client->NPC_class)
01552         {
01553         case CLASS_PROBE:       // droid cases here to shut-up compiler
01554         case CLASS_GONK:
01555         case CLASS_R2D2:
01556         case CLASS_R5D2:
01557         case CLASS_MARK1:
01558         case CLASS_MARK2:
01559         case CLASS_PROTOCOL:
01560         case CLASS_ATST: // hmm, not really your average droid
01561         case CLASS_MOUSE:
01562         case CLASS_SEEKER:
01563         case CLASS_REMOTE:
01564                 runSpeed = ent->NPC->stats.runSpeed;
01565                 break;
01566 
01567         default:
01568                 runSpeed = ent->NPC->stats.runSpeed*1.3f; //rww - seems to slow in MP for some reason.
01569                 break;
01570         }
01571 
01572         return runSpeed;
01573 }
01574 
01575 //Seems like a slightly less than ideal method for this, could it be done on the client?
01576 extern qboolean FlyingCreature( gentity_t *ent );
01577 void G_CheckMovingLoopingSounds( gentity_t *ent, usercmd_t *ucmd )
01578 {
01579         if ( ent->client )
01580         {
01581                 if ( (ent->NPC&&!VectorCompare( vec3_origin, ent->client->ps.moveDir ))//moving using moveDir
01582                         || ucmd->forwardmove || ucmd->rightmove//moving using ucmds
01583                         || (ucmd->upmove&&FlyingCreature( ent ))//flier using ucmds to move
01584                         || (FlyingCreature( ent )&&!VectorCompare( vec3_origin, ent->client->ps.velocity )&&ent->health>0))//flier using velocity to move
01585                 {
01586                         switch( ent->client->NPC_class )
01587                         {
01588                         case CLASS_R2D2:
01589                                 ent->s.loopSound = G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp.wav" );
01590                                 break;
01591                         case CLASS_R5D2:
01592                                 ent->s.loopSound = G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp2.wav" );
01593                                 break;
01594                         case CLASS_MARK2:
01595                                 ent->s.loopSound = G_SoundIndex( "sound/chars/mark2/misc/mark2_move_lp" );
01596                                 break;
01597                         case CLASS_MOUSE:
01598                                 ent->s.loopSound = G_SoundIndex( "sound/chars/mouse/misc/mouse_lp" );
01599                                 break;
01600                         case CLASS_PROBE:
01601                                 ent->s.loopSound = G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
01602                         }
01603                 }
01604                 else
01605                 {//not moving under your own control, stop loopSound
01606                         if ( ent->client->NPC_class == CLASS_R2D2 || ent->client->NPC_class == CLASS_R5D2 
01607                                         || ent->client->NPC_class == CLASS_MARK2 || ent->client->NPC_class == CLASS_MOUSE 
01608                                         || ent->client->NPC_class == CLASS_PROBE )
01609                         {
01610                                 ent->s.loopSound = 0;
01611                         }
01612                 }
01613         }
01614 }
01615 
01616 void G_HeldByMonster( gentity_t *ent, usercmd_t **ucmd )
01617 {
01618         if ( ent 
01619                 && ent->client
01620                 && ent->client->ps.hasLookTarget )//NOTE: lookTarget is an entity number, so this presumes that client 0 is NOT a Rancor...
01621         {
01622                 gentity_t *monster = &g_entities[ent->client->ps.lookTarget];
01623                 if ( monster && monster->client )
01624                 {
01625                         //take the monster's waypoint as your own
01626                         ent->waypoint = monster->waypoint;
01627                         if ( monster->s.NPC_class == CLASS_RANCOR )
01628                         {//only possibility right now, may add Wampa and Sand Creature later
01629                                 BG_AttachToRancor( monster->ghoul2, //ghoul2 info
01630                                         monster->r.currentAngles[YAW],
01631                                         monster->r.currentOrigin,
01632                                         level.time,
01633                                         NULL,
01634                                         monster->modelScale,
01635                                         (monster->client->ps.eFlags2&EF2_GENERIC_NPC_FLAG),
01636                                         ent->client->ps.origin,
01637                                         ent->client->ps.viewangles,
01638                                         NULL );
01639                         }
01640                         VectorClear( ent->client->ps.velocity );
01641                         G_SetOrigin( ent, ent->client->ps.origin );
01642                         SetClientViewAngle( ent, ent->client->ps.viewangles );
01643                         G_SetAngles( ent, ent->client->ps.viewangles );
01644                         trap_LinkEntity( ent );//redundant?
01645                 }
01646         }
01647         // don't allow movement, weapon switching, and most kinds of button presses
01648         (*ucmd)->forwardmove = 0;
01649         (*ucmd)->rightmove = 0;
01650         (*ucmd)->upmove = 0;
01651 }
01652 
01653 typedef enum
01654 {
01655         TAUNT_TAUNT = 0,
01656         TAUNT_BOW,
01657         TAUNT_MEDITATE,
01658         TAUNT_FLOURISH,
01659         TAUNT_GLOAT
01660 };
01661 
01662 void G_SetTauntAnim( gentity_t *ent, int taunt )
01663 {
01664         if (ent->client->pers.cmd.upmove ||
01665                 ent->client->pers.cmd.forwardmove ||
01666                 ent->client->pers.cmd.rightmove)
01667         { //hack, don't do while moving
01668                 return;
01669         }
01670         if ( taunt != TAUNT_TAUNT )
01671         {//normal taunt always allowed
01672                 if ( g_gametype.integer != GT_DUEL
01673                         && g_gametype.integer != GT_POWERDUEL )
01674                 {//no taunts unless in Duel
01675                         return;
01676                 }
01677         }
01678         if ( ent->client->ps.torsoTimer < 1 
01679                 && ent->client->ps.forceHandExtend == HANDEXTEND_NONE 
01680                 && ent->client->ps.legsTimer < 1 
01681                 && ent->client->ps.weaponTime < 1 
01682                 && ent->client->ps.saberLockTime < level.time )
01683         {
01684                 int anim = -1;
01685                 switch ( taunt )
01686                 {
01687                 case TAUNT_TAUNT:
01688                         if ( ent->client->ps.weapon != WP_SABER )
01689                         {
01690                                 anim = BOTH_ENGAGETAUNT;
01691                         }
01692                         else if ( ent->client->saber[0].tauntAnim != -1 )
01693                         {
01694                                 anim = ent->client->saber[0].tauntAnim;
01695                         }
01696                         else if ( ent->client->saber[1].model 
01697                                         && ent->client->saber[1].model[0]
01698                                         && ent->client->saber[1].tauntAnim != -1 )
01699                         {
01700                                 anim = ent->client->saber[1].tauntAnim;
01701                         }
01702                         else
01703                         {
01704                                 switch ( ent->client->ps.fd.saberAnimLevel )
01705                                 {
01706                                 case SS_FAST:
01707                                 case SS_TAVION:
01708                                         if ( ent->client->ps.saberHolstered == 1 
01709                                                 && ent->client->saber[1].model 
01710                                                 && ent->client->saber[1].model[0] )
01711                                         {//turn off second saber
01712                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOff );
01713                                         }
01714                                         else if ( ent->client->ps.saberHolstered == 0 )
01715                                         {//turn off first
01716                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOff );
01717                                         }
01718                                         ent->client->ps.saberHolstered = 2;
01719                                         anim = BOTH_GESTURE1;
01720                                         break;
01721                                 case SS_MEDIUM:
01722                                 case SS_STRONG:
01723                                 case SS_DESANN:
01724                                         anim = BOTH_ENGAGETAUNT;
01725                                         break;
01726                                 case SS_DUAL:
01727                                         if ( ent->client->ps.saberHolstered == 1 
01728                                                 && ent->client->saber[1].model 
01729                                                 && ent->client->saber[1].model[0] )
01730                                         {//turn on second saber
01731                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOn );
01732                                         }
01733                                         else if ( ent->client->ps.saberHolstered == 2 )
01734                                         {//turn on first
01735                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
01736                                         }
01737                                         ent->client->ps.saberHolstered = 0;
01738                                         anim = BOTH_DUAL_TAUNT;
01739                                         break;
01740                                 case SS_STAFF:
01741                                         if ( ent->client->ps.saberHolstered > 0 )
01742                                         {//turn on all blades
01743                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
01744                                         }
01745                                         ent->client->ps.saberHolstered = 0;
01746                                         anim = BOTH_STAFF_TAUNT;
01747                                         break;
01748                                 }
01749                         }
01750                         break;
01751                 case TAUNT_BOW:
01752                         if ( ent->client->saber[0].bowAnim != -1 )
01753                         {
01754                                 anim = ent->client->saber[0].bowAnim;
01755                         }
01756                         else if ( ent->client->saber[1].model 
01757                                         && ent->client->saber[1].model[0]
01758                                         && ent->client->saber[1].bowAnim != -1 )
01759                         {
01760                                 anim = ent->client->saber[1].bowAnim;
01761                         }
01762                         else
01763                         {
01764                                 anim = BOTH_BOW;
01765                         }
01766                         if ( ent->client->ps.saberHolstered == 1 
01767                                 && ent->client->saber[1].model 
01768                                 && ent->client->saber[1].model[0] )
01769                         {//turn off second saber
01770                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOff );
01771                         }
01772                         else if ( ent->client->ps.saberHolstered == 0 )
01773                         {//turn off first
01774                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOff );
01775                         }
01776                         ent->client->ps.saberHolstered = 2;
01777                         break;
01778                 case TAUNT_MEDITATE:
01779                         if ( ent->client->saber[0].meditateAnim != -1 )
01780                         {
01781                                 anim = ent->client->saber[0].meditateAnim;
01782                         }
01783                         else if ( ent->client->saber[1].model 
01784                                         && ent->client->saber[1].model[0]
01785                                         && ent->client->saber[1].meditateAnim != -1 )
01786                         {
01787                                 anim = ent->client->saber[1].meditateAnim;
01788                         }
01789                         else
01790                         {
01791                                 anim = BOTH_MEDITATE;
01792                         }
01793                         if ( ent->client->ps.saberHolstered == 1 
01794                                 && ent->client->saber[1].model 
01795                                 && ent->client->saber[1].model[0] )
01796                         {//turn off second saber
01797                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOff );
01798                         }
01799                         else if ( ent->client->ps.saberHolstered == 0 )
01800                         {//turn off first
01801                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOff );
01802                         }
01803                         ent->client->ps.saberHolstered = 2;
01804                         break;
01805                 case TAUNT_FLOURISH:
01806                         if ( ent->client->ps.weapon == WP_SABER )
01807                         {
01808                                 if ( ent->client->ps.saberHolstered == 1 
01809                                         && ent->client->saber[1].model 
01810                                         && ent->client->saber[1].model[0] )
01811                                 {//turn on second saber
01812                                         G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOn );
01813                                 }
01814                                 else if ( ent->client->ps.saberHolstered == 2 )
01815                                 {//turn on first
01816                                         G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
01817                                 }
01818                                 ent->client->ps.saberHolstered = 0;
01819                                 if ( ent->client->saber[0].flourishAnim != -1 )
01820                                 {
01821                                         anim = ent->client->saber[0].flourishAnim;
01822                                 }
01823                                 else if ( ent->client->saber[1].model 
01824                                         && ent->client->saber[1].model[0]
01825                                         && ent->client->saber[1].flourishAnim != -1 )
01826                                 {
01827                                         anim = ent->client->saber[1].flourishAnim;
01828                                 }
01829                                 else
01830                                 {
01831                                         switch ( ent->client->ps.fd.saberAnimLevel )
01832                                         {
01833                                         case SS_FAST:
01834                                         case SS_TAVION:
01835                                                 anim = BOTH_SHOWOFF_FAST;
01836                                                 break;
01837                                         case SS_MEDIUM:
01838                                                 anim = BOTH_SHOWOFF_MEDIUM;
01839                                                 break;
01840                                         case SS_STRONG:
01841                                         case SS_DESANN:
01842                                                 anim = BOTH_SHOWOFF_STRONG;
01843                                                 break;
01844                                         case SS_DUAL:
01845                                                 anim = BOTH_SHOWOFF_DUAL;
01846                                                 break;
01847                                         case SS_STAFF:
01848                                                 anim = BOTH_SHOWOFF_STAFF;
01849                                                 break;
01850                                         }
01851                                 }
01852                         }
01853                         break;
01854                 case TAUNT_GLOAT:
01855                         if ( ent->client->saber[0].gloatAnim != -1 )
01856                         {
01857                                 anim = ent->client->saber[0].gloatAnim;
01858                         }
01859                         else if ( ent->client->saber[1].model 
01860                                         && ent->client->saber[1].model[0]
01861                                         && ent->client->saber[1].gloatAnim != -1 )
01862                         {
01863                                 anim = ent->client->saber[1].gloatAnim;
01864                         }
01865                         else
01866                         {
01867                                 switch ( ent->client->ps.fd.saberAnimLevel )
01868                                 {
01869                                 case SS_FAST:
01870                                 case SS_TAVION:
01871                                         anim = BOTH_VICTORY_FAST;
01872                                         break;
01873                                 case SS_MEDIUM:
01874                                         anim = BOTH_VICTORY_MEDIUM;
01875                                         break;
01876                                 case SS_STRONG:
01877                                 case SS_DESANN:
01878                                         if ( ent->client->ps.saberHolstered )
01879                                         {//turn on first
01880                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
01881                                         }
01882                                         ent->client->ps.saberHolstered = 0;
01883                                         anim = BOTH_VICTORY_STRONG;
01884                                         break;
01885                                 case SS_DUAL:
01886                                         if ( ent->client->ps.saberHolstered == 1 
01887                                                 && ent->client->saber[1].model 
01888                                                 && ent->client->saber[1].model[0] )
01889                                         {//turn on second saber
01890                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOn );
01891                                         }
01892                                         else if ( ent->client->ps.saberHolstered == 2 )
01893                                         {//turn on first
01894                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
01895                                         }
01896                                         ent->client->ps.saberHolstered = 0;
01897                                         anim = BOTH_VICTORY_DUAL;
01898                                         break;
01899                                 case SS_STAFF:
01900                                         if ( ent->client->ps.saberHolstered )
01901                                         {//turn on first
01902                                                 G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
01903                                         }
01904                                         ent->client->ps.saberHolstered = 0;
01905                                         anim = BOTH_VICTORY_STAFF;
01906                                         break;
01907                                 }
01908                         }
01909                         break;
01910                 }
01911                 if ( anim != -1 )
01912                 {
01913                         if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE ) 
01914                         {
01915                                 ent->client->ps.forceHandExtend = HANDEXTEND_TAUNT;
01916                                 ent->client->ps.forceDodgeAnim = anim;
01917                                 ent->client->ps.forceHandExtendTime = level.time + BG_AnimLength(ent->localAnimIndex, (animNumber_t)anim);
01918                         }
01919                         if ( taunt != TAUNT_MEDITATE 
01920                                 && taunt != TAUNT_BOW )
01921                         {//no sound for meditate or bow
01922                                 G_AddEvent( ent, EV_TAUNT, taunt );
01923                         }
01924                 }
01925         }
01926 }
01927 
01928 /*
01929 ==============
01930 ClientThink
01931 
01932 This will be called once for each client frame, which will
01933 usually be a couple times for each server frame on fast clients.
01934 
01935 If "g_synchronousClients 1" is set, this will be called exactly
01936 once for each server frame, which makes for smooth demo recording.
01937 ==============
01938 */
01939 void ClientThink_real( gentity_t *ent ) {
01940         gclient_t       *client;
01941         pmove_t         pm;
01942         int                     oldEventSequence;
01943         int                     msec;
01944         usercmd_t       *ucmd;
01945         qboolean        isNPC = qfalse;
01946         qboolean        controlledByPlayer = qfalse;
01947         qboolean        killJetFlags = qtrue;
01948 
01949         client = ent->client;
01950 
01951         if (ent->s.eType == ET_NPC)
01952         {
01953                 isNPC = qtrue;
01954         }
01955 
01956         // don't think if the client is not yet connected (and thus not yet spawned in)
01957         if (client->pers.connected != CON_CONNECTED && !isNPC) {
01958                 return;
01959         }
01960 
01961         // This code was moved here from clientThink to fix a problem with g_synchronousClients 
01962         // being set to 1 when in vehicles. 
01963         if ( ent->s.number < MAX_CLIENTS && ent->client->ps.m_iVehicleNum )
01964         {//driving a vehicle
01965                 if (g_entities[ent->client->ps.m_iVehicleNum].client)
01966                 {
01967                         gentity_t *veh = &g_entities[ent->client->ps.m_iVehicleNum];
01968 
01969                         if (veh->m_pVehicle &&
01970                                 veh->m_pVehicle->m_pPilot == (bgEntity_t *)ent)
01971                         { //only take input from the pilot...
01972                                 veh->client->ps.commandTime = ent->client->ps.commandTime;
01973                                 memcpy(&veh->m_pVehicle->m_ucmd, &ent->client->pers.cmd, sizeof(usercmd_t));
01974                                 if ( veh->m_pVehicle->m_ucmd.buttons & BUTTON_TALK )
01975                                 { //forced input if "chat bubble" is up
01976                                         veh->m_pVehicle->m_ucmd.buttons = BUTTON_TALK;
01977                                         veh->m_pVehicle->m_ucmd.forwardmove = 0;
01978                                         veh->m_pVehicle->m_ucmd.rightmove = 0;
01979                                         veh->m_pVehicle->m_ucmd.upmove = 0;
01980                                 }
01981                         }
01982                 }
01983         }
01984 
01985         if (!(client->ps.pm_flags & PMF_FOLLOW))
01986         {
01987                 if (g_gametype.integer == GT_SIEGE &&
01988                         client->siegeClass != -1 &&
01989                         bgSiegeClasses[client->siegeClass].saberStance)
01990                 { //the class says we have to use this stance set.
01991                         if (!(bgSiegeClasses[client->siegeClass].saberStance & (1 << client->ps.fd.saberAnimLevel)))
01992                         { //the current stance is not in the bitmask, so find the first one that is.
01993                                 int i = SS_FAST;
01994 
01995                                 while (i < SS_NUM_SABER_STYLES)
01996                                 {
01997                                         if (bgSiegeClasses[client->siegeClass].saberStance & (1 << i))
01998                                         {
01999                                                 if (i == SS_DUAL 
02000                                                         && client->ps.saberHolstered == 1 )
02001                                                 {//one saber should be off, adjust saberAnimLevel accordinly
02002                                                         client->ps.fd.saberAnimLevelBase = i;
02003                                                         client->ps.fd.saberAnimLevel = SS_FAST;
02004                                                         client->ps.fd.saberDrawAnimLevel = client->ps.fd.saberAnimLevel;
02005                                                 }
02006                                                 else if ( i == SS_STAFF
02007                                                         && client->ps.saberHolstered == 1 
02008                                                         && client->saber[0].singleBladeStyle != SS_NONE)
02009                                                 {//one saber or blade should be off, adjust saberAnimLevel accordinly
02010                                                         client->ps.fd.saberAnimLevelBase = i;
02011                                                         client->ps.fd.saberAnimLevel = client->saber[0].singleBladeStyle;
02012                                                         client->ps.fd.saberDrawAnimLevel = client->ps.fd.saberAnimLevel;
02013                                                 }
02014                                                 else
02015                                                 {
02016                                                         client->ps.fd.saberAnimLevelBase = client->ps.fd.saberAnimLevel = i;
02017                                                         client->ps.fd.saberDrawAnimLevel = i;
02018                                                 }
02019                                                 break;
02020                                         }
02021 
02022                                         i++;
02023                                 }
02024                         }
02025                 }
02026                 else if (client->saber[0].model[0] && client->saber[1].model[0])
02027                 { //with two sabs always use akimbo style
02028                         if ( client->ps.saberHolstered == 1 )
02029                         {//one saber should be off, adjust saberAnimLevel accordinly
02030                                 client->ps.fd.saberAnimLevelBase = SS_DUAL;
02031                                 client->ps.fd.saberAnimLevel = SS_FAST;
02032                                 client->ps.fd.saberDrawAnimLevel = client->ps.fd.saberAnimLevel;
02033                         }
02034                         else
02035                         {
02036                                 if ( !WP_SaberStyleValidForSaber( &client->saber[0], &client->saber[1], client->ps.saberHolstered, client->ps.fd.saberAnimLevel ) )
02037                                 {//only use dual style if the style we're trying to use isn't valid
02038                                         client->ps.fd.saberAnimLevelBase = client->ps.fd.saberAnimLevel = SS_DUAL;
02039                                 }
02040                                 client->ps.fd.saberDrawAnimLevel = client->ps.fd.saberAnimLevel;
02041                         }
02042                 }
02043                 else
02044                 {
02045                         if (client->saber[0].stylesLearned == (1<<SS_STAFF) )
02046                         { //then *always* use the staff style
02047                                 client->ps.fd.saberAnimLevelBase = SS_STAFF;
02048                         }
02049                         if ( client->ps.fd.saberAnimLevelBase == SS_STAFF )
02050                         {//using staff style
02051                                 if ( client->ps.saberHolstered == 1 
02052                                         && client->saber[0].singleBladeStyle != SS_NONE)
02053                                 {//one blade should be off, adjust saberAnimLevel accordinly
02054                                         client->ps.fd.saberAnimLevel = client->saber[0].singleBladeStyle;
02055                                         client->ps.fd.saberDrawAnimLevel = client->ps.fd.saberAnimLevel;
02056                                 }
02057                                 else
02058                                 {
02059                                         client->ps.fd.saberAnimLevel = SS_STAFF;
02060                                         client->ps.fd.saberDrawAnimLevel = client->ps.fd.saberAnimLevel;
02061                                 }
02062                         }
02063                 }
02064         }
02065 
02066         // mark the time, so the connection sprite can be removed
02067         ucmd = &ent->client->pers.cmd;
02068 
02069         if ( client && (client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
02070         {
02071                 G_HeldByMonster( ent, &ucmd );
02072         }
02073 
02074         // sanity check the command time to prevent speedup cheating
02075         if ( ucmd->serverTime > level.time + 200 ) {
02076                 ucmd->serverTime = level.time + 200;
02077 //              G_Printf("serverTime <<<<<\n" );
02078         }
02079         if ( ucmd->serverTime < level.time - 1000 ) {
02080                 ucmd->serverTime = level.time - 1000;
02081 //              G_Printf("serverTime >>>>>\n" );
02082         } 
02083 
02084         if (isNPC && (ucmd->serverTime - client->ps.commandTime) < 1)
02085         {
02086                 ucmd->serverTime = client->ps.commandTime + 100;
02087         }
02088 
02089         msec = ucmd->serverTime - client->ps.commandTime;
02090         // following others may result in bad times, but we still want
02091         // to check for follow toggles
02092         if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) {
02093                 return;
02094         }
02095 
02096         if ( msec > 200 ) {
02097                 msec = 200;
02098         }
02099 
02100         if ( pmove_msec.integer < 8 ) {
02101                 trap_Cvar_Set("pmove_msec", "8");
02102         }
02103         else if (pmove_msec.integer > 33) {
02104                 trap_Cvar_Set("pmove_msec", "33");
02105         }
02106 
02107         if ( pmove_fixed.integer || client->pers.pmoveFixed ) {
02108                 ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
02109                 //if (ucmd->serverTime - client->ps.commandTime <= 0)
02110                 //      return;
02111         }
02112 
02113         //
02114         // check for exiting intermission
02115         //
02116         if ( level.intermissiontime ) 
02117         {
02118                 if ( ent->s.number < MAX_CLIENTS
02119                         || client->NPC_class == CLASS_VEHICLE )
02120                 {//players and vehicles do nothing in intermissions
02121                         ClientIntermissionThink( client );
02122                         return;
02123                 }
02124         }
02125 
02126         // spectators don't do much
02127         if ( client->sess.sessionTeam == TEAM_SPECTATOR || client->tempSpectate > level.time ) {
02128                 if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
02129                         return;
02130                 }
02131                 SpectatorThink( ent, ucmd );
02132                 return;
02133         }
02134 
02135         if (ent && ent->client && (ent->client->ps.eFlags & EF_INVULNERABLE))
02136         {
02137                 if (ent->client->invulnerableTimer <= level.time)
02138                 {
02139                         ent->client->ps.eFlags &= ~EF_INVULNERABLE;
02140                 }
02141         }
02142 
02143         if (ent->s.eType != ET_NPC)
02144         {
02145                 // check for inactivity timer, but never drop the local client of a non-dedicated server
02146                 if ( !ClientInactivityTimer( client ) ) {
02147                         return;
02148                 }
02149         }
02150 
02151         //Check if we should have a fullbody push effect around the player
02152         if (client->pushEffectTime > level.time)
02153         {
02154                 client->ps.eFlags |= EF_BODYPUSH;
02155         }
02156         else if (client->pushEffectTime)
02157         {
02158                 client->pushEffectTime = 0;
02159                 client->ps.eFlags &= ~EF_BODYPUSH;
02160         }
02161 
02162         if (client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_JETPACK))
02163         {
02164                 client->ps.eFlags |= EF_JETPACK;
02165         }
02166         else
02167         {
02168                 client->ps.eFlags &= ~EF_JETPACK;
02169         }
02170 
02171         if ( client->noclip ) {
02172                 client->ps.pm_type = PM_NOCLIP;
02173         } else if ( client->ps.eFlags & EF_DISINTEGRATION ) {
02174                 client->ps.pm_type = PM_NOCLIP;
02175         } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
02176                 client->ps.pm_type = PM_DEAD;
02177         } else {
02178                 if (client->ps.forceGripChangeMovetype)
02179                 {
02180                         client->ps.pm_type = client->ps.forceGripChangeMovetype;
02181                 }
02182                 else
02183                 {
02184                         if (client->jetPackOn)
02185                         {
02186                                 client->ps.pm_type = PM_JETPACK;
02187                                 client->ps.eFlags |= EF_JETPACK_ACTIVE;
02188                                 killJetFlags = qfalse;
02189                         }
02190                         else
02191                         {
02192                                 client->ps.pm_type = PM_NORMAL;
02193                         }
02194                 }
02195         }
02196 
02197         if (killJetFlags)
02198         {
02199                 client->ps.eFlags &= ~EF_JETPACK_ACTIVE;
02200                 client->ps.eFlags &= ~EF_JETPACK_FLAMING;
02201         }
02202 
02203 #define SLOWDOWN_DIST   128.0f
02204 #define MIN_NPC_SPEED   16.0f
02205 
02206         if (client->bodyGrabIndex != ENTITYNUM_NONE)
02207         {
02208                 gentity_t *grabbed = &g_entities[client->bodyGrabIndex];
02209 
02210                 if (!grabbed->inuse || grabbed->s.eType != ET_BODY ||
02211                         (grabbed->s.eFlags & EF_DISINTEGRATION) ||
02212                         (grabbed->s.eFlags & EF_NODRAW))
02213                 {
02214                         if (grabbed->inuse && grabbed->s.eType == ET_BODY)
02215                         {
02216                                 grabbed->s.ragAttach = 0;
02217                         }
02218                         client->bodyGrabIndex = ENTITYNUM_NONE;
02219                 }
02220                 else
02221                 {
02222                         mdxaBone_t rhMat;
02223                         vec3_t rhOrg, tAng;
02224                         vec3_t bodyDir;
02225                         float bodyDist;
02226 
02227                         ent->client->ps.forceHandExtend = HANDEXTEND_DRAGGING;
02228 
02229                         if (ent->client->ps.forceHandExtendTime < level.time + 500)
02230                         {
02231                                 ent->client->ps.forceHandExtendTime = level.time + 1000;
02232                         }
02233 
02234                         VectorSet(tAng, 0, ent->client->ps.viewangles[YAW], 0);
02235                         trap_G2API_GetBoltMatrix(ent->ghoul2, 0, 0, &rhMat, tAng, ent->client->ps.origin, level.time,
02236                                 NULL, ent->modelScale); //0 is always going to be right hand bolt
02237                         BG_GiveMeVectorFromMatrix(&rhMat, ORIGIN, rhOrg);
02238 
02239                         VectorSubtract(rhOrg, grabbed->r.currentOrigin, bodyDir);
02240                         bodyDist = VectorLength(bodyDir);
02241 
02242                         if (bodyDist > 40.0f)
02243                         { //can no longer reach
02244                                 grabbed->s.ragAttach = 0;
02245                                 client->bodyGrabIndex = ENTITYNUM_NONE;
02246                         }
02247                         else if (bodyDist > 24.0f)
02248                         {
02249                                 bodyDir[2] = 0; //don't want it floating
02250                                 //VectorScale(bodyDir, 0.1f, bodyDir);
02251                                 VectorAdd(grabbed->epVelocity, bodyDir, grabbed->epVelocity);
02252                                 G_Sound(grabbed, CHAN_AUTO, G_SoundIndex("sound/player/roll1.wav"));
02253                         }
02254                 }
02255         }
02256         else if (ent->client->ps.forceHandExtend == HANDEXTEND_DRAGGING)
02257         {
02258                 ent->client->ps.forceHandExtend = HANDEXTEND_WEAPONREADY;
02259         }
02260 
02261         if (ent->NPC && ent->s.NPC_class != CLASS_VEHICLE) //vehicles manage their own speed
02262         {
02263                 //FIXME: swoop should keep turning (and moving forward?) for a little bit?
02264                 if ( ent->NPC->combatMove == qfalse )
02265                 {
02266                         //if ( !(ucmd->buttons & BUTTON_USE) )
02267                         if (1)
02268                         {//Not leaning
02269                                 qboolean Flying = (ucmd->upmove && (ent->client->ps.eFlags2&EF2_FLYING));//ent->client->moveType == MT_FLYSWIM);
02270                                 qboolean Climbing = (ucmd->upmove && ent->watertype&CONTENTS_LADDER );
02271 
02272                                 //client->ps.friction = 6;
02273 
02274                                 if ( ucmd->forwardmove || ucmd->rightmove || Flying )
02275                                 {
02276                                         //if ( ent->NPC->behaviorState != BS_FORMATION )
02277                                         {//In - Formation NPCs set thier desiredSpeed themselves
02278                                                 if ( ucmd->buttons & BUTTON_WALKING )
02279                                                 {
02280                                                         ent->NPC->desiredSpeed = NPC_GetWalkSpeed( ent );//ent->NPC->stats.walkSpeed;
02281                                                 }
02282                                                 else//running
02283                                                 {
02284                                                         ent->NPC->desiredSpeed = NPC_GetRunSpeed( ent );//ent->NPC->stats.runSpeed;
02285                                                 }
02286 
02287                                                 if ( ent->NPC->currentSpeed >= 80 && !controlledByPlayer )
02288                                                 {//At higher speeds, need to slow down close to stuff
02289                                                         //Slow down as you approach your goal
02290                                                 //      if ( ent->NPC->distToGoal < SLOWDOWN_DIST && client->race != RACE_BORG && !(ent->NPC->aiFlags&NPCAI_NO_SLOWDOWN) )//128
02291                                                         if ( ent->NPC->distToGoal < SLOWDOWN_DIST && !(ent->NPC->aiFlags&NPCAI_NO_SLOWDOWN) )//128
02292                                                         {
02293                                                                 if ( ent->NPC->desiredSpeed > MIN_NPC_SPEED )
02294                                                                 {
02295                                                                         float slowdownSpeed = ((float)ent->NPC->desiredSpeed) * ent->NPC->distToGoal / SLOWDOWN_DIST;
02296 
02297                                                                         ent->NPC->desiredSpeed = ceil(slowdownSpeed);
02298                                                                         if ( ent->NPC->desiredSpeed < MIN_NPC_SPEED )
02299                                                                         {//don't slow down too much
02300                                                                                 ent->NPC->desiredSpeed = MIN_NPC_SPEED;
02301                                                                         }
02302                                                                 }
02303                                                         }
02304                                                 }
02305                                         }
02306                                 }
02307                                 else if ( Climbing )
02308                                 {
02309                                         ent->NPC->desiredSpeed = ent->NPC->stats.walkSpeed;
02310                                 }
02311                                 else
02312                                 {//We want to stop
02313                                         ent->NPC->desiredSpeed = 0;
02314                                 }
02315 
02316                                 NPC_Accelerate( ent, qfalse, qfalse );
02317 
02318                                 if ( ent->NPC->currentSpeed <= 24 && ent->NPC->desiredSpeed < ent->NPC->currentSpeed )
02319                                 {//No-one walks this slow
02320                                         client->ps.speed = ent->NPC->currentSpeed = 0;//Full stop
02321                                         ucmd->forwardmove = 0;
02322                                         ucmd->rightmove = 0;
02323                                 }
02324                                 else
02325                                 {
02326                                         if ( ent->NPC->currentSpeed <= ent->NPC->stats.walkSpeed )
02327                                         {//Play the walkanim
02328                                                 ucmd->buttons |= BUTTON_WALKING;
02329                                         }
02330                                         else
02331                                         {
02332                                                 ucmd->buttons &= ~BUTTON_WALKING;
02333                                         }
02334 
02335                                         if ( ent->NPC->currentSpeed > 0 )
02336                                         {//We should be moving
02337                                                 if ( Climbing || Flying )
02338                                                 {
02339                                                         if ( !ucmd->upmove )
02340                                                         {//We need to force them to take a couple more steps until stopped
02341                                                                 ucmd->upmove = ent->NPC->last_ucmd.upmove;//was last_upmove;
02342                                                         }
02343                                                 }
02344                                                 else if ( !ucmd->forwardmove && !ucmd->rightmove )
02345                                                 {//We need to force them to take a couple more steps until stopped
02346                                                         ucmd->forwardmove = ent->NPC->last_ucmd.forwardmove;//was last_forwardmove;
02347                                                         ucmd->rightmove = ent->NPC->last_ucmd.rightmove;//was last_rightmove;
02348                                                 }
02349                                         }
02350 
02351                                         client->ps.speed = ent->NPC->currentSpeed;
02352                                 //      if ( player && player->client && player->client->ps.viewEntity == ent->s.number )
02353                                 //      {
02354                                 //      }
02355                                 //      else
02356                                         //rwwFIXMEFIXME: do this and also check for all real client
02357                                         if (1)
02358                                         {
02359                                                 //Slow down on turns - don't orbit!!!
02360                                                 float turndelta = 0; 
02361                                                 // if the NPC is locked into a Yaw, we want to check the lockedDesiredYaw...otherwise the NPC can't walk backwards, because it always thinks it trying to turn according to desiredYaw
02362                                                 //if( client->renderInfo.renderFlags & RF_LOCKEDANGLE ) // yeah I know the RF_ flag is a pretty ugly hack...
02363                                                 if (0) //rwwFIXMEFIXME: ...
02364                                                 {       
02365                                                         turndelta = (180 - fabs( AngleDelta( ent->r.currentAngles[YAW], ent->NPC->lockedDesiredYaw ) ))/180;
02366                                                 }
02367                                                 else
02368                                                 {
02369                                                         turndelta = (180 - fabs( AngleDelta( ent->r.currentAngles[YAW], ent->NPC->desiredYaw ) ))/180;
02370                                                 }
02371                                                                                                 
02372                                                 if ( turndelta < 0.75f )
02373                                                 {
02374                                                         client->ps.speed = 0;
02375                                                 }
02376                                                 else if ( ent->NPC->distToGoal < 100 && turndelta < 1.0 )
02377                                                 {//Turn is greater than 45 degrees or closer than 100 to goal
02378                                                         client->ps.speed = floor(((float)(client->ps.speed))*turndelta);
02379                                                 }
02380                                         }
02381                                 }
02382                         }
02383                 }
02384                 else
02385                 {       
02386                         ent->NPC->desiredSpeed = ( ucmd->buttons & BUTTON_WALKING ) ? NPC_GetWalkSpeed( ent ) : NPC_GetRunSpeed( ent );
02387 
02388                         client->ps.speed = ent->NPC->desiredSpeed;
02389                 }
02390 
02391                 if (ucmd->buttons & BUTTON_WALKING)
02392                 { //sort of a hack I guess since MP handles walking differently from SP (has some proxy cheat prevention methods)
02393                         /*
02394                         if (ent->client->ps.speed > 64)
02395                         {
02396                                 ent->client->ps.speed = 64;
02397                         }
02398                         */
02399 
02400                         if (ucmd->forwardmove > 64)
02401                         {
02402                                 ucmd->forwardmove = 64; 
02403                         }
02404                         else if (ucmd->forwardmove < -64)
02405                         {
02406                                 ucmd->forwardmove = -64;
02407                         }
02408                         
02409                         if (ucmd->rightmove > 64)
02410                         {
02411                                 ucmd->rightmove = 64;
02412                         }
02413                         else if ( ucmd->rightmove < -64)
02414                         {
02415                                 ucmd->rightmove = -64;
02416                         }
02417 
02418                         //ent->client->ps.speed = ent->client->ps.basespeed = NPC_GetRunSpeed( ent );
02419                 }
02420                 client->ps.basespeed = client->ps.speed;
02421         }
02422         else if (!client->ps.m_iVehicleNum &&
02423                 (!ent->NPC || ent->s.NPC_class != CLASS_VEHICLE)) //if riding a vehicle it will manage our speed and such
02424         {
02425                 // set speed
02426                 client->ps.speed = g_speed.value;
02427 
02428                 //Check for a siege class speed multiplier
02429                 if (g_gametype.integer == GT_SIEGE &&
02430                         client->siegeClass != -1)
02431                 {
02432                         client->ps.speed *= bgSiegeClasses[client->siegeClass].speed;
02433                 }
02434 
02435                 if (client->bodyGrabIndex != ENTITYNUM_NONE)
02436                 { //can't go nearly as fast when dragging a body around
02437                         client->ps.speed *= 0.2f;
02438                 }
02439 
02440                 client->ps.basespeed = client->ps.speed;
02441         }
02442 
02443         if ( !ent->NPC || !(ent->NPC->aiFlags&NPCAI_CUSTOM_GRAVITY) )
02444         {//use global gravity
02445                 if (ent->NPC && ent->s.NPC_class == CLASS_VEHICLE &&
02446                         ent->m_pVehicle && ent->m_pVehicle->m_pVehicleInfo->gravity)
02447                 { //use custom veh gravity
02448                         client->ps.gravity = ent->m_pVehicle->m_pVehicleInfo->gravity;
02449                 }
02450                 else
02451                 {
02452                         if (ent->client->inSpaceIndex && ent->client->inSpaceIndex != ENTITYNUM_NONE)
02453                         { //in space, so no gravity...
02454                                 client->ps.gravity = 1.0f;
02455                                 if (ent->s.number < MAX_CLIENTS)
02456                                 {
02457                                         VectorScale(client->ps.velocity, 0.8f, client->ps.velocity);
02458                                 }
02459                         }
02460                         else
02461                         {
02462                                 if (client->ps.eFlags2 & EF2_SHIP_DEATH)
02463                                 { //float there
02464                                         VectorClear(client->ps.velocity);
02465                                         client->ps.gravity = 1.0f;
02466                                 }
02467                                 else
02468                                 {
02469                                         client->ps.gravity = g_gravity.value;
02470                                 }
02471                         }
02472                 }
02473         }
02474 
02475         if (ent->client->ps.duelInProgress)
02476         {
02477                 gentity_t *duelAgainst = &g_entities[ent->client->ps.duelIndex];
02478 
02479                 //Keep the time updated, so once this duel ends this player can't engage in a duel for another
02480                 //10 seconds. This will give other people a chance to engage in duels in case this player wants
02481                 //to engage again right after he's done fighting and someone else is waiting.
02482                 ent->client->ps.fd.privateDuelTime = level.time + 10000;
02483 
02484                 if (ent->client->ps.duelTime < level.time)
02485                 {
02486                         //Bring out the sabers
02487                         if (ent->client->ps.weapon == WP_SABER 
02488                                 && ent->client->ps.saberHolstered 
02489                                 && ent->client->ps.duelTime )
02490                         {
02491                                 ent->client->ps.saberHolstered = 0;
02492 
02493                                 if (ent->client->saber[0].soundOn)
02494                                 {
02495                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOn);
02496                                 }
02497                                 if (ent->client->saber[1].soundOn)
02498                                 {
02499                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOn);
02500                                 }
02501 
02502                                 G_AddEvent(ent, EV_PRIVATE_DUEL, 2);
02503 
02504                                 ent->client->ps.duelTime = 0;
02505                         }
02506 
02507                         if (duelAgainst 
02508                                 && duelAgainst->client 
02509                                 && duelAgainst->inuse 
02510                                 && duelAgainst->client->ps.weapon == WP_SABER 
02511                                 && duelAgainst->client->ps.saberHolstered 
02512                                 && duelAgainst->client->ps.duelTime)
02513                         {
02514                                 duelAgainst->client->ps.saberHolstered = 0;
02515 
02516                                 if (duelAgainst->client->saber[0].soundOn)
02517                                 {
02518                                         G_Sound(duelAgainst, CHAN_AUTO, duelAgainst->client->saber[0].soundOn);
02519                                 }
02520                                 if (duelAgainst->client->saber[1].soundOn)
02521                                 {
02522                                         G_Sound(duelAgainst, CHAN_AUTO, duelAgainst->client->saber[1].soundOn);
02523                                 }
02524 
02525                                 G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 2);
02526 
02527                                 duelAgainst->client->ps.duelTime = 0;
02528                         }
02529                 }
02530                 else
02531                 {
02532                         client->ps.speed = 0;
02533                         client->ps.basespeed = 0;
02534                         ucmd->forwardmove = 0;
02535                         ucmd->rightmove = 0;
02536                         ucmd->upmove = 0;
02537                 }
02538 
02539                 if (!duelAgainst || !duelAgainst->client || !duelAgainst->inuse ||
02540                         duelAgainst->client->ps.duelIndex != ent->s.number)
02541                 {
02542                         ent->client->ps.duelInProgress = 0;
02543                         G_AddEvent(ent, EV_PRIVATE_DUEL, 0);
02544                 }
02545                 else if (duelAgainst->health < 1 || duelAgainst->client->ps.stats[STAT_HEALTH] < 1)
02546                 {
02547                         ent->client->ps.duelInProgress = 0;
02548                         duelAgainst->client->ps.duelInProgress = 0;
02549 
02550                         G_AddEvent(ent, EV_PRIVATE_DUEL, 0);
02551                         G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0);
02552 
02553                         //Winner gets full health.. providing he's still alive
02554                         if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0)
02555                         {
02556                                 if (ent->health < ent->client->ps.stats[STAT_MAX_HEALTH])
02557                                 {
02558                                         ent->client->ps.stats[STAT_HEALTH] = ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
02559                                 }
02560 
02561                                 if (g_spawnInvulnerability.integer)
02562                                 {
02563                                         ent->client->ps.eFlags |= EF_INVULNERABLE;
02564                                         ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer;
02565                                 }
02566                         }
02567 
02568                         /*
02569                         trap_SendServerCommand( ent-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELWINNER")) );
02570                         trap_SendServerCommand( duelAgainst-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELWINNER")) );
02571                         */
02572                         //Private duel announcements are now made globally because we only want one duel at a time.
02573                         if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0)
02574                         {
02575                                 trap_SendServerCommand( -1, va("cp \"%s %s %s!\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELWINNER"), duelAgainst->client->pers.netname) );
02576                         }
02577                         else
02578                         { //it was a draw, because we both managed to die in the same frame
02579                                 trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PLDUELTIE")) );
02580                         }
02581                 }
02582                 else
02583                 {
02584                         vec3_t vSub;
02585                         float subLen = 0;
02586 
02587                         VectorSubtract(ent->client->ps.origin, duelAgainst->client->ps.origin, vSub);
02588                         subLen = VectorLength(vSub);
02589 
02590                         if (subLen >= 1024)
02591                         {
02592                                 ent->client->ps.duelInProgress = 0;
02593                                 duelAgainst->client->ps.duelInProgress = 0;
02594 
02595                                 G_AddEvent(ent, EV_PRIVATE_DUEL, 0);
02596                                 G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0);
02597 
02598                                 trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PLDUELSTOP")) );
02599                         }
02600                 }
02601         }
02602 
02603         if (ent->client->doingThrow > level.time)
02604         {
02605                 gentity_t *throwee = &g_entities[ent->client->throwingIndex];
02606 
02607                 if (!throwee->inuse || !throwee->client || throwee->health < 1 ||
02608                         throwee->client->sess.sessionTeam == TEAM_SPECTATOR ||
02609                         (throwee->client->ps.pm_flags & PMF_FOLLOW) ||
02610                         throwee->client->throwingIndex != ent->s.number)
02611                 {
02612                         ent->client->doingThrow = 0;
02613                         ent->client->ps.forceHandExtend = HANDEXTEND_NONE;
02614 
02615                         if (throwee->inuse && throwee->client)
02616                         {
02617                                 throwee->client->ps.heldByClient = 0;
02618                                 throwee->client->beingThrown = 0;
02619 
02620                                 if (throwee->client->ps.forceHandExtend != HANDEXTEND_POSTTHROWN)
02621                                 {
02622                                         throwee->client->ps.forceHandExtend = HANDEXTEND_NONE;
02623                                 }
02624                         }
02625                 }
02626         }
02627 
02628         if (ent->client->beingThrown > level.time)
02629         {
02630                 gentity_t *thrower = &g_entities[ent->client->throwingIndex];
02631 
02632                 if (!thrower->inuse || !thrower->client || thrower->health < 1 ||
02633                         thrower->client->sess.sessionTeam == TEAM_SPECTATOR ||
02634                         (thrower->client->ps.pm_flags & PMF_FOLLOW) ||
02635                         thrower->client->throwingIndex != ent->s.number)
02636                 {
02637                         ent->client->ps.heldByClient = 0;
02638                         ent->client->beingThrown = 0;
02639 
02640                         if (ent->client->ps.forceHandExtend != HANDEXTEND_POSTTHROWN)
02641                         {
02642                                 ent->client->ps.forceHandExtend = HANDEXTEND_NONE;
02643                         }
02644 
02645                         if (thrower->inuse && thrower->client)
02646                         {
02647                                 thrower->client->doingThrow = 0;
02648                                 thrower->client->ps.forceHandExtend = HANDEXTEND_NONE;
02649                         }
02650                 }
02651                 else if (thrower->inuse && thrower->client && thrower->ghoul2 &&
02652                         trap_G2_HaveWeGhoul2Models(thrower->ghoul2))
02653                 {
02654 #if 0
02655                         int lHandBolt = trap_G2API_AddBolt(thrower->ghoul2, 0, "*l_hand");
02656                         int pelBolt = trap_G2API_AddBolt(thrower->ghoul2, 0, "pelvis");
02657 
02658 
02659                         if (lHandBolt != -1 && pelBolt != -1)
02660 #endif
02661                         {
02662                                 float pDif = 40.0f;
02663                                 vec3_t boltOrg, pBoltOrg;
02664                                 vec3_t tAngles;
02665                                 vec3_t vDif;
02666                                 vec3_t entDir, otherAngles;
02667                                 vec3_t fwd, right;
02668 
02669                                 //Always look at the thrower.
02670                                 VectorSubtract( thrower->client->ps.origin, ent->client->ps.origin, entDir );
02671                                 VectorCopy( ent->client->ps.viewangles, otherAngles );
02672                                 otherAngles[YAW] = vectoyaw( entDir );
02673                                 SetClientViewAngle( ent, otherAngles );
02674 
02675                                 VectorCopy(thrower->client->ps.viewangles, tAngles);
02676                                 tAngles[PITCH] = tAngles[ROLL] = 0;
02677 
02678                                 //Get the direction between the pelvis and position of the hand
02679 #if 0
02680                                 mdxaBone_t boltMatrix, pBoltMatrix;
02681 
02682                                 trap_G2API_GetBoltMatrix(thrower->ghoul2, 0, lHandBolt, &boltMatrix, tAngles, thrower->client->ps.origin, level.time, 0, thrower->modelScale);
02683                                 boltOrg[0] = boltMatrix.matrix[0][3];
02684                                 boltOrg[1] = boltMatrix.matrix[1][3];
02685                                 boltOrg[2] = boltMatrix.matrix[2][3];
02686 
02687                                 trap_G2API_GetBoltMatrix(thrower->ghoul2, 0, pelBolt, &pBoltMatrix, tAngles, thrower->client->ps.origin, level.time, 0, thrower->modelScale);
02688                                 pBoltOrg[0] = pBoltMatrix.matrix[0][3];
02689                                 pBoltOrg[1] = pBoltMatrix.matrix[1][3];
02690                                 pBoltOrg[2] = pBoltMatrix.matrix[2][3];
02691 #else //above tends to not work once in a while, for various reasons I suppose.
02692                                 VectorCopy(thrower->client->ps.origin, pBoltOrg);
02693                                 AngleVectors(tAngles, fwd, right, 0);
02694                                 boltOrg[0] = pBoltOrg[0] + fwd[0]*8 + right[0]*pDif;
02695                                 boltOrg[1] = pBoltOrg[1] + fwd[1]*8 + right[1]*pDif;
02696                                 boltOrg[2] = pBoltOrg[2];
02697 #endif
02698                                 //G_TestLine(boltOrg, pBoltOrg, 0x0000ff, 50);
02699 
02700                                 VectorSubtract(ent->client->ps.origin, boltOrg, vDif);
02701                                 if (VectorLength(vDif) > 32.0f && (thrower->client->doingThrow - level.time) < 4500)
02702                                 { //the hand is too far away, and can no longer hold onto us, so escape.
02703                                         ent->client->ps.heldByClient = 0;
02704                                         ent->client->beingThrown = 0;
02705                                         thrower->client->doingThrow = 0;
02706 
02707                                         thrower->client->ps.forceHandExtend = HANDEXTEND_NONE;
02708                                         G_EntitySound( thrower, CHAN_VOICE, G_SoundIndex("*pain25.wav") );
02709 
02710                                         ent->client->ps.forceDodgeAnim = 2;
02711                                         ent->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
02712                                         ent->client->ps.forceHandExtendTime = level.time + 500;
02713                                         ent->client->ps.velocity[2] = 400;
02714                                         G_PreDefSound(ent->client->ps.origin, PDSOUND_FORCEJUMP);
02715                                 }
02716                                 else if ((client->beingThrown - level.time) < 4000)
02717                                 { //step into the next part of the throw, and go flying back
02718                                         float vScale = 400.0f;
02719                                         ent->client->ps.forceHandExtend = HANDEXTEND_POSTTHROWN;
02720                                         ent->client->ps.forceHandExtendTime = level.time + 1200;
02721                                         ent->client->ps.forceDodgeAnim = 0;
02722 
02723                                         thrower->client->ps.forceHandExtend = HANDEXTEND_POSTTHROW;
02724                                         thrower->client->ps.forceHandExtendTime = level.time + 200;
02725 
02726                                         ent->client->ps.heldByClient = 0;
02727 
02728                                         ent->client->ps.heldByClient = 0;
02729                                         ent->client->beingThrown = 0;
02730                                         thrower->client->doingThrow = 0;
02731 
02732                                         AngleVectors(thrower->client->ps.viewangles, vDif, 0, 0);
02733                                         ent->client->ps.velocity[0] = vDif[0]*vScale;
02734                                         ent->client->ps.velocity[1] = vDif[1]*vScale;
02735                                         ent->client->ps.velocity[2] = 400;
02736 
02737                                         G_EntitySound( ent, CHAN_VOICE, G_SoundIndex("*pain100.wav") );
02738                                         G_EntitySound( thrower, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
02739 
02740                                         //Set the thrower as the "other killer", so if we die from fall/impact damage he is credited.
02741                                         ent->client->ps.otherKiller = thrower->s.number;
02742                                         ent->client->ps.otherKillerTime = level.time + 8000;
02743                                         ent->client->ps.otherKillerDebounceTime = level.time + 100;
02744                                 }
02745                                 else
02746                                 { //see if we can move to be next to the hand.. if it's not clear, break the throw.
02747                                         vec3_t intendedOrigin;
02748                                         trace_t tr;
02749                                         trace_t tr2;
02750 
02751                                         VectorSubtract(boltOrg, pBoltOrg, vDif);
02752                                         VectorNormalize(vDif);
02753 
02754                                         VectorClear(ent->client->ps.velocity);
02755                                         intendedOrigin[0] = pBoltOrg[0] + vDif[0]*pDif;
02756                                         intendedOrigin[1] = pBoltOrg[1] + vDif[1]*pDif;
02757                                         intendedOrigin[2] = thrower->client->ps.origin[2];
02758 
02759                                         trap_Trace(&tr, intendedOrigin, ent->r.mins, ent->r.maxs, intendedOrigin, ent->s.number, ent->clipmask);
02760                                         trap_Trace(&tr2, ent->client->ps.origin, ent->r.mins, ent->r.maxs, intendedOrigin, ent->s.number, CONTENTS_SOLID);
02761 
02762                                         if (tr.fraction == 1.0 && !tr.startsolid && tr2.fraction == 1.0 && !tr2.startsolid)
02763                                         {
02764                                                 VectorCopy(intendedOrigin, ent->client->ps.origin);
02765                                                 
02766                                                 if ((client->beingThrown - level.time) < 4800)
02767                                                 {
02768                                                         ent->client->ps.heldByClient = thrower->s.number+1;
02769                                                 }
02770                                         }
02771                                         else
02772                                         { //if the guy can't be put here then it's time to break the throw off.
02773                                                 ent->client->ps.heldByClient = 0;
02774                                                 ent->client->beingThrown = 0;
02775                                                 thrower->client->doingThrow = 0;
02776 
02777                                                 thrower->client->ps.forceHandExtend = HANDEXTEND_NONE;
02778                                                 G_EntitySound( thrower, CHAN_VOICE, G_SoundIndex("*pain25.wav") );
02779 
02780                                                 ent->client->ps.forceDodgeAnim = 2;
02781                                                 ent->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
02782                                                 ent->client->ps.forceHandExtendTime = level.time + 500;
02783                                                 ent->client->ps.velocity[2] = 400;
02784                                                 G_PreDefSound(ent->client->ps.origin, PDSOUND_FORCEJUMP);
02785                                         }
02786                                 }
02787                         }
02788                 }
02789         }
02790         else if (ent->client->ps.heldByClient)
02791         {
02792                 ent->client->ps.heldByClient = 0;
02793         }
02794 
02795         /*
02796         if ( client->ps.powerups[PW_HASTE] ) {
02797                 client->ps.speed *= 1.3;
02798         }
02799         */
02800 
02801         //Will probably never need this again, since we have g2 properly serverside now.
02802         //But just in case.
02803         /*
02804         if (client->ps.usingATST && ent->health > 0)
02805         { //we have special shot clip boxes as an ATST
02806                 ent->r.contents |= CONTENTS_NOSHOT;
02807                 ATST_ManageDamageBoxes(ent);
02808         }
02809         else
02810         {
02811                 ent->r.contents &= ~CONTENTS_NOSHOT;
02812                 client->damageBoxHandle_Head = 0;
02813                 client->damageBoxHandle_RLeg = 0;
02814                 client->damageBoxHandle_LLeg = 0;
02815         }
02816         */
02817 
02818         //rww - moved this stuff into the pmove code so that it's predicted properly
02819         //BG_AdjustClientSpeed(&client->ps, &client->pers.cmd, level.time);
02820 
02821         // set up for pmove
02822         oldEventSequence = client->ps.eventSequence;
02823 
02824         memset (&pm, 0, sizeof(pm));
02825 
02826         if ( ent->flags & FL_FORCE_GESTURE ) {
02827                 ent->flags &= ~FL_FORCE_GESTURE;
02828                 ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
02829         }
02830 
02831         if (ent->client && ent->client->ps.fallingToDeath &&
02832                 (level.time - FALL_FADE_TIME) > ent->client->ps.fallingToDeath)
02833         { //die!
02834                 if (ent->health > 0)
02835                 {
02836                         gentity_t *otherKiller = ent;
02837                         if (ent->client->ps.otherKillerTime > level.time &&
02838                                 ent->client->ps.otherKiller != ENTITYNUM_NONE)
02839                         {
02840                                 otherKiller = &g_entities[ent->client->ps.otherKiller];
02841 
02842                                 if (!otherKiller->inuse)
02843                                 {
02844                                         otherKiller = ent;
02845                                 }
02846                         }
02847                         G_Damage(ent, otherKiller, otherKiller, NULL, ent->client->ps.origin, 9999, DAMAGE_NO_PROTECTION, MOD_FALLING);
02848                         //player_die(ent, ent, ent, 100000, MOD_FALLING);
02849         //              if (!ent->NPC)
02850         //              {
02851         //                      respawn(ent);
02852         //              }
02853         //              ent->client->ps.fallingToDeath = 0;
02854 
02855                         G_MuteSound(ent->s.number, CHAN_VOICE); //stop screaming, because you are dead!
02856                 }
02857         }
02858 
02859         if (ent->client->ps.otherKillerTime > level.time &&
02860                 ent->client->ps.groundEntityNum != ENTITYNUM_NONE &&
02861                 ent->client->ps.otherKillerDebounceTime < level.time)
02862         {
02863                 ent->client->ps.otherKillerTime = 0;
02864                 ent->client->ps.otherKiller = ENTITYNUM_NONE;
02865         }
02866         else if (ent->client->ps.otherKillerTime > level.time &&
02867                 ent->client->ps.groundEntityNum == ENTITYNUM_NONE)
02868         {
02869                 if (ent->client->ps.otherKillerDebounceTime < (level.time + 100))
02870                 {
02871                         ent->client->ps.otherKillerDebounceTime = level.time + 100;
02872                 }
02873         }
02874 
02875 //      WP_ForcePowersUpdate( ent, msec, ucmd); //update any active force powers
02876 //      WP_SaberPositionUpdate(ent, ucmd); //check the server-side saber point, do apprioriate server-side actions (effects are cs-only)
02877 
02878         //NOTE: can't put USE here *before* PMove!!
02879         if ( ent->client->ps.useDelay > level.time 
02880                 && ent->client->ps.m_iVehicleNum )
02881         {//when in a vehicle, debounce the use...
02882                 ucmd->buttons &= ~BUTTON_USE;
02883         }
02884 
02885         //FIXME: need to do this before check to avoid walls and cliffs (or just cliffs?)
02886         G_AddPushVecToUcmd( ent, ucmd );
02887 
02888         //play/stop any looping sounds tied to controlled movement
02889         G_CheckMovingLoopingSounds( ent, ucmd );
02890 
02891         pm.ps = &client->ps;
02892         pm.cmd = *ucmd;
02893         if ( pm.ps->pm_type == PM_DEAD ) {
02894                 pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
02895         }
02896         else if ( ent->r.svFlags & SVF_BOT ) {
02897                 pm.tracemask = MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP;
02898         }
02899         else {
02900                 pm.tracemask = MASK_PLAYERSOLID;
02901         }
02902         pm.trace = trap_Trace;
02903         pm.pointcontents = trap_PointContents;
02904         pm.debugLevel = g_debugMove.integer;
02905         pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;
02906 
02907         pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
02908         pm.pmove_msec = pmove_msec.integer;
02909 
02910         pm.animations = bgAllAnims[ent->localAnimIndex].anims;//NULL;
02911 
02912         //rww - bgghoul2
02913         pm.ghoul2 = NULL;
02914 
02915 #ifdef _DEBUG
02916         if (g_disableServerG2.integer)
02917         {
02918 
02919         }
02920         else
02921 #endif
02922         if (ent->ghoul2)
02923         {
02924                 if (ent->localAnimIndex > 1)
02925                 { //if it isn't humanoid then we will be having none of this.
02926                         pm.ghoul2 = NULL;
02927                 }
02928                 else
02929                 {
02930                         pm.ghoul2 = ent->ghoul2;
02931                         pm.g2Bolts_LFoot = trap_G2API_AddBolt(ent->ghoul2, 0, "*l_leg_foot");
02932                         pm.g2Bolts_RFoot = trap_G2API_AddBolt(ent->ghoul2, 0, "*r_leg_foot");
02933                 }
02934         }
02935 
02936         //point the saber data to the right place
02937 #if 0
02938         k = 0;
02939         while (k < MAX_SABERS)
02940         {
02941                 if (ent->client->saber[k].model[0])
02942                 {
02943                         pm.saber[k] = &ent->client->saber[k];
02944                 }
02945                 else
02946                 {
02947                         pm.saber[k] = NULL;
02948                 }
02949                 k++;
02950         }
02951 #endif
02952 
02953         //I'll just do this every frame in case the scale changes in realtime (don't need to update the g2 inst for that)
02954         VectorCopy(ent->modelScale, pm.modelScale);
02955         //rww end bgghoul2
02956 
02957         pm.gametype = g_gametype.integer;
02958         pm.debugMelee = g_debugMelee.integer;
02959         pm.stepSlideFix = g_stepSlideFix.integer;
02960 
02961         pm.noSpecMove = g_noSpecMove.integer;
02962 
02963         pm.nonHumanoid = (ent->localAnimIndex > 0);
02964 
02965         VectorCopy( client->ps.origin, client->oldOrigin );
02966 
02967         /*
02968         if (level.intermissionQueued != 0 && g_singlePlayer.integer) {
02969                 if ( level.time - level.intermissionQueued >= 1000  ) {
02970                         pm.cmd.buttons = 0;
02971                         pm.cmd.forwardmove = 0;
02972                         pm.cmd.rightmove = 0;
02973                         pm.cmd.upmove = 0;
02974                         if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) {
02975                                 trap_SendConsoleCommand( EXEC_APPEND, "centerview\n");
02976                         }
02977                         ent->client->ps.pm_type = PM_SPINTERMISSION;
02978                 }
02979         }
02980         */
02981 
02982         //Set up bg entity data
02983         pm.baseEnt = (bgEntity_t *)g_entities;
02984         pm.entSize = sizeof(gentity_t);
02985 
02986         if (ent->client->ps.saberLockTime > level.time)
02987         {
02988                 gentity_t *blockOpp = &g_entities[ent->client->ps.saberLockEnemy];
02989 
02990                 if (blockOpp && blockOpp->inuse && blockOpp->client)
02991                 {
02992                         vec3_t lockDir, lockAng;
02993 
02994                         //VectorClear( ent->client->ps.velocity );
02995                         VectorSubtract( blockOpp->r.currentOrigin, ent->r.currentOrigin, lockDir );
02996                         //lockAng[YAW] = vectoyaw( defDir );
02997                         vectoangles(lockDir, lockAng);
02998                         SetClientViewAngle( ent, lockAng );
02999                 }
03000 
03001                 if ( ent->client->ps.saberLockHitCheckTime < level.time )
03002                 {//have moved to next frame since last lock push
03003                         ent->client->ps.saberLockHitCheckTime = level.time;//so we don't push more than once per server frame
03004                         if ( ( ent->client->buttons & BUTTON_ATTACK ) && ! ( ent->client->oldbuttons & BUTTON_ATTACK ) )
03005                         {
03006                                 if ( ent->client->ps.saberLockHitIncrementTime < level.time )
03007                                 {//have moved to next frame since last saberlock attack button press
03008                                         int lockHits = 0;
03009                                         ent->client->ps.saberLockHitIncrementTime = level.time;//so we don't register an attack key press more than once per server frame
03010                                         //NOTE: FP_SABER_OFFENSE level already taken into account in PM_SaberLocked
03011                                         if ( (ent->client->ps.fd.forcePowersActive&(1<<FP_RAGE)) )
03012                                         {//raging: push harder
03013                                                 lockHits = 1+ent->client->ps.fd.forcePowerLevel[FP_RAGE];
03014                                         }
03015                                         else
03016                                         {//normal attack
03017                                                 switch ( ent->client->ps.fd.saberAnimLevel )
03018                                                 {
03019                                                 case SS_FAST:
03020                                                         lockHits = 1;
03021                                                         break;
03022                                                 case SS_MEDIUM:
03023                                                 case SS_TAVION:
03024                                                 case SS_DUAL:
03025                                                 case SS_STAFF:
03026                                                         lockHits = 2;
03027                                                         break;
03028                                                 case SS_STRONG:
03029                                                 case SS_DESANN:
03030                                                         lockHits = 3;
03031                                                         break;
03032                                                 }
03033                                         }
03034                                         if ( ent->client->ps.fd.forceRageRecoveryTime > level.time 
03035                                                 && Q_irand( 0, 1 ) )
03036                                         {//finished raging: weak
03037                                                 lockHits -= 1;
03038                                         }
03039                                         lockHits += ent->client->saber[0].lockBonus;
03040                                         if ( ent->client->saber[1].model
03041                                                 && ent->client->saber[1].model[0]
03042                                                 && !ent->client->ps.saberHolstered )
03043                                         {
03044                                                 lockHits += ent->client->saber[1].lockBonus;
03045                                         }
03046                                         ent->client->ps.saberLockHits += lockHits;
03047                                         if ( g_saberLockRandomNess.integer )
03048                                         {
03049                                                 ent->client->ps.saberLockHits += Q_irand( 0, g_saberLockRandomNess.integer );
03050                                                 if ( ent->client->ps.saberLockHits < 0 )
03051                                                 {
03052                                                         ent->client->ps.saberLockHits = 0;
03053                                                 }
03054                                         }
03055                                 }
03056                         }
03057                         if ( ent->client->ps.saberLockHits > 0 )
03058                         {
03059                                 if ( !ent->client->ps.saberLockAdvance )
03060                                 {
03061                                         ent->client->ps.saberLockHits--;
03062                                 }
03063                                 ent->client->ps.saberLockAdvance = qtrue;
03064                         }
03065                 }
03066         }
03067         else
03068         {
03069                 ent->client->ps.saberLockFrame = 0;
03070                 //check for taunt
03071                 if ( (pm.cmd.generic_cmd == GENCMD_ENGAGE_DUEL) && (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) )
03072                 {//already in a duel, make it a taunt command
03073                         pm.cmd.buttons |= BUTTON_GESTURE;
03074                 }
03075         }
03076 
03077         if (ent->s.number >= MAX_CLIENTS)
03078         {
03079                 VectorCopy(ent->r.mins, pm.mins);
03080                 VectorCopy(ent->r.maxs, pm.maxs);
03081 #if 1
03082                 if (ent->s.NPC_class == CLASS_VEHICLE &&
03083                         ent->m_pVehicle )
03084                 {
03085                         if ( ent->m_pVehicle->m_pPilot)
03086                         { //vehicles want to use their last pilot ucmd I guess
03087                                 if ((level.time - ent->m_pVehicle->m_ucmd.serverTime) > 2000)
03088                                 { //Previous owner disconnected, maybe
03089                                         ent->m_pVehicle->m_ucmd.serverTime = level.time;
03090                                         ent->client->ps.commandTime = level.time-100;
03091                                         msec = 100;
03092                                 }
03093 
03094                                 memcpy(&pm.cmd, &ent->m_pVehicle->m_ucmd, sizeof(usercmd_t));
03095                                 
03096                                 //no veh can strafe
03097                                 pm.cmd.rightmove = 0;
03098                                 //no crouching or jumping!
03099                                 pm.cmd.upmove = 0;
03100 
03101                                 //NOTE: button presses were getting lost!
03102                                 assert(g_entities[ent->m_pVehicle->m_pPilot->s.number].client);
03103                                 pm.cmd.buttons = (g_entities[ent->m_pVehicle->m_pPilot->s.number].client->pers.cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK));
03104                         }
03105                         if ( ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
03106                         {
03107                                 if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
03108                                 {//ATST crushes anything underneath it
03109                                         gentity_t       *under = &g_entities[ent->client->ps.groundEntityNum];
03110                                         if ( under && under->health && under->takedamage )
03111                                         {
03112                                                 vec3_t  down = {0,0,-1};
03113                                                 //FIXME: we'll be doing traces down from each foot, so we'll have a real impact origin
03114                                                 G_Damage( under, ent, ent, down, under->r.currentOrigin, 100, 0, MOD_CRUSH );
03115                                         }
03116                                 }
03117                         }
03118                 }
03119 #endif
03120         }
03121 
03122         Pmove (&pm);
03123 
03124         if (ent->client->solidHack)
03125         {
03126                 if (ent->client->solidHack > level.time)
03127                 { //whee!
03128                         ent->r.contents = 0;
03129                 }
03130                 else
03131                 {
03132                         ent->r.contents = CONTENTS_BODY;
03133                         ent->client->solidHack = 0;
03134                 }
03135         }
03136         
03137         if ( ent->NPC )
03138         {
03139                 VectorCopy( ent->client->ps.viewangles, ent->r.currentAngles );
03140         }
03141 
03142         if (pm.checkDuelLoss)
03143         {
03144                 if (pm.checkDuelLoss > 0 && (pm.checkDuelLoss <= MAX_CLIENTS || (pm.checkDuelLoss < (MAX_GENTITIES-1) && g_entities[pm.checkDuelLoss-1].s.eType == ET_NPC) ) )
03145                 {
03146                         gentity_t *clientLost = &g_entities[pm.checkDuelLoss-1];
03147 
03148                         if (clientLost && clientLost->inuse && clientLost->client && Q_irand(0, 40) > clientLost->health)
03149                         {
03150                                 vec3_t attDir;
03151                                 VectorSubtract(ent->client->ps.origin, clientLost->client->ps.origin, attDir);
03152                                 VectorNormalize(attDir);
03153 
03154                                 VectorClear(clientLost->client->ps.velocity);
03155                                 clientLost->client->ps.forceHandExtend = HANDEXTEND_NONE;
03156                                 clientLost->client->ps.forceHandExtendTime = 0;
03157 
03158                                 gGAvoidDismember = 1;
03159                                 G_Damage(clientLost, ent, ent, attDir, clientLost->client->ps.origin, 9999, DAMAGE_NO_PROTECTION, MOD_SABER);
03160 
03161                                 if (clientLost->health < 1)
03162                                 {
03163                                         gGAvoidDismember = 2;
03164                                         G_CheckForDismemberment(clientLost, ent, clientLost->client->ps.origin, 999, (clientLost->client->ps.legsAnim), qfalse);
03165                                 }
03166 
03167                                 gGAvoidDismember = 0;
03168                         }
03169                         else if (clientLost && clientLost->inuse && clientLost->client &&
03170                                 clientLost->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN && clientLost->client->ps.saberEntityNum)
03171                         { //if we didn't knock down it was a circle lock. So as punishment, make them lose their saber and go into a proper anim
03172                                 saberCheckKnockdown_DuelLoss(&g_entities[clientLost->client->ps.saberEntityNum], clientLost, ent);
03173                         }
03174                 }
03175 
03176                 pm.checkDuelLoss = 0;
03177         }
03178 
03179         if (pm.cmd.generic_cmd &&
03180                 (pm.cmd.generic_cmd != ent->client->lastGenCmd || ent->client->lastGenCmdTime < level.time))
03181         {
03182                 ent->client->lastGenCmd = pm.cmd.generic_cmd;
03183                 if (pm.cmd.generic_cmd != GENCMD_FORCE_THROW &&
03184                         pm.cmd.generic_cmd != GENCMD_FORCE_PULL)
03185                 { //these are the only two where you wouldn't care about a delay between
03186                         ent->client->lastGenCmdTime = level.time + 300; //default 100ms debounce between issuing the same command.
03187                 }
03188 
03189                 switch(pm.cmd.generic_cmd)
03190                 {
03191                 case 0:
03192                         break;
03193                 case GENCMD_SABERSWITCH:
03194                         Cmd_ToggleSaber_f(ent);
03195                         break;
03196                 case GENCMD_ENGAGE_DUEL:
03197                         if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL )
03198                         {//already in a duel, made it a taunt command
03199                         }
03200                         else
03201                         {
03202                                 Cmd_EngageDuel_f(ent);
03203                         }
03204                         break;
03205                 case GENCMD_FORCE_HEAL:
03206                         ForceHeal(ent);
03207                         break;
03208                 case GENCMD_FORCE_SPEED:
03209                         ForceSpeed(ent, 0);
03210                         break;
03211                 case GENCMD_FORCE_THROW:
03212                         ForceThrow(ent, qfalse);
03213                         break;
03214                 case GENCMD_FORCE_PULL:
03215                         ForceThrow(ent, qtrue);
03216                         break;
03217                 case GENCMD_FORCE_DISTRACT:
03218                         ForceTelepathy(ent);
03219                         break;
03220                 case GENCMD_FORCE_RAGE:
03221                         ForceRage(ent);
03222                         break;
03223                 case GENCMD_FORCE_PROTECT:
03224                         ForceProtect(ent);
03225                         break;
03226                 case GENCMD_FORCE_ABSORB:
03227                         ForceAbsorb(ent);
03228                         break;
03229                 case GENCMD_FORCE_HEALOTHER:
03230                         ForceTeamHeal(ent);
03231                         break;
03232                 case GENCMD_FORCE_FORCEPOWEROTHER:
03233                         ForceTeamForceReplenish(ent);
03234                         break;
03235                 case GENCMD_FORCE_SEEING:
03236                         ForceSeeing(ent);
03237                         break;
03238                 case GENCMD_USE_SEEKER:
03239                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER)) &&
03240                                 G_ItemUsable(&ent->client->ps, HI_SEEKER) )
03241                         {
03242                                 ItemUse_Seeker(ent);
03243                                 G_AddEvent(ent, EV_USE_ITEM0+HI_SEEKER, 0);
03244                                 ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SEEKER);
03245                         }
03246                         break;
03247                 case GENCMD_USE_FIELD:
03248                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD)) &&
03249                                 G_ItemUsable(&ent->client->ps, HI_SHIELD) )
03250                         {
03251                                 ItemUse_Shield(ent);
03252                                 G_AddEvent(ent, EV_USE_ITEM0+HI_SHIELD, 0);
03253                                 ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SHIELD);
03254                         }
03255                         break;
03256                 case GENCMD_USE_BACTA:
03257                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC)) &&
03258                                 G_ItemUsable(&ent->client->ps, HI_MEDPAC) )
03259                         {
03260                                 ItemUse_MedPack(ent);
03261                                 G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC, 0);
03262                                 ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC);
03263                         }
03264                         break;
03265                 case GENCMD_USE_BACTABIG:
03266                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC_BIG)) &&
03267                                 G_ItemUsable(&ent->client->ps, HI_MEDPAC_BIG) )
03268                         {
03269                                 ItemUse_MedPack_Big(ent);
03270                                 G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC_BIG, 0);
03271                                 ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC_BIG);
03272                         }
03273                         break;
03274                 case GENCMD_USE_ELECTROBINOCULARS:
03275                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) &&
03276                                 G_ItemUsable(&ent->client->ps, HI_BINOCULARS) )
03277                         {
03278                                 ItemUse_Binoculars(ent);
03279                                 if (ent->client->ps.zoomMode == 0)
03280                                 {
03281                                         G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1);
03282                                 }
03283                                 else
03284                                 {
03285                                         G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2);
03286                                 }
03287                         }
03288                         break;
03289                 case GENCMD_ZOOM:
03290                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) &&
03291                                 G_ItemUsable(&ent->client->ps, HI_BINOCULARS) )
03292                         {
03293                                 ItemUse_Binoculars(ent);
03294                                 if (ent->client->ps.zoomMode == 0)
03295                                 {
03296                                         G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1);
03297                                 }
03298                                 else
03299                                 {
03300                                         G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2);
03301                                 }
03302                         }
03303                         break;
03304                 case GENCMD_USE_SENTRY:
03305                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN)) &&
03306                                 G_ItemUsable(&ent->client->ps, HI_SENTRY_GUN) )
03307                         {
03308                                 ItemUse_Sentry(ent);
03309                                 G_AddEvent(ent, EV_USE_ITEM0+HI_SENTRY_GUN, 0);
03310                                 ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SENTRY_GUN);
03311                         }
03312                         break;
03313                 case GENCMD_USE_JETPACK:
03314                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_JETPACK)) &&
03315                                 G_ItemUsable(&ent->client->ps, HI_JETPACK) )
03316                         {
03317                                 ItemUse_Jetpack(ent);
03318                                 G_AddEvent(ent, EV_USE_ITEM0+HI_JETPACK, 0);
03319                                 /*
03320                                 if (ent->client->ps.zoomMode == 0)
03321                                 {
03322                                         G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1);
03323                                 }
03324                                 else
03325                                 {
03326                                         G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2);
03327                                 }
03328                                 */
03329                         }
03330                         break;
03331                 case GENCMD_USE_HEALTHDISP:
03332                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_HEALTHDISP)) &&
03333                                 G_ItemUsable(&ent->client->ps, HI_HEALTHDISP) )
03334                         {
03335                                 //ItemUse_UseDisp(ent, HI_HEALTHDISP);
03336                                 G_AddEvent(ent, EV_USE_ITEM0+HI_HEALTHDISP, 0);
03337                         }
03338                         break;
03339                 case GENCMD_USE_AMMODISP:
03340                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_AMMODISP)) &&
03341                                 G_ItemUsable(&ent->client->ps, HI_AMMODISP) )
03342                         {
03343                                 //ItemUse_UseDisp(ent, HI_AMMODISP);
03344                                 G_AddEvent(ent, EV_USE_ITEM0+HI_AMMODISP, 0);
03345                         }
03346                         break;
03347                 case GENCMD_USE_EWEB:
03348                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_EWEB)) &&
03349                                 G_ItemUsable(&ent->client->ps, HI_EWEB) )
03350                         {
03351                                 ItemUse_UseEWeb(ent);
03352                                 G_AddEvent(ent, EV_USE_ITEM0+HI_EWEB, 0);
03353                         }
03354                         break;
03355                 case GENCMD_USE_CLOAK:
03356                         if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_CLOAK)) &&
03357                                 G_ItemUsable(&ent->client->ps, HI_CLOAK) )
03358                         {
03359                                 if ( ent->client->ps.powerups[PW_CLOAKED] )
03360                                 {//decloak
03361                                         Jedi_Decloak( ent );
03362                                 }
03363                                 else
03364                                 {//cloak
03365                                         Jedi_Cloak( ent );
03366                                 }
03367                         }
03368                         break;
03369                 case GENCMD_SABERATTACKCYCLE:
03370                         Cmd_SaberAttackCycle_f(ent);
03371                         break;
03372                 case GENCMD_TAUNT:
03373                         G_SetTauntAnim( ent, TAUNT_TAUNT );
03374                         break;
03375                 case GENCMD_BOW:
03376                         G_SetTauntAnim( ent, TAUNT_BOW );
03377                         break;
03378                 case GENCMD_MEDITATE:
03379                         G_SetTauntAnim( ent, TAUNT_MEDITATE );
03380                         break;
03381                 case GENCMD_FLOURISH:
03382                         G_SetTauntAnim( ent, TAUNT_FLOURISH );
03383                         break;
03384                 case GENCMD_GLOAT:
03385                         G_SetTauntAnim( ent, TAUNT_GLOAT );
03386                         break;
03387                 default:
03388                         break;
03389                 }
03390         }
03391 
03392         // save results of pmove
03393         if ( ent->client->ps.eventSequence != oldEventSequence ) {
03394                 ent->eventTime = level.time;
03395         }
03396         if (g_smoothClients.integer) {
03397                 BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qfalse );
03398                 //rww - 12-03-02 - Don't snap the origin of players! It screws prediction all up.
03399         }
03400         else {
03401                 BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qfalse );
03402         }
03403 
03404         if (isNPC)
03405         {
03406                 ent->s.eType = ET_NPC;
03407         }
03408 
03409         SendPendingPredictableEvents( &ent->client->ps );
03410 
03411         if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
03412                 client->fireHeld = qfalse;              // for grapple
03413         }
03414 
03415         // use the snapped origin for linking so it matches client predicted versions
03416         VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
03417 
03418         if (ent->s.eType != ET_NPC ||
03419                 ent->s.NPC_class != CLASS_VEHICLE ||
03420                 !ent->m_pVehicle ||
03421                 !ent->m_pVehicle->m_iRemovedSurfaces)
03422         { //let vehicles that are getting broken apart do their own crazy sizing stuff
03423                 VectorCopy (pm.mins, ent->r.mins);
03424                 VectorCopy (pm.maxs, ent->r.maxs);
03425         }
03426 
03427         ent->waterlevel = pm.waterlevel;
03428         ent->watertype = pm.watertype;
03429 
03430         // execute client events
03431         ClientEvents( ent, oldEventSequence );
03432 
03433         if ( pm.useEvent )
03434         {
03435                 //TODO: Use
03436 //              TryUse( ent );
03437         }
03438         if ((ent->client->pers.cmd.buttons & BUTTON_USE) && ent->client->ps.useDelay < level.time)
03439         {
03440                 TryUse(ent);
03441                 ent->client->ps.useDelay = level.time + 100;
03442         }
03443 
03444         // link entity now, after any personal teleporters have been used
03445         trap_LinkEntity (ent);
03446         if ( !ent->client->noclip ) {
03447                 G_TouchTriggers( ent );
03448         }
03449 
03450         // NOTE: now copy the exact origin over otherwise clients can be snapped into solid
03451         VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
03452 
03453         //test for solid areas in the AAS file
03454 //      BotTestAAS(ent->r.currentOrigin);
03455 
03456         // touch other objects
03457         ClientImpacts( ent, &pm );
03458 
03459         // save results of triggers and client events
03460         if (ent->client->ps.eventSequence != oldEventSequence) {
03461                 ent->eventTime = level.time;
03462         }
03463 
03464         // swap and latch button actions
03465         client->oldbuttons = client->buttons;
03466         client->buttons = ucmd->buttons;
03467         client->latched_buttons |= client->buttons & ~client->oldbuttons;
03468 
03469 //      G_VehicleAttachDroidUnit( ent );
03470 
03471         // Did we kick someone in our pmove sequence?
03472         if (client->ps.forceKickFlip)
03473         {
03474                 gentity_t *faceKicked = &g_entities[client->ps.forceKickFlip-1];
03475 
03476                 if (faceKicked && faceKicked->client && (!OnSameTeam(ent, faceKicked) || g_friendlyFire.integer) &&
03477                         (!faceKicked->client->ps.duelInProgress || faceKicked->client->ps.duelIndex == ent->s.number) &&
03478                         (!ent->client->ps.duelInProgress || ent->client->ps.duelIndex == faceKicked->s.number))
03479                 {
03480                         if ( faceKicked && faceKicked->client && faceKicked->health && faceKicked->takedamage )
03481                         {//push them away and do pain
03482                                 vec3_t oppDir;
03483                                 int strength = (int)VectorNormalize2( client->ps.velocity, oppDir );
03484 
03485                                 strength *= 0.05;
03486 
03487                                 VectorScale( oppDir, -1, oppDir );
03488 
03489                                 G_Damage( faceKicked, ent, ent, oppDir, client->ps.origin, strength, DAMAGE_NO_ARMOR, MOD_MELEE );
03490 
03491                                 if ( faceKicked->client->ps.weapon != WP_SABER ||
03492                                          faceKicked->client->ps.fd.saberAnimLevel != FORCE_LEVEL_3 ||
03493                                          (!BG_SaberInAttack(faceKicked->client->ps.saberMove) && !PM_SaberInStart(faceKicked->client->ps.saberMove) && !PM_SaberInReturn(faceKicked->client->ps.saberMove) && !PM_SaberInTransition(faceKicked->client->ps.saberMove)) )
03494                                 {
03495                                         if (faceKicked->health > 0 &&
03496                                                 faceKicked->client->ps.stats[STAT_HEALTH] > 0 &&
03497                                                 faceKicked->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN)
03498                                         {
03499                                                 if (BG_KnockDownable(&faceKicked->client->ps) && Q_irand(1, 10) <= 3)
03500                                                 { //only actually knock over sometimes, but always do velocity hit
03501                                                         faceKicked->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
03502                                                         faceKicked->client->ps.forceHandExtendTime = level.time + 1100;
03503                                                         faceKicked->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
03504                                                 }
03505 
03506                                                 faceKicked->client->ps.otherKiller = ent->s.number;
03507                                                 faceKicked->client->ps.otherKillerTime = level.time + 5000;
03508                                                 faceKicked->client->ps.otherKillerDebounceTime = level.time + 100;
03509 
03510                                                 faceKicked->client->ps.velocity[0] = oppDir[0]*(strength*40);
03511                                                 faceKicked->client->ps.velocity[1] = oppDir[1]*(strength*40);
03512                                                 faceKicked->client->ps.velocity[2] = 200;
03513                                         }
03514                                 }
03515 
03516                                 G_Sound( faceKicked, CHAN_AUTO, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
03517                         }
03518                 }
03519 
03520                 client->ps.forceKickFlip = 0;
03521         }
03522 
03523         // check for respawning
03524         if ( client->ps.stats[STAT_HEALTH] <= 0 
03525                 && !(client->ps.eFlags2&EF2_HELD_BY_MONSTER)//can't respawn while being eaten
03526                 && ent->s.eType != ET_NPC ) {
03527                 // wait for the attack button to be pressed
03528                 if ( level.time > client->respawnTime && !gDoSlowMoDuel ) {
03529                         // forcerespawn is to prevent users from waiting out powerups
03530                         int forceRes = g_forcerespawn.integer;
03531 
03532                         if (g_gametype.integer == GT_POWERDUEL)
03533                         {
03534                                 forceRes = 1;
03535                         }
03536                         else if (g_gametype.integer == GT_SIEGE &&
03537                                 g_siegeRespawn.integer)
03538                         { //wave respawning on
03539                                 forceRes = 1;
03540                         }
03541 
03542                         if ( forceRes > 0 && 
03543                                 ( level.time - client->respawnTime ) > forceRes * 1000 ) {
03544                                 respawn( ent );
03545                                 return;
03546                         }
03547                 
03548                         // pressing attack or use is the normal respawn method
03549                         if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
03550                                 respawn( ent );
03551                         }
03552                 }
03553                 else if (gDoSlowMoDuel)
03554                 {
03555                         client->respawnTime = level.time + 1000;
03556                 }
03557                 return;
03558         }
03559 
03560         // perform once-a-second actions
03561         ClientTimerActions( ent, msec );
03562 
03563         G_UpdateClientBroadcasts ( ent );
03564 
03565         //try some idle anims on ent if getting no input and not moving for some time
03566         G_CheckClientIdle( ent, ucmd );
03567 
03568         // This code was moved here from clientThink to fix a problem with g_synchronousClients 
03569         // being set to 1 when in vehicles. 
03570         if ( ent->s.number < MAX_CLIENTS && ent->client->ps.m_iVehicleNum )
03571         {//driving a vehicle
03572                 //run it
03573                 if (g_entities[ent->client->ps.m_iVehicleNum].inuse && g_entities[ent->client->ps.m_iVehicleNum].client)
03574                 {
03575                         ClientThink(ent->client->ps.m_iVehicleNum, &g_entities[ent->client->ps.m_iVehicleNum].m_pVehicle->m_ucmd);
03576                 }
03577                 else
03578                 { //vehicle no longer valid?
03579                         ent->client->ps.m_iVehicleNum = 0;
03580                 }
03581         }
03582 
03583 }
03584 
03585 /*
03586 ==================
03587 G_CheckClientTimeouts
03588 
03589 Checks whether a client has exceded any timeouts and act accordingly
03590 ==================
03591 */
03592 void G_CheckClientTimeouts ( gentity_t *ent )
03593 {
03594         // Only timeout supported right now is the timeout to spectator mode
03595         if ( !g_timeouttospec.integer )
03596         {
03597                 return;
03598         }
03599 
03600         // Already a spectator, no need to boot them to spectator
03601         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
03602         {
03603                 return;
03604         }
03605 
03606         // See how long its been since a command was received by the client and if its 
03607         // longer than the timeout to spectator then force this client into spectator mode
03608         if ( level.time - ent->client->pers.cmd.serverTime > g_timeouttospec.integer * 1000 )
03609         {
03610                 SetTeam ( ent, "spectator" );
03611         }
03612 }
03613 
03614 /*
03615 ==================
03616 ClientThink
03617 
03618 A new command has arrived from the client
03619 ==================
03620 */
03621 void ClientThink( int clientNum,usercmd_t *ucmd ) {
03622         gentity_t *ent;
03623 
03624         ent = g_entities + clientNum;
03625         if (clientNum < MAX_CLIENTS)
03626         {
03627                 trap_GetUsercmd( clientNum, &ent->client->pers.cmd );
03628         }
03629 
03630         // mark the time we got info, so we can display the
03631         // phone jack if they don't get any for a while
03632         ent->client->lastCmdTime = level.time;
03633 
03634         if (ucmd)
03635         {
03636                 ent->client->pers.cmd = *ucmd;
03637         }
03638 
03639 /*      This was moved to clientthink_real, but since its sort of a risky change i left it here for 
03640     now as a more concrete reference - BSD
03641   
03642         if ( clientNum < MAX_CLIENTS
03643                 && ent->client->ps.m_iVehicleNum )
03644         {//driving a vehicle
03645                 if (g_entities[ent->client->ps.m_iVehicleNum].client)
03646                 {
03647                         gentity_t *veh = &g_entities[ent->client->ps.m_iVehicleNum];
03648 
03649                         if (veh->m_pVehicle &&
03650                                 veh->m_pVehicle->m_pPilot == (bgEntity_t *)ent)
03651                         { //only take input from the pilot...
03652                                 veh->client->ps.commandTime = ent->client->ps.commandTime;
03653                                 memcpy(&veh->m_pVehicle->m_ucmd, &ent->client->pers.cmd, sizeof(usercmd_t));
03654                                 if ( veh->m_pVehicle->m_ucmd.buttons & BUTTON_TALK )
03655                                 { //forced input if "chat bubble" is up
03656                                         veh->m_pVehicle->m_ucmd.buttons = BUTTON_TALK;
03657                                         veh->m_pVehicle->m_ucmd.forwardmove = 0;
03658                                         veh->m_pVehicle->m_ucmd.rightmove = 0;
03659                                         veh->m_pVehicle->m_ucmd.upmove = 0;
03660                                 }
03661                         }
03662                 }
03663         }
03664 */
03665         if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) {
03666                 ClientThink_real( ent );
03667         }
03668         // vehicles are clients and when running synchronous they still need to think here
03669         // so special case them.
03670         else if ( clientNum >= MAX_CLIENTS ) {
03671                 ClientThink_real( ent );
03672         }
03673 
03674 /*      This was moved to clientthink_real, but since its sort of a risky change i left it here for 
03675     now as a more concrete reference - BSD
03676     
03677         if ( clientNum < MAX_CLIENTS
03678                 && ent->client->ps.m_iVehicleNum )
03679         {//driving a vehicle
03680                 //run it
03681                 if (g_entities[ent->client->ps.m_iVehicleNum].inuse &&
03682                         g_entities[ent->client->ps.m_iVehicleNum].client)
03683                 {
03684                         ClientThink(ent->client->ps.m_iVehicleNum, &g_entities[ent->client->ps.m_iVehicleNum].m_pVehicle->m_ucmd);
03685                 }
03686                 else
03687                 { //vehicle no longer valid?
03688                         ent->client->ps.m_iVehicleNum = 0;
03689                 }
03690         }
03691 */
03692 }
03693 
03694 
03695 void G_RunClient( gentity_t *ent ) {
03696         if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) {
03697                 return;
03698         }
03699         ent->client->pers.cmd.serverTime = level.time;
03700         ClientThink_real( ent );
03701 }
03702 
03703 
03704 /*
03705 ==================
03706 SpectatorClientEndFrame
03707 
03708 ==================
03709 */
03710 void SpectatorClientEndFrame( gentity_t *ent ) {
03711         gclient_t       *cl;
03712 
03713         if (ent->s.eType == ET_NPC)
03714         {
03715                 assert(0);
03716                 return;
03717         }
03718 
03719         // if we are doing a chase cam or a remote view, grab the latest info
03720         if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
03721                 int             clientNum;//, flags;
03722 
03723                 clientNum = ent->client->sess.spectatorClient;
03724 
03725                 // team follow1 and team follow2 go to whatever clients are playing
03726                 if ( clientNum == -1 ) {
03727                         clientNum = level.follow1;
03728                 } else if ( clientNum == -2 ) {
03729                         clientNum = level.follow2;
03730                 }
03731                 if ( clientNum >= 0 ) {
03732                         cl = &level.clients[ clientNum ];
03733                         if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) {
03734                                 //flags = (cl->mGameFlags & ~(PSG_VOTED | PSG_TEAMVOTED)) | (ent->client->mGameFlags & (PSG_VOTED | PSG_TEAMVOTED));
03735                                 //ent->client->mGameFlags = flags;
03736                                 ent->client->ps.eFlags = cl->ps.eFlags;
03737                                 ent->client->ps = cl->ps;
03738                                 ent->client->ps.pm_flags |= PMF_FOLLOW;
03739                                 return;
03740                         } else {
03741                                 // drop them to free spectators unless they are dedicated camera followers
03742                                 if ( ent->client->sess.spectatorClient >= 0 ) {
03743                                         ent->client->sess.spectatorState = SPECTATOR_FREE;
03744                                         ClientBegin( ent->client - level.clients, qtrue );
03745                                 }
03746                         }
03747                 }
03748         }
03749 
03750         if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
03751                 ent->client->ps.pm_flags |= PMF_SCOREBOARD;
03752         } else {
03753                 ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
03754         }
03755 }
03756 
03757 /*
03758 ==============
03759 ClientEndFrame
03760 
03761 Called at the end of each server frame for each connected client
03762 A fast client will have multiple ClientThink for each ClientEdFrame,
03763 while a slow client may have multiple ClientEndFrame between ClientThink.
03764 ==============
03765 */
03766 void ClientEndFrame( gentity_t *ent ) {
03767         int                     i;
03768         clientPersistant_t      *pers;
03769         qboolean isNPC = qfalse;
03770 
03771         if (ent->s.eType == ET_NPC)
03772         {
03773                 isNPC = qtrue;
03774         }
03775 
03776         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
03777                 SpectatorClientEndFrame( ent );
03778                 return;
03779         }
03780 
03781         pers = &ent->client->pers;
03782 
03783         // turn off any expired powerups
03784         for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
03785                 if ( ent->client->ps.powerups[ i ] < level.time ) {
03786                         ent->client->ps.powerups[ i ] = 0;
03787                 }
03788         }
03789 
03790         // save network bandwidth
03791 #if 0
03792         if ( !g_synchronousClients->integer && (ent->client->ps.pm_type == PM_NORMAL || ent->client->ps.pm_type == PM_JETPACK || ent->client->ps.pm_type == PM_FLOAT) ) {
03793                 // FIXME: this must change eventually for non-sync demo recording
03794                 VectorClear( ent->client->ps.viewangles );
03795         }
03796 #endif
03797 
03798         //
03799         // If the end of unit layout is displayed, don't give
03800         // the player any normal movement attributes
03801         //
03802         if ( level.intermissiontime ) {
03803                 if ( ent->s.number < MAX_CLIENTS
03804                         || ent->client->NPC_class == CLASS_VEHICLE )
03805                 {//players and vehicles do nothing in intermissions
03806                         return;
03807                 }
03808         }
03809 
03810         // burn from lava, etc
03811         P_WorldEffects (ent);
03812 
03813         // apply all the damage taken this frame
03814         P_DamageFeedback (ent);
03815 
03816         // add the EF_CONNECTION flag if we haven't gotten commands recently
03817         if ( level.time - ent->client->lastCmdTime > 1000 ) {
03818                 ent->s.eFlags |= EF_CONNECTION;
03819         } else {
03820                 ent->s.eFlags &= ~EF_CONNECTION;
03821         }
03822 
03823         ent->client->ps.stats[STAT_HEALTH] = ent->health;       // FIXME: get rid of ent->health...
03824 
03825         G_SetClientSound (ent);
03826 
03827         // set the latest infor
03828         if (g_smoothClients.integer) {
03829                 BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qfalse );
03830                 //rww - 12-03-02 - Don't snap the origin of players! It screws prediction all up.
03831         }
03832         else {
03833                 BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qfalse );
03834         }
03835 
03836         if (isNPC)
03837         {
03838                 ent->s.eType = ET_NPC;
03839         }
03840 
03841         SendPendingPredictableEvents( &ent->client->ps );
03842 
03843         // set the bit for the reachability area the client is currently in
03844 //      i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin );
03845 //      ent->client->areabits[i >> 3] |= 1 << (i & 7);
03846 }
03847 
03848