codemp/game/g_combat.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // g_combat.c
00004 
00005 //#include "g_local.h"
00006 #include "b_local.h"
00007 #include "bg_saga.h"
00008 
00009 extern int G_ShipSurfaceForSurfName( const char *surfaceName );
00010 extern qboolean G_FlyVehicleDestroySurface( gentity_t *veh, int surface );
00011 extern void G_VehicleSetDamageLocFlags( gentity_t *veh, int impactDir, int deathPoint );
00012 extern void G_VehUpdateShields( gentity_t *targ );
00013 extern void G_LetGoOfWall( gentity_t *ent );
00014 extern void BG_ClearRocketLock( playerState_t *ps );
00015 //rww - pd
00016 void BotDamageNotification(gclient_t *bot, gentity_t *attacker);
00017 //end rww
00018 
00019 void ThrowSaberToAttacker(gentity_t *self, gentity_t *attacker);
00020 
00021 void ObjectDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
00022 {
00023         if(self->target)
00024         {
00025                 G_UseTargets(self, attacker);
00026         }
00027 
00028         //remove my script_targetname
00029         G_FreeEntity( self );
00030 }
00031 
00032 qboolean G_HeavyMelee( gentity_t *attacker )
00033 {
00034         if (g_gametype.integer == GT_SIEGE 
00035                 && attacker 
00036                 && attacker->client
00037                 && attacker->client->siegeClass != -1 
00038                 && (bgSiegeClasses[attacker->client->siegeClass].classflags & (1<<CFL_HEAVYMELEE)) )
00039         {
00040                 return qtrue;
00041         }
00042         return qfalse;
00043 }
00044 
00045 int G_GetHitLocation(gentity_t *target, vec3_t ppoint)
00046 {
00047         vec3_t                  point, point_dir;
00048         vec3_t                  forward, right, up;
00049         vec3_t                  tangles, tcenter;
00050         float                   tradius;
00051         float                   udot, fdot, rdot;
00052         int                             Vertical, Forward, Lateral;
00053         int                             HitLoc;
00054 
00055         // Get target forward, right and up.
00056         if(target->client)
00057         {
00058                 // Ignore player's pitch and roll.
00059                 VectorSet(tangles, 0, target->r.currentAngles[YAW], 0);
00060         }
00061 
00062         AngleVectors(tangles, forward, right, up);
00063 
00064         // Get center of target.
00065         VectorAdd(target->r.absmin, target->r.absmax, tcenter);
00066         VectorScale(tcenter, 0.5, tcenter);
00067 
00068         // Get radius width of target.
00069         tradius = (fabs(target->r.maxs[0]) + fabs(target->r.maxs[1]) + fabs(target->r.mins[0]) + fabs(target->r.mins[1]))/4;
00070 
00071         // Get impact point.
00072         if(ppoint && !VectorCompare(ppoint, vec3_origin))
00073         {
00074                 VectorCopy(ppoint, point);
00075         }
00076         else
00077         {
00078                 return HL_NONE;
00079         }
00080 
00081 /*
00082 //get impact dir
00083         if(pdir && !VectorCompare(pdir, vec3_origin))
00084         {
00085                 VectorCopy(pdir, dir);
00086         }
00087         else
00088         {
00089                 return;
00090         }
00091 
00092 //put point at controlled distance from center
00093         VectorSubtract(point, tcenter, tempvec);
00094         tempvec[2] = 0;
00095         hdist = VectorLength(tempvec);
00096 
00097         VectorMA(point, hdist - tradius, dir, point);
00098         //now a point on the surface of a cylinder with a radius of tradius
00099 */      
00100         VectorSubtract(point, tcenter, point_dir);
00101         VectorNormalize(point_dir);
00102 
00103         // Get bottom to top (vertical) position index
00104         udot = DotProduct(up, point_dir);
00105         if(udot>.800)
00106         {
00107                 Vertical = 4;
00108         }
00109         else if(udot>.400)
00110         {
00111                 Vertical = 3;
00112         }
00113         else if(udot>-.333)
00114         {
00115                 Vertical = 2;
00116         }
00117         else if(udot>-.666)
00118         {
00119                 Vertical = 1;
00120         }
00121         else
00122         {
00123                 Vertical = 0;
00124         }
00125 
00126         // Get back to front (forward) position index.
00127         fdot = DotProduct(forward, point_dir);
00128         if(fdot>.666)
00129         {
00130                 Forward = 4;
00131         }
00132         else if(fdot>.333)
00133         {
00134                 Forward = 3;
00135         }
00136         else if(fdot>-.333)
00137         {
00138                 Forward = 2;
00139         }
00140         else if(fdot>-.666)
00141         {
00142                 Forward = 1;
00143         }
00144         else
00145         {
00146                 Forward = 0;
00147         }
00148 
00149         // Get left to right (lateral) position index.
00150         rdot = DotProduct(right, point_dir);
00151         if(rdot>.666)
00152         {
00153                 Lateral = 4;
00154         }
00155         else if(rdot>.333)
00156         {
00157                 Lateral = 3;
00158         }
00159         else if(rdot>-.333)
00160         {
00161                 Lateral = 2;
00162         }
00163         else if(rdot>-.666)
00164         {
00165                 Lateral = 1;
00166         }
00167         else
00168         {
00169                 Lateral = 0;
00170         }
00171 
00172         HitLoc = Vertical * 25 + Forward * 5 + Lateral;
00173 
00174         if(HitLoc <= 10)
00175         {
00176                 // Feet.
00177                 if ( rdot > 0 )
00178                 {
00179                         return HL_FOOT_RT;
00180                 }
00181                 else
00182                 {
00183                         return HL_FOOT_LT;
00184                 }
00185         }
00186         else if(HitLoc <= 50)
00187         {
00188                 // Legs.
00189                 if ( rdot > 0 )
00190                 {
00191                         return HL_LEG_RT;
00192                 }
00193                 else
00194                 {
00195                         return HL_LEG_LT;
00196                 }
00197         }
00198         else if(HitLoc == 56||HitLoc == 60||HitLoc == 61||HitLoc == 65||HitLoc == 66||HitLoc == 70)
00199         {
00200                 // Hands.
00201                 if ( rdot > 0 )
00202                 {
00203                         return HL_HAND_RT;
00204                 }
00205                 else
00206                 {
00207                         return HL_HAND_LT;
00208                 }
00209         }
00210         else if(HitLoc == 83||HitLoc == 87||HitLoc == 88||HitLoc == 92||HitLoc == 93||HitLoc == 97)
00211         {
00212                 // Arms.
00213                 if ( rdot > 0 )
00214                 {
00215                         return HL_ARM_RT;
00216                 }
00217                 else
00218                 {
00219                         return HL_ARM_LT;
00220                 }
00221         }
00222         else if((HitLoc >= 107 && HitLoc <= 109)||(HitLoc >= 112 && HitLoc <= 114)||(HitLoc >= 117 && HitLoc <= 119))
00223         {
00224                 // Head.
00225                 return HL_HEAD;
00226         }
00227         else
00228         {
00229                 if(udot < 0.3)
00230                 {
00231                         return HL_WAIST;
00232                 }
00233                 else if(fdot < 0)
00234                 {
00235                         if(rdot > 0.4)
00236                         {
00237                                 return HL_BACK_RT;
00238                         }
00239                         else if(rdot < -0.4)
00240                         {
00241                                 return HL_BACK_LT;
00242                         }
00243                         else if(fdot < 0)
00244                         {
00245                                 return HL_BACK;
00246                         }
00247                 }
00248                 else
00249                 {
00250                         if(rdot > 0.3)
00251                         {
00252                                 return HL_CHEST_RT;
00253                         }
00254                         else if(rdot < -0.3)
00255                         {
00256                                 return HL_CHEST_LT;
00257                         }
00258                         else if(fdot < 0)
00259                         {
00260                                 return HL_CHEST;
00261                         }
00262                 }
00263         }
00264         return HL_NONE;
00265 }
00266 
00267 /*
00268 int G_PickPainAnim( gentity_t *self, vec3_t point, int damage )
00269 {
00270         switch( G_GetHitLocation( self, point ) )
00271         {
00272         case HL_FOOT_RT:
00273                 return BOTH_PAIN12;
00274                 //PAIN12 = right foot
00275                 break;
00276         case HL_FOOT_LT:
00277                 return -1;
00278                 break;
00279         case HL_LEG_RT:
00280                 if ( !Q_irand( 0, 1 ) )
00281                 {
00282                         return BOTH_PAIN11;
00283                 }
00284                 else
00285                 {
00286                         return BOTH_PAIN13;
00287                 }
00288                 //PAIN11 = twitch right leg
00289                 //PAIN13 = right knee
00290                 break;
00291         case HL_LEG_LT:
00292                 return BOTH_PAIN14;
00293                 //PAIN14 = twitch left leg
00294                 break;
00295         case HL_BACK_RT:
00296                 return BOTH_PAIN7;
00297                 //PAIN7 = med left shoulder
00298                 break;
00299         case HL_BACK_LT:
00300                 return Q_irand( BOTH_PAIN15, BOTH_PAIN16 );
00301                 //PAIN15 = med right shoulder
00302                 //PAIN16 = twitch right shoulder
00303                 break;
00304         case HL_BACK:
00305                 if ( !Q_irand( 0, 1 ) )
00306                 {
00307                         return BOTH_PAIN1;
00308                 }
00309                 else
00310                 {
00311                         return BOTH_PAIN5;
00312                 }
00313                 //PAIN1 = back
00314                 //PAIN5 = same as 1
00315                 break;
00316         case HL_CHEST_RT:
00317                 return BOTH_PAIN3;
00318                 //PAIN3 = long, right shoulder
00319                 break;
00320         case HL_CHEST_LT:
00321                 return BOTH_PAIN2;
00322                 //PAIN2 = long, left shoulder
00323                 break;
00324         case HL_WAIST:
00325         case HL_CHEST:
00326                 if ( !Q_irand( 0, 3 ) )
00327                 {
00328                         return BOTH_PAIN6;
00329                 }
00330                 else if ( !Q_irand( 0, 2 ) )
00331                 {
00332                         return BOTH_PAIN8;
00333                 }
00334                 else if ( !Q_irand( 0, 1 ) )
00335                 {
00336                         return BOTH_PAIN17;
00337                 }
00338                 else
00339                 {
00340                         return BOTH_PAIN19;
00341                 }
00342                 //PAIN6 = gut
00343                 //PAIN8 = chest
00344                 //PAIN17 = twitch crotch
00345                 //PAIN19 = med crotch
00346                 break;
00347         case HL_ARM_RT:
00348         case HL_HAND_RT:
00349                 return BOTH_PAIN9;
00350                 //PAIN9 = twitch right arm
00351                 break;
00352         case HL_ARM_LT:
00353         case HL_HAND_LT:
00354                 return BOTH_PAIN10;
00355                 //PAIN10 = twitch left arm
00356                 break;
00357         case HL_HEAD:
00358                 return BOTH_PAIN4;
00359                 //PAIN4 = head
00360                 break;
00361         default:
00362                 return -1;
00363                 break;
00364         }
00365 }
00366 */
00367 
00368 void ExplodeDeath( gentity_t *self ) 
00369 {
00370 //      gentity_t       *tent;
00371         vec3_t          forward;
00372 
00373         self->takedamage = qfalse;//stop chain reaction runaway loops
00374 
00375         self->s.loopSound = 0;
00376         self->s.loopIsSoundset = qfalse;
00377 
00378         VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
00379 
00380 //      tent = G_TempEntity( self->s.origin, EV_FX_EXPLOSION );
00381         AngleVectors(self->s.angles, forward, NULL, NULL);
00382 
00383 /*      
00384         if ( self->fxID > 0 )
00385         {
00386                 G_PlayEffect( self->fxID, self->r.currentOrigin, forward );
00387         }
00388         else
00389         */
00390 
00391         {
00392 //              CG_SurfaceExplosion( self->r.currentOrigin, forward, 20.0f, 12.0f, ((self->spawnflags&4)==qfalse) );    //FIXME: This needs to be consistent to all exploders!
00393 //              G_Sound(self, self->sounds );
00394         }
00395         
00396         if(self->splashDamage > 0 && self->splashRadius > 0)
00397         {
00398                 gentity_t *attacker = self;
00399                 if ( self->parent )
00400                 {
00401                         attacker = self->parent;
00402                 }
00403                 G_RadiusDamage( self->r.currentOrigin, attacker, self->splashDamage, self->splashRadius, 
00404                                 attacker, NULL, MOD_UNKNOWN );
00405         }
00406 
00407         ObjectDie( self, self, self, 20, 0 );
00408 }
00409 
00410 
00411 /*
00412 ============
00413 ScorePlum
00414 ============
00415 */
00416 void ScorePlum( gentity_t *ent, vec3_t origin, int score ) {
00417         gentity_t *plum;
00418 
00419         plum = G_TempEntity( origin, EV_SCOREPLUM );
00420         // only send this temp entity to a single client
00421         plum->r.svFlags |= SVF_SINGLECLIENT;
00422         plum->r.singleClient = ent->s.number;
00423         //
00424         plum->s.otherEntityNum = ent->s.number;
00425         plum->s.time = score;
00426 }
00427 
00428 /*
00429 ============
00430 AddScore
00431 
00432 Adds score to both the client and his team
00433 ============
00434 */
00435 extern qboolean g_dontPenalizeTeam; //g_cmds.c
00436 void AddScore( gentity_t *ent, vec3_t origin, int score )
00437 {
00438         /*
00439         if (g_gametype.integer == GT_SIEGE)
00440         { //no scoring in this gametype at all.
00441                 return;
00442         }
00443         */
00444 
00445         if ( !ent->client ) {
00446                 return;
00447         }
00448         // no scoring during pre-match warmup
00449         if ( level.warmupTime ) {
00450                 return;
00451         }
00452         // show score plum
00453         //ScorePlum(ent, origin, score);
00454         //
00455         ent->client->ps.persistant[PERS_SCORE] += score;
00456         if ( g_gametype.integer == GT_TEAM && !g_dontPenalizeTeam )
00457                 level.teamScores[ ent->client->ps.persistant[PERS_TEAM] ] += score;
00458         CalculateRanks();
00459 }
00460 
00461 /*
00462 =================
00463 TossClientItems
00464 
00465 rww - Toss the weapon away from the player in the specified direction
00466 =================
00467 */
00468 void TossClientWeapon(gentity_t *self, vec3_t direction, float speed)
00469 {
00470         vec3_t vel;
00471         gitem_t *item;
00472         gentity_t *launched;
00473         int weapon = self->s.weapon;
00474         int ammoSub;
00475 
00476         if (g_gametype.integer == GT_SIEGE)
00477         { //no dropping weaps
00478                 return;
00479         }
00480 
00481         if (weapon <= WP_BRYAR_PISTOL)
00482         { //can't have this
00483                 return;
00484         }
00485 
00486         if (weapon == WP_EMPLACED_GUN ||
00487                 weapon == WP_TURRET)
00488         {
00489                 return;
00490         }
00491 
00492         // find the item type for this weapon
00493         item = BG_FindItemForWeapon( weapon );
00494 
00495         ammoSub = (self->client->ps.ammo[weaponData[weapon].ammoIndex] - bg_itemlist[BG_GetItemIndexByTag(weapon, IT_WEAPON)].quantity);
00496 
00497         if (ammoSub < 0)
00498         {
00499                 int ammoQuan = item->quantity;
00500                 ammoQuan -= (-ammoSub);
00501 
00502                 if (ammoQuan <= 0)
00503                 { //no ammo
00504                         return;
00505                 }
00506         }
00507 
00508         vel[0] = direction[0]*speed;
00509         vel[1] = direction[1]*speed;
00510         vel[2] = direction[2]*speed;
00511 
00512         launched = LaunchItem(item, self->client->ps.origin, vel);
00513 
00514         launched->s.generic1 = self->s.number;
00515         launched->s.powerups = level.time + 1500;
00516 
00517         launched->count = bg_itemlist[BG_GetItemIndexByTag(weapon, IT_WEAPON)].quantity;
00518 
00519         self->client->ps.ammo[weaponData[weapon].ammoIndex] -= bg_itemlist[BG_GetItemIndexByTag(weapon, IT_WEAPON)].quantity;
00520 
00521         if (self->client->ps.ammo[weaponData[weapon].ammoIndex] < 0)
00522         {
00523                 launched->count -= (-self->client->ps.ammo[weaponData[weapon].ammoIndex]);
00524                 self->client->ps.ammo[weaponData[weapon].ammoIndex] = 0;
00525         }
00526 
00527         if ((self->client->ps.ammo[weaponData[weapon].ammoIndex] < 1 && weapon != WP_DET_PACK) ||
00528                 (weapon != WP_THERMAL && weapon != WP_DET_PACK && weapon != WP_TRIP_MINE))
00529         {
00530                 int i = 0;
00531                 int weap = -1;
00532 
00533                 self->client->ps.stats[STAT_WEAPONS] &= ~(1 << weapon);
00534 
00535                 while (i < WP_NUM_WEAPONS)
00536                 {
00537                         if ((self->client->ps.stats[STAT_WEAPONS] & (1 << i)) && i != WP_NONE)
00538                         { //this one's good
00539                                 weap = i;
00540                                 break;
00541                         }
00542                         i++;
00543                 }
00544 
00545                 if (weap != -1)
00546                 {
00547                         self->s.weapon = weap;
00548                         self->client->ps.weapon = weap;
00549                 }
00550                 else
00551                 {
00552                         self->s.weapon = 0;
00553                         self->client->ps.weapon = 0;
00554                 }
00555 
00556                 G_AddEvent(self, EV_NOAMMO, weapon);
00557         }
00558 }
00559 
00560 /*
00561 =================
00562 TossClientItems
00563 
00564 Toss the weapon and powerups for the killed player
00565 =================
00566 */
00567 void TossClientItems( gentity_t *self ) {
00568         gitem_t         *item;
00569         int                     weapon;
00570         float           angle;
00571         int                     i;
00572         gentity_t       *drop;
00573 
00574         if (g_gametype.integer == GT_SIEGE)
00575         { //just don't drop anything then
00576                 return;
00577         }
00578 
00579         // drop the weapon if not a gauntlet or machinegun
00580         weapon = self->s.weapon;
00581 
00582         // make a special check to see if they are changing to a new
00583         // weapon that isn't the mg or gauntlet.  Without this, a client
00584         // can pick up a weapon, be killed, and not drop the weapon because
00585         // their weapon change hasn't completed yet and they are still holding the MG.
00586         if ( weapon == WP_BRYAR_PISTOL) {
00587                 if ( self->client->ps.weaponstate == WEAPON_DROPPING ) {
00588                         weapon = self->client->pers.cmd.weapon;
00589                 }
00590                 if ( !( self->client->ps.stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
00591                         weapon = WP_NONE;
00592                 }
00593         }
00594 
00595         self->s.bolt2 = weapon;
00596 
00597         if ( weapon > WP_BRYAR_PISTOL && 
00598                 weapon != WP_EMPLACED_GUN &&
00599                 weapon != WP_TURRET &&
00600                 self->client->ps.ammo[ weaponData[weapon].ammoIndex ] ) {
00601                 gentity_t *te;
00602 
00603                 // find the item type for this weapon
00604                 item = BG_FindItemForWeapon( weapon );
00605 
00606                 // tell all clients to remove the weapon model on this guy until he respawns
00607                 te = G_TempEntity( vec3_origin, EV_DESTROY_WEAPON_MODEL );
00608                 te->r.svFlags |= SVF_BROADCAST;
00609                 te->s.eventParm = self->s.number;
00610 
00611                 // spawn the item
00612                 Drop_Item( self, item, 0 );
00613         }
00614 
00615         // drop all the powerups if not in teamplay
00616         if ( g_gametype.integer != GT_TEAM && g_gametype.integer != GT_SIEGE ) {
00617                 angle = 45;
00618                 for ( i = 1 ; i < PW_NUM_POWERUPS ; i++ ) {
00619                         if ( self->client->ps.powerups[ i ] > level.time ) {
00620                                 item = BG_FindItemForPowerup( i );
00621                                 if ( !item ) {
00622                                         continue;
00623                                 }
00624                                 drop = Drop_Item( self, item, angle );
00625                                 // decide how many seconds it has left
00626                                 drop->count = ( self->client->ps.powerups[ i ] - level.time ) / 1000;
00627                                 if ( drop->count < 1 ) {
00628                                         drop->count = 1;
00629                                 }
00630                                 angle += 45;
00631                         }
00632                 }
00633         }
00634 }
00635 
00636 
00637 /*
00638 ==================
00639 LookAtKiller
00640 ==================
00641 */
00642 void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker ) {
00643         vec3_t          dir;
00644         vec3_t          angles;
00645 
00646         if ( attacker && attacker != self ) {
00647                 VectorSubtract (attacker->s.pos.trBase, self->s.pos.trBase, dir);
00648         } else if ( inflictor && inflictor != self ) {
00649                 VectorSubtract (inflictor->s.pos.trBase, self->s.pos.trBase, dir);
00650         } else {
00651                 self->client->ps.stats[STAT_DEAD_YAW] = self->s.angles[YAW];
00652                 return;
00653         }
00654 
00655         self->client->ps.stats[STAT_DEAD_YAW] = vectoyaw ( dir );
00656 
00657         angles[YAW] = vectoyaw ( dir );
00658         angles[PITCH] = 0; 
00659         angles[ROLL] = 0;
00660 }
00661 
00662 /*
00663 ==================
00664 GibEntity
00665 ==================
00666 */
00667 void GibEntity( gentity_t *self, int killer ) {
00668         G_AddEvent( self, EV_GIB_PLAYER, killer );
00669         self->takedamage = qfalse;
00670         self->s.eType = ET_INVISIBLE;
00671         self->r.contents = 0;
00672 }
00673 
00674 void BodyRid(gentity_t *ent)
00675 {
00676         trap_UnlinkEntity( ent );
00677         ent->physicsObject = qfalse;
00678 }
00679 
00680 /*
00681 ==================
00682 body_die
00683 ==================
00684 */
00685 void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
00686         // NOTENOTE No gibbing right now, this is star wars.
00687         qboolean doDisint = qfalse;
00688 
00689         if (self->s.eType == ET_NPC)
00690         { //well, just rem it then, so long as it's done with its death anim and it's not a standard weapon.
00691                 if ( self->client && self->client->ps.torsoTimer <= 0 &&
00692                          (meansOfDeath == MOD_UNKNOWN ||
00693                           meansOfDeath == MOD_WATER ||
00694                           meansOfDeath == MOD_SLIME ||
00695                           meansOfDeath == MOD_LAVA ||
00696                           meansOfDeath == MOD_CRUSH ||
00697                           meansOfDeath == MOD_TELEFRAG ||
00698                           meansOfDeath == MOD_FALLING ||
00699                           meansOfDeath == MOD_SUICIDE ||
00700                           meansOfDeath == MOD_TARGET_LASER ||
00701                           meansOfDeath == MOD_TRIGGER_HURT) )
00702                 {
00703                         self->think = G_FreeEntity;
00704                         self->nextthink = level.time;
00705                 }
00706                 return;
00707         }
00708 
00709         if (self->health < (GIB_HEALTH+1))
00710         {
00711                 self->health = GIB_HEALTH+1;
00712 
00713                 if (self->client && (level.time - self->client->respawnTime) < 2000)
00714                 {
00715                         doDisint = qfalse;
00716                 }
00717                 else
00718                 {
00719                         doDisint = qtrue;
00720                 }
00721         }
00722 
00723         if (self->client && (self->client->ps.eFlags & EF_DISINTEGRATION))
00724         {
00725                 return;
00726         }
00727         else if (self->s.eFlags & EF_DISINTEGRATION)
00728         {
00729                 return;
00730         }
00731 
00732         if (doDisint)
00733         {
00734                 if (self->client)
00735                 {
00736                         self->client->ps.eFlags |= EF_DISINTEGRATION;
00737                         VectorCopy(self->client->ps.origin, self->client->ps.lastHitLoc);
00738                 }
00739                 else
00740                 {
00741                         self->s.eFlags |= EF_DISINTEGRATION;
00742                         VectorCopy(self->r.currentOrigin, self->s.origin2);
00743 
00744                         //since it's the corpse entity, tell it to "remove" itself
00745                         self->think = BodyRid;
00746                         self->nextthink = level.time + 1000;
00747                 }
00748                 return;
00749         }
00750 }
00751 
00752 
00753 // these are just for logging, the client prints its own messages
00754 char    *modNames[MOD_MAX] = {
00755         "MOD_UNKNOWN",
00756         "MOD_STUN_BATON",
00757         "MOD_MELEE",
00758         "MOD_SABER",
00759         "MOD_BRYAR_PISTOL",
00760         "MOD_BRYAR_PISTOL_ALT",
00761         "MOD_BLASTER",
00762         "MOD_TURBLAST",
00763         "MOD_DISRUPTOR",
00764         "MOD_DISRUPTOR_SPLASH",
00765         "MOD_DISRUPTOR_SNIPER",
00766         "MOD_BOWCASTER",
00767         "MOD_REPEATER",
00768         "MOD_REPEATER_ALT",
00769         "MOD_REPEATER_ALT_SPLASH",
00770         "MOD_DEMP2",
00771         "MOD_DEMP2_ALT",
00772         "MOD_FLECHETTE",
00773         "MOD_FLECHETTE_ALT_SPLASH",
00774         "MOD_ROCKET",
00775         "MOD_ROCKET_SPLASH",
00776         "MOD_ROCKET_HOMING",
00777         "MOD_ROCKET_HOMING_SPLASH",
00778         "MOD_THERMAL",
00779         "MOD_THERMAL_SPLASH",
00780         "MOD_TRIP_MINE_SPLASH",
00781         "MOD_TIMED_MINE_SPLASH",
00782         "MOD_DET_PACK_SPLASH",
00783         "MOD_VEHICLE",
00784         "MOD_CONC",
00785         "MOD_CONC_ALT",
00786         "MOD_FORCE_DARK",
00787         "MOD_SENTRY",
00788         "MOD_WATER",
00789         "MOD_SLIME",
00790         "MOD_LAVA",
00791         "MOD_CRUSH",
00792         "MOD_TELEFRAG",
00793         "MOD_FALLING",
00794         "MOD_SUICIDE",
00795         "MOD_TARGET_LASER",
00796         "MOD_TRIGGER_HURT"
00797 };
00798 
00799 
00800 /*
00801 ==================
00802 CheckAlmostCapture
00803 ==================
00804 */
00805 void CheckAlmostCapture( gentity_t *self, gentity_t *attacker ) {
00806 #if 0
00807         gentity_t       *ent;
00808         vec3_t          dir;
00809         char            *classname;
00810 
00811         // if this player was carrying a flag
00812         if ( self->client->ps.powerups[PW_REDFLAG] ||
00813                 self->client->ps.powerups[PW_BLUEFLAG] ||
00814                 self->client->ps.powerups[PW_NEUTRALFLAG] ) {
00815                 // get the goal flag this player should have been going for
00816                 if ( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY ) {
00817                         if ( self->client->sess.sessionTeam == TEAM_BLUE ) {
00818                                 classname = "team_CTF_blueflag";
00819                         }
00820                         else {
00821                                 classname = "team_CTF_redflag";
00822                         }
00823                 }
00824                 else {
00825                         if ( self->client->sess.sessionTeam == TEAM_BLUE ) {
00826                                 classname = "team_CTF_redflag";
00827                         }
00828                         else {
00829                                 classname = "team_CTF_blueflag";
00830                         }
00831                 }
00832                 ent = NULL;
00833                 do
00834                 {
00835                         ent = G_Find(ent, FOFS(classname), classname);
00836                 } while (ent && (ent->flags & FL_DROPPED_ITEM));
00837                 // if we found the destination flag and it's not picked up
00838                 if (ent && !(ent->r.svFlags & SVF_NOCLIENT) ) {
00839                         // if the player was *very* close
00840                         VectorSubtract( self->client->ps.origin, ent->s.origin, dir );
00841                         if ( VectorLength(dir) < 200 ) {
00842                                 self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT;
00843                                 if ( attacker->client ) {
00844                                         attacker->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT;
00845                                 }
00846                         }
00847                 }
00848         }
00849 #endif
00850 }
00851 
00852 qboolean G_InKnockDown( playerState_t *ps )
00853 {
00854         switch ( (ps->legsAnim) )
00855         {
00856         case BOTH_KNOCKDOWN1:
00857         case BOTH_KNOCKDOWN2:
00858         case BOTH_KNOCKDOWN3:
00859         case BOTH_KNOCKDOWN4:
00860         case BOTH_KNOCKDOWN5:
00861                 return qtrue;
00862                 break;
00863         case BOTH_GETUP1:
00864         case BOTH_GETUP2:
00865         case BOTH_GETUP3:
00866         case BOTH_GETUP4:
00867         case BOTH_GETUP5:
00868         case BOTH_FORCE_GETUP_F1:
00869         case BOTH_FORCE_GETUP_F2:
00870         case BOTH_FORCE_GETUP_B1:
00871         case BOTH_FORCE_GETUP_B2:
00872         case BOTH_FORCE_GETUP_B3:
00873         case BOTH_FORCE_GETUP_B4:
00874         case BOTH_FORCE_GETUP_B5:
00875                 return qtrue;
00876                 break;
00877         }
00878         return qfalse;
00879 }
00880 
00881 static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc )
00882 {
00883         int deathAnim = -1;
00884 
00885         if ( BG_InRoll( &self->client->ps, self->client->ps.legsAnim ) )
00886         {
00887                 deathAnim = BOTH_DEATH_ROLL;            //# Death anim from a roll
00888         }
00889         else if ( BG_FlippingAnim( self->client->ps.legsAnim ) )
00890         {
00891                 deathAnim = BOTH_DEATH_FLIP;            //# Death anim from a flip
00892         }
00893         else if ( G_InKnockDown( &self->client->ps ) )
00894         {//since these happen a lot, let's handle them case by case
00895                 int animLength = bgAllAnims[self->localAnimIndex].anims[self->client->ps.legsAnim].numFrames * fabs((float)(bgHumanoidAnimations[self->client->ps.legsAnim].frameLerp));
00896                 switch ( self->client->ps.legsAnim )
00897                 {
00898                 case BOTH_KNOCKDOWN1:
00899                         if ( animLength - self->client->ps.legsTimer > 100 )
00900                         {//on our way down
00901                                 if ( self->client->ps.legsTimer > 600 )
00902                                 {//still partially up
00903                                         deathAnim = BOTH_DEATH_FALLING_UP;
00904                                 }
00905                                 else
00906                                 {//down
00907                                         deathAnim = BOTH_DEATH_LYING_UP;
00908                                 }
00909                         }
00910                         break;
00911                 case BOTH_KNOCKDOWN2:
00912                         if ( animLength - self->client->ps.legsTimer > 700 )
00913                         {//on our way down
00914                                 if ( self->client->ps.legsTimer > 600 )
00915                                 {//still partially up
00916                                         deathAnim = BOTH_DEATH_FALLING_UP;
00917                                 }
00918                                 else
00919                                 {//down
00920                                         deathAnim = BOTH_DEATH_LYING_UP;
00921                                 }
00922                         }
00923                         break;
00924                 case BOTH_KNOCKDOWN3:
00925                         if ( animLength - self->client->ps.legsTimer > 100 )
00926                         {//on our way down
00927                                 if ( self->client->ps.legsTimer > 1300 )
00928                                 {//still partially up
00929                                         deathAnim = BOTH_DEATH_FALLING_DN;
00930                                 }
00931                                 else
00932                                 {//down
00933                                         deathAnim = BOTH_DEATH_LYING_DN;
00934                                 }
00935                         }
00936                         break;
00937                 case BOTH_KNOCKDOWN4:
00938                         if ( animLength - self->client->ps.legsTimer > 300 )
00939                         {//on our way down
00940                                 if ( self->client->ps.legsTimer > 350 )
00941                                 {//still partially up
00942                                         deathAnim = BOTH_DEATH_FALLING_UP;
00943                                 }
00944                                 else
00945                                 {//down
00946                                         deathAnim = BOTH_DEATH_LYING_UP;
00947                                 }
00948                         }
00949                         else
00950                         {//crouch death
00951                                 vec3_t fwd;
00952                                 float thrown = 0;
00953 
00954                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
00955                                 thrown = DotProduct( fwd, self->client->ps.velocity );
00956 
00957                                 if ( thrown < -150 )
00958                                 {
00959                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
00960                                 }
00961                                 else
00962                                 {
00963                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
00964                                 }
00965                         }
00966                         break;
00967                 case BOTH_KNOCKDOWN5:
00968                         if ( self->client->ps.legsTimer < 750 )
00969                         {//flat
00970                                 deathAnim = BOTH_DEATH_LYING_DN;
00971                         }
00972                         break;
00973                 case BOTH_GETUP1:
00974                         if ( self->client->ps.legsTimer < 350 )
00975                         {//standing up
00976                         }
00977                         else if ( self->client->ps.legsTimer < 800 )
00978                         {//crouching
00979                                 vec3_t fwd;
00980                                 float thrown = 0;
00981 
00982                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
00983                                 thrown = DotProduct( fwd, self->client->ps.velocity );
00984                                 if ( thrown < -150 )
00985                                 {
00986                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
00987                                 }
00988                                 else
00989                                 {
00990                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
00991                                 }
00992                         }
00993                         else
00994                         {//lying down
00995                                 if ( animLength - self->client->ps.legsTimer > 450 )
00996                                 {//partially up
00997                                         deathAnim = BOTH_DEATH_FALLING_UP;
00998                                 }
00999                                 else
01000                                 {//down
01001                                         deathAnim = BOTH_DEATH_LYING_UP;
01002                                 }
01003                         }
01004                         break;
01005                 case BOTH_GETUP2:
01006                         if ( self->client->ps.legsTimer < 150 )
01007                         {//standing up
01008                         }
01009                         else if ( self->client->ps.legsTimer < 850 )
01010                         {//crouching
01011                                 vec3_t fwd;
01012                                 float thrown = 0;
01013 
01014                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01015                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01016 
01017                                 if ( thrown < -150 )
01018                                 {
01019                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01020                                 }
01021                                 else
01022                                 {
01023                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01024                                 }
01025                         }
01026                         else
01027                         {//lying down
01028                                 if ( animLength - self->client->ps.legsTimer > 500 )
01029                                 {//partially up
01030                                         deathAnim = BOTH_DEATH_FALLING_UP;
01031                                 }
01032                                 else
01033                                 {//down
01034                                         deathAnim = BOTH_DEATH_LYING_UP;
01035                                 }
01036                         }
01037                         break;
01038                 case BOTH_GETUP3:
01039                         if ( self->client->ps.legsTimer < 250 )
01040                         {//standing up
01041                         }
01042                         else if ( self->client->ps.legsTimer < 600 )
01043                         {//crouching
01044                                 vec3_t fwd;
01045                                 float thrown = 0;
01046                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01047                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01048 
01049                                 if ( thrown < -150 )
01050                                 {
01051                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01052                                 }
01053                                 else
01054                                 {
01055                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01056                                 }
01057                         }
01058                         else
01059                         {//lying down
01060                                 if ( animLength - self->client->ps.legsTimer > 150 )
01061                                 {//partially up
01062                                         deathAnim = BOTH_DEATH_FALLING_DN;
01063                                 }
01064                                 else
01065                                 {//down
01066                                         deathAnim = BOTH_DEATH_LYING_DN;
01067                                 }
01068                         }
01069                         break;
01070                 case BOTH_GETUP4:
01071                         if ( self->client->ps.legsTimer < 250 )
01072                         {//standing up
01073                         }
01074                         else if ( self->client->ps.legsTimer < 600 )
01075                         {//crouching
01076                                 vec3_t fwd;
01077                                 float thrown = 0;
01078 
01079                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01080                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01081 
01082                                 if ( thrown < -150 )
01083                                 {
01084                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01085                                 }
01086                                 else
01087                                 {
01088                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01089                                 }
01090                         }
01091                         else
01092                         {//lying down
01093                                 if ( animLength - self->client->ps.legsTimer > 850 )
01094                                 {//partially up
01095                                         deathAnim = BOTH_DEATH_FALLING_DN;
01096                                 }
01097                                 else
01098                                 {//down
01099                                         deathAnim = BOTH_DEATH_LYING_UP;
01100                                 }
01101                         }
01102                         break;
01103                 case BOTH_GETUP5:
01104                         if ( self->client->ps.legsTimer > 850 )
01105                         {//lying down
01106                                 if ( animLength - self->client->ps.legsTimer > 1500 )
01107                                 {//partially up
01108                                         deathAnim = BOTH_DEATH_FALLING_DN;
01109                                 }
01110                                 else
01111                                 {//down
01112                                         deathAnim = BOTH_DEATH_LYING_DN;
01113                                 }
01114                         }
01115                         break;
01116                 case BOTH_GETUP_CROUCH_B1:
01117                         if ( self->client->ps.legsTimer < 800 )
01118                         {//crouching
01119                                 vec3_t fwd;
01120                                 float thrown = 0;
01121 
01122                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01123                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01124 
01125                                 if ( thrown < -150 )
01126                                 {
01127                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01128                                 }
01129                                 else
01130                                 {
01131                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01132                                 }
01133                         }
01134                         else
01135                         {//lying down
01136                                 if ( animLength - self->client->ps.legsTimer > 400 )
01137                                 {//partially up
01138                                         deathAnim = BOTH_DEATH_FALLING_UP;
01139                                 }
01140                                 else
01141                                 {//down
01142                                         deathAnim = BOTH_DEATH_LYING_UP;
01143                                 }
01144                         }
01145                         break;
01146                 case BOTH_GETUP_CROUCH_F1:
01147                         if ( self->client->ps.legsTimer < 800 )
01148                         {//crouching
01149                                 vec3_t fwd;
01150                                 float thrown = 0;
01151 
01152                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01153                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01154 
01155                                 if ( thrown < -150 )
01156                                 {
01157                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01158                                 }
01159                                 else
01160                                 {
01161                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01162                                 }
01163                         }
01164                         else
01165                         {//lying down
01166                                 if ( animLength - self->client->ps.legsTimer > 150 )
01167                                 {//partially up
01168                                         deathAnim = BOTH_DEATH_FALLING_DN;
01169                                 }
01170                                 else
01171                                 {//down
01172                                         deathAnim = BOTH_DEATH_LYING_DN;
01173                                 }
01174                         }
01175                         break;
01176                 case BOTH_FORCE_GETUP_B1:
01177                         if ( self->client->ps.legsTimer < 325 )
01178                         {//standing up
01179                         }
01180                         else if ( self->client->ps.legsTimer < 725 )
01181                         {//spinning up
01182                                 deathAnim = BOTH_DEATH_SPIN_180;        //# Death anim when facing backwards
01183                         }
01184                         else if ( self->client->ps.legsTimer < 900 )
01185                         {//crouching
01186                                 vec3_t fwd;
01187                                 float thrown = 0;
01188 
01189                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01190                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01191 
01192                                 if ( thrown < -150 )
01193                                 {
01194                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01195                                 }
01196                                 else
01197                                 {
01198                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01199                                 }
01200                         }
01201                         else
01202                         {//lying down
01203                                 if ( animLength - self->client->ps.legsTimer > 50 )
01204                                 {//partially up
01205                                         deathAnim = BOTH_DEATH_FALLING_UP;
01206                                 }
01207                                 else
01208                                 {//down
01209                                         deathAnim = BOTH_DEATH_LYING_UP;
01210                                 }
01211                         }
01212                         break;
01213                 case BOTH_FORCE_GETUP_B2:
01214                         if ( self->client->ps.legsTimer < 575 )
01215                         {//standing up
01216                         }
01217                         else if ( self->client->ps.legsTimer < 875 )
01218                         {//spinning up
01219                                 deathAnim = BOTH_DEATH_SPIN_180;        //# Death anim when facing backwards
01220                         }
01221                         else if ( self->client->ps.legsTimer < 900 )
01222                         {//crouching
01223                                 vec3_t fwd;
01224                                 float thrown = 0;
01225 
01226                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01227                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01228 
01229                                 if ( thrown < -150 )
01230                                 {
01231                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01232                                 }
01233                                 else
01234                                 {
01235                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01236                                 }
01237                         }
01238                         else
01239                         {//lying down
01240                                 //partially up
01241                                 deathAnim = BOTH_DEATH_FALLING_UP;
01242                         }
01243                         break;
01244                 case BOTH_FORCE_GETUP_B3:
01245                         if ( self->client->ps.legsTimer < 150 )
01246                         {//standing up
01247                         }
01248                         else if ( self->client->ps.legsTimer < 775 )
01249                         {//flipping
01250                                 deathAnim = BOTH_DEATHBACKWARD2; //backflip
01251                         }
01252                         else
01253                         {//lying down
01254                                 //partially up
01255                                 deathAnim = BOTH_DEATH_FALLING_UP;
01256                         }
01257                         break;
01258                 case BOTH_FORCE_GETUP_B4:
01259                         if ( self->client->ps.legsTimer < 325 )
01260                         {//standing up
01261                         }
01262                         else
01263                         {//lying down
01264                                 if ( animLength - self->client->ps.legsTimer > 150 )
01265                                 {//partially up
01266                                         deathAnim = BOTH_DEATH_FALLING_UP;
01267                                 }
01268                                 else
01269                                 {//down
01270                                         deathAnim = BOTH_DEATH_LYING_UP;
01271                                 }
01272                         }
01273                         break;
01274                 case BOTH_FORCE_GETUP_B5:
01275                         if ( self->client->ps.legsTimer < 550 )
01276                         {//standing up
01277                         }
01278                         else if ( self->client->ps.legsTimer < 1025 )
01279                         {//kicking up
01280                                 deathAnim = BOTH_DEATHBACKWARD2; //backflip
01281                         }
01282                         else
01283                         {//lying down
01284                                 if ( animLength - self->client->ps.legsTimer > 50 )
01285                                 {//partially up
01286                                         deathAnim = BOTH_DEATH_FALLING_UP;
01287                                 }
01288                                 else
01289                                 {//down
01290                                         deathAnim = BOTH_DEATH_LYING_UP;
01291                                 }
01292                         }
01293                         break;
01294                 case BOTH_FORCE_GETUP_B6:
01295                         if ( self->client->ps.legsTimer < 225 )
01296                         {//standing up
01297                         }
01298                         else if ( self->client->ps.legsTimer < 425 )
01299                         {//crouching up
01300                                 vec3_t fwd;
01301                                 float thrown = 0;
01302 
01303                                 AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
01304                                 thrown = DotProduct( fwd, self->client->ps.velocity );
01305 
01306                                 if ( thrown < -150 )
01307                                 {
01308                                         deathAnim = BOTH_DEATHBACKWARD1;        //# Death anim when crouched and thrown back
01309                                 }
01310                                 else
01311                                 {
01312                                         deathAnim = BOTH_DEATH_CROUCHED;        //# Death anim when crouched
01313                                 }
01314                         }
01315                         else if ( self->client->ps.legsTimer < 825 )
01316                         {//flipping up
01317                                 deathAnim = BOTH_DEATHFORWARD3; //backflip
01318                         }
01319                         else
01320                         {//lying down
01321                                 if ( animLength - self->client->ps.legsTimer > 225 )
01322                                 {//partially up
01323                                         deathAnim = BOTH_DEATH_FALLING_UP;
01324                                 }
01325                                 else
01326                                 {//down
01327                                         deathAnim = BOTH_DEATH_LYING_UP;
01328                                 }
01329                         }
01330                         break;
01331                 case BOTH_FORCE_GETUP_F1:
01332                         if ( self->client->ps.legsTimer < 275 )
01333                         {//standing up
01334                         }
01335                         else if ( self->client->ps.legsTimer < 750 )
01336                         {//flipping
01337                                 deathAnim = BOTH_DEATH14;
01338                         }
01339                         else
01340                         {//lying down
01341                                 if ( animLength - self->client->ps.legsTimer > 100 )
01342                                 {//partially up
01343                                         deathAnim = BOTH_DEATH_FALLING_DN;
01344                                 }
01345                                 else
01346                                 {//down
01347                                         deathAnim = BOTH_DEATH_LYING_DN;
01348                                 }
01349                         }
01350                         break;
01351                 case BOTH_FORCE_GETUP_F2:
01352                         if ( self->client->ps.legsTimer < 1200 )
01353                         {//standing
01354                         }
01355                         else
01356                         {//lying down
01357                                 if ( animLength - self->client->ps.legsTimer > 225 )
01358                                 {//partially up
01359                                         deathAnim = BOTH_DEATH_FALLING_DN;
01360                                 }
01361                                 else
01362                                 {//down
01363                                         deathAnim = BOTH_DEATH_LYING_DN;
01364                                 }
01365                         }
01366                         break;
01367                 }
01368         }
01369 
01370         return deathAnim;
01371 }
01372 
01373 int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc )
01374 {//FIXME: play dead flop anims on body if in an appropriate _DEAD anim when this func is called
01375         int deathAnim = -1;
01376         int max_health;
01377         int legAnim = 0;
01378         vec3_t objVelocity;
01379 
01380         if (!self || !self->client)
01381         {
01382                 if (!self || self->s.eType != ET_NPC)
01383                 { //g2animent
01384                         return 0;
01385                 }
01386         }
01387 
01388         if (self->client)
01389         {
01390                 max_health = self->client->ps.stats[STAT_MAX_HEALTH];
01391 
01392                 if (self->client->inSpaceIndex && self->client->inSpaceIndex != ENTITYNUM_NONE)
01393                 {
01394                         return BOTH_CHOKE3;
01395                 }
01396         }
01397         else
01398         {
01399                 max_health = 60;
01400         }
01401 
01402         if (self->client)
01403         {
01404                 VectorCopy(self->client->ps.velocity, objVelocity);
01405         }
01406         else
01407         {
01408                 VectorCopy(self->s.pos.trDelta, objVelocity);
01409         }
01410 
01411         if ( hitLoc == HL_NONE )
01412         {
01413                 hitLoc = G_GetHitLocation( self, point );//self->hitLoc
01414         }
01415 
01416         if (self->client)
01417         {
01418                 legAnim = self->client->ps.legsAnim;
01419         }
01420         else
01421         {
01422                 legAnim = self->s.legsAnim;
01423         }
01424 
01425         if (gGAvoidDismember)
01426         {
01427                 return BOTH_RIGHTHANDCHOPPEDOFF;
01428         }
01429 
01430         //dead flops
01431         switch( legAnim )
01432         {
01433         case BOTH_DEATH1:               //# First Death anim
01434         case BOTH_DEAD1:
01435         case BOTH_DEATH2:                       //# Second Death anim
01436         case BOTH_DEAD2:
01437         case BOTH_DEATH8:                       //# 
01438         case BOTH_DEAD8:
01439         case BOTH_DEATH13:                      //# 
01440         case BOTH_DEAD13:
01441         case BOTH_DEATH14:                      //# 
01442         case BOTH_DEAD14:
01443         case BOTH_DEATH16:                      //# 
01444         case BOTH_DEAD16:
01445         case BOTH_DEADBACKWARD1:                //# First thrown backward death finished pose
01446         case BOTH_DEADBACKWARD2:                //# Second thrown backward death finished pose
01447                 deathAnim = -2;
01448                 /*
01449                 if ( PM_FinishedCurrentLegsAnim( self ) )
01450                 {//done with the anim
01451                         deathAnim = BOTH_DEADFLOP2;
01452                 }
01453                 else
01454                 {
01455                         deathAnim = -2;
01456                 }
01457                 break;
01458         case BOTH_DEADFLOP2:
01459                 deathAnim = BOTH_DEADFLOP2;
01460                 break;
01461                 */
01462         case BOTH_DEATH10:                      //# 
01463         case BOTH_DEAD10:
01464         case BOTH_DEATH15:                      //# 
01465         case BOTH_DEAD15:
01466         case BOTH_DEADFORWARD1:         //# First thrown forward death finished pose
01467         case BOTH_DEADFORWARD2:         //# Second thrown forward death finished pose
01468                 deathAnim = -2;
01469                 /*
01470                 if ( PM_FinishedCurrentLegsAnim( self ) )
01471                 {//done with the anim
01472                         deathAnim = BOTH_DEADFLOP1;
01473                 }
01474                 else
01475                 {
01476                         deathAnim = -2;
01477                 }
01478                 break;
01479                 */
01480         case BOTH_DEADFLOP1:
01481                 deathAnim = -2;
01482                 //deathAnim = BOTH_DEADFLOP1;
01483                 break;
01484         case BOTH_DEAD3:                                //# Third Death finished pose
01485         case BOTH_DEAD4:                                //# Fourth Death finished pose
01486         case BOTH_DEAD5:                                //# Fifth Death finished pose
01487         case BOTH_DEAD6:                                //# Sixth Death finished pose
01488         case BOTH_DEAD7:                                //# Seventh Death finished pose
01489         case BOTH_DEAD9:                                //# 
01490         case BOTH_DEAD11:                       //#
01491         case BOTH_DEAD12:                       //# 
01492         case BOTH_DEAD17:                       //# 
01493         case BOTH_DEAD18:                       //# 
01494         case BOTH_DEAD19:                       //# 
01495         case BOTH_LYINGDEAD1:           //# Killed lying down death finished pose
01496         case BOTH_STUMBLEDEAD1:         //# Stumble forward death finished pose
01497         case BOTH_FALLDEAD1LAND:                //# Fall forward and splat death finished pose
01498         case BOTH_DEATH3:                       //# Third Death anim
01499         case BOTH_DEATH4:                       //# Fourth Death anim
01500         case BOTH_DEATH5:                       //# Fifth Death anim
01501         case BOTH_DEATH6:                       //# Sixth Death anim
01502         case BOTH_DEATH7:                       //# Seventh Death anim
01503         case BOTH_DEATH9:                       //# 
01504         case BOTH_DEATH11:                      //#
01505         case BOTH_DEATH12:                      //# 
01506         case BOTH_DEATH17:                      //# 
01507         case BOTH_DEATH18:                      //# 
01508         case BOTH_DEATH19:                      //# 
01509         case BOTH_DEATHFORWARD1:                //# First Death in which they get thrown forward
01510         case BOTH_DEATHFORWARD2:                //# Second Death in which they get thrown forward
01511         case BOTH_DEATHBACKWARD1:       //# First Death in which they get thrown backward
01512         case BOTH_DEATHBACKWARD2:       //# Second Death in which they get thrown backward
01513         case BOTH_DEATH1IDLE:           //# Idle while close to death
01514         case BOTH_LYINGDEATH1:          //# Death to play when killed lying down
01515         case BOTH_STUMBLEDEATH1:                //# Stumble forward and fall face first death
01516         case BOTH_FALLDEATH1:           //# Fall forward off a high cliff and splat death - start
01517         case BOTH_FALLDEATH1INAIR:      //# Fall forward off a high cliff and splat death - loop
01518         case BOTH_FALLDEATH1LAND:       //# Fall forward off a high cliff and splat death - hit bottom
01519                 deathAnim = -2;
01520                 break;
01521         }
01522         if ( deathAnim == -1 )
01523         {
01524                 if (self->client)
01525                 {
01526                         deathAnim = G_CheckSpecialDeathAnim( self, point, damage, mod, hitLoc );
01527                 }
01528 
01529                 if (deathAnim == -1)
01530                 {
01531                         //death anims
01532                         switch( hitLoc )
01533                         {
01534                         case HL_FOOT_RT:
01535                         case HL_FOOT_LT:
01536                                 if ( mod == MOD_SABER && !Q_irand( 0, 2 ) )
01537                                 {
01538                                         return BOTH_DEATH10;//chest: back flip
01539                                 }
01540                                 else if ( !Q_irand( 0, 2 ) )
01541                                 {
01542                                         deathAnim = BOTH_DEATH4;//back: forward
01543                                 }
01544                                 else if ( !Q_irand( 0, 1 ) )
01545                                 {
01546                                         deathAnim = BOTH_DEATH5;//same as 4
01547                                 }
01548                                 else
01549                                 {
01550                                         deathAnim = BOTH_DEATH15;//back: forward
01551                                 }
01552                                 break;
01553                         case HL_LEG_RT:
01554                                 if ( !Q_irand( 0, 2 ) )
01555                                 {
01556                                         deathAnim = BOTH_DEATH4;//back: forward
01557                                 }
01558                                 else if ( !Q_irand( 0, 1 ) )
01559                                 {
01560                                         deathAnim = BOTH_DEATH5;//same as 4
01561                                 }
01562                                 else
01563                                 {
01564                                         deathAnim = BOTH_DEATH15;//back: forward
01565                                 }
01566                                 break;
01567                         case HL_LEG_LT:
01568                                 if ( !Q_irand( 0, 2 ) )
01569                                 {
01570                                         deathAnim = BOTH_DEATH4;//back: forward
01571                                 }
01572                                 else if ( !Q_irand( 0, 1 ) )
01573                                 {
01574                                         deathAnim = BOTH_DEATH5;//same as 4
01575                                 }
01576                                 else
01577                                 {
01578                                         deathAnim = BOTH_DEATH15;//back: forward
01579                                 }
01580                                 break;
01581                         case HL_BACK:
01582                                 if ( !VectorLengthSquared( objVelocity ) )
01583                                 {
01584                                         deathAnim = BOTH_DEATH17;//head/back: croak
01585                                 }
01586                                 else
01587                                 {
01588                                         if ( !Q_irand( 0, 2 ) )
01589                                         {
01590                                                 deathAnim = BOTH_DEATH4;//back: forward
01591                                         }
01592                                         else if ( !Q_irand( 0, 1 ) )
01593                                         {
01594                                                 deathAnim = BOTH_DEATH5;//same as 4
01595                                         }
01596                                         else
01597                                         {
01598                                                 deathAnim = BOTH_DEATH15;//back: forward
01599                                         }
01600                                 }
01601                                 break;
01602                         case HL_CHEST_RT:
01603                         case HL_ARM_RT:
01604                         case HL_HAND_RT:
01605                         case HL_BACK_RT:
01606                                 if ( damage <= max_health*0.25 )
01607                                 {
01608                                         deathAnim = BOTH_DEATH9;//chest right: snap, fall forward
01609                                 }
01610                                 else if ( damage <= max_health*0.5 )
01611                                 {
01612                                         deathAnim = BOTH_DEATH3;//chest right: back
01613                                 }
01614                                 else if ( damage <= max_health*0.75 )
01615                                 {
01616                                         deathAnim = BOTH_DEATH6;//chest right: spin
01617                                 }
01618                                 else 
01619                                 {
01620                                         //TEMP HACK: play spinny deaths less often
01621                                         if ( Q_irand( 0, 1 ) )
01622                                         {
01623                                                 deathAnim = BOTH_DEATH8;//chest right: spin high
01624                                         }
01625                                         else
01626                                         {
01627                                                 switch ( Q_irand( 0, 2 ) )
01628                                                 {
01629                                                 default:
01630                                                 case 0:
01631                                                         deathAnim = BOTH_DEATH9;//chest right: snap, fall forward
01632                                                         break;
01633                                                 case 1:
01634                                                         deathAnim = BOTH_DEATH3;//chest right: back
01635                                                         break;
01636                                                 case 2:
01637                                                         deathAnim = BOTH_DEATH6;//chest right: spin
01638                                                         break;
01639                                                 }
01640                                         }
01641                                 }
01642                                 break;
01643                         case HL_CHEST_LT:
01644                         case HL_ARM_LT:
01645                         case HL_HAND_LT:
01646                         case HL_BACK_LT:
01647                                 if ( damage <= max_health*0.25 )
01648                                 {
01649                                         deathAnim = BOTH_DEATH11;//chest left: snap, fall forward
01650                                 }
01651                                 else if ( damage <= max_health*0.5 )
01652                                 {
01653                                         deathAnim = BOTH_DEATH7;//chest left: back
01654                                 }
01655                                 else if ( damage <= max_health*0.75 )
01656                                 {
01657                                         deathAnim = BOTH_DEATH12;//chest left: spin
01658                                 }
01659                                 else
01660                                 {
01661                                         //TEMP HACK: play spinny deaths less often
01662                                         if ( Q_irand( 0, 1 ) )
01663                                         {
01664                                                 deathAnim = BOTH_DEATH14;//chest left: spin high
01665                                         }
01666                                         else
01667                                         {
01668                                                 switch ( Q_irand( 0, 2 ) )
01669                                                 {
01670                                                 default:
01671                                                 case 0:
01672                                                         deathAnim = BOTH_DEATH11;//chest left: snap, fall forward
01673                                                         break;
01674                                                 case 1:
01675                                                         deathAnim = BOTH_DEATH7;//chest left: back
01676                                                         break;
01677                                                 case 2:
01678                                                         deathAnim = BOTH_DEATH12;//chest left: spin
01679                                                         break;
01680                                                 }
01681                                         }
01682                                 }
01683                                 break;
01684                         case HL_CHEST:
01685                         case HL_WAIST:
01686                                 if ( damage <= max_health*0.25 || !VectorLengthSquared( objVelocity ) )
01687                                 {
01688                                         if ( !Q_irand( 0, 1 ) )
01689                                         {
01690                                                 deathAnim = BOTH_DEATH18;//gut: fall right
01691                                         }
01692                                         else
01693                                         {
01694                                                 deathAnim = BOTH_DEATH19;//gut: fall left
01695                                         }
01696                                 }
01697                                 else if ( damage <= max_health*0.5 )
01698                                 {
01699                                         deathAnim = BOTH_DEATH2;//chest: backward short
01700                                 }
01701                                 else if ( damage <= max_health*0.75 )
01702                                 {
01703                                         if ( !Q_irand( 0, 1 ) )
01704                                         {
01705                                                 deathAnim = BOTH_DEATH1;//chest: backward med
01706                                         }
01707                                         else
01708                                         {
01709                                                 deathAnim = BOTH_DEATH16;//same as 1
01710                                         }
01711                                 }
01712                                 else
01713                                 {
01714                                         deathAnim = BOTH_DEATH10;//chest: back flip
01715                                 }
01716                                 break;
01717                         case HL_HEAD:
01718                                 if ( damage <= max_health*0.5 )
01719                                 {
01720                                         deathAnim = BOTH_DEATH17;//head/back: croak
01721                                 }
01722                                 else
01723                                 {
01724                                         deathAnim = BOTH_DEATH13;//head: stumble, fall back
01725                                 }
01726                                 break;
01727                         default:
01728                                 break;
01729                         }
01730                 }
01731         }
01732 
01733         // Validate.....
01734         if ( deathAnim == -1 || !BG_HasAnimation( self->localAnimIndex, deathAnim ))
01735         {
01736                 // I guess we'll take what we can get.....
01737                 deathAnim = BG_PickAnim( self->localAnimIndex, BOTH_DEATH1, BOTH_DEATH25 );
01738         }
01739 
01740         return deathAnim;
01741 }
01742 
01743 gentity_t *G_GetJediMaster(void)
01744 {
01745         int i = 0;
01746         gentity_t *ent;
01747 
01748         while (i < MAX_CLIENTS)
01749         {
01750                 ent = &g_entities[i];
01751 
01752                 if (ent && ent->inuse && ent->client && ent->client->ps.isJediMaster)
01753                 {
01754                         return ent;
01755                 }
01756 
01757                 i++;
01758         }
01759 
01760         return NULL;
01761 }
01762 
01763 /*
01764 -------------------------
01765 G_AlertTeam
01766 -------------------------
01767 */
01768 
01769 void G_AlertTeam( gentity_t *victim, gentity_t *attacker, float radius, float soundDist )
01770 {
01771         int                     radiusEnts[ 128 ];
01772         gentity_t       *check;
01773         vec3_t          mins, maxs;
01774         int                     numEnts;
01775         int                     i;
01776         float           distSq, sndDistSq = (soundDist*soundDist);
01777 
01778         if ( attacker == NULL || attacker->client == NULL )
01779                 return;
01780 
01781         //Setup the bbox to search in
01782         for ( i = 0; i < 3; i++ )
01783         {
01784                 mins[i] = victim->r.currentOrigin[i] - radius;
01785                 maxs[i] = victim->r.currentOrigin[i] + radius;
01786         }
01787 
01788         //Get the number of entities in a given space
01789         numEnts = trap_EntitiesInBox( mins, maxs, radiusEnts, 128 );
01790 
01791         //Cull this list
01792         for ( i = 0; i < numEnts; i++ )
01793         {
01794                 check = &g_entities[radiusEnts[i]];
01795 
01796                 //Validate clients
01797                 if ( check->client == NULL )
01798                         continue;
01799 
01800                 //only want NPCs
01801                 if ( check->NPC == NULL )
01802                         continue;
01803 
01804                 //Don't bother if they're ignoring enemies
01805 //              if ( check->svFlags & SVF_IGNORE_ENEMIES )
01806 //                      continue;
01807 
01808                 //This NPC specifically flagged to ignore alerts
01809                 if ( check->NPC->scriptFlags & SCF_IGNORE_ALERTS )
01810                         continue;
01811 
01812                 //This NPC specifically flagged to ignore alerts
01813                 if ( !(check->NPC->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
01814                         continue;
01815 
01816                 //this ent does not participate in group AI
01817                 if ( (check->NPC->scriptFlags&SCF_NO_GROUPS) )
01818                         continue;
01819 
01820                 //Skip the requested avoid check if present
01821                 if ( check == victim )
01822                         continue;
01823 
01824                 //Skip the attacker
01825                 if ( check == attacker )
01826                         continue;
01827 
01828                 //Must be on the same team
01829                 if ( check->client->playerTeam != victim->client->playerTeam )
01830                         continue;
01831 
01832                 //Must be alive
01833                 if ( check->health <= 0 )
01834                         continue;
01835 
01836                 if ( check->enemy == NULL )
01837                 {//only do this if they're not already mad at someone
01838                         distSq = DistanceSquared( check->r.currentOrigin, victim->r.currentOrigin );
01839                         if ( distSq > 16384 /*128 squared*/ && !trap_InPVS( victim->r.currentOrigin, check->r.currentOrigin ) )
01840                         {//not even potentially visible/hearable
01841                                 continue;
01842                         }
01843                         //NOTE: this allows sound alerts to still go through doors/PVS if the teammate is within 128 of the victim...
01844                         if ( soundDist <= 0 || distSq > sndDistSq )
01845                         {//out of sound range
01846                                 if ( !InFOV( victim, check, check->NPC->stats.hfov, check->NPC->stats.vfov ) 
01847                                         ||  !NPC_ClearLOS2( check, victim->r.currentOrigin ) )
01848                                 {//out of FOV or no LOS
01849                                         continue;
01850                                 }
01851                         }
01852 
01853                         //FIXME: This can have a nasty cascading effect if setup wrong...
01854                         G_SetEnemy( check, attacker );
01855                 }
01856         }
01857 }
01858 
01859 /*
01860 -------------------------
01861 G_DeathAlert
01862 -------------------------
01863 */
01864 
01865 #define DEATH_ALERT_RADIUS                      512
01866 #define DEATH_ALERT_SOUND_RADIUS        512
01867 
01868 void G_DeathAlert( gentity_t *victim, gentity_t *attacker )
01869 {//FIXME: with all the other alert stuff, do we really need this?
01870         G_AlertTeam( victim, attacker, DEATH_ALERT_RADIUS, DEATH_ALERT_SOUND_RADIUS );
01871 }
01872 
01873 /*
01874 ----------------------------------------
01875 DeathFX
01876 
01877 Applies appropriate special effects that occur while the entity is dying
01878 Not to be confused with NPC_RemoveBodyEffects (NPC.cpp), which only applies effect when removing the body
01879 ----------------------------------------
01880 */
01881 
01882 void DeathFX( gentity_t *ent )
01883 {
01884         vec3_t          effectPos, right;
01885         vec3_t          defaultDir;
01886 
01887         if ( !ent || !ent->client )
01888                 return;
01889 
01890         VectorSet(defaultDir, 0, 0, 1);
01891 
01892         // team no longer indicates species/race.  NPC_class should be used to identify certain npc types
01893         switch(ent->client->NPC_class)
01894         {
01895         case CLASS_MOUSE:
01896                 VectorCopy( ent->r.currentOrigin, effectPos );
01897                 effectPos[2] -= 20;
01898                 G_PlayEffectID( G_EffectIndex("env/small_explode"), effectPos, defaultDir );
01899                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mouse/misc/death1") );
01900                 break;
01901 
01902         case CLASS_PROBE:
01903                 VectorCopy( ent->r.currentOrigin, effectPos );
01904                 effectPos[2] += 50;
01905                 G_PlayEffectID( G_EffectIndex("explosions/probeexplosion1"), effectPos, defaultDir );
01906                 break;
01907                 
01908         case CLASS_ATST: 
01909                 AngleVectors( ent->r.currentAngles, NULL, right, NULL );
01910                 VectorMA( ent->r.currentOrigin, 20, right, effectPos );
01911                 effectPos[2] += 180;
01912                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01913                 VectorMA( effectPos, -40, right, effectPos );
01914                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01915                 break;
01916 
01917         case CLASS_SEEKER:
01918         case CLASS_REMOTE:
01919                 G_PlayEffectID( G_EffectIndex("env/small_explode"), ent->r.currentOrigin, defaultDir );
01920                 break;
01921 
01922         case CLASS_GONK:
01923                 VectorCopy( ent->r.currentOrigin, effectPos );
01924                 effectPos[2] -= 5;
01925 //              statusTextIndex = Q_irand( IGT_RESISTANCEISFUTILE, IGT_NAMEIS8OF12 );
01926                 G_Sound( ent, CHAN_AUTO, G_SoundIndex(va("sound/chars/gonk/misc/death%d.wav",Q_irand( 1, 3 ))) );
01927                 G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
01928                 break;
01929 
01930         // should list all remaining droids here, hope I didn't miss any
01931         case CLASS_R2D2:
01932                 VectorCopy( ent->r.currentOrigin, effectPos );
01933                 effectPos[2] -= 10;
01934                 G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
01935                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_explo") );
01936                 break;
01937 
01938         case CLASS_PROTOCOL: //c3p0
01939         case CLASS_R5D2:
01940                 VectorCopy( ent->r.currentOrigin, effectPos );
01941                 effectPos[2] -= 10;
01942                 G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
01943                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_explo") );
01944                 break;
01945 
01946         case CLASS_MARK2:
01947                 VectorCopy( ent->r.currentOrigin, effectPos );
01948                 effectPos[2] -= 15;
01949                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01950                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_explo") );
01951                 break;
01952 
01953         case CLASS_INTERROGATOR:
01954                 VectorCopy( ent->r.currentOrigin, effectPos );
01955                 effectPos[2] -= 15;
01956                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01957                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/interrogator/misc/int_droid_explo") );
01958                 break;
01959 
01960         case CLASS_MARK1:
01961                 AngleVectors( ent->r.currentAngles, NULL, right, NULL );
01962                 VectorMA( ent->r.currentOrigin, 10, right, effectPos );
01963                 effectPos[2] -= 15;
01964                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01965                 VectorMA( effectPos, -20, right, effectPos );
01966                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01967                 VectorMA( effectPos, -20, right, effectPos );
01968                 G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
01969                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_explo") );
01970                 break;
01971 
01972         case CLASS_SENTRY:
01973                 G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/sentry/misc/sentry_explo") );
01974                 VectorCopy( ent->r.currentOrigin, effectPos );
01975                 G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
01976                 break;
01977 
01978         default:
01979                 break;
01980 
01981         }
01982 
01983 }
01984 
01985 void G_CheckVictoryScript(gentity_t *self)
01986 {
01987         if ( !G_ActivateBehavior( self, BSET_VICTORY ) )
01988         {
01989                 if ( self->NPC && self->s.weapon == WP_SABER )
01990                 {//Jedi taunt from within their AI
01991                         self->NPC->blockedSpeechDebounceTime = 0;//get them ready to taunt
01992                         return;
01993                 }
01994                 if ( self->client && self->client->NPC_class == CLASS_GALAKMECH )
01995                 {
01996                         self->wait = 1;
01997                         TIMER_Set( self, "gloatTime", Q_irand( 5000, 8000 ) );
01998                         self->NPC->blockedSpeechDebounceTime = 0;//get him ready to taunt
01999                         return;
02000                 }
02001                 //FIXME: any way to not say this *right away*?  Wait for victim's death anim/scream to finish?
02002                 if ( self->NPC && self->NPC->group && self->NPC->group->commander && self->NPC->group->commander->NPC && self->NPC->group->commander->NPC->rank > self->NPC->rank && !Q_irand( 0, 2 ) )
02003                 {//sometimes have the group commander speak instead
02004                         self->NPC->group->commander->NPC->greetingDebounceTime = level.time + Q_irand( 2000, 5000 );
02005                         //G_AddVoiceEvent( self->NPC->group->commander, Q_irand(EV_VICTORY1, EV_VICTORY3), 2000 );
02006                 }
02007                 else if ( self->NPC )
02008                 {
02009                         self->NPC->greetingDebounceTime = level.time + Q_irand( 2000, 5000 );
02010                         //G_AddVoiceEvent( self, Q_irand(EV_VICTORY1, EV_VICTORY3), 2000 );
02011                 }
02012         }
02013 }
02014 
02015 void G_AddPowerDuelScore(int team, int score)
02016 {
02017         int i = 0;
02018         gentity_t *check;
02019 
02020         while (i < MAX_CLIENTS)
02021         {
02022                 check = &g_entities[i];
02023                 if (check->inuse && check->client &&
02024                         check->client->pers.connected == CON_CONNECTED && !check->client->iAmALoser &&
02025                         check->client->ps.stats[STAT_HEALTH] > 0 &&
02026                         check->client->sess.sessionTeam != TEAM_SPECTATOR &&
02027                         check->client->sess.duelTeam == team)
02028                 { //found a living client on the specified team
02029                         check->client->sess.wins += score;
02030                         ClientUserinfoChanged(check->s.number);
02031                 }
02032                 i++;
02033         }
02034 }
02035 
02036 void G_AddPowerDuelLoserScore(int team, int score)
02037 {
02038         int i = 0;
02039         gentity_t *check;
02040 
02041         while (i < MAX_CLIENTS)
02042         {
02043                 check = &g_entities[i];
02044                 if (check->inuse && check->client &&
02045                         check->client->pers.connected == CON_CONNECTED &&
02046                         (check->client->iAmALoser || (check->client->ps.stats[STAT_HEALTH] <= 0 && check->client->sess.sessionTeam != TEAM_SPECTATOR)) &&
02047                         check->client->sess.duelTeam == team)
02048                 { //found a living client on the specified team
02049                         check->client->sess.losses += score;
02050                         ClientUserinfoChanged(check->s.number);
02051                 }
02052                 i++;
02053         }
02054 }
02055 
02056 /*
02057 ==================
02058 player_die
02059 ==================
02060 */
02061 extern stringID_table_t animTable[MAX_ANIMATIONS+1];
02062 
02063 extern void AI_DeleteSelfFromGroup( gentity_t *self );
02064 extern void AI_GroupMemberKilled( gentity_t *self );
02065 extern void Boba_FlyStop( gentity_t *self );
02066 extern qboolean Jedi_WaitingAmbush( gentity_t *self );
02067 void CheckExitRules( void );
02068 extern void Rancor_DropVictim( gentity_t *self );
02069 
02070 extern qboolean g_dontFrickinCheck;
02071 extern qboolean g_endPDuel;
02072 extern qboolean g_noPDuelCheck;
02073 void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
02074         gentity_t       *ent;
02075         int                     anim;
02076         int                     contents;
02077         int                     killer;
02078         int                     i;
02079         char            *killerName, *obit;
02080         qboolean        wasJediMaster = qfalse;
02081         int                     sPMType = 0;
02082 
02083         if ( self->client->ps.pm_type == PM_DEAD ) {
02084                 return;
02085         }
02086 
02087         if ( level.intermissiontime ) {
02088                 return;
02089         }
02090 
02091         //check player stuff
02092         g_dontFrickinCheck = qfalse;
02093 
02094         if (g_gametype.integer == GT_POWERDUEL)
02095         { //don't want to wait til later in the frame if this is the case
02096                 CheckExitRules();
02097 
02098                 if ( level.intermissiontime )
02099                 {
02100                         return;
02101                 }
02102         }
02103 
02104         if (self->s.eType == ET_NPC &&
02105                 self->s.NPC_class == CLASS_VEHICLE &&
02106                 self->m_pVehicle &&
02107                 !self->m_pVehicle->m_pVehicleInfo->explosionDelay &&
02108                 (self->m_pVehicle->m_pPilot || self->m_pVehicle->m_iNumPassengers > 0 || self->m_pVehicle->m_pDroidUnit))
02109         { //kill everyone on board in the name of the attacker... if the vehicle has no death delay
02110                 gentity_t *murderer = NULL;
02111                 gentity_t *killEnt;
02112                 int i = 0;
02113 
02114                 if (self->client->ps.otherKillerTime >= level.time)
02115                 { //use the last attacker
02116                         murderer = &g_entities[self->client->ps.otherKiller];
02117                         if (!murderer->inuse || !murderer->client)
02118                         {
02119                                 murderer = NULL;
02120                         }
02121                         else
02122                         {
02123                                 if (murderer->s.number >= MAX_CLIENTS &&
02124                                         murderer->s.eType == ET_NPC &&
02125                                         murderer->s.NPC_class == CLASS_VEHICLE &&
02126                                         murderer->m_pVehicle &&
02127                                         murderer->m_pVehicle->m_pPilot)
02128                                 {
02129                                         gentity_t *murderPilot = &g_entities[murderer->m_pVehicle->m_pPilot->s.number];
02130                                         if (murderPilot->inuse && murderPilot->client)
02131                                         { //give the pilot of the offending vehicle credit for the kill
02132                                                 murderer = murderPilot;
02133                                         }
02134                                 }
02135                         }
02136                 }
02137                 else if (attacker && attacker->inuse && attacker->client)
02138                 {
02139                         if (attacker->s.number >= MAX_CLIENTS &&
02140                                 attacker->s.eType == ET_NPC &&
02141                                 attacker->s.NPC_class == CLASS_VEHICLE &&
02142                                 attacker->m_pVehicle &&
02143                                 attacker->m_pVehicle->m_pPilot)
02144                         { //set vehicles pilot's killer as murderer
02145                                 murderer = &g_entities[attacker->m_pVehicle->m_pPilot->s.number];
02146                                 if (murderer->inuse && murderer->client &&murderer->client->ps.otherKillerTime >= level.time)
02147                                 {
02148                                         murderer = &g_entities[murderer->client->ps.otherKiller];
02149                                         if (!murderer->inuse || !murderer->client)
02150                                         {
02151                                                 murderer = NULL;
02152                                         }
02153                                 }
02154                                 else
02155                                 {
02156                                         murderer = NULL;
02157                                 }
02158                         }
02159                         else
02160                         {
02161                                 murderer = &g_entities[attacker->s.number];
02162                         }
02163                 }
02164                 else if (self->m_pVehicle->m_pPilot)
02165                 {
02166                         murderer = (gentity_t *)self->m_pVehicle->m_pPilot;
02167                         if (!murderer->inuse || !murderer->client)
02168                         {
02169                                 murderer = NULL;
02170                         }
02171                 }
02172 
02173                 //no valid murderer.. just use self I guess
02174                 if (!murderer)
02175                 {
02176                         murderer = self;
02177                 }
02178 
02179                 if ( self->m_pVehicle->m_pVehicleInfo->hideRider )
02180                 {//pilot is *inside* me, so kill him, too
02181                         killEnt = (gentity_t *)self->m_pVehicle->m_pPilot;
02182                         if (killEnt && killEnt->inuse && killEnt->client)
02183                         {
02184                                 G_Damage(killEnt, murderer, murderer, NULL, killEnt->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_BLASTER);
02185                         }
02186                         if ( self->m_pVehicle->m_pVehicleInfo )
02187                         {//FIXME: this wile got stuck in an endless loop, that's BAD!!  This method SUCKS (not initting "i", not incrementing it or using it directly, all sorts of badness), so I'm rewriting it
02188                                 //while (i < self->m_pVehicle->m_iNumPassengers)
02189                                 int numPass = self->m_pVehicle->m_iNumPassengers;
02190                                 for ( i = 0; i < numPass && self->m_pVehicle->m_iNumPassengers; i++ )
02191                                 {//go through and eject the last passenger
02192                                         killEnt = (gentity_t *)self->m_pVehicle->m_ppPassengers[self->m_pVehicle->m_iNumPassengers-1];
02193                                         if ( killEnt )
02194                                         {
02195                                                 self->m_pVehicle->m_pVehicleInfo->Eject(self->m_pVehicle, (bgEntity_t *)killEnt, qtrue);
02196                                                 if ( killEnt->inuse && killEnt->client )
02197                                                 {
02198                                                         G_Damage(killEnt, murderer, murderer, NULL, killEnt->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_BLASTER);
02199                                                 }
02200                                         }
02201                                 }
02202                         }
02203                 }
02204                 killEnt = (gentity_t *)self->m_pVehicle->m_pDroidUnit;
02205                 if (killEnt && killEnt->inuse && killEnt->client)
02206                 {
02207                         killEnt->flags &= ~FL_UNDYING;
02208                         G_Damage(killEnt, murderer, murderer, NULL, killEnt->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_BLASTER);
02209                 }
02210         }
02211 
02212         self->client->ps.emplacedIndex = 0;
02213 
02214         G_BreakArm(self, 0); //unbreak anything we have broken
02215         self->client->ps.saberEntityNum = self->client->saberStoredIndex; //in case we died while our saber was knocked away.
02216 
02217         self->client->bodyGrabIndex = ENTITYNUM_NONE;
02218         self->client->bodyGrabTime = 0;
02219 
02220         if (self->client->holdingObjectiveItem > 0)
02221         { //carrying a siege objective item - make sure it updates and removes itself from us now in case this is an instant death-respawn situation
02222                 gentity_t *objectiveItem = &g_entities[self->client->holdingObjectiveItem];
02223 
02224                 if (objectiveItem->inuse && objectiveItem->think)
02225                 {
02226             objectiveItem->think(objectiveItem);
02227                 }
02228         }
02229 
02230         if ( (self->client->inSpaceIndex && self->client->inSpaceIndex != ENTITYNUM_NONE) ||
02231                  (self->client->ps.eFlags2 & EF2_SHIP_DEATH) )
02232         {
02233                 self->client->noCorpse = qtrue;
02234         }
02235 
02236         if ( self->client->NPC_class != CLASS_VEHICLE
02237                 && self->client->ps.m_iVehicleNum )
02238         { //I'm riding a vehicle
02239                 //tell it I'm getting off
02240                 gentity_t *veh = &g_entities[self->client->ps.m_iVehicleNum];
02241 
02242                 if (veh->inuse && veh->client && veh->m_pVehicle)
02243                 {
02244                         veh->m_pVehicle->m_pVehicleInfo->Eject(veh->m_pVehicle, (bgEntity_t *)self, qtrue);
02245 
02246                         if (veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
02247                         { //go into "die in ship" mode with flag
02248                                 self->client->ps.eFlags2 |= EF2_SHIP_DEATH;
02249 
02250                                 //put me over where my vehicle exploded
02251                                 G_SetOrigin(self, veh->client->ps.origin);
02252                                 VectorCopy(veh->client->ps.origin, self->client->ps.origin);
02253                         }
02254                 }
02255                 //droids throw heads if they haven't yet
02256                 switch(self->client->NPC_class)
02257                 {
02258                 case CLASS_R2D2:
02259                         if ( !trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" ) )
02260                         {
02261                                 vec3_t  up;
02262                                 AngleVectors( self->r.currentAngles, NULL, NULL, up );
02263                                 G_PlayEffectID( G_EffectIndex("chunks/r2d2head_veh"), self->r.currentOrigin, up );
02264                         }
02265                         break;
02266 
02267                 case CLASS_R5D2:
02268                         if ( !trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" ) )
02269                         {
02270                                 vec3_t  up;
02271                                 AngleVectors( self->r.currentAngles, NULL, NULL, up );
02272                                 G_PlayEffectID( G_EffectIndex("chunks/r5d2head_veh"), self->r.currentOrigin, up );
02273                         }
02274                         break;
02275                 }
02276         }
02277 
02278         if ( self->NPC )
02279         {
02280                 if ( self->client && Jedi_WaitingAmbush( self ) )
02281                 {//ambushing trooper
02282                         self->client->noclip = qfalse;
02283                 }
02284                 NPC_FreeCombatPoint( self->NPC->combatPoint, qfalse );
02285                 if ( self->NPC->group )
02286                 {
02287                         //lastInGroup = (self->NPC->group->numGroup < 2);
02288                         AI_GroupMemberKilled( self );
02289                         AI_DeleteSelfFromGroup( self );
02290                 }
02291 
02292                 if ( self->NPC->tempGoal )
02293                 {
02294                         G_FreeEntity( self->NPC->tempGoal );
02295                         self->NPC->tempGoal = NULL;
02296                 }
02297                 /*
02298                 if ( self->s.eFlags & EF_LOCKED_TO_WEAPON )
02299                 {
02300                         // dumb, just get the NPC out of the chair
02301 extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd );
02302 
02303                         usercmd_t cmd, *ad_cmd;
02304 
02305                         memset( &cmd, 0, sizeof( usercmd_t ));
02306 
02307                         //gentity_t *old = self->owner;
02308 
02309                         if ( self->owner )
02310                         {
02311                                 self->owner->s.frame = self->owner->startFrame = self->owner->endFrame = 0;
02312                                 self->owner->svFlags &= ~SVF_ANIMATING;
02313                         }
02314 
02315                         cmd.buttons |= BUTTON_USE;
02316                         ad_cmd = &cmd;
02317                         RunEmplacedWeapon( self, &ad_cmd );
02318                         //self->owner = old;
02319                 }
02320                 */
02321                 //if ( self->client->NPC_class == CLASS_BOBAFETT && self->client->moveType == MT_FLYSWIM )
02322                 if (0)
02323                 {
02324                         Boba_FlyStop( self );
02325                 }
02326                 if ( self->s.NPC_class == CLASS_RANCOR )
02327                 {
02328                         Rancor_DropVictim( self );
02329                 }
02330         }
02331         if ( attacker && attacker->NPC && attacker->NPC->group && attacker->NPC->group->enemy == self )
02332         {
02333                 attacker->NPC->group->enemy = NULL;
02334         }
02335 
02336         //Cheap method until/if I decide to put fancier stuff in (e.g. sabers falling out of hand and slowly
02337         //holstering on death like sp)
02338         if (self->client->ps.weapon == WP_SABER &&
02339                 !self->client->ps.saberHolstered &&
02340                 self->client->ps.saberEntityNum)
02341         {
02342                 if (!self->client->ps.saberInFlight &&
02343                         self->client->saber[0].soundOff)
02344                 {
02345                         G_Sound(self, CHAN_AUTO, self->client->saber[0].soundOff);
02346                 }
02347                 if (self->client->saber[1].soundOff &&
02348                         self->client->saber[1].model[0])
02349                 {
02350                         G_Sound(self, CHAN_AUTO, self->client->saber[1].soundOff);
02351                 }
02352         }
02353 
02354         //Use any target we had
02355         G_UseTargets( self, self );
02356 
02357         if (g_slowmoDuelEnd.integer && (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && attacker && attacker->inuse && attacker->client)
02358         {
02359                 if (!gDoSlowMoDuel)
02360                 {
02361                         gDoSlowMoDuel = qtrue;
02362                         gSlowMoDuelTime = level.time;
02363                 }
02364         }
02365         /*
02366         else if (self->NPC && attacker && attacker->client && attacker->s.number < MAX_CLIENTS && !gDoSlowMoDuel)
02367         {
02368                 gDoSlowMoDuel = qtrue;
02369                 gSlowMoDuelTime = level.time;
02370         }
02371         */
02372 
02373         //Make sure the jetpack is turned off.
02374         Jetpack_Off(self);
02375 
02376         self->client->ps.heldByClient = 0;
02377         self->client->beingThrown = 0;
02378         self->client->doingThrow = 0;
02379         BG_ClearRocketLock( &self->client->ps );
02380         self->client->isHacking = 0;
02381         self->client->ps.hackingTime = 0;
02382 
02383         if (inflictor && inflictor->activator && !inflictor->client && !attacker->client &&
02384                 inflictor->activator->client && inflictor->activator->inuse &&
02385                 inflictor->s.weapon == WP_TURRET)
02386         {
02387                 attacker = inflictor->activator;
02388         }
02389 
02390         if (self->client && self->client->ps.isJediMaster)
02391         {
02392                 wasJediMaster = qtrue;
02393         }
02394 
02395         //if he was charging or anything else, kill the sound
02396         G_MuteSound(self->s.number, CHAN_WEAPON);
02397 
02398         BlowDetpacks(self); //blow detpacks if they're planted
02399 
02400         self->client->ps.fd.forceDeactivateAll = 1;
02401 
02402         if ((self == attacker || !attacker->client) &&
02403                 (meansOfDeath == MOD_CRUSH || meansOfDeath == MOD_FALLING || meansOfDeath == MOD_TRIGGER_HURT || meansOfDeath == MOD_UNKNOWN) &&
02404                 self->client->ps.otherKillerTime > level.time)
02405         {
02406                 attacker = &g_entities[self->client->ps.otherKiller];
02407         }
02408 
02409         // check for an almost capture
02410         CheckAlmostCapture( self, attacker );
02411 
02412         self->client->ps.pm_type = PM_DEAD;
02413         self->client->ps.pm_flags &= ~PMF_STUCK_TO_WALL;
02414 
02415         if ( attacker ) {
02416                 killer = attacker->s.number;
02417                 if ( attacker->client ) {
02418                         killerName = attacker->client->pers.netname;
02419                 } else {
02420                         killerName = "<non-client>";
02421                 }
02422         } else {
02423                 killer = ENTITYNUM_WORLD;
02424                 killerName = "<world>";
02425         }
02426 
02427         if ( killer < 0 || killer >= MAX_CLIENTS ) {
02428                 killer = ENTITYNUM_WORLD;
02429                 killerName = "<world>";
02430         }
02431 
02432         if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) {
02433                 obit = "<bad obituary>";
02434         } else {
02435                 obit = modNames[ meansOfDeath ];
02436         }
02437 
02438         G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", 
02439                 killer, self->s.number, meansOfDeath, killerName, 
02440                 self->client->pers.netname, obit );
02441 
02442         if ( g_austrian.integer 
02443                 && (g_gametype.integer == GT_DUEL) 
02444                 && level.numPlayingClients >= 2 )
02445         {
02446                 int spawnTime = (level.clients[level.sortedClients[0]].respawnTime > level.clients[level.sortedClients[1]].respawnTime) ? level.clients[level.sortedClients[0]].respawnTime : level.clients[level.sortedClients[1]].respawnTime;
02447                 G_LogPrintf("Duel Kill Details:\n");
02448                 G_LogPrintf("Kill Time: %d\n", level.time-spawnTime );
02449                 G_LogPrintf("victim: %s, hits on enemy %d\n", self->client->pers.netname, self->client->ps.persistant[PERS_HITS] );
02450                 if ( attacker && attacker->client )
02451                 {
02452                         G_LogPrintf("killer: %s, hits on enemy %d, health: %d\n", attacker->client->pers.netname, attacker->client->ps.persistant[PERS_HITS], attacker->health );
02453                         //also - if MOD_SABER, list the animation and saber style
02454                         if ( meansOfDeath == MOD_SABER )
02455                         {
02456                                 G_LogPrintf("killer saber style: %d, killer saber anim %s\n", attacker->client->ps.fd.saberAnimLevel, animTable[(attacker->client->ps.torsoAnim)].name );
02457                         }
02458                 }
02459         }
02460 
02461         G_LogWeaponKill(killer, meansOfDeath);
02462         G_LogWeaponDeath(self->s.number, self->s.weapon);
02463         if (attacker && attacker->client && attacker->inuse)
02464         {
02465                 G_LogWeaponFrag(killer, self->s.number);
02466         }
02467 
02468         // broadcast the death event to everyone
02469         if (self->s.eType != ET_NPC && !g_noPDuelCheck)
02470         {
02471                 ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
02472                 ent->s.eventParm = meansOfDeath;
02473                 ent->s.otherEntityNum = self->s.number;
02474                 ent->s.otherEntityNum2 = killer;
02475                 ent->r.svFlags = SVF_BROADCAST; // send to everyone
02476                 ent->s.isJediMaster = wasJediMaster;
02477         }
02478 
02479         self->enemy = attacker;
02480 
02481         self->client->ps.persistant[PERS_KILLED]++;
02482 
02483         if (self == attacker)
02484         {
02485                 self->client->ps.fd.suicides++;
02486         }
02487 
02488         if (attacker && attacker->client) {
02489                 attacker->client->lastkilled_client = self->s.number;
02490 
02491                 G_CheckVictoryScript(attacker);
02492 
02493                 if ( attacker == self || OnSameTeam (self, attacker ) ) {
02494                         if (g_gametype.integer == GT_DUEL)
02495                         { //in duel, if you kill yourself, the person you are dueling against gets a kill for it
02496                                 int otherClNum = -1;
02497                                 if (level.sortedClients[0] == self->s.number)
02498                                 {
02499                                         otherClNum = level.sortedClients[1];
02500                                 }
02501                                 else if (level.sortedClients[1] == self->s.number)
02502                                 {
02503                                         otherClNum = level.sortedClients[0];
02504                                 }
02505 
02506                                 if (otherClNum >= 0 && otherClNum < MAX_CLIENTS &&
02507                                         g_entities[otherClNum].inuse && g_entities[otherClNum].client &&
02508                                         otherClNum != attacker->s.number)
02509                                 {
02510                                         AddScore( &g_entities[otherClNum], self->r.currentOrigin, 1 );
02511                                 }
02512                                 else
02513                                 {
02514                                         AddScore( attacker, self->r.currentOrigin, -1 );
02515                                 }
02516                         }
02517                         else
02518                         {
02519                                 AddScore( attacker, self->r.currentOrigin, -1 );
02520                         }
02521                         if (g_gametype.integer == GT_JEDIMASTER)
02522                         {
02523                                 if (self->client && self->client->ps.isJediMaster)
02524                                 { //killed ourself so return the saber to the original position
02525                                   //(to avoid people jumping off ledges and making the saber
02526                                   //unreachable for 60 seconds)
02527                                         ThrowSaberToAttacker(self, NULL);
02528                                         self->client->ps.isJediMaster = qfalse;
02529                                 }
02530                         }
02531                 } else {
02532                         if (g_gametype.integer == GT_JEDIMASTER)
02533                         {
02534                                 if ((attacker->client && attacker->client->ps.isJediMaster) ||
02535                                         (self->client && self->client->ps.isJediMaster))
02536                                 {
02537                                         AddScore( attacker, self->r.currentOrigin, 1 );
02538                                         
02539                                         if (self->client && self->client->ps.isJediMaster)
02540                                         {
02541                                                 ThrowSaberToAttacker(self, attacker);
02542                                                 self->client->ps.isJediMaster = qfalse;
02543                                         }
02544                                 }
02545                                 else
02546                                 {
02547                                         gentity_t *jmEnt = G_GetJediMaster();
02548 
02549                                         if (jmEnt && jmEnt->client)
02550                                         {
02551                                                 AddScore( jmEnt, self->r.currentOrigin, 1 );
02552                                         }
02553                                 }
02554                         }
02555                         else
02556                         {
02557                                 AddScore( attacker, self->r.currentOrigin, 1 );
02558                         }
02559 
02560                         if( meansOfDeath == MOD_STUN_BATON ) {
02561                                 
02562                                 // play humiliation on player
02563                                 attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
02564 
02565                                 attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
02566 
02567                                 // also play humiliation on target
02568                                 self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD;
02569                         }
02570 
02571                         // check for two kills in a short amount of time
02572                         // if this is close enough to the last kill, give a reward sound
02573                         if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) {
02574                                 // play excellent on player
02575                                 attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
02576 
02577                                 attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
02578                         }
02579                         attacker->client->lastKillTime = level.time;
02580 
02581                 }
02582         } else {
02583                 if (self->client && self->client->ps.isJediMaster)
02584                 { //killed ourself so return the saber to the original position
02585                   //(to avoid people jumping off ledges and making the saber
02586                   //unreachable for 60 seconds)
02587                         ThrowSaberToAttacker(self, NULL);
02588                         self->client->ps.isJediMaster = qfalse;
02589                 }
02590 
02591                 if (g_gametype.integer == GT_DUEL)
02592                 { //in duel, if you kill yourself, the person you are dueling against gets a kill for it
02593                         int otherClNum = -1;
02594                         if (level.sortedClients[0] == self->s.number)
02595                         {
02596                                 otherClNum = level.sortedClients[1];
02597                         }
02598                         else if (level.sortedClients[1] == self->s.number)
02599                         {
02600                                 otherClNum = level.sortedClients[0];
02601                         }
02602 
02603                         if (otherClNum >= 0 && otherClNum < MAX_CLIENTS &&
02604                                 g_entities[otherClNum].inuse && g_entities[otherClNum].client &&
02605                                 otherClNum != self->s.number)
02606                         {
02607                                 AddScore( &g_entities[otherClNum], self->r.currentOrigin, 1 );
02608                         }
02609                         else
02610                         {
02611                                 AddScore( self, self->r.currentOrigin, -1 );
02612                         }
02613                 }
02614                 else
02615                 {
02616                         AddScore( self, self->r.currentOrigin, -1 );
02617                 }
02618         }
02619 
02620         // Add team bonuses
02621         Team_FragBonuses(self, inflictor, attacker);
02622 
02623         // if I committed suicide, the flag does not fall, it returns.
02624         if (meansOfDeath == MOD_SUICIDE) {
02625                 if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {              // only happens in One Flag CTF
02626                         Team_ReturnFlag( TEAM_FREE );
02627                         self->client->ps.powerups[PW_NEUTRALFLAG] = 0;
02628                 }
02629                 else if ( self->client->ps.powerups[PW_REDFLAG] ) {             // only happens in standard CTF
02630                         Team_ReturnFlag( TEAM_RED );
02631                         self->client->ps.powerups[PW_REDFLAG] = 0;
02632                 }
02633                 else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {    // only happens in standard CTF
02634                         Team_ReturnFlag( TEAM_BLUE );
02635                         self->client->ps.powerups[PW_BLUEFLAG] = 0;
02636                 }
02637         }
02638 
02639         // if client is in a nodrop area, don't drop anything (but return CTF flags!)
02640         contents = trap_PointContents( self->r.currentOrigin, -1 );
02641         if ( !( contents & CONTENTS_NODROP ) && !self->client->ps.fallingToDeath) {
02642                 if (self->s.eType != ET_NPC)
02643                 {
02644                         TossClientItems( self );
02645                 }
02646         }
02647         else {
02648                 if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {              // only happens in One Flag CTF
02649                         Team_ReturnFlag( TEAM_FREE );
02650                 }
02651                 else if ( self->client->ps.powerups[PW_REDFLAG] ) {             // only happens in standard CTF
02652                         Team_ReturnFlag( TEAM_RED );
02653                 }
02654                 else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {    // only happens in standard CTF
02655                         Team_ReturnFlag( TEAM_BLUE );
02656                 }
02657         }
02658 
02659         if ( MOD_TEAM_CHANGE == meansOfDeath )
02660         {
02661                 // Give them back a point since they didn't really die.
02662                 AddScore( self, self->r.currentOrigin, 1 );
02663         }
02664         else
02665         {
02666                 Cmd_Score_f( self );            // show scores
02667         }
02668 
02669         // send updated scores to any clients that are following this one,
02670         // or they would get stale scoreboards
02671         for ( i = 0 ; i < level.maxclients ; i++ ) {
02672                 gclient_t       *client;
02673 
02674                 client = &level.clients[i];
02675                 if ( client->pers.connected != CON_CONNECTED ) {
02676                         continue;
02677                 }
02678                 if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
02679                         continue;
02680                 }
02681                 if ( client->sess.spectatorClient == self->s.number ) {
02682                         Cmd_Score_f( g_entities + i );
02683                 }
02684         }
02685 
02686         self->takedamage = qtrue;       // can still be gibbed
02687 
02688         self->s.weapon = WP_NONE;
02689         self->s.powerups = 0;
02690         if (self->s.eType != ET_NPC)
02691         { //handled differently for NPCs
02692                 self->r.contents = CONTENTS_CORPSE;
02693         }
02694         self->client->ps.zoomMode = 0;  // Turn off zooming when we die
02695 
02696         //rww - 07/19/02 - I removed this because it isn't working and it's ugly (for people on the outside)
02697         /*
02698         self->s.angles[0] = 0;
02699         self->s.angles[2] = 0;
02700         LookAtKiller (self, inflictor, attacker);
02701 
02702         VectorCopy( self->s.angles, self->client->ps.viewangles );
02703         */
02704 
02705         self->s.loopSound = 0;
02706         self->s.loopIsSoundset = qfalse;
02707 
02708         if (self->s.eType != ET_NPC)
02709         { //handled differently for NPCs
02710                 self->r.maxs[2] = -8;
02711         }
02712 
02713         // don't allow respawn until the death anim is done
02714         // g_forcerespawn may force spawning at some later time
02715         self->client->respawnTime = level.time + 1700;
02716 
02717         // remove powerups
02718         memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );
02719 
02720         // NOTENOTE No gib deaths right now, this is star wars.
02721         /*
02722         // never gib in a nodrop
02723         if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) 
02724         {
02725                 // gib death
02726                 GibEntity( self, killer );
02727         } 
02728         else 
02729         */
02730         {
02731                 // normal death
02732                 
02733                 static int i;
02734 
02735                 anim = G_PickDeathAnim(self, self->pos1, damage, meansOfDeath, HL_NONE);
02736 
02737                 if (anim >= 1)
02738                 { //Some droids don't have death anims
02739                         // for the no-blood option, we need to prevent the health
02740                         // from going to gib level
02741                         if ( self->health <= GIB_HEALTH ) {
02742                                 self->health = GIB_HEALTH+1;
02743                         }
02744 
02745                         self->client->respawnTime = level.time + 1000;//((self->client->animations[anim].numFrames*40)/(50.0f / self->client->animations[anim].frameLerp))+300;
02746 
02747                         sPMType = self->client->ps.pm_type;
02748                         self->client->ps.pm_type = PM_NORMAL; //don't want pm type interfering with our setanim calls.
02749 
02750                         if (self->inuse)
02751                         { //not disconnecting
02752                                 G_SetAnim(self, NULL, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART, 0);
02753                         }
02754 
02755                         self->client->ps.pm_type = sPMType;
02756 
02757                         if (meansOfDeath == MOD_SABER || (meansOfDeath == MOD_MELEE && G_HeavyMelee( attacker )) )//saber or heavy melee (claws)
02758                         { //update the anim on the actual skeleton (so bolt point will reflect the correct position) and then check for dismem
02759                                 G_UpdateClientAnims(self, 1.0f);
02760                                 G_CheckForDismemberment(self, attacker, self->pos1, damage, anim, qfalse);
02761                         }
02762                 }
02763                 else if (self->NPC && self->client && self->client->NPC_class != CLASS_MARK1 &&
02764                         self->client->NPC_class != CLASS_VEHICLE)
02765                 { //in this case if we're an NPC it's my guess that we want to get removed straight away.
02766                         self->think = G_FreeEntity;
02767                         self->nextthink = level.time;
02768                 }
02769 
02770                 //self->client->ps.legsAnim = anim;
02771                 //self->client->ps.torsoAnim = anim;
02772 //              self->client->ps.pm_flags |= PMF_UPDATE_ANIM;           // Make sure the pmove sets up the GHOUL2 anims.
02773 
02774                 //rww - do this on respawn, not death
02775                 //CopyToBodyQue (self);
02776 
02777                 //G_AddEvent( self, EV_DEATH1 + i, killer );
02778                 if (wasJediMaster)
02779                 {
02780                         G_AddEvent( self, EV_DEATH1 + i, 1 );
02781                 }
02782                 else
02783                 {
02784                         G_AddEvent( self, EV_DEATH1 + i, 0 );
02785                 }
02786 
02787                 if (self != attacker)
02788                 { //don't make NPCs want to murder you on respawn for killing yourself!
02789                         G_DeathAlert( self, attacker );
02790                 }
02791 
02792                 // the body can still be gibbed
02793                 if (!self->NPC)
02794                 { //don't remove NPCs like this!
02795                         self->die = body_die;
02796                 }
02797 
02798                 //It won't gib, it will disintegrate (because this is Star Wars).
02799                 self->takedamage = qtrue;
02800 
02801                 // globally cycle through the different death animations
02802                 i = ( i + 1 ) % 3;
02803         }
02804 
02805         if ( self->NPC )
02806         {//If an NPC, make sure we start running our scripts again- this gets set to infinite while we fall to our deaths
02807                 self->NPC->nextBStateThink = level.time;
02808         }
02809 
02810         if ( G_ActivateBehavior( self, BSET_DEATH ) )
02811         {
02812                 //deathScript = qtrue;
02813         }
02814         
02815         if ( self->NPC && (self->NPC->scriptFlags&SCF_FFDEATH) )
02816         {
02817                 if ( G_ActivateBehavior( self, BSET_FFDEATH ) )  
02818                 {//FIXME: should running this preclude running the normal deathscript?
02819                         //deathScript = qtrue;
02820                 }
02821                 G_UseTargets2( self, self, self->target4 );
02822         }
02823         
02824         /*
02825         if ( !deathScript && !(self->svFlags&SVF_KILLED_SELF) )
02826         {
02827                 //Should no longer run scripts
02828                 //WARNING!!! DO NOT DO THIS WHILE RUNNING A SCRIPT, ICARUS WILL CRASH!!!
02829                 //FIXME: shouldn't ICARUS handle this internally?
02830                 ICARUS_FreeEnt(self);
02831         }
02832         */
02833         //rwwFIXMEFIXME: Do this too?
02834 
02835         // Free up any timers we may have on us.
02836         TIMER_Clear2( self );
02837 
02838         trap_LinkEntity (self);
02839 
02840         if ( self->NPC )
02841         {
02842                 self->NPC->timeOfDeath = level.time;//this will change - used for debouncing post-death events
02843         }
02844 
02845         // Start any necessary death fx for this entity
02846         DeathFX( self );
02847 
02848 
02849         if (g_gametype.integer == GT_POWERDUEL && !g_noPDuelCheck)
02850         { //powerduel checks
02851                 if (self->client->sess.duelTeam == DUELTEAM_LONE)
02852                 { //automatically means a win as there is only one
02853                         G_AddPowerDuelScore(DUELTEAM_DOUBLE, 1);
02854                         G_AddPowerDuelLoserScore(DUELTEAM_LONE, 1);
02855                         g_endPDuel = qtrue;
02856                 }
02857                 else if (self->client->sess.duelTeam == DUELTEAM_DOUBLE)
02858                 {
02859                         int i = 0;
02860                         gentity_t *check;
02861                         qboolean heLives = qfalse;
02862 
02863                         while (i < MAX_CLIENTS)
02864                         {
02865                                 check = &g_entities[i];
02866                                 if (check->inuse && check->client && check->s.number != self->s.number &&
02867                                         check->client->pers.connected == CON_CONNECTED && !check->client->iAmALoser &&
02868                                         check->client->ps.stats[STAT_HEALTH] > 0 &&
02869                                         check->client->sess.sessionTeam != TEAM_SPECTATOR &&
02870                                         check->client->sess.duelTeam == DUELTEAM_DOUBLE)
02871                                 { //still an active living paired duelist so it's not over yet.
02872                                         heLives = qtrue;
02873                                         break;
02874                                 }
02875                                 i++;
02876                         }
02877 
02878                         if (!heLives)
02879                         { //they're all dead, give the lone duelist the win.
02880                                 G_AddPowerDuelScore(DUELTEAM_LONE, 1);
02881                                 G_AddPowerDuelLoserScore(DUELTEAM_DOUBLE, 1);
02882                                 g_endPDuel = qtrue;
02883                         }
02884                 }
02885         }
02886 }
02887 
02888 
02889 /*
02890 ================
02891 CheckArmor
02892 ================
02893 */
02894 int CheckArmor (gentity_t *ent, int damage, int dflags)
02895 {
02896         gclient_t       *client;
02897         int                     save;
02898         int                     count;
02899 
02900         if (!damage)
02901                 return 0;
02902 
02903         client = ent->client;
02904 
02905         if (!client)
02906                 return 0;
02907 
02908         if (dflags & DAMAGE_NO_ARMOR)
02909                 return 0;
02910 
02911         if ( client->NPC_class == CLASS_VEHICLE
02912                 && ent->m_pVehicle
02913                 && ent->client->ps.electrifyTime > level.time )
02914         {//ion-cannon has disabled this ship's shields, take damage on hull!
02915                 return 0;
02916         }
02917         // armor
02918         count = client->ps.stats[STAT_ARMOR];
02919 
02920         if (dflags & DAMAGE_HALF_ABSORB)
02921         {       // Half the damage gets absorbed by the shields, rather than 100%
02922                 save = ceil( damage * ARMOR_PROTECTION );
02923         }
02924         else
02925         {       // All the damage gets absorbed by the shields.
02926                 save = damage;
02927         }
02928 
02929         // save is the most damage that the armor is elibigle to protect, of course, but it's limited by the total armor.
02930         if (save >= count)
02931                 save = count;
02932 
02933         if (!save)
02934                 return 0;
02935 
02936         if (dflags & DAMAGE_HALF_ARMOR_REDUCTION)               // Armor isn't whittled so easily by sniper shots.
02937         {
02938                 client->ps.stats[STAT_ARMOR] -= (int)(save*ARMOR_REDUCTION_FACTOR);
02939         }
02940         else
02941         {
02942                 client->ps.stats[STAT_ARMOR] -= save;
02943         }
02944 
02945         return save;
02946 }
02947 
02948 
02949 void G_ApplyKnockback( gentity_t *targ, vec3_t newDir, float knockback )
02950 {
02951         vec3_t  kvel;
02952         float   mass;
02953 
02954         if ( targ->physicsBounce > 0 )  //overide the mass
02955                 mass = targ->physicsBounce;
02956         else
02957                 mass = 200;
02958 
02959         if ( g_gravity.value > 0 )
02960         {
02961                 VectorScale( newDir, g_knockback.value * (float)knockback / mass * 0.8, kvel );
02962                 kvel[2] = newDir[2] * g_knockback.value * (float)knockback / mass * 1.5;
02963         }
02964         else
02965         {
02966                 VectorScale( newDir, g_knockback.value * (float)knockback / mass, kvel );
02967         }
02968 
02969         if ( targ->client )
02970         {
02971                 VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );
02972         }
02973         else if ( targ->s.pos.trType != TR_STATIONARY && targ->s.pos.trType != TR_LINEAR_STOP && targ->s.pos.trType != TR_NONLINEAR_STOP )
02974         {
02975                 VectorAdd( targ->s.pos.trDelta, kvel, targ->s.pos.trDelta );
02976                 VectorCopy( targ->r.currentOrigin, targ->s.pos.trBase );
02977                 targ->s.pos.trTime = level.time;
02978         }
02979 
02980         // set the timer so that the other client can't cancel
02981         // out the movement immediately
02982         if ( targ->client && !targ->client->ps.pm_time ) 
02983         {
02984                 int             t;
02985 
02986                 t = knockback * 2;
02987                 if ( t < 50 ) {
02988                         t = 50;
02989                 }
02990                 if ( t > 200 ) {
02991                         t = 200;
02992                 }
02993                 targ->client->ps.pm_time = t;
02994                 targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
02995         }
02996 }
02997 
02998 /*
02999 ================
03000 RaySphereIntersections
03001 ================
03002 */
03003 int RaySphereIntersections( vec3_t origin, float radius, vec3_t point, vec3_t dir, vec3_t intersections[2] ) {
03004         float b, c, d, t;
03005 
03006         //      | origin - (point + t * dir) | = radius
03007         //      a = dir[0]^2 + dir[1]^2 + dir[2]^2;
03008         //      b = 2 * (dir[0] * (point[0] - origin[0]) + dir[1] * (point[1] - origin[1]) + dir[2] * (point[2] - origin[2]));
03009         //      c = (point[0] - origin[0])^2 + (point[1] - origin[1])^2 + (point[2] - origin[2])^2 - radius^2;
03010 
03011         // normalize dir so a = 1
03012         VectorNormalize(dir);
03013         b = 2 * (dir[0] * (point[0] - origin[0]) + dir[1] * (point[1] - origin[1]) + dir[2] * (point[2] - origin[2]));
03014         c = (point[0] - origin[0]) * (point[0] - origin[0]) +
03015                 (point[1] - origin[1]) * (point[1] - origin[1]) +
03016                 (point[2] - origin[2]) * (point[2] - origin[2]) -
03017                 radius * radius;
03018 
03019         d = b * b - 4 * c;
03020         if (d > 0) {
03021                 t = (- b + sqrt(d)) / 2;
03022                 VectorMA(point, t, dir, intersections[0]);
03023                 t = (- b - sqrt(d)) / 2;
03024                 VectorMA(point, t, dir, intersections[1]);
03025                 return 2;
03026         }
03027         else if (d == 0) {
03028                 t = (- b ) / 2;
03029                 VectorMA(point, t, dir, intersections[0]);
03030                 return 1;
03031         }
03032         return 0;
03033 }
03034 
03035 /*
03036 ===================================
03037 rww - beginning of the majority of the dismemberment and location based damage code.
03038 ===================================
03039 */
03040 char *hitLocName[HL_MAX] = 
03041 {
03042         "none", //HL_NONE = 0,
03043         "right foot",   //HL_FOOT_RT,
03044         "left foot",    //HL_FOOT_LT,
03045         "right leg",    //HL_LEG_RT,
03046         "left leg",     //HL_LEG_LT,
03047         "waist",        //HL_WAIST,
03048         "back right shoulder",  //HL_BACK_RT,
03049         "back left shoulder",   //HL_BACK_LT,
03050         "back", //HL_BACK,
03051         "front right shouler",  //HL_CHEST_RT,
03052         "front left shoulder",  //HL_CHEST_LT,
03053         "chest",        //HL_CHEST,
03054         "right arm",    //HL_ARM_RT,
03055         "left arm",     //HL_ARM_LT,
03056         "right hand",   //HL_HAND_RT,
03057         "left hand",    //HL_HAND_LT,
03058         "head", //HL_HEAD
03059         "generic1",     //HL_GENERIC1,
03060         "generic2",     //HL_GENERIC2,
03061         "generic3",     //HL_GENERIC3,
03062         "generic4",     //HL_GENERIC4,
03063         "generic5",     //HL_GENERIC5,
03064         "generic6"      //HL_GENERIC6
03065 };
03066 
03067 void G_GetDismemberLoc(gentity_t *self, vec3_t boltPoint, int limbType)
03068 { //Just get the general area without using server-side ghoul2
03069         vec3_t fwd, right, up;
03070 
03071         AngleVectors(self->r.currentAngles, fwd, right, up);
03072 
03073         VectorCopy(self->r.currentOrigin, boltPoint);
03074 
03075         switch (limbType)
03076         {
03077         case G2_MODELPART_HEAD:
03078                 boltPoint[0] += up[0]*24;
03079                 boltPoint[1] += up[1]*24;
03080                 boltPoint[2] += up[2]*24;
03081                 break;
03082         case G2_MODELPART_WAIST:
03083                 boltPoint[0] += up[0]*4;
03084                 boltPoint[1] += up[1]*4;
03085                 boltPoint[2] += up[2]*4;
03086                 break;
03087         case G2_MODELPART_LARM:
03088                 boltPoint[0] += up[0]*18;
03089                 boltPoint[1] += up[1]*18;
03090                 boltPoint[2] += up[2]*18;
03091 
03092                 boltPoint[0] -= right[0]*10;
03093                 boltPoint[1] -= right[1]*10;
03094                 boltPoint[2] -= right[2]*10;
03095                 break;
03096         case G2_MODELPART_RARM:
03097                 boltPoint[0] += up[0]*18;
03098                 boltPoint[1] += up[1]*18;
03099                 boltPoint[2] += up[2]*18;
03100 
03101                 boltPoint[0] += right[0]*10;
03102                 boltPoint[1] += right[1]*10;
03103                 boltPoint[2] += right[2]*10;
03104                 break;
03105         case G2_MODELPART_RHAND:
03106                 boltPoint[0] += up[0]*8;
03107                 boltPoint[1] += up[1]*8;
03108                 boltPoint[2] += up[2]*8;
03109 
03110                 boltPoint[0] += right[0]*10;
03111                 boltPoint[1] += right[1]*10;
03112                 boltPoint[2] += right[2]*10;
03113                 break;
03114         case G2_MODELPART_LLEG:
03115                 boltPoint[0] -= up[0]*4;
03116                 boltPoint[1] -= up[1]*4;
03117                 boltPoint[2] -= up[2]*4;
03118 
03119                 boltPoint[0] -= right[0]*10;
03120                 boltPoint[1] -= right[1]*10;
03121                 boltPoint[2] -= right[2]*10;
03122                 break;
03123         case G2_MODELPART_RLEG:
03124                 boltPoint[0] -= up[0]*4;
03125                 boltPoint[1] -= up[1]*4;
03126                 boltPoint[2] -= up[2]*4;
03127 
03128                 boltPoint[0] += right[0]*10;
03129                 boltPoint[1] += right[1]*10;
03130                 boltPoint[2] += right[2]*10;
03131                 break;
03132         default:
03133                 break;
03134         }
03135 
03136         return;
03137 }
03138 
03139 void G_GetDismemberBolt(gentity_t *self, vec3_t boltPoint, int limbType)
03140 {
03141         int useBolt = self->genericValue5;
03142         vec3_t properOrigin, properAngles, addVel;
03143         //vec3_t legAxis[3];
03144         mdxaBone_t      boltMatrix;
03145         float fVSpeed = 0;
03146         char *rotateBone = NULL;
03147 
03148         switch (limbType)
03149         {
03150         case G2_MODELPART_HEAD:
03151                 rotateBone = "cranium";
03152                 break;
03153         case G2_MODELPART_WAIST:
03154                 if (self->localAnimIndex <= 1)
03155                 { //humanoid
03156                         rotateBone = "thoracic";
03157                 }
03158                 else
03159                 {
03160                         rotateBone = "pelvis";
03161                 }
03162                 break;
03163         case G2_MODELPART_LARM:
03164                 rotateBone = "lradius";
03165                 break;
03166         case G2_MODELPART_RARM:
03167                 rotateBone = "rradius";
03168                 break;
03169         case G2_MODELPART_RHAND:
03170                 rotateBone = "rhand";
03171                 break;
03172         case G2_MODELPART_LLEG:
03173                 rotateBone = "ltibia";
03174                 break;
03175         case G2_MODELPART_RLEG:
03176                 rotateBone = "rtibia";
03177                 break;
03178         default:
03179                 rotateBone = "rtibia";
03180                 break;
03181         }
03182 
03183         useBolt = trap_G2API_AddBolt(self->ghoul2, 0, rotateBone);
03184 
03185         VectorCopy(self->client->ps.origin, properOrigin);
03186         VectorCopy(self->client->ps.viewangles, properAngles);
03187 
03188         //try to predict the origin based on velocity so it's more like what the client is seeing
03189         VectorCopy(self->client->ps.velocity, addVel);
03190         VectorNormalize(addVel);
03191 
03192         if (self->client->ps.velocity[0] < 0)
03193         {
03194                 fVSpeed += (-self->client->ps.velocity[0]);
03195         }
03196         else
03197         {
03198                 fVSpeed += self->client->ps.velocity[0];
03199         }
03200         if (self->client->ps.velocity[1] < 0)
03201         {
03202                 fVSpeed += (-self->client->ps.velocity[1]);
03203         }
03204         else
03205         {
03206                 fVSpeed += self->client->ps.velocity[1];
03207         }
03208         if (self->client->ps.velocity[2] < 0)
03209         {
03210                 fVSpeed += (-self->client->ps.velocity[2]);
03211         }
03212         else
03213         {
03214                 fVSpeed += self->client->ps.velocity[2];
03215         }
03216 
03217         fVSpeed *= 0.08;
03218 
03219         properOrigin[0] += addVel[0]*fVSpeed;
03220         properOrigin[1] += addVel[1]*fVSpeed;
03221         properOrigin[2] += addVel[2]*fVSpeed;
03222 
03223         properAngles[0] = 0;
03224         properAngles[1] = self->client->ps.viewangles[YAW];
03225         properAngles[2] = 0;
03226 
03227         trap_G2API_GetBoltMatrix(self->ghoul2, 0, useBolt, &boltMatrix, properAngles, properOrigin, level.time, NULL, self->modelScale);
03228 
03229         boltPoint[0] = boltMatrix.matrix[0][3];
03230         boltPoint[1] = boltMatrix.matrix[1][3];
03231         boltPoint[2] = boltMatrix.matrix[2][3];
03232 
03233         trap_G2API_GetBoltMatrix(self->ghoul2, 1, 0, &boltMatrix, properAngles, properOrigin, level.time, NULL, self->modelScale);
03234 
03235         if (self->client && limbType == G2_MODELPART_RHAND)
03236         { //Make some saber hit sparks over the severed wrist area
03237                 vec3_t boltAngles;
03238                 gentity_t *te;
03239 
03240                 boltAngles[0] = -boltMatrix.matrix[0][1];
03241                 boltAngles[1] = -boltMatrix.matrix[1][1];
03242                 boltAngles[2] = -boltMatrix.matrix[2][1];
03243 
03244                 te = G_TempEntity( boltPoint, EV_SABER_HIT );
03245                 te->s.otherEntityNum = self->s.number;
03246                 te->s.otherEntityNum2 = ENTITYNUM_NONE;
03247                 te->s.weapon = 0;//saberNum
03248                 te->s.legsAnim = 0;//bladeNum
03249 
03250                 VectorCopy(boltPoint, te->s.origin);
03251                 VectorCopy(boltAngles, te->s.angles);
03252                 
03253                 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
03254                 { //don't let it play with no direction
03255                         te->s.angles[1] = 1;
03256                 }
03257 
03258                 te->s.eventParm = 16; //lots of sparks
03259         }
03260 }
03261 
03262 void LimbTouch( gentity_t *self, gentity_t *other, trace_t *trace )
03263 {
03264 }
03265 
03266 void LimbThink( gentity_t *ent )
03267 {
03268         float gravity = 3.0f;
03269         float mass = 0.09f;
03270         float bounce = 1.3f;
03271 
03272         switch (ent->s.modelGhoul2)
03273         {
03274         case G2_MODELPART_HEAD:
03275                 mass = 0.08f;
03276                 bounce = 1.4f;
03277                 break;
03278         case G2_MODELPART_WAIST:
03279                 mass = 0.1f;
03280                 bounce = 1.2f;
03281                 break;
03282         case G2_MODELPART_LARM:
03283         case G2_MODELPART_RARM:
03284         case G2_MODELPART_RHAND:
03285         case G2_MODELPART_LLEG:
03286         case G2_MODELPART_RLEG:
03287         default:
03288                 break;
03289         }
03290 
03291         if (ent->speed < level.time)
03292         {
03293                 ent->think = G_FreeEntity;
03294                 ent->nextthink = level.time;
03295                 return;
03296         }
03297 
03298         if (ent->genericValue5 <= level.time)
03299         { //this will be every frame by standard, but we want to compensate in case sv_fps is not 20.
03300                 G_RunExPhys(ent, gravity, mass, bounce, qtrue, NULL, 0);
03301                 ent->genericValue5 = level.time + 50;
03302         }
03303 
03304         ent->nextthink = level.time;
03305 }
03306 
03307 #include "../namespace_begin.h"
03308 extern qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize );
03309 #include "../namespace_end.h"
03310 
03311 void G_Dismember( gentity_t *ent, gentity_t *enemy, vec3_t point, int limbType, float limbRollBase, float limbPitchBase, int deathAnim, qboolean postDeath )
03312 {
03313         vec3_t  newPoint, dir, vel;
03314         gentity_t *limb;
03315         char    limbName[MAX_QPATH];
03316         char    stubName[MAX_QPATH];
03317         char    stubCapName[MAX_QPATH];
03318 
03319         if (limbType == G2_MODELPART_HEAD)
03320         {
03321                 Q_strncpyz( limbName , "head", sizeof( limbName  ) );
03322                 Q_strncpyz( stubCapName, "torso_cap_head", sizeof( stubCapName ) );
03323         }
03324         else if (limbType == G2_MODELPART_WAIST)
03325         {
03326                 Q_strncpyz( limbName, "torso", sizeof( limbName ) );
03327                 Q_strncpyz( stubCapName, "hips_cap_torso", sizeof( stubCapName ) );
03328         }
03329         else if (limbType == G2_MODELPART_LARM)
03330         {
03331                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "l_arm", limbName, sizeof(limbName) );
03332                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "torso", stubName, sizeof(stubName) );
03333                 Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_arm", stubName );
03334         }
03335         else if (limbType == G2_MODELPART_RARM)
03336         {
03337                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_arm", limbName, sizeof(limbName) );
03338                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "torso", stubName, sizeof(stubName) );
03339                 Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_arm", stubName );
03340         }
03341         else if (limbType == G2_MODELPART_RHAND)
03342         {
03343                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_hand", limbName, sizeof(limbName) );
03344                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_arm", stubName, sizeof(stubName) );
03345                 Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_hand", stubName );
03346         }
03347         else if (limbType == G2_MODELPART_LLEG)
03348         {
03349                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "l_leg", limbName, sizeof(limbName) );
03350                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "hips", stubName, sizeof(stubName) );
03351                 Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_leg", stubName );
03352         }
03353         else if (limbType == G2_MODELPART_RLEG)
03354         {
03355                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_leg", limbName, sizeof(limbName) );
03356                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "hips", stubName, sizeof(stubName) );
03357                 Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
03358         }
03359         else
03360         {//umm... just default to the right leg, I guess (same as on client)
03361                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_leg", limbName, sizeof(limbName) );
03362                 BG_GetRootSurfNameWithVariant( ent->ghoul2, "hips", stubName, sizeof(stubName) );
03363                 Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
03364         }
03365 
03366         if (ent->ghoul2 && limbName && trap_G2API_GetSurfaceRenderStatus(ent->ghoul2, 0, limbName))
03367         { //is it already off? If so there's no reason to be doing it again, so get out of here.
03368                 return;
03369         }
03370 
03371         VectorCopy( point, newPoint );
03372         limb = G_Spawn();
03373         limb->classname = "playerlimb";
03374 
03375         /*
03376         if (limbType == G2_MODELPART_WAIST)
03377         { //slight hack
03378                 newPoint[2] += 1;
03379         }
03380         */
03381 
03382         G_SetOrigin( limb, newPoint );
03383         VectorCopy( newPoint, limb->s.pos.trBase );
03384         limb->think = LimbThink;
03385         limb->touch = LimbTouch;
03386         limb->speed = level.time + Q_irand(8000, 16000);
03387         limb->nextthink = level.time + FRAMETIME;
03388 
03389         limb->r.svFlags = SVF_USE_CURRENT_ORIGIN;
03390         limb->clipmask = MASK_SOLID;
03391         limb->r.contents = CONTENTS_TRIGGER;
03392         limb->physicsObject = qtrue;
03393         VectorSet( limb->r.mins, -6.0f, -6.0f, -3.0f );
03394         VectorSet( limb->r.maxs, 6.0f, 6.0f, 6.0f );
03395 
03396         limb->s.g2radius = 200;
03397 
03398         limb->s.eType = ET_GENERAL;
03399         limb->s.weapon = G2_MODEL_PART;
03400         limb->s.modelGhoul2 = limbType;
03401         limb->s.modelindex = ent->s.number;
03402         if (!ent->client)
03403         {
03404                 limb->s.modelindex = -1;
03405                 limb->s.otherEntityNum2 = ent->s.number;
03406         }
03407 
03408         VectorClear(limb->s.apos.trDelta);
03409 
03410         if (ent->client)
03411         {
03412                 VectorCopy(ent->client->ps.viewangles, limb->r.currentAngles);
03413                 VectorCopy(ent->client->ps.viewangles, limb->s.apos.trBase);
03414         }
03415         else
03416         {
03417                 VectorCopy(ent->r.currentAngles, limb->r.currentAngles);
03418                 VectorCopy(ent->r.currentAngles, limb->s.apos.trBase);
03419         }
03420 
03421         //Set up the ExPhys values for the entity.
03422         limb->epGravFactor = 0;
03423         VectorClear(limb->epVelocity);
03424         VectorSubtract( point, ent->r.currentOrigin, dir );
03425         VectorNormalize( dir );
03426         if (ent->client)
03427         {
03428                 VectorCopy(ent->client->ps.velocity, vel);
03429         }
03430         else
03431         {
03432                 VectorCopy(ent->s.pos.trDelta, vel);
03433         }
03434         VectorMA( vel, 80, dir, limb->epVelocity );
03435 
03436         //add some vertical velocity
03437         if (limbType == G2_MODELPART_HEAD ||
03438                 limbType == G2_MODELPART_WAIST)
03439         {
03440                 limb->epVelocity[2] += 10;
03441         }
03442 
03443         if (enemy && enemy->client && ent && ent != enemy && ent->s.number != enemy->s.number &&
03444                 enemy->client->ps.weapon == WP_SABER && enemy->client->olderIsValid &&
03445                 (level.time - enemy->client->lastSaberStorageTime) < 200)
03446         { //The enemy has valid saber positions between this and last frame. Use them to factor in direction of the limb.
03447                 vec3_t dif;
03448                 float totalDistance;
03449                 const float distScale = 1.2f;
03450 
03451                 //scale down the initial velocity first, which is based on the speed of the limb owner.
03452                 //ExPhys object velocity operates on a slightly different scale than Q3-based physics velocity.
03453                 VectorScale(limb->epVelocity, 0.4f, limb->epVelocity);
03454 
03455                 VectorSubtract(enemy->client->lastSaberBase_Always, enemy->client->olderSaberBase, dif);
03456                 totalDistance = VectorNormalize(dif);
03457 
03458                 VectorScale(dif, totalDistance*distScale, dif);
03459                 VectorAdd(limb->epVelocity, dif, limb->epVelocity);
03460 
03461                 if (ent->client && (ent->client->ps.torsoTimer > 0 || !BG_InDeathAnim(ent->client->ps.torsoAnim)))
03462                 { //if he's done with his death anim we don't actually want the limbs going far
03463                         vec3_t preVel;
03464 
03465                         VectorCopy(limb->epVelocity, preVel);
03466                         preVel[2] = 0;
03467                         totalDistance = VectorNormalize(preVel);
03468 
03469                         if (totalDistance < 40.0f)
03470                         {
03471                                 float mAmt = 40.0f;//60.0f/totalDistance;
03472 
03473                                 limb->epVelocity[0] = preVel[0]*mAmt;
03474                                 limb->epVelocity[1] = preVel[1]*mAmt;
03475                         }
03476                 }
03477                 else if (ent->client)
03478                 {
03479                         VectorScale(limb->epVelocity, 0.3f, limb->epVelocity);
03480                 }
03481         }
03482 
03483         if (ent->s.eType == ET_NPC && ent->ghoul2 && limbName && stubCapName)
03484         { //if it's an npc remove these surfs on the server too. For players we don't even care cause there's no further dismemberment after death.
03485                 trap_G2API_SetSurfaceOnOff(ent->ghoul2, limbName, 0x00000100);
03486                 trap_G2API_SetSurfaceOnOff(ent->ghoul2, stubCapName, 0);
03487         }
03488 
03489         limb->s.customRGBA[0] = ent->s.customRGBA[0];
03490         limb->s.customRGBA[1] = ent->s.customRGBA[1];
03491         limb->s.customRGBA[2] = ent->s.customRGBA[2];
03492         limb->s.customRGBA[3] = ent->s.customRGBA[3];
03493 
03494         trap_LinkEntity( limb );
03495 }
03496 
03497 void DismembermentTest(gentity_t *self)
03498 {
03499         int sect = G2_MODELPART_HEAD;
03500         vec3_t boltPoint;
03501 
03502         while (sect <= G2_MODELPART_RLEG)
03503         {
03504                 G_GetDismemberBolt(self, boltPoint, sect);
03505                 G_Dismember( self, self, boltPoint, sect, 90, 0, BOTH_DEATH1, qfalse );
03506                 sect++;
03507         }
03508 }
03509 
03510 void DismembermentByNum(gentity_t *self, int num)
03511 {
03512         int sect = G2_MODELPART_HEAD;
03513         vec3_t boltPoint;
03514 
03515         switch (num)
03516         {
03517         case 0:
03518                 sect = G2_MODELPART_HEAD;
03519                 break;
03520         case 1:
03521                 sect = G2_MODELPART_WAIST;
03522                 break;
03523         case 2:
03524                 sect = G2_MODELPART_LARM;
03525                 break;
03526         case 3:
03527                 sect = G2_MODELPART_RARM;
03528                 break;
03529         case 4:
03530                 sect = G2_MODELPART_RHAND;
03531                 break;
03532         case 5:
03533                 sect = G2_MODELPART_LLEG;
03534                 break;
03535         case 6:
03536                 sect = G2_MODELPART_RLEG;
03537                 break;
03538         default:
03539                 break;
03540         }
03541 
03542         G_GetDismemberBolt(self, boltPoint, sect);
03543         G_Dismember( self, self, boltPoint, sect, 90, 0, BOTH_DEATH1, qfalse );
03544 }
03545 
03546 int G_GetHitQuad( gentity_t *self, vec3_t hitloc )
03547 {
03548         vec3_t diff, fwdangles={0,0,0}, right;
03549         vec3_t clEye;
03550         float rightdot;
03551         float zdiff;
03552         int hitLoc = gPainHitLoc;
03553 
03554         if (self->client)
03555         {
03556                 VectorCopy(self->client->ps.origin, clEye);
03557                 clEye[2] += self->client->ps.viewheight;
03558         }
03559         else
03560         {
03561                 VectorCopy(self->s.pos.trBase, clEye);
03562                 clEye[2] += 16;
03563         }
03564 
03565         VectorSubtract( hitloc, clEye, diff );
03566         diff[2] = 0;
03567         VectorNormalize( diff );
03568 
03569         if (self->client)
03570         {
03571                 fwdangles[1] = self->client->ps.viewangles[1];
03572         }
03573         else
03574         {
03575                 fwdangles[1] = self->s.apos.trBase[1];
03576         }
03577         // Ultimately we might care if the shot was ahead or behind, but for now, just quadrant is fine.
03578         AngleVectors( fwdangles, NULL, right, NULL );
03579 
03580         rightdot = DotProduct(right, diff);
03581         zdiff = hitloc[2] - clEye[2];
03582         
03583         if ( zdiff > 0 )
03584         {
03585                 if ( rightdot > 0.3 )
03586                 {
03587                         hitLoc = G2_MODELPART_RARM;
03588                 }
03589                 else if ( rightdot < -0.3 )
03590                 {
03591                         hitLoc = G2_MODELPART_LARM;
03592                 }
03593                 else
03594                 {
03595                         hitLoc = G2_MODELPART_HEAD;
03596                 }
03597         }
03598         else if ( zdiff > -20 )
03599         {
03600                 if ( rightdot > 0.1 )
03601                 {
03602                         hitLoc = G2_MODELPART_RARM;
03603                 }
03604                 else if ( rightdot < -0.1 )
03605                 {
03606                         hitLoc = G2_MODELPART_LARM;
03607                 }
03608                 else
03609                 {
03610                         hitLoc = G2_MODELPART_HEAD;
03611                 }
03612         }
03613         else
03614         {
03615                 if ( rightdot >= 0 )
03616                 {
03617                         hitLoc = G2_MODELPART_RLEG;
03618                 }
03619                 else
03620                 {
03621                         hitLoc = G2_MODELPART_LLEG;
03622                 }
03623         }
03624 
03625         return hitLoc;
03626 }
03627 
03628 int gGAvoidDismember = 0;
03629 
03630 void UpdateClientRenderBolts(gentity_t *self, vec3_t renderOrigin, vec3_t renderAngles);
03631 
03632 qboolean G_GetHitLocFromSurfName( gentity_t *ent, const char *surfName, int *hitLoc, vec3_t point, vec3_t dir, vec3_t bladeDir, int mod )
03633 {
03634         qboolean dismember = qfalse;
03635         int actualTime;
03636         int kneeLBolt = -1;
03637         int kneeRBolt = -1;
03638         int handRBolt = -1;
03639         int handLBolt = -1;
03640         int footRBolt = -1;
03641         int footLBolt = -1;
03642 
03643         *hitLoc = HL_NONE;
03644 
03645         if ( !surfName || !surfName[0] )
03646         {
03647                 return qfalse;
03648         }
03649 
03650         if( !ent->client )
03651         {
03652                 return qfalse;
03653         }
03654 
03655         if (!point)
03656         {
03657                 return qfalse;
03658         }
03659 
03660         if ( ent->client 
03661                 && ( ent->client->NPC_class == CLASS_R2D2 
03662                         || ent->client->NPC_class == CLASS_R2D2 
03663                         || ent->client->NPC_class == CLASS_GONK
03664                         || ent->client->NPC_class == CLASS_MOUSE
03665                         || ent->client->NPC_class == CLASS_SENTRY
03666                         || ent->client->NPC_class == CLASS_INTERROGATOR
03667                         || ent->client->NPC_class == CLASS_SENTRY
03668                         || ent->client->NPC_class == CLASS_PROBE ) )
03669         {//we don't care about per-surface hit-locations or dismemberment for these guys 
03670                 return qfalse;
03671         }
03672 
03673         if (ent->localAnimIndex <= 1)
03674         { //humanoid
03675                 handLBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*l_hand");
03676                 handRBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*r_hand");
03677                 kneeLBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*hips_l_knee");
03678                 kneeRBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*hips_r_knee");
03679                 footLBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*l_leg_foot");
03680                 footRBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*r_leg_foot");
03681         }
03682 
03683         if ( ent->client && (ent->client->NPC_class == CLASS_ATST) )
03684         {
03685                 //FIXME: almost impossible to hit these... perhaps we should
03686                 //              check for splashDamage and do radius damage to these parts?
03687                 //              Or, if we ever get bbox G2 traces, that may fix it, too
03688                 if (!Q_stricmp("head_light_blaster_cann",surfName))
03689                 {
03690                         *hitLoc = HL_ARM_LT;
03691                 }
03692                 else if (!Q_stricmp("head_concussion_charger",surfName))
03693                 {
03694                         *hitLoc = HL_ARM_RT;
03695                 }
03696                 return(qfalse);
03697         }
03698         else if ( ent->client && (ent->client->NPC_class == CLASS_MARK1) )
03699         {
03700                 if (!Q_stricmp("l_arm",surfName))
03701                 {
03702                         *hitLoc = HL_ARM_LT;
03703                 }
03704                 else if (!Q_stricmp("r_arm",surfName))
03705                 {
03706                         *hitLoc = HL_ARM_RT;
03707                 }
03708                 else if (!Q_stricmp("torso_front",surfName))
03709                 {
03710                         *hitLoc = HL_CHEST;
03711                 }
03712                 else if (!Q_stricmp("torso_tube1",surfName))
03713                 {
03714                         *hitLoc = HL_GENERIC1;
03715                 }
03716                 else if (!Q_stricmp("torso_tube2",surfName))
03717                 {
03718                         *hitLoc = HL_GENERIC2;
03719                 }
03720                 else if (!Q_stricmp("torso_tube3",surfName))
03721                 {
03722                         *hitLoc = HL_GENERIC3;
03723                 }
03724                 else if (!Q_stricmp("torso_tube4",surfName))
03725                 {
03726                         *hitLoc = HL_GENERIC4;
03727                 }
03728                 else if (!Q_stricmp("torso_tube5",surfName))
03729                 {
03730                         *hitLoc = HL_GENERIC5;
03731                 }
03732                 else if (!Q_stricmp("torso_tube6",surfName))
03733                 {
03734                         *hitLoc = HL_GENERIC6;
03735                 }
03736                 return(qfalse);
03737         }
03738         else if ( ent->client && (ent->client->NPC_class == CLASS_MARK2) )
03739         {
03740                 if (!Q_stricmp("torso_canister1",surfName))
03741                 {
03742                         *hitLoc = HL_GENERIC1;
03743                 }
03744                 else if (!Q_stricmp("torso_canister2",surfName))
03745                 {
03746                         *hitLoc = HL_GENERIC2;
03747                 }
03748                 else if (!Q_stricmp("torso_canister3",surfName))
03749                 {
03750                         *hitLoc = HL_GENERIC3;
03751                 }
03752                 return(qfalse);
03753         }
03754         else if ( ent->client && (ent->client->NPC_class == CLASS_GALAKMECH) )
03755         {
03756                 if (!Q_stricmp("torso_antenna",surfName)||!Q_stricmp("torso_antenna_base",surfName))
03757                 {
03758                         *hitLoc = HL_GENERIC1;
03759                 }
03760                 else if (!Q_stricmp("torso_shield",surfName))
03761                 {
03762                         *hitLoc = HL_GENERIC2;
03763                 }
03764                 else
03765                 {
03766                         *hitLoc = HL_CHEST;
03767                 }
03768                 return(qfalse);
03769         }
03770 
03771         //FIXME: check the hitLoc and hitDir against the cap tag for the place 
03772         //where the split will be- if the hit dir is roughly perpendicular to 
03773         //the direction of the cap, then the split is allowed, otherwise we
03774         //hit it at the wrong angle and should not dismember...
03775         actualTime = level.time;
03776         if ( !Q_strncmp( "hips", surfName, 4 ) )
03777         {//FIXME: test properly for legs
03778                 *hitLoc = HL_WAIST;
03779                 if ( ent->client != NULL && ent->ghoul2 )
03780                 {
03781                         mdxaBone_t      boltMatrix;
03782                         vec3_t  tagOrg, angles;
03783 
03784                         VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
03785                         if (kneeLBolt>=0)
03786                         {
03787                                 trap_G2API_GetBoltMatrix( ent->ghoul2, 0, kneeLBolt, 
03788                                                                 &boltMatrix, angles, ent->r.currentOrigin,
03789                                                                 actualTime, NULL, ent->modelScale );
03790                                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
03791                                 if ( DistanceSquared( point, tagOrg ) < 100 )
03792                                 {//actually hit the knee
03793                                         *hitLoc = HL_LEG_LT;
03794                                 }
03795                         }
03796                         if (*hitLoc == HL_WAIST)
03797                         {
03798                                 if (kneeRBolt>=0)
03799                                 {
03800                                         trap_G2API_GetBoltMatrix( ent->ghoul2, 0, kneeRBolt, 
03801                                                                         &boltMatrix, angles, ent->r.currentOrigin,
03802                                                                         actualTime, NULL, ent->modelScale );
03803                                         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
03804                                         if ( DistanceSquared( point, tagOrg ) < 100 )
03805                                         {//actually hit the knee
03806                                                 *hitLoc = HL_LEG_RT;
03807                                         }
03808                                 }
03809                         }
03810                 }
03811         }
03812         else if ( !Q_strncmp( "torso", surfName, 5 ) )
03813         {
03814                 if ( !ent->client )
03815                 {
03816                         *hitLoc = HL_CHEST;
03817                 }
03818                 else
03819                 {
03820                         vec3_t  t_fwd, t_rt, t_up, dirToImpact;
03821                         float frontSide, rightSide, upSide;
03822                         AngleVectors( ent->client->renderInfo.torsoAngles, t_fwd, t_rt, t_up );
03823 
03824                         if (ent->client->renderInfo.boltValidityTime != level.time)
03825                         {
03826                                 vec3_t renderAng;
03827 
03828                                 renderAng[0] = 0;
03829                                 renderAng[1] = ent->client->ps.viewangles[YAW];
03830                                 renderAng[2] = 0;
03831 
03832                                 UpdateClientRenderBolts(ent, ent->client->ps.origin, renderAng);
03833                         }
03834 
03835                         VectorSubtract( point, ent->client->renderInfo.torsoPoint, dirToImpact );
03836                         frontSide = DotProduct( t_fwd, dirToImpact );
03837                         rightSide = DotProduct( t_rt, dirToImpact );
03838                         upSide = DotProduct( t_up, dirToImpact );
03839                         if ( upSide < -10 )
03840                         {//hit at waist
03841                                 *hitLoc = HL_WAIST;
03842                         }
03843                         else
03844                         {//hit on upper torso
03845                                 if ( rightSide > 4 )
03846                                 {
03847                                         *hitLoc = HL_ARM_RT;
03848                                 }
03849                                 else if ( rightSide < -4 )
03850                                 {
03851                                         *hitLoc = HL_ARM_LT;
03852                                 }
03853                                 else if ( rightSide > 2 )
03854                                 {
03855                                         if ( frontSide > 0 )
03856                                         {
03857                                                 *hitLoc = HL_CHEST_RT;
03858                                         }
03859                                         else
03860                                         {
03861                                                 *hitLoc = HL_BACK_RT;
03862                                         }
03863                                 }
03864                                 else if ( rightSide < -2 )
03865                                 {
03866                                         if ( frontSide > 0 )
03867                                         {
03868                                                 *hitLoc = HL_CHEST_LT;
03869                                         }
03870                                         else
03871                                         {
03872                                                 *hitLoc = HL_BACK_LT;
03873                                         }
03874                                 }
03875                                 else if ( upSide > -3 && mod == MOD_SABER )
03876                                 {
03877                                         *hitLoc = HL_HEAD;
03878                                 }
03879                                 else if ( frontSide > 0 )
03880                                 {
03881                                         *hitLoc = HL_CHEST;
03882                                 }
03883                                 else
03884                                 {
03885                                         *hitLoc = HL_BACK;
03886                                 }
03887                         }
03888                 }
03889         }
03890         else if ( !Q_strncmp( "head", surfName, 4 ) )
03891         {
03892                 *hitLoc = HL_HEAD;
03893         }
03894         else if ( !Q_strncmp( "r_arm", surfName, 5 ) )
03895         {
03896                 *hitLoc = HL_ARM_RT;
03897                 if ( ent->client != NULL && ent->ghoul2 )
03898                 {
03899                         mdxaBone_t      boltMatrix;
03900                         vec3_t  tagOrg, angles;
03901 
03902                         VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
03903                         if (handRBolt>=0)
03904                         {
03905                                 trap_G2API_GetBoltMatrix( ent->ghoul2, 0, handRBolt, 
03906                                                                 &boltMatrix, angles, ent->r.currentOrigin,
03907                                                                 actualTime, NULL, ent->modelScale );
03908                                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
03909                                 if ( DistanceSquared( point, tagOrg ) < 256 )
03910                                 {//actually hit the hand
03911                                         *hitLoc = HL_HAND_RT;
03912                                 }
03913                         }
03914                 }
03915         }
03916         else if ( !Q_strncmp( "l_arm", surfName, 5 ) )
03917         {
03918                 *hitLoc = HL_ARM_LT;
03919                 if ( ent->client != NULL && ent->ghoul2 )
03920                 {
03921                         mdxaBone_t      boltMatrix;
03922                         vec3_t  tagOrg, angles;
03923 
03924                         VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
03925                         if (handLBolt>=0)
03926                         {
03927                                 trap_G2API_GetBoltMatrix( ent->ghoul2, 0, handLBolt, 
03928                                                                 &boltMatrix, angles, ent->r.currentOrigin,
03929                                                                 actualTime, NULL, ent->modelScale );
03930                                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
03931                                 if ( DistanceSquared( point, tagOrg ) < 256 )
03932                                 {//actually hit the hand
03933                                         *hitLoc = HL_HAND_LT;
03934                                 }
03935                         }
03936                 }
03937         }
03938         else if ( !Q_strncmp( "r_leg", surfName, 5 ) )
03939         {
03940                 *hitLoc = HL_LEG_RT;
03941                 if ( ent->client != NULL && ent->ghoul2 )
03942                 {
03943                         mdxaBone_t      boltMatrix;
03944                         vec3_t  tagOrg, angles;
03945 
03946                         VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
03947                         if (footRBolt>=0)
03948                         {
03949                                 trap_G2API_GetBoltMatrix( ent->ghoul2, 0, footRBolt, 
03950                                                                 &boltMatrix, angles, ent->r.currentOrigin,
03951                                                                 actualTime, NULL, ent->modelScale );
03952                                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
03953                                 if ( DistanceSquared( point, tagOrg ) < 100 )
03954                                 {//actually hit the foot
03955                                         *hitLoc = HL_FOOT_RT;
03956                                 }
03957                         }
03958                 }
03959         }
03960         else if ( !Q_strncmp( "l_leg", surfName, 5 ) )
03961         {
03962                 *hitLoc = HL_LEG_LT;
03963                 if ( ent->client != NULL && ent->ghoul2 )
03964                 {
03965                         mdxaBone_t      boltMatrix;
03966                         vec3_t  tagOrg, angles;
03967 
03968                         VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
03969                         if (footLBolt>=0)
03970                         {
03971                                 trap_G2API_GetBoltMatrix( ent->ghoul2, 0, footLBolt, 
03972                                                                 &boltMatrix, angles, ent->r.currentOrigin,
03973                                                                 actualTime, NULL, ent->modelScale );
03974                                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
03975                                 if ( DistanceSquared( point, tagOrg ) < 100 )
03976                                 {//actually hit the foot
03977                                         *hitLoc = HL_FOOT_LT;
03978                                 }
03979                         }
03980                 }
03981         }
03982         else if ( !Q_strncmp( "r_hand", surfName, 6 ) || !Q_strncmp( "w_", surfName, 2 ) )
03983         {//right hand or weapon
03984                 *hitLoc = HL_HAND_RT;
03985         }
03986         else if ( !Q_strncmp( "l_hand", surfName, 6 ) )
03987         {
03988                 *hitLoc = HL_HAND_LT;
03989         }
03990         /*
03991 #ifdef _DEBUG
03992         else
03993         {
03994                 Com_Printf( "ERROR: surface %s does not belong to any hitLocation!!!\n", surfName );
03995         }
03996 #endif //_DEBUG
03997         */
03998 
03999         //if ( g_dismemberment->integer >= 11381138 || !ent->client->dismembered )
04000         if (g_dismember.integer == 100)
04001         { //full probability...
04002                 if ( ent->client && ent->client->NPC_class == CLASS_PROTOCOL )
04003                 {
04004                         dismember = qtrue;
04005                 }
04006                 else if ( dir && (dir[0] || dir[1] || dir[2]) &&
04007                         bladeDir && (bladeDir[0] || bladeDir[1] || bladeDir[2]) )
04008                 {//we care about direction (presumably for dismemberment)
04009                         //if ( g_dismemberProbabilities->value<=0.0f||G_Dismemberable( ent, *hitLoc ) )
04010                         if (1) //Fix me?
04011                         {//either we don't care about probabilties or the probability let us continue
04012                                 char *tagName = NULL;
04013                                 float   aoa = 0.5f;
04014                                 //dir must be roughly perpendicular to the hitLoc's cap bolt
04015                                 switch ( *hitLoc )
04016                                 {
04017                                         case HL_LEG_RT:
04018                                                 tagName = "*hips_cap_r_leg";
04019                                                 break;
04020                                         case HL_LEG_LT:
04021                                                 tagName = "*hips_cap_l_leg";
04022                                                 break;
04023                                         case HL_WAIST:
04024                                                 tagName = "*hips_cap_torso";
04025                                                 aoa = 0.25f;
04026                                                 break;
04027                                         case HL_CHEST_RT:
04028                                         case HL_ARM_RT:
04029                                         case HL_BACK_LT:
04030                                                 tagName = "*torso_cap_r_arm";
04031                                                 break;
04032                                         case HL_CHEST_LT:
04033                                         case HL_ARM_LT:
04034                                         case HL_BACK_RT:
04035                                                 tagName = "*torso_cap_l_arm";
04036                                                 break;
04037                                         case HL_HAND_RT:
04038                                                 tagName = "*r_arm_cap_r_hand";
04039                                                 break;
04040                                         case HL_HAND_LT:
04041                                                 tagName = "*l_arm_cap_l_hand";
04042                                                 break;
04043                                         case HL_HEAD:
04044                                                 tagName = "*torso_cap_head";
04045                                                 aoa = 0.25f;
04046                                                 break;
04047                                         case HL_CHEST:
04048                                         case HL_BACK:
04049                                         case HL_FOOT_RT:
04050                                         case HL_FOOT_LT:
04051                                         default:
04052                                                 //no dismemberment possible with these, so no checks needed
04053                                                 break;
04054                                 }
04055                                 if ( tagName )
04056                                 {
04057                                         int tagBolt = trap_G2API_AddBolt( ent->ghoul2, 0, tagName );
04058                                         if ( tagBolt != -1 )
04059                                         {
04060                                                 mdxaBone_t      boltMatrix;
04061                                                 vec3_t  tagOrg, tagDir, angles;
04062 
04063                                                 VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
04064                                                 trap_G2API_GetBoltMatrix( ent->ghoul2, 0, tagBolt, 
04065                                                                                 &boltMatrix, angles, ent->r.currentOrigin,
04066                                                                                 actualTime, NULL, ent->modelScale );
04067                                                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
04068                                                 BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, tagDir );
04069                                                 if ( DistanceSquared( point, tagOrg ) < 256 )
04070                                                 {//hit close
04071                                                         float dot = DotProduct( dir, tagDir );
04072                                                         if ( dot < aoa && dot > -aoa )
04073                                                         {//hit roughly perpendicular
04074                                                                 dot = DotProduct( bladeDir, tagDir );
04075                                                                 if ( dot < aoa && dot > -aoa )
04076                                                                 {//blade was roughly perpendicular
04077                                                                         dismember = qtrue;
04078                                                                 }
04079                                                         }
04080                                                 }
04081                                         }
04082                                 }
04083                         }
04084                 }
04085                 else
04086                 { //hmm, no direction supplied.
04087                         dismember = qtrue;
04088                 }
04089         }
04090         return dismember;
04091 }
04092 
04093 void G_CheckForDismemberment(gentity_t *ent, gentity_t *enemy, vec3_t point, int damage, int deathAnim, qboolean postDeath)
04094 {
04095         int hitLoc = -1, hitLocUse = -1;
04096         vec3_t boltPoint;
04097         int dismember = g_dismember.integer;
04098 
04099         if (ent->localAnimIndex > 1)
04100         {
04101                 if (!ent->NPC)
04102                 {
04103                         return;
04104                 }
04105 
04106                 if (ent->client->NPC_class != CLASS_PROTOCOL)
04107                 { //this is the only non-humanoid allowed to do dismemberment.
04108                         return;
04109                 }
04110         }
04111 
04112         if (!dismember)
04113         {
04114                 return;
04115         }
04116 
04117         if (gGAvoidDismember == 1)
04118         {
04119                 return;
04120         }
04121 
04122         if (gGAvoidDismember != 2)
04123         { //this means do the dismemberment regardless of randomness and damage
04124                 if (Q_irand(0, 100) > dismember)
04125                 {
04126                         return;
04127                 }
04128 
04129                 if (damage < 5)
04130                 {
04131                         return;
04132                 }
04133         }
04134 
04135         if (gGAvoidDismember == 2)
04136         {
04137                 hitLoc = HL_HAND_RT;
04138         }
04139         else
04140         {
04141                 if (d_saberGhoul2Collision.integer && ent->client && ent->client->g2LastSurfaceTime == level.time)
04142                 {
04143                         char hitSurface[MAX_QPATH];
04144 
04145                         trap_G2API_GetSurfaceName(ent->ghoul2, ent->client->g2LastSurfaceHit, 0, hitSurface);
04146 
04147                         if (hitSurface[0])
04148                         {
04149                                 G_GetHitLocFromSurfName(ent, hitSurface, &hitLoc, point, vec3_origin, vec3_origin, MOD_UNKNOWN);
04150                         }
04151                 }
04152 
04153                 if (hitLoc == -1)
04154                 {
04155                         hitLoc = G_GetHitLocation( ent, point );
04156                 }
04157         }
04158 
04159         switch(hitLoc)
04160         {
04161         case HL_FOOT_RT:
04162         case HL_LEG_RT:
04163                 hitLocUse = G2_MODELPART_RLEG;
04164                 break;
04165         case HL_FOOT_LT:
04166         case HL_LEG_LT:
04167                 hitLocUse = G2_MODELPART_LLEG;
04168                 break;
04169                 
04170         case HL_WAIST:
04171                 hitLocUse = G2_MODELPART_WAIST;
04172                 break;
04173                 /*
04174         case HL_BACK_RT:
04175         case HL_BACK_LT:
04176         case HL_BACK:
04177         case HL_CHEST_RT:
04178         case HL_CHEST_LT:
04179         case HL_CHEST:
04180                 break;
04181                 */
04182         case HL_ARM_RT:
04183                 hitLocUse = G2_MODELPART_RARM;
04184                 break;
04185         case HL_HAND_RT:
04186                 hitLocUse = G2_MODELPART_RHAND;
04187                 break;
04188         case HL_ARM_LT:
04189         case HL_HAND_LT:
04190                 hitLocUse = G2_MODELPART_LARM;
04191                 break;
04192         case HL_HEAD:
04193                 hitLocUse = G2_MODELPART_HEAD;
04194                 break;
04195         default:
04196                 hitLocUse = G_GetHitQuad(ent, point);
04197                 break;
04198         }
04199 
04200         if (hitLocUse == -1)
04201         {
04202                 return;
04203         }
04204 
04205         if (ent->client)
04206         {
04207                 G_GetDismemberBolt(ent, boltPoint, hitLocUse);
04208                 if ( g_austrian.integer 
04209                         && (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) )
04210                 {
04211                         G_LogPrintf( "Duel Dismemberment: %s dismembered at %s\n", ent->client->pers.netname, hitLocName[hitLoc] );
04212                 }
04213         }
04214         else
04215         {
04216                 G_GetDismemberLoc(ent, boltPoint, hitLocUse);
04217         }
04218         G_Dismember(ent, enemy, boltPoint, hitLocUse, 90, 0, deathAnim, postDeath);
04219 }
04220 
04221 void G_LocationBasedDamageModifier(gentity_t *ent, vec3_t point, int mod, int dflags, int *damage)
04222 {
04223         int hitLoc = -1;
04224 
04225         if (!g_locationBasedDamage.integer)
04226         { //then leave it alone
04227                 return;
04228         }
04229 
04230         if ( (dflags&DAMAGE_NO_HIT_LOC) )
04231         { //then leave it alone
04232                 return;
04233         }
04234 
04235         if (mod == MOD_SABER && *damage <= 1)
04236         { //don't bother for idle damage
04237                 return;
04238         }
04239 
04240         if (!point)
04241         {
04242                 return;
04243         }
04244 
04245         if ( ent->client && ent->client->NPC_class == CLASS_VEHICLE )
04246         {//no location-based damage on vehicles
04247                 return;
04248         }
04249 
04250         if ((d_saberGhoul2Collision.integer && ent->client && ent->client->g2LastSurfaceTime == level.time && mod == MOD_SABER) || //using ghoul2 collision? Then if the mod is a saber we should have surface data from the last hit (unless thrown).
04251                 (d_projectileGhoul2Collision.integer && ent->client && ent->client->g2LastSurfaceTime == level.time)) //It's safe to assume we died from the projectile that just set our surface index. So, go ahead and use that as the surf I guess.
04252         {
04253                 char hitSurface[MAX_QPATH];
04254 
04255                 trap_G2API_GetSurfaceName(ent->ghoul2, ent->client->g2LastSurfaceHit, 0, hitSurface);
04256 
04257                 if (hitSurface[0])
04258                 {
04259                         G_GetHitLocFromSurfName(ent, hitSurface, &hitLoc, point, vec3_origin, vec3_origin, MOD_UNKNOWN);
04260                 }
04261         }
04262 
04263         if (hitLoc == -1)
04264         {
04265                 hitLoc = G_GetHitLocation( ent, point );
04266         }
04267 
04268         switch (hitLoc)
04269         {
04270         case HL_FOOT_RT:
04271         case HL_FOOT_LT:
04272                 *damage *= 0.5;
04273                 break;
04274         case HL_LEG_RT:
04275         case HL_LEG_LT:
04276                 *damage *= 0.7;
04277                 break;
04278         case HL_WAIST:
04279         case HL_BACK_RT:
04280         case HL_BACK_LT:
04281         case HL_BACK:
04282         case HL_CHEST_RT:
04283         case HL_CHEST_LT:
04284         case HL_CHEST:
04285                 break; //normal damage
04286         case HL_ARM_RT:
04287         case HL_ARM_LT:
04288                 *damage *= 0.85;
04289                 break;
04290         case HL_HAND_RT:
04291         case HL_HAND_LT:
04292                 *damage *= 0.6;
04293                 break;
04294         case HL_HEAD:
04295                 *damage *= 1.3;
04296                 break;
04297         default:
04298                 break; //do nothing then
04299         }
04300 }
04301 /*
04302 ===================================
04303 rww - end dismemberment/lbd
04304 ===================================
04305 */
04306 
04307 qboolean G_ThereIsAMaster(void)
04308 {
04309         int i = 0;
04310         gentity_t *ent;
04311 
04312         while (i < MAX_CLIENTS)
04313         {
04314                 ent = &g_entities[i];
04315 
04316                 if (ent && ent->client && ent->client->ps.isJediMaster)
04317                 {
04318                         return qtrue;
04319                 }
04320 
04321                 i++;
04322         }
04323 
04324         return qfalse;
04325 }
04326 
04327 void G_Knockdown( gentity_t *victim )
04328 {
04329         if ( victim && victim->client && BG_KnockDownable(&victim->client->ps) )
04330         {
04331                 victim->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
04332                 victim->client->ps.forceDodgeAnim = 0;
04333                 victim->client->ps.forceHandExtendTime = level.time + 1100;
04334                 victim->client->ps.quickerGetup = qfalse;
04335         }
04336 }
04337 
04338 /*
04339 ============
04340 T_Damage
04341 
04342 targ            entity that is being damaged
04343 inflictor       entity that is causing the damage
04344 attacker        entity that caused the inflictor to damage targ
04345         example: targ=monster, inflictor=rocket, attacker=player
04346 
04347 dir                     direction of the attack for knockback
04348 point           point at which the damage is being inflicted, used for headshots
04349 damage          amount of damage being inflicted
04350 knockback       force to be applied against targ as a result of the damage
04351 
04352 inflictor, attacker, dir, and point can be NULL for environmental effects
04353 
04354 dflags          these flags are used to control how T_Damage works
04355         DAMAGE_RADIUS                   damage was indirect (from a nearby explosion)
04356         DAMAGE_NO_ARMOR                 armor does not protect from this damage
04357         DAMAGE_NO_KNOCKBACK             do not affect velocity, just view angles
04358         DAMAGE_NO_PROTECTION    kills godmode, armor, everything
04359         DAMAGE_HALF_ABSORB              half shields, half health
04360         DAMAGE_HALF_ARMOR_REDUCTION             Any damage that shields incur is halved
04361 ============
04362 */
04363 extern qboolean gSiegeRoundBegun;
04364 
04365 int gPainMOD = 0;
04366 int gPainHitLoc = -1;
04367 vec3_t gPainPoint;
04368 
04369 void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
04370                            vec3_t dir, vec3_t point, int damage, int dflags, int mod ) {
04371         gclient_t       *client;
04372         int                     take;
04373         int                     save;
04374         int                     asave;
04375         int                     knockback;
04376         int                     max;
04377         int                     subamt = 0;
04378         float           famt = 0;
04379         float           hamt = 0;
04380         float           shieldAbsorbed = 0;
04381 
04382         if (targ && targ->damageRedirect)
04383         {
04384                 G_Damage(&g_entities[targ->damageRedirectTo], inflictor, attacker, dir, point, damage, dflags, mod);
04385                 return;
04386         }
04387 
04388         if (mod == MOD_DEMP2 && targ && targ->inuse && targ->client)
04389         {
04390                 if ( targ->client->ps.electrifyTime < level.time )
04391                 {//electrocution effect
04392                         if (targ->s.eType == ET_NPC && targ->s.NPC_class == CLASS_VEHICLE &&
04393                                 targ->m_pVehicle && (targ->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER || targ->m_pVehicle->m_pVehicleInfo->type == VH_WALKER))
04394                         { //do some extra stuff to speeders/walkers
04395                                 targ->client->ps.electrifyTime = level.time + Q_irand( 3000, 4000 );
04396                         }
04397                         else if ( targ->s.NPC_class != CLASS_VEHICLE 
04398                                 || (targ->m_pVehicle && targ->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER) )
04399                         {//don't do this to fighters
04400                                 targ->client->ps.electrifyTime = level.time + Q_irand( 300, 800 );
04401                         }
04402                 }
04403         }
04404 
04405         if (g_gametype.integer == GT_SIEGE &&
04406                 !gSiegeRoundBegun)
04407         { //nothing can be damaged til the round starts.
04408                 return;
04409         }
04410 
04411         if (!targ->takedamage) {
04412                 return;
04413         }
04414 
04415         if ( (targ->flags&FL_SHIELDED) && mod != MOD_SABER  && !targ->client)
04416         {//magnetically protected, this thing can only be damaged by lightsabers
04417                 return;
04418         }
04419 
04420         if ((targ->flags & FL_DMG_BY_SABER_ONLY) && mod != MOD_SABER)
04421         { //saber-only damage
04422                 return;
04423         }
04424 
04425         if ( targ->client )
04426         {//don't take damage when in a walker, or fighter
04427                 //unless the walker/fighter is dead!!! -rww
04428                 if ( targ->client->ps.clientNum < MAX_CLIENTS && targ->client->ps.m_iVehicleNum )
04429                 {
04430                         gentity_t *veh = &g_entities[targ->client->ps.m_iVehicleNum];
04431                         if ( veh->m_pVehicle && veh->health > 0 )
04432                         {
04433                                 if ( veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER ||
04434                                          veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
04435                                 {
04436                                         if (!(dflags & DAMAGE_NO_PROTECTION))
04437                                         {
04438                                                 return;
04439                                         }
04440                                 }
04441                         }
04442                 }
04443         }
04444 
04445         if ((targ->flags & FL_DMG_BY_HEAVY_WEAP_ONLY))
04446         { //only take damage from explosives and such
04447                 if (mod != MOD_REPEATER_ALT &&
04448                         mod != MOD_ROCKET &&
04449                         mod != MOD_FLECHETTE_ALT_SPLASH &&
04450                         mod != MOD_ROCKET_HOMING &&
04451                         mod != MOD_THERMAL &&
04452                         mod != MOD_THERMAL_SPLASH &&
04453                         mod != MOD_TRIP_MINE_SPLASH &&
04454                         mod != MOD_TIMED_MINE_SPLASH &&
04455                         mod != MOD_DET_PACK_SPLASH &&
04456                         mod != MOD_VEHICLE &&
04457                         mod != MOD_CONC &&
04458                         mod != MOD_CONC_ALT &&
04459                         mod != MOD_SABER &&
04460                         mod != MOD_TURBLAST &&
04461                         mod != MOD_SUICIDE &&
04462                         mod != MOD_FALLING &&
04463                         mod != MOD_CRUSH &&
04464                         mod != MOD_TELEFRAG &&
04465                         mod != MOD_TRIGGER_HURT)
04466                 {
04467                         if ( mod != MOD_MELEE || !G_HeavyMelee( attacker ) )
04468                         { //let classes with heavy melee ability damage heavy wpn dmg doors with fists
04469                                 return;
04470                         }
04471                 }
04472         }
04473 
04474         if (targ->flags & FL_BBRUSH)
04475         {
04476                 if (mod == MOD_DEMP2 ||
04477                         mod == MOD_DEMP2_ALT ||
04478                         mod == MOD_BRYAR_PISTOL ||
04479                         mod == MOD_BRYAR_PISTOL_ALT ||
04480                         mod == MOD_MELEE)
04481                 { //these don't damage bbrushes.. ever
04482                         if ( mod != MOD_MELEE || !G_HeavyMelee( attacker ) )
04483                         { //let classes with heavy melee ability damage breakable brushes with fists
04484                                 return;
04485                         }
04486                 }
04487         }
04488 
04489         if (targ && targ->client && targ->client->ps.duelInProgress)
04490         {
04491                 if (attacker && attacker->client && attacker->s.number != targ->client->ps.duelIndex)
04492                 {
04493                         return;
04494                 }
04495                 else if (attacker && attacker->client && mod != MOD_SABER)
04496                 {
04497                         return;
04498                 }
04499         }
04500         if (attacker && attacker->client && attacker->client->ps.duelInProgress)
04501         {
04502                 if (targ && targ->client && targ->s.number != attacker->client->ps.duelIndex)
04503                 {
04504                         return;
04505                 }
04506                 else if (targ && targ->client && mod != MOD_SABER)
04507                 {
04508                         return;
04509                 }
04510         }
04511 
04512         if ( !(dflags & DAMAGE_NO_PROTECTION) ) 
04513         {//rage overridden by no_protection
04514                 if (targ && targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_RAGE)))
04515                 {
04516                         damage *= 0.5;
04517                 }
04518         }
04519 
04520         // the intermission has allready been qualified for, so don't
04521         // allow any extra scoring
04522         if ( level.intermissionQueued ) {
04523                 return;
04524         }
04525         if ( !inflictor ) {
04526                 inflictor = &g_entities[ENTITYNUM_WORLD];
04527         }
04528         if ( !attacker ) {
04529                 attacker = &g_entities[ENTITYNUM_WORLD];
04530         }
04531 
04532         // shootable doors / buttons don't actually have any health
04533 
04534         //if genericValue4 == 1 then it's glass or a breakable and those do have health
04535         if ( targ->s.eType == ET_MOVER && targ->genericValue4 != 1 ) {
04536                 if ( targ->use && targ->moverState == MOVER_POS1 ) {
04537                         GlobalUse( targ, inflictor, attacker );
04538                 }
04539                 return;
04540         }
04541         // reduce damage by the attacker's handicap value
04542         // unless they are rocket jumping
04543         if ( attacker->client 
04544                 && attacker != targ 
04545                 && attacker->s.eType == ET_PLAYER 
04546                 && g_gametype.integer != GT_SIEGE ) 
04547         {
04548                 max = attacker->client->ps.stats[STAT_MAX_HEALTH];
04549                 damage = damage * max / 100;
04550         }
04551 
04552         if ( !(dflags&DAMAGE_NO_HIT_LOC) )
04553         {//see if we should modify it by damage location
04554                 if (targ->inuse && (targ->client || targ->s.eType == ET_NPC) &&
04555                         attacker->inuse && (attacker->client || attacker->s.eType == ET_NPC))
04556                 { //check for location based damage stuff.
04557                         G_LocationBasedDamageModifier(targ, point, mod, dflags, &damage);
04558                 }
04559         }
04560 
04561         if ( targ->client 
04562                 && targ->client->NPC_class == CLASS_RANCOR 
04563                 && (!attacker||!attacker->client||attacker->client->NPC_class!=CLASS_RANCOR) )
04564         {
04565                 // I guess always do 10 points of damage...feel free to tweak as needed
04566                 if ( damage < 10 )
04567                 {//ignore piddly little damage
04568                         damage = 0;
04569                 }
04570                 else if ( damage >= 10 )
04571                 {
04572                         damage = 10;
04573                 }
04574         }
04575 
04576         client = targ->client;
04577 
04578         if ( client ) {
04579                 if ( client->noclip ) {
04580                         return;
04581                 }
04582         }
04583 
04584         if ( !dir ) {
04585                 dflags |= DAMAGE_NO_KNOCKBACK;
04586         } else {
04587                 VectorNormalize(dir);
04588         }
04589 
04590         knockback = damage;
04591         if ( knockback > 200 ) {
04592                 knockback = 200;
04593         }
04594         if ( targ->flags & FL_NO_KNOCKBACK ) {
04595                 knockback = 0;
04596         }
04597         if ( dflags & DAMAGE_NO_KNOCKBACK ) {
04598                 knockback = 0;
04599         }
04600 
04601         // figure momentum add, even if the damage won't be taken
04602         if ( knockback && targ->client ) {
04603                 vec3_t  kvel;
04604                 float   mass;
04605 
04606                 mass = 200;
04607 
04608                 if (mod == MOD_SABER)
04609                 {
04610                         float saberKnockbackScale = g_saberDmgVelocityScale.value;
04611                         if ( (dflags&DAMAGE_SABER_KNOCKBACK1)
04612                                 || (dflags&DAMAGE_SABER_KNOCKBACK2) )
04613                         {//saber does knockback, scale it by the right number
04614                                 if ( !saberKnockbackScale )
04615                                 {
04616                                         saberKnockbackScale = 1.0f;
04617                                 }
04618                                 if ( attacker
04619                                         && attacker->client )
04620                                 {
04621                                         if ( (dflags&DAMAGE_SABER_KNOCKBACK1) )
04622                                         {
04623                                                 if ( attacker && attacker->client )
04624                                                 {
04625                                                         saberKnockbackScale *= attacker->client->saber[0].knockbackScale;
04626                                                 }
04627                                         }
04628                                         if ( (dflags&DAMAGE_SABER_KNOCKBACK1_B2) )
04629                                         {
04630                                                 if ( attacker && attacker->client )
04631                                                 {
04632                                                         saberKnockbackScale *= attacker->client->saber[0].knockbackScale2;
04633                                                 }
04634                                         }
04635                                         if ( (dflags&DAMAGE_SABER_KNOCKBACK2) )
04636                                         {
04637                                                 if ( attacker && attacker->client )
04638                                                 {
04639                                                         saberKnockbackScale *= attacker->client->saber[1].knockbackScale;
04640                                                 }
04641                                         }
04642                                         if ( (dflags&DAMAGE_SABER_KNOCKBACK2_B2) )
04643                                         {
04644                                                 if ( attacker && attacker->client )
04645                                                 {
04646                                                         saberKnockbackScale *= attacker->client->saber[1].knockbackScale2;
04647                                                 }
04648                                         }
04649                                 }
04650                         }
04651                         VectorScale (dir, (g_knockback.value * (float)knockback / mass)*saberKnockbackScale, kvel);
04652                 }
04653                 else
04654                 {
04655                         VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel);
04656                 }
04657                 VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity);
04658 
04659                 if (attacker && attacker->client && attacker != targ)
04660                 {
04661                         float dur = 5000;
04662                         float dur2 = 100;
04663                         if (targ->client && targ->s.eType == ET_NPC && targ->s.NPC_class == CLASS_VEHICLE)
04664                         {
04665                                 dur = 25000;
04666                                 dur2 = 25000;
04667                         }
04668                         targ->client->ps.otherKiller = attacker->s.number;
04669                         targ->client->ps.otherKillerTime = level.time + dur;
04670                         targ->client->ps.otherKillerDebounceTime = level.time + dur2;
04671                 }
04672                 // set the timer so that the other client can't cancel
04673                 // out the movement immediately
04674                 if ( !targ->client->ps.pm_time && (g_saberDmgVelocityScale.integer || mod != MOD_SABER || (dflags&DAMAGE_SABER_KNOCKBACK1) || (dflags&DAMAGE_SABER_KNOCKBACK2) || (dflags&DAMAGE_SABER_KNOCKBACK1_B2) || (dflags&DAMAGE_SABER_KNOCKBACK2_B2) ) ) {
04675                         int             t;
04676 
04677                         t = knockback * 2;
04678                         if ( t < 50 ) {
04679                                 t = 50;
04680                         }
04681                         if ( t > 200 ) {
04682                                 t = 200;
04683                         }
04684                         targ->client->ps.pm_time = t;
04685                         targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
04686                 }
04687         }
04688         else if (targ->client && targ->s.eType == ET_NPC && targ->s.NPC_class == CLASS_VEHICLE && attacker != targ)
04689         {
04690                 targ->client->ps.otherKiller = attacker->s.number;
04691                 targ->client->ps.otherKillerTime = level.time + 25000;
04692                 targ->client->ps.otherKillerDebounceTime = level.time + 25000;
04693         }
04694 
04695         
04696         if ( (g_trueJedi.integer || g_gametype.integer == GT_SIEGE)
04697                 && client )
04698         {//less explosive damage for jedi, more saber damage for non-jedi
04699                 if ( client->ps.trueJedi 
04700                         || (g_gametype.integer == GT_SIEGE&&client->ps.weapon == WP_SABER))
04701                 {//if the target is a trueJedi, reduce splash and explosive damage to 1/2
04702                         switch ( mod )
04703                         {
04704                         case MOD_REPEATER_ALT:
04705                         case MOD_REPEATER_ALT_SPLASH:
04706                         case MOD_DEMP2_ALT:
04707                         case MOD_FLECHETTE_ALT_SPLASH:
04708                         case MOD_ROCKET:
04709                         case MOD_ROCKET_SPLASH:
04710                         case MOD_ROCKET_HOMING:
04711                         case MOD_ROCKET_HOMING_SPLASH:
04712                         case MOD_THERMAL:
04713                         case MOD_THERMAL_SPLASH:
04714                         case MOD_TRIP_MINE_SPLASH:
04715                         case MOD_TIMED_MINE_SPLASH:
04716                         case MOD_DET_PACK_SPLASH:
04717                                 damage *= 0.75;
04718                                 break;
04719                         }
04720                 }
04721                 else if ( (client->ps.trueNonJedi || (g_gametype.integer == GT_SIEGE&&client->ps.weapon != WP_SABER))
04722                         && mod == MOD_SABER )
04723                 {//if the target is a trueNonJedi, take more saber damage... combined with the 1.5 in the w_saber stuff, this is 6 times damage!
04724                         if ( damage < 100 )
04725                         {
04726                                 damage *= 4;
04727                                 if ( damage > 100 )
04728                                 {
04729                                         damage = 100;
04730                                 }
04731                         }
04732                 }
04733         }
04734 
04735         if (attacker->client && targ->client && g_gametype.integer == GT_SIEGE &&
04736                 targ->client->siegeClass != -1 && (bgSiegeClasses[targ->client->siegeClass].classflags & (1<<CFL_STRONGAGAINSTPHYSICAL)))
04737         { //this class is flagged to take less damage from physical attacks.
04738                 //For now I'm just decreasing against any client-based attack, this can be changed later I guess.
04739                 damage *= 0.5;
04740         }
04741 
04742         // check for completely getting out of the damage
04743         if ( !(dflags & DAMAGE_NO_PROTECTION) ) {
04744 
04745                 // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target
04746                 // if the attacker was on the same team
04747                 if ( targ != attacker)
04748                 {
04749                         if (OnSameTeam (targ, attacker))
04750                         {
04751                                 if ( !g_friendlyFire.integer )
04752                                 {
04753                                         return;
04754                                 }
04755                         }
04756                         else if (attacker && attacker->inuse &&
04757                                 !attacker->client && attacker->activator &&
04758                                 targ != attacker->activator &&
04759                                 attacker->activator->inuse && attacker->activator->client)
04760                         { //emplaced guns don't hurt teammates of user
04761                                 if (OnSameTeam (targ, attacker->activator))
04762                                 {
04763                                         if ( !g_friendlyFire.integer )
04764                                         {
04765                                                 return;
04766                                         }
04767                                 }
04768                         }
04769                         else if (targ->inuse && targ->client &&
04770                                 g_gametype.integer >= GT_TEAM &&
04771                                 attacker->s.number >= MAX_CLIENTS &&
04772                                 attacker->alliedTeam &&
04773                                 targ->client->sess.sessionTeam == attacker->alliedTeam &&
04774                                 !g_friendlyFire.integer)
04775                         { //things allied with my team should't hurt me.. I guess
04776                                 return;
04777                         }
04778                 }
04779 
04780                 if (g_gametype.integer == GT_JEDIMASTER && !g_friendlyFire.integer &&
04781                         targ && targ->client && attacker && attacker->client &&
04782                         targ != attacker && !targ->client->ps.isJediMaster && !attacker->client->ps.isJediMaster &&
04783                         G_ThereIsAMaster())
04784                 {
04785                         return;
04786                 }
04787 
04788                 if (targ->s.number >= MAX_CLIENTS && targ->client 
04789                         && targ->s.shouldtarget && targ->s.teamowner &&
04790                         attacker && attacker->inuse && attacker->client && targ->s.owner >= 0 && targ->s.owner < MAX_CLIENTS)
04791                 {
04792                         gentity_t *targown = &g_entities[targ->s.owner];
04793 
04794                         if (targown && targown->inuse && targown->client && OnSameTeam(targown, attacker))
04795                         {
04796                                 if (!g_friendlyFire.integer)
04797                                 {
04798                                         return;
04799                                 }
04800                         }
04801                 }
04802 
04803                 // check for godmode
04804                 if ( (targ->flags & FL_GODMODE) && targ->s.eType != ET_NPC ) {
04805                         return;
04806                 }
04807 
04808                 if (targ && targ->client && (targ->client->ps.eFlags & EF_INVULNERABLE) &&
04809                         attacker && attacker->client && targ != attacker)
04810                 {
04811                         if (targ->client->invulnerableTimer <= level.time)
04812                         {
04813                                 targ->client->ps.eFlags &= ~EF_INVULNERABLE;
04814                         }
04815                         else
04816                         {
04817                                 return;
04818                         }
04819                 }
04820         }
04821 
04822         //check for teamnodmg
04823         //NOTE: non-client objects hitting clients (and clients hitting clients) purposely doesn't obey this teamnodmg (for emplaced guns)
04824         if ( attacker && !targ->client )
04825         {//attacker hit a non-client
04826                 if ( g_gametype.integer == GT_SIEGE &&
04827                         !g_ff_objectives.integer )
04828                 {//in siege mode (and...?)
04829                         if ( targ->teamnodmg )
04830                         {//targ shouldn't take damage from a certain team
04831                                 if ( attacker->client )
04832                                 {//a client hit a non-client object
04833                                         if ( targ->teamnodmg == attacker->client->sess.sessionTeam )
04834                                         {
04835                                                 return;
04836                                         }
04837                                 }
04838                                 else if ( attacker->teamnodmg )
04839                                 {//a non-client hit a non-client object
04840                                         //FIXME: maybe check alliedTeam instead?
04841                                         if ( targ->teamnodmg == attacker->teamnodmg )
04842                                         {
04843                                                 if (attacker->activator &&
04844                                                         attacker->activator->inuse &&
04845                                                         attacker->activator->s.number < MAX_CLIENTS &&
04846                                                         attacker->activator->client &&
04847                                                         attacker->activator->client->sess.sessionTeam != targ->teamnodmg)
04848                                                 { //uh, let them damage it I guess.
04849                                                 }
04850                                                 else
04851                                                 {
04852                                                         return;
04853                                                 }
04854                                         }
04855                                 }
04856                         }
04857                 }
04858         }
04859 
04860         // battlesuit protects from all radius damage (but takes knockback)
04861         // and protects 50% against all damage
04862         if ( client && client->ps.powerups[PW_BATTLESUIT] ) {
04863                 G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 );
04864                 if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) {
04865                         return;
04866                 }
04867                 damage *= 0.5;
04868         }
04869 
04870         // add to the attacker's hit counter (if the target isn't a general entity like a prox mine)
04871         if ( attacker->client && targ != attacker && targ->health > 0
04872                         && targ->s.eType != ET_MISSILE
04873                         && targ->s.eType != ET_GENERAL
04874                         && client) {
04875                 if ( OnSameTeam( targ, attacker ) ) {
04876                         attacker->client->ps.persistant[PERS_HITS]--;
04877                 } else {
04878                         attacker->client->ps.persistant[PERS_HITS]++;
04879                 }
04880                 attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]);
04881         }
04882 
04883         // always give half damage if hurting self... but not in siege.  Heavy weapons need a counter.
04884         // calculated after knockback, so rocket jumping works
04885         if ( targ == attacker && !(dflags & DAMAGE_NO_SELF_PROTECTION)) {
04886                 if ( g_gametype.integer == GT_SIEGE )
04887                 {
04888                         damage *= 1.5;
04889                 }
04890                 else
04891                 {
04892                         damage *= 0.5;
04893                 }
04894         }
04895 
04896         if ( damage < 1 ) {
04897                 damage = 1;
04898         }
04899         take = damage;
04900         save = 0;
04901 
04902         // save some from armor
04903         asave = CheckArmor (targ, take, dflags);
04904 
04905         if (asave)
04906         {
04907                 shieldAbsorbed = asave;
04908         }
04909 
04910         take -= asave;
04911         if ( targ->client )
04912         {//update vehicle shields and armor, check for explode 
04913                 if ( targ->client->NPC_class == CLASS_VEHICLE &&
04914                         targ->m_pVehicle )
04915                 {//FIXME: should be in its own function in g_vehicles.c now, too big to be here
04916                         int surface = -1;
04917                         if ( attacker )
04918                         {//so we know the last guy who shot at us
04919                                 targ->enemy = attacker;
04920                         }
04921 
04922                         if ( ( targ->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL ) )
04923                         {
04924                                 //((CVehicleNPC *)targ->NPC)->m_ulFlags |= CVehicleNPC::VEH_BUCKING;
04925                         }
04926 
04927                         targ->m_pVehicle->m_iShields = targ->client->ps.stats[STAT_ARMOR];
04928                         G_VehUpdateShields( targ );
04929                         targ->m_pVehicle->m_iArmor -= take;
04930                         if ( targ->m_pVehicle->m_iArmor <= 0 ) 
04931                         {
04932                                 targ->s.eFlags |= EF_DEAD;
04933                                 targ->client->ps.eFlags |= EF_DEAD;
04934                                 targ->m_pVehicle->m_iArmor = 0;
04935                         }
04936                         if ( targ->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
04937                         {//get the last surf that was hit
04938                                 if ( targ->client && targ->client->g2LastSurfaceTime == level.time)
04939                                 {
04940                                         char hitSurface[MAX_QPATH];
04941 
04942                                         trap_G2API_GetSurfaceName(targ->ghoul2, targ->client->g2LastSurfaceHit, 0, hitSurface);
04943 
04944                                         if (hitSurface[0])
04945                                         {
04946                                                 surface = G_ShipSurfaceForSurfName( &hitSurface[0] );
04947 
04948                                                 if ( take && surface > 0 )
04949                                                 {//hit a certain part of the ship
04950                                                         int deathPoint = 0;
04951 
04952                                                         targ->locationDamage[surface] += take;
04953 
04954                                                         switch(surface)
04955                                                         {
04956                                                         case SHIPSURF_FRONT:
04957                                                                 deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_front;
04958                                                                 break;
04959                                                         case SHIPSURF_BACK:
04960                                                                 deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_back;
04961                                                                 break;
04962                                                         case SHIPSURF_RIGHT:
04963                                                                 deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_right;
04964                                                                 break;
04965                                                         case SHIPSURF_LEFT:
04966                                                                 deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_left;
04967                                                                 break;
04968                                                         default:
04969                                                                 break;
04970                                                         }
04971 
04972                                                         //presume 0 means it wasn't set and so it should never die.
04973                                                         if ( deathPoint )
04974                                                         {
04975                                                                 if ( targ->locationDamage[surface] >= deathPoint)
04976                                                                 { //this area of the ship is now dead
04977                                                                         if ( G_FlyVehicleDestroySurface( targ, surface ) )
04978                                                                         {//actually took off a surface
04979                                                                                 G_VehicleSetDamageLocFlags( targ, surface, deathPoint );
04980                                                                         }
04981                                                                 }
04982                                                                 else
04983                                                                 {
04984                                                                         G_VehicleSetDamageLocFlags( targ, surface, deathPoint );
04985                                                                 }
04986                                                         }
04987                                                 }
04988                                         }
04989                                 }
04990                         }
04991                         if ( targ->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
04992                         {
04993                                 /*
04994                                 if ( targ->m_pVehicle->m_iArmor <= 0 ) 
04995                                 {//vehicle all out of armor
04996                                         Vehicle_t *pVeh = targ->m_pVehicle;
04997                                         if ( pVeh->m_iDieTime == 0 )
04998                                         {//just start the flaming effect and explosion delay, if it's not going already...
04999                                                 pVeh->m_pVehicleInfo->StartDeathDelay( pVeh, 0 );
05000                                         }
05001                                 }
05002                                 else*/
05003                                 if ( attacker 
05004                                                 //&& attacker->client 
05005                                                 && targ != attacker
05006                                                 && point 
05007                                                 && !VectorCompare( targ->client->ps.origin, point )
05008                                                 && targ->m_pVehicle->m_LandTrace.fraction >= 1.0f)
05009                                 {//just took a hit, knock us around
05010                                         vec3_t  vUp, impactDir;
05011                                         float   impactStrength = (damage/200.0f)*10.0f;
05012                                         float   dot = 0.0f;
05013                                         if ( impactStrength > 10.0f )
05014                                         {
05015                                                 impactStrength = 10.0f;
05016                                         }
05017                                         //pitch or roll us based on where we were hit
05018                                         AngleVectors( targ->m_pVehicle->m_vOrientation, NULL, NULL, vUp );
05019                                         VectorSubtract( point, targ->r.currentOrigin, impactDir );
05020                                         VectorNormalize( impactDir );
05021                                         if ( surface <= 0 )
05022                                         {//no surf guess where we were hit, then
05023                                                 vec3_t  vFwd, vRight;
05024                                                 AngleVectors( targ->m_pVehicle->m_vOrientation, vFwd, vRight, vUp );
05025                                                 dot = DotProduct( vRight, impactDir );
05026                                                 if ( dot > 0.4f )
05027                                                 {
05028                                                         surface = SHIPSURF_RIGHT;
05029                                                 }
05030                                                 else if ( dot < -0.4f )
05031                                                 {
05032                                                         surface = SHIPSURF_LEFT;
05033                                                 }
05034                                                 else
05035                                                 {
05036                                                         dot = DotProduct( vFwd, impactDir );
05037                                                         if ( dot > 0.0f )
05038                                                         {
05039                                                                 surface = SHIPSURF_FRONT;
05040                                                         }
05041                                                         else
05042                                                         {
05043                                                                 surface = SHIPSURF_BACK;
05044                                                         }
05045                                                 }
05046                                         }
05047                                         switch ( surface )
05048                                         {
05049                                         case SHIPSURF_FRONT:
05050                                                 dot = DotProduct( vUp, impactDir );
05051                                                 if ( dot > 0 )
05052                                                 {
05053                                                         targ->m_pVehicle->m_vOrientation[PITCH] += impactStrength;
05054                                                 }
05055                                                 else
05056                                                 {
05057                                                         targ->m_pVehicle->m_vOrientation[PITCH] -= impactStrength;
05058                                                 }
05059                                                 break;
05060                                         case SHIPSURF_BACK:
05061                                                 dot = DotProduct( vUp, impactDir );
05062                                                 if ( dot > 0 )
05063                                                 {
05064                                                         targ->m_pVehicle->m_vOrientation[PITCH] -= impactStrength;
05065                                                 }
05066                                                 else
05067                                                 {
05068                                                         targ->m_pVehicle->m_vOrientation[PITCH] += impactStrength;
05069                                                 }
05070                                                 break;
05071                                         case SHIPSURF_RIGHT:
05072                                                 dot = DotProduct( vUp, impactDir );
05073                                                 if ( dot > 0 )
05074                                                 {
05075                                                         targ->m_pVehicle->m_vOrientation[ROLL] -= impactStrength;
05076                                                 }
05077                                                 else
05078                                                 {
05079                                                         targ->m_pVehicle->m_vOrientation[ROLL] += impactStrength;
05080                                                 }
05081                                                 break;
05082                                         case SHIPSURF_LEFT:
05083                                                 dot = DotProduct( vUp, impactDir );
05084                                                 if ( dot > 0 )
05085                                                 {
05086                                                         targ->m_pVehicle->m_vOrientation[ROLL] += impactStrength;
05087                                                 }
05088                                                 else
05089                                                 {
05090                                                         targ->m_pVehicle->m_vOrientation[ROLL] -= impactStrength;
05091                                                 }
05092                                                 break;
05093                                         }
05094 
05095                                 }
05096                         }
05097                 }
05098         }
05099 
05100         if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
05101         {//FIXME: screw with non-animal vehicles, too?
05102                 if ( client )
05103                 {
05104                         if ( client->NPC_class == CLASS_VEHICLE 
05105                                 && targ->m_pVehicle
05106                                 && targ->m_pVehicle->m_pVehicleInfo
05107                                 && targ->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
05108                         {//all damage goes into the disruption of shields and systems
05109                                 take = 0;
05110                         }
05111                         else
05112                         {
05113 
05114                                 if (client->jetPackOn)
05115                                 { //disable jetpack temporarily
05116                                         Jetpack_Off(targ);
05117                                         client->jetPackToggleTime = level.time + Q_irand(3000, 10000);
05118                                 }
05119 
05120                                 if ( client->NPC_class == CLASS_PROTOCOL || client->NPC_class == CLASS_SEEKER ||
05121                                         client->NPC_class == CLASS_R2D2 || client->NPC_class == CLASS_R5D2 ||
05122                                         client->NPC_class == CLASS_MOUSE || client->NPC_class == CLASS_GONK )
05123                                 {
05124                                         // DEMP2 does more damage to these guys.
05125                                         take *= 2;
05126                                 }
05127                                 else if ( client->NPC_class == CLASS_PROBE || client->NPC_class == CLASS_INTERROGATOR ||
05128                                                         client->NPC_class == CLASS_MARK1 || client->NPC_class == CLASS_MARK2 || client->NPC_class == CLASS_SENTRY ||
05129                                                         client->NPC_class == CLASS_ATST )
05130                                 {
05131                                         // DEMP2 does way more damage to these guys.
05132                                         take *= 5;
05133                                 }
05134                                 else
05135                                 {
05136                                         if (take > 0)
05137                                         {
05138                                                 take /= 3;
05139                                                 if (take < 1)
05140                                                 {
05141                                                         take = 1;
05142                                                 }
05143                                         }
05144                                 }
05145                         }
05146                 }
05147         }
05148 
05149 #ifndef FINAL_BUILD
05150         if ( g_debugDamage.integer ) {
05151                 G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number,
05152                         targ->health, take, asave );
05153         }
05154 #endif
05155 
05156         // add to the damage inflicted on a player this frame
05157         // the total will be turned into screen blends and view angle kicks
05158         // at the end of the frame
05159         if ( client ) {
05160                 if ( attacker ) {
05161                         client->ps.persistant[PERS_ATTACKER] = attacker->s.number;
05162                 } else {
05163                         client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD;
05164                 }
05165                 client->damage_armor += asave;
05166                 client->damage_blood += take;
05167                 client->damage_knockback += knockback;
05168                 if ( dir ) {
05169                         VectorCopy ( dir, client->damage_from );
05170                         client->damage_fromWorld = qfalse;
05171                 } else {
05172                         VectorCopy ( targ->r.currentOrigin, client->damage_from );
05173                         client->damage_fromWorld = qtrue;
05174                 }
05175 
05176                 if (attacker && attacker->client)
05177                 {
05178                         BotDamageNotification(client, attacker);
05179                 }
05180                 else if (inflictor && inflictor->client)
05181                 {
05182                         BotDamageNotification(client, inflictor);
05183                 }
05184         }
05185 
05186         // See if it's the player hurting the emeny flag carrier
05187         if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) {
05188                 Team_CheckHurtCarrier(targ, attacker);
05189         }
05190 
05191         if (targ->client) {
05192                 // set the last client who damaged the target
05193                 targ->client->lasthurt_client = attacker->s.number;
05194                 targ->client->lasthurt_mod = mod;
05195         }
05196 
05197         if ( !(dflags & DAMAGE_NO_PROTECTION) ) 
05198         {//protect overridden by no_protection
05199                 if (take && targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_PROTECT)))
05200                 {
05201                         if (targ->client->ps.fd.forcePower)
05202                         {
05203                                 int maxtake = take;
05204 
05205                                 //G_Sound(targ, CHAN_AUTO, protectHitSound);
05206                                 if (targ->client->forcePowerSoundDebounce < level.time)
05207                                 {
05208                                         G_PreDefSound(targ->client->ps.origin, PDSOUND_PROTECTHIT);
05209                                         targ->client->forcePowerSoundDebounce = level.time + 400;
05210                                 }
05211 
05212                                 if (targ->client->ps.fd.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_1)
05213                                 {
05214                                         famt = 1;
05215                                         hamt = 0.40;
05216 
05217                                         if (maxtake > 100)
05218                                         {
05219                                                 maxtake = 100;
05220                                         }
05221                                 }
05222                                 else if (targ->client->ps.fd.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_2)
05223                                 {
05224                                         famt = 0.5;
05225                                         hamt = 0.60;
05226 
05227                                         if (maxtake > 200)
05228                                         {
05229                                                 maxtake = 200;
05230                                         }
05231                                 }
05232                                 else if (targ->client->ps.fd.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_3)
05233                                 {
05234                                         famt = 0.25;
05235                                         hamt = 0.80;
05236 
05237                                         if (maxtake > 400)
05238                                         {
05239                                                 maxtake = 400;
05240                                         }
05241                                 }
05242 
05243                                 if (!targ->client->ps.powerups[PW_FORCE_BOON])
05244                                 {
05245                                         targ->client->ps.fd.forcePower -= maxtake*famt;
05246                                 }
05247                                 else
05248                                 {
05249                                         targ->client->ps.fd.forcePower -= (maxtake*famt)/2;
05250                                 }
05251                                 subamt = (maxtake*hamt)+(take-maxtake);
05252                                 if (targ->client->ps.fd.forcePower < 0)
05253                                 {
05254                                         subamt += targ->client->ps.fd.forcePower;
05255                                         targ->client->ps.fd.forcePower = 0;
05256                                 }
05257                                 if (subamt)
05258                                 {
05259                                         take -= subamt;
05260 
05261                                         if (take < 0)
05262                                         {
05263                                                 take = 0;
05264                                         }
05265                                 }
05266                         }
05267                 }
05268         }
05269 
05270         if (shieldAbsorbed)
05271         {
05272                 /*
05273                 if ( targ->client->NPC_class == CLASS_VEHICLE )
05274                 {
05275                         targ->client->ps.electrifyTime = level.time + Q_irand( 500, 1000 );
05276                 }
05277                 else
05278                 */
05279                 {
05280                         gentity_t       *evEnt;
05281 
05282                         // Send off an event to show a shield shell on the player, pointing in the right direction.
05283                         //evEnt = G_TempEntity(vec3_origin, EV_SHIELD_HIT);
05284                         //rww - er.. what the? This isn't broadcast, why is it being set on vec3_origin?!
05285                         evEnt = G_TempEntity(targ->r.currentOrigin, EV_SHIELD_HIT);
05286                         evEnt->s.otherEntityNum = targ->s.number;
05287                         evEnt->s.eventParm = DirToByte(dir);
05288                         evEnt->s.time2=shieldAbsorbed;
05289         /*
05290                         shieldAbsorbed *= 20;
05291 
05292                         if (shieldAbsorbed > 1500)
05293                         {
05294                                 shieldAbsorbed = 1500;
05295                         }
05296                         if (shieldAbsorbed < 200)
05297                         {
05298                                 shieldAbsorbed = 200;
05299                         }
05300 
05301                         if (targ->client->ps.powerups[PW_SHIELDHIT] < (level.time + shieldAbsorbed))
05302                         {
05303                                 targ->client->ps.powerups[PW_SHIELDHIT] = level.time + shieldAbsorbed;
05304                         }
05305                         //flicker for as many ms as damage was absorbed (*20)
05306                         //therefore 10 damage causes 1/5 of a seond of flickering, whereas
05307                         //a full 100 causes 2 seconds (but is reduced to 1.5 seconds due to the max)
05308 
05309         */
05310                 }
05311         }
05312 
05313         // do the damage
05314         if (take) 
05315         {
05316                 if (targ->client && targ->s.number < MAX_CLIENTS &&
05317                         (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT))
05318                 { //uh.. shock them or something. what the hell, I don't know.
05319             if (targ->client->ps.weaponTime <= 0)
05320                         { //yeah, we were supposed to be beta a week ago, I don't feel like
05321                                 //breaking the game so I'm gonna be safe and only do this only
05322                                 //if your weapon is not busy
05323                                 targ->client->ps.weaponTime = 2000;
05324                                 targ->client->ps.electrifyTime = level.time + 2000;
05325                                 if (targ->client->ps.weaponstate == WEAPON_CHARGING ||
05326                                         targ->client->ps.weaponstate == WEAPON_CHARGING_ALT)
05327                                 {
05328                                         targ->client->ps.weaponstate = WEAPON_READY;
05329                                 }
05330                         }
05331                 }
05332 
05333                 if ( !(dflags & DAMAGE_NO_PROTECTION) ) 
05334                 {//rage overridden by no_protection
05335                         if (targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_RAGE)) && (inflictor->client || attacker->client))
05336                         {
05337                                 take /= (targ->client->ps.fd.forcePowerLevel[FP_RAGE]+1);
05338                         }
05339                 }
05340                 targ->health = targ->health - take;
05341 
05342                 if ( (targ->flags&FL_UNDYING) )
05343                 {//take damage down to 1, but never die
05344                         if ( targ->health < 1 )
05345                         {
05346                                 targ->health = 1;
05347                         }
05348                 }
05349 
05350                 if ( targ->client ) {
05351                         targ->client->ps.stats[STAT_HEALTH] = targ->health;
05352                 }
05353 
05354                 if ( !(dflags & DAMAGE_NO_PROTECTION) ) 
05355                 {//rage overridden by no_protection
05356                         if (targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_RAGE)) && (inflictor->client || attacker->client))
05357                         {
05358                                 if (targ->health <= 0)
05359                                 {
05360                                         targ->health = 1;
05361                                 }
05362                                 if (targ->client->ps.stats[STAT_HEALTH] <= 0)
05363                                 {
05364                                         targ->client->ps.stats[STAT_HEALTH] = 1;
05365                                 }
05366                         }
05367                 }
05368 
05369                 //We want to go ahead and set gPainHitLoc regardless of if we have a pain func,
05370                 //so we can adjust the location damage too.
05371                 if (targ->client && targ->ghoul2 && targ->client->g2LastSurfaceTime == level.time)
05372                 { //We updated the hit surface this frame, so it's valid.
05373                         char hitSurface[MAX_QPATH];
05374 
05375                         trap_G2API_GetSurfaceName(targ->ghoul2, targ->client->g2LastSurfaceHit, 0, hitSurface);
05376 
05377                         if (hitSurface[0])
05378                         {
05379                                 G_GetHitLocFromSurfName(targ, hitSurface, &gPainHitLoc, point, dir, vec3_origin, mod);
05380                         }
05381                         else
05382                         {
05383                                 gPainHitLoc = -1;
05384                         }
05385 
05386                         if (gPainHitLoc < HL_MAX && gPainHitLoc >= 0 && targ->locationDamage[gPainHitLoc] < Q3_INFINITE &&
05387                                 (targ->s.eType == ET_PLAYER || targ->s.NPC_class != CLASS_VEHICLE))
05388                         {
05389                                 targ->locationDamage[gPainHitLoc] += take;
05390 
05391                                 if (g_armBreakage.integer && !targ->client->ps.brokenLimbs &&
05392                                         targ->client->ps.stats[STAT_HEALTH] > 0 && targ->health > 0 &&
05393                                         !(targ->s.eFlags & EF_DEAD))
05394                                 { //check for breakage
05395                                         if (targ->locationDamage[HL_ARM_RT]+targ->locationDamage[HL_HAND_RT] >= 80)
05396                                         {
05397                                                 G_BreakArm(targ, BROKENLIMB_RARM);
05398                                         }
05399                                         else if (targ->locationDamage[HL_ARM_LT]+targ->locationDamage[HL_HAND_LT] >= 80)
05400                                         {
05401                                                 G_BreakArm(targ, BROKENLIMB_LARM);
05402                                         }
05403                                 }
05404                         }
05405                 }
05406                 else
05407                 {
05408                         gPainHitLoc = -1;
05409                 }
05410 
05411                 if (targ->maxHealth)
05412                 { //if this is non-zero this guy should be updated his s.health to send to the client
05413                         G_ScaleNetHealth(targ);
05414                 }
05415 
05416                 if ( targ->health <= 0 ) {
05417                         if ( client )
05418                         {
05419                                 targ->flags |= FL_NO_KNOCKBACK;
05420 
05421                                 if (point)
05422                                 {
05423                                         VectorCopy( point, targ->pos1 );
05424                                 }
05425                                 else
05426                                 {
05427                                         VectorCopy(targ->client->ps.origin, targ->pos1);
05428                                 }
05429                         }
05430                         else if (targ->s.eType == ET_NPC)
05431                         { //g2animent
05432                                 VectorCopy(point, targ->pos1);
05433                         }
05434 
05435                         if (targ->health < -999)
05436                                 targ->health = -999;
05437 
05438                         // If we are a breaking glass brush, store the damage point so we can do cool things with it.
05439                         if ( targ->r.svFlags & SVF_GLASS_BRUSH )
05440                         {
05441                                 VectorCopy( point, targ->pos1 );
05442                                 if (dir)
05443                                 {
05444                                         VectorCopy( dir, targ->pos2 );
05445                                 }
05446                                 else
05447                                 {
05448                                         VectorClear(targ->pos2);
05449                                 }
05450                         }
05451 
05452                         if (targ->s.eType == ET_NPC &&
05453                                 targ->client &&
05454                                 (targ->s.eFlags & EF_DEAD))
05455                         { //an NPC that's already dead. Maybe we can cut some more limbs off!
05456                                 if ( (mod == MOD_SABER || (mod == MOD_MELEE && G_HeavyMelee( attacker )) )//saber or heavy melee (claws)
05457                                         && take > 2
05458                                         && !(dflags&DAMAGE_NO_DISMEMBER) )
05459                                 {
05460                                         G_CheckForDismemberment(targ, attacker, targ->pos1, take, targ->client->ps.torsoAnim, qtrue);
05461                                 }
05462                         }
05463 
05464                         targ->enemy = attacker;
05465                         targ->die (targ, inflictor, attacker, take, mod);
05466                         G_ActivateBehavior( targ, BSET_DEATH );
05467                         return;
05468                 } 
05469                 else 
05470                 {
05471                         if ( g_debugMelee.integer )
05472                         {//getting hurt makes you let go of the wall
05473                                 if ( targ->client && (targ->client->ps.pm_flags&PMF_STUCK_TO_WALL) )
05474                                 {
05475                                         G_LetGoOfWall( targ );
05476                                 }
05477                         }
05478                         if ( targ->pain ) 
05479                         {
05480                                 if (targ->s.eType != ET_NPC || mod != MOD_SABER || take > 1)
05481                                 { //don't even notify NPCs of pain if it's just idle saber damage
05482                                         gPainMOD = mod;
05483                                         if (point)
05484                                         {
05485                                                 VectorCopy(point, gPainPoint);
05486                                         }
05487                                         else
05488                                         {
05489                                                 VectorCopy(targ->r.currentOrigin, gPainPoint);
05490                                         }
05491                                         targ->pain (targ, attacker, take);
05492                                 }
05493                         }
05494                 }
05495 
05496                 G_LogWeaponDamage(attacker->s.number, mod, take);
05497         }
05498 
05499 }
05500 
05501 
05502 /*
05503 ============
05504 CanDamage
05505 
05506 Returns qtrue if the inflictor can directly damage the target.  Used for
05507 explosions and melee attacks.
05508 ============
05509 */
05510 qboolean CanDamage (gentity_t *targ, vec3_t origin) {
05511         vec3_t  dest;
05512         trace_t tr;
05513         vec3_t  midpoint;
05514 
05515         // use the midpoint of the bounds instead of the origin, because
05516         // bmodels may have their origin is 0,0,0
05517         VectorAdd (targ->r.absmin, targ->r.absmax, midpoint);
05518         VectorScale (midpoint, 0.5, midpoint);
05519 
05520         VectorCopy (midpoint, dest);
05521         trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID);
05522         if (tr.fraction == 1.0 || tr.entityNum == targ->s.number)
05523                 return qtrue;
05524 
05525         // this should probably check in the plane of projection, 
05526         // rather than in world coordinate, and also include Z
05527         VectorCopy (midpoint, dest);
05528         dest[0] += 15.0;
05529         dest[1] += 15.0;
05530         trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID);
05531         if (tr.fraction == 1.0)
05532                 return qtrue;
05533 
05534         VectorCopy (midpoint, dest);
05535         dest[0] += 15.0;
05536         dest[1] -= 15.0;
05537         trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID);
05538         if (tr.fraction == 1.0)
05539                 return qtrue;
05540 
05541         VectorCopy (midpoint, dest);
05542         dest[0] -= 15.0;
05543         dest[1] += 15.0;
05544         trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID);
05545         if (tr.fraction == 1.0)
05546                 return qtrue;
05547 
05548         VectorCopy (midpoint, dest);
05549         dest[0] -= 15.0;
05550         dest[1] -= 15.0;
05551         trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID);
05552         if (tr.fraction == 1.0)
05553                 return qtrue;
05554 
05555 
05556         return qfalse;
05557 }
05558 
05559 
05560 /*
05561 ============
05562 G_RadiusDamage
05563 ============
05564 */
05565 qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius,
05566                                          gentity_t *ignore, gentity_t *missile, int mod) {
05567         float           points, dist;
05568         gentity_t       *ent;
05569         int                     entityList[MAX_GENTITIES];
05570         int                     numListedEntities;
05571         vec3_t          mins, maxs;
05572         vec3_t          v;
05573         vec3_t          dir;
05574         int                     i, e;
05575         qboolean        hitClient = qfalse;
05576         qboolean        roastPeople = qfalse;
05577 
05578         /*
05579         if (missile && !missile->client && missile->s.weapon > WP_NONE &&
05580                 missile->s.weapon < WP_NUM_WEAPONS && missile->r.ownerNum >= 0 &&
05581                 (missile->r.ownerNum < MAX_CLIENTS || g_entities[missile->r.ownerNum].s.eType == ET_NPC))
05582         { //sounds like it's a valid weapon projectile.. is it a valid explosive to create marks from?
05583                 switch(missile->s.weapon)
05584                 {
05585                 case WP_FLECHETTE: //flechette issuing this will be alt-fire
05586                 case WP_ROCKET_LAUNCHER:
05587                 case WP_THERMAL:
05588                 case WP_TRIP_MINE:
05589                 case WP_DET_PACK:
05590                         roastPeople = qtrue; //Then create explosive marks
05591                         break;
05592                 default:
05593                         break;
05594                 }
05595         }
05596         */
05597         //oh well.. maybe sometime? I am trying to cut down on tempent use.
05598 
05599         if ( radius < 1 ) {
05600                 radius = 1;
05601         }
05602 
05603         for ( i = 0 ; i < 3 ; i++ ) {
05604                 mins[i] = origin[i] - radius;
05605                 maxs[i] = origin[i] + radius;
05606         }
05607 
05608         numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
05609 
05610         for ( e = 0 ; e < numListedEntities ; e++ ) {
05611                 ent = &g_entities[entityList[ e ]];
05612 
05613                 if (ent == ignore)
05614                         continue;
05615                 if (!ent->takedamage)
05616                         continue;
05617 
05618                 // find the distance from the edge of the bounding box
05619                 for ( i = 0 ; i < 3 ; i++ ) {
05620                         if ( origin[i] < ent->r.absmin[i] ) {
05621                                 v[i] = ent->r.absmin[i] - origin[i];
05622                         } else if ( origin[i] > ent->r.absmax[i] ) {
05623                                 v[i] = origin[i] - ent->r.absmax[i];
05624                         } else {
05625                                 v[i] = 0;
05626                         }
05627                 }
05628 
05629                 dist = VectorLength( v );
05630                 if ( dist >= radius ) {
05631                         continue;
05632                 }
05633 
05634                 points = damage * ( 1.0 - dist / radius );
05635 
05636                 if( CanDamage (ent, origin) ) {
05637                         if( LogAccuracyHit( ent, attacker ) ) {
05638                                 hitClient = qtrue;
05639                         }
05640                         VectorSubtract (ent->r.currentOrigin, origin, dir);
05641                         // push the center of mass higher than the origin so players
05642                         // get knocked into the air more
05643                         dir[2] += 24;
05644                         if (attacker && attacker->inuse && attacker->client &&
05645                                 attacker->s.eType == ET_NPC && attacker->s.NPC_class == CLASS_VEHICLE &&
05646                                 attacker->m_pVehicle && attacker->m_pVehicle->m_pPilot)
05647                         { //say my pilot did it.
05648                                 G_Damage (ent, NULL, (gentity_t *)attacker->m_pVehicle->m_pPilot, dir, origin, (int)points, DAMAGE_RADIUS, mod);
05649                         }
05650                         else
05651                         {
05652                                 G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod);
05653                         }
05654 
05655                         if (ent && ent->client && roastPeople && missile &&
05656                                 !VectorCompare(ent->r.currentOrigin, missile->r.currentOrigin))
05657                         { //the thing calling this function can create burn marks on people, so create an event to do so
05658                                 gentity_t *evEnt = G_TempEntity(ent->r.currentOrigin, EV_GHOUL2_MARK);
05659 
05660                                 evEnt->s.otherEntityNum = ent->s.number; //the entity the mark should be placed on
05661                                 evEnt->s.weapon = WP_ROCKET_LAUNCHER; //always say it's rocket so we make the right mark
05662 
05663                                 //Try to place the decal by going from the missile location to the location of the person that was hit
05664                                 VectorCopy(missile->r.currentOrigin, evEnt->s.origin);
05665                                 VectorCopy(ent->r.currentOrigin, evEnt->s.origin2);
05666 
05667                                 //it's hacky, but we want to move it up so it's more likely to hit
05668                                 //the torso.
05669                                 if (missile->r.currentOrigin[2] < ent->r.currentOrigin[2])
05670                                 { //move it up less so the decal is placed lower on the model then
05671                                         evEnt->s.origin2[2] += 8;
05672                                 }
05673                                 else
05674                                 {
05675                                         evEnt->s.origin2[2] += 24;
05676                                 }
05677 
05678                                 //Special col check
05679                                 evEnt->s.eventParm = 1;
05680                         }
05681                 }
05682         }
05683 
05684         return hitClient;
05685 }