1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 // g_combat.c
25 
26 #include "g_local.h"
27 #include "b_local.h"
28 #include "g_functions.h"
29 #include "anims.h"
30 #include "objectives.h"
31 #include "../cgame/cg_local.h"
32 #include "wp_saber.h"
33 #include "g_vehicles.h"
34 #include "Q3_Interface.h"
35 #include "g_navigator.h"
36 
37 #define TURN_OFF			0x00000100
38 
39 extern qboolean Rosh_TwinPresent( gentity_t *self );
40 extern void G_CheckCharmed( gentity_t *self );
41 extern qboolean Wampa_CheckDropVictim( gentity_t *self, qboolean excludeMe );
42 
43 extern	cvar_t	*g_debugDamage;
44 extern qboolean	stop_icarus;
45 extern cvar_t	*g_dismemberment;
46 extern cvar_t	*g_saberRealisticCombat;
47 extern cvar_t	*g_saberPickuppableDroppedSabers;
48 extern cvar_t		*g_timescale;
49 extern cvar_t		*d_slowmodeath;
50 extern gentity_t *player;
51 extern cvar_t	*debug_subdivision;
52 extern cvar_t	*g_dismemberProbabilities;
53 
54 gentity_t *g_lastClientDamaged;
55 
56 extern int killPlayerTimer;
57 
58 extern void G_VehicleStartExplosionDelay( gentity_t *self );
59 extern void NPC_TempLookTarget ( gentity_t *self, int lookEntNum, int minLookTime, int maxLookTime );
60 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
61 extern qboolean PM_HasAnimation( gentity_t *ent, int animation );
62 extern qboolean G_TeamEnemy( gentity_t *self );
63 extern void CG_ChangeWeapon( int num );
64 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
65 
66 extern void G_SetEnemy( gentity_t *self, gentity_t *enemy );
67 extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
68 extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
69 extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim );
70 extern qboolean PM_InOnGroundAnim ( playerState_t *ps );
71 extern void G_ATSTCheckPain( gentity_t *self, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc );
72 extern qboolean Jedi_WaitingAmbush( gentity_t *self );
73 extern qboolean G_ClearViewEntity( gentity_t *ent );
74 extern qboolean PM_CrouchAnim( int anim );
75 extern qboolean PM_InKnockDown( playerState_t *ps );
76 extern qboolean PM_InRoll( playerState_t *ps );
77 extern qboolean PM_SpinningAnim( int anim );
78 extern qboolean PM_RunningAnim( int anim );
79 extern int PM_PowerLevelForSaberAnim( playerState_t *ps, int saberNum = 0 );
80 extern qboolean PM_SaberInSpecialAttack( int anim );
81 extern qboolean PM_SpinningSaberAnim( int anim );
82 extern qboolean PM_FlippingAnim( int anim );
83 extern qboolean PM_InSpecialJump( int anim );
84 extern qboolean PM_RollingAnim( int anim );
85 extern qboolean PM_InAnimForSaberMove( int anim, int saberMove );
86 extern qboolean PM_SaberInStart( int move );
87 extern qboolean PM_SaberInReturn( int move );
88 extern int PM_AnimLength( int index, animNumber_t anim );
89 extern qboolean PM_LockedAnim( int anim );
90 extern qboolean PM_KnockDownAnim( int anim );
91 extern void G_SpeechEvent( gentity_t *self, int event );
92 extern qboolean Rosh_BeingHealed( gentity_t *self );
93 
94 static int G_CheckForLedge( gentity_t *self, vec3_t fallCheckDir, float checkDist );
95 static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc );
96 static int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc );
97 static void G_TrackWeaponUsage( gentity_t *self, gentity_t *inflictor, int add, int mod );
98 static qboolean G_Dismemberable( gentity_t *self, int hitLoc );
99 extern gitem_t	*FindItemForAmmo( ammo_t ammo );
100 extern void WP_RemoveSaber( gentity_t *ent, int saberNum );
101 
102 
103 qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, char *returnSurfName, int returnSize );
104 /*
105 ============
106 AddScore
107 
108 Adds score to both the client and his team
109 ============
110 */
AddScore(gentity_t * ent,int score)111 void AddScore( gentity_t *ent, int score ) {
112 	if ( !ent->client ) {
113 		return;
114 	}
115 	// no scoring during pre-match warmup
116 	ent->client->ps.persistant[PERS_SCORE] += score;
117 }
118 
119 /*
120 =================
121 TossClientItems
122 
123 Toss the weapon and powerups for the killed player
124 =================
125 */
126 extern gentity_t *WP_DropThermal( gentity_t *ent );
127 extern qboolean WP_SaberLose( gentity_t *self, vec3_t throwDir );
TossClientItems(gentity_t * self)128 gentity_t *TossClientItems( gentity_t *self )
129 {
130 	//FIXME: drop left-hand weapon, too?
131 	gentity_t	*dropped = NULL;
132 	gitem_t		*item = NULL;
133 	int			weapon;
134 
135 	if ( self->client->NPC_class == CLASS_SEEKER
136 		|| self->client->NPC_class == CLASS_REMOTE
137 		|| self->client->NPC_class == CLASS_SABER_DROID
138 		|| self->client->NPC_class == CLASS_VEHICLE
139 		|| self->client->NPC_class == CLASS_ATST)
140 	{
141 		// these things are so small that they shouldn't bother throwing anything
142 		return NULL;
143 	}
144 
145 	// drop the weapon if not a saber or enemy-only weapon
146 	weapon = self->s.weapon;
147 	if ( weapon == WP_SABER )
148 	{
149 		if ( self->weaponModel[0] < 0 )
150 		{//don't have one in right hand
151 			self->s.weapon = WP_NONE;
152 		}
153 		else if ( !(self->client->ps.saber[0].saberFlags&SFL_NOT_DISARMABLE)
154 			|| g_saberPickuppableDroppedSabers->integer )
155 		{//okay to drop it
156 			if ( WP_SaberLose( self, NULL ) )
157 			{
158 				self->s.weapon = WP_NONE;
159 			}
160 		}
161 		if ( g_saberPickuppableDroppedSabers->integer )
162 		{//drop your left one, too
163 			if ( self->weaponModel[1] >= 0 )
164 			{//have one in left
165 				if ( !(self->client->ps.saber[0].saberFlags&SFL_NOT_DISARMABLE)
166 					|| g_saberPickuppableDroppedSabers->integer )
167 				{//okay to drop it
168 					//just drop an item
169 					if ( self->client->ps.saber[1].name
170 						&& self->client->ps.saber[1].name[0] )
171 					{//have a valid string to use for saberType
172 						//turn it into a pick-uppable item!
173 						if ( G_DropSaberItem( self->client->ps.saber[1].name, self->client->ps.saber[1].blade[0].color, self->client->renderInfo.handLPoint, self->client->ps.velocity, self->currentAngles ) != NULL )
174 						{//dropped it
175 							WP_RemoveSaber( self, 1 );
176 						}
177 					}
178 				}
179 			}
180 		}
181 	}
182 	else if ( weapon == WP_BLASTER_PISTOL )
183 	{//FIXME: either drop the pistol and make the pickup only give ammo or drop ammo
184 	}
185 	else if ( weapon == WP_STUN_BATON
186 		|| weapon == WP_MELEE )
187 	{//never drop these
188 	}
189 	else if ( weapon > WP_SABER && weapon <= MAX_PLAYER_WEAPONS )//&& self->client->ps.ammo[ weaponData[weapon].ammoIndex ]
190 	{
191 		self->s.weapon = WP_NONE;
192 
193 		if ( weapon == WP_THERMAL && self->client->ps.torsoAnim == BOTH_ATTACK10 )
194 		{//we were getting ready to throw the thermal, drop it!
195 			self->client->ps.weaponChargeTime = level.time - FRAMETIME;//so it just kind of drops it
196 			dropped = WP_DropThermal( self );
197 		}
198 		else
199 		{// find the item type for this weapon
200 			item = FindItemForWeapon( (weapon_t) weapon );
201 		}
202 		if ( item && !dropped )
203 		{
204 			// spawn the item
205 			dropped = Drop_Item( self, item, 0, qtrue );
206 			//TEST: dropped items never go away
207 			dropped->e_ThinkFunc = thinkF_NULL;
208 			dropped->nextthink = -1;
209 
210 			if ( !self->s.number )
211 			{//player's dropped items never go away
212 				//dropped->e_ThinkFunc = thinkF_NULL;
213 				//dropped->nextthink = -1;
214 				dropped->count = 0;//no ammo
215 			}
216 			else
217 			{//FIXME: base this on the NPC's actual amount of ammo he's used up...
218 				switch ( weapon )
219 				{
220 				case WP_BRYAR_PISTOL:
221 				case WP_BLASTER_PISTOL:
222 					dropped->count = 20;
223 					break;
224 				case WP_BLASTER:
225 					dropped->count = 15;
226 					break;
227 				case WP_DISRUPTOR:
228 					dropped->count = 20;
229 					break;
230 				case WP_BOWCASTER:
231 					dropped->count = 5;
232 					break;
233 				case WP_REPEATER:
234 					dropped->count = 20;
235 					break;
236 				case WP_DEMP2:
237 					dropped->count = 10;
238 					break;
239 				case WP_FLECHETTE:
240 					dropped->count = 30;
241 					break;
242 				case WP_ROCKET_LAUNCHER:
243 					dropped->count = 3;
244 					break;
245 				case WP_CONCUSSION:
246 					dropped->count = 200;//12;
247 					break;
248 				case WP_THERMAL:
249 					dropped->count = 4;
250 					break;
251 				case WP_TRIP_MINE:
252 					dropped->count = 3;
253 					break;
254 				case WP_DET_PACK:
255 					dropped->count = 1;
256 					break;
257 				case WP_STUN_BATON:
258 					dropped->count = 20;
259 					break;
260 				default:
261 					dropped->count = 0;
262 					break;
263 				}
264 			}
265 			// well, dropped weapons are G2 models, so they have to be initialised if they want to draw..give us a radius so we don't get prematurely culled
266 			if ( weapon != WP_THERMAL
267 				&& weapon != WP_TRIP_MINE
268 				&& weapon != WP_DET_PACK )
269 			{
270 				gi.G2API_InitGhoul2Model( dropped->ghoul2, item->world_model, G_ModelIndex( item->world_model ), NULL_HANDLE, NULL_HANDLE, 0, 0);
271 				dropped->s.radius = 10;
272 			}
273 		}
274 	}
275 //	else if (( self->client->NPC_class == CLASS_SENTRY ) || ( self->client->NPC_class == CLASS_PROBE )) // Looks dumb, Steve told us to take it out.
276 //	{
277 //		item = FindItemForAmmo( AMMO_BLASTER );
278 //		Drop_Item( self, item, 0, qtrue );
279 //	}
280 	else if ( self->client->NPC_class == CLASS_MARK1 )
281 	{
282 
283 		if (Q_irand( 1, 2 )>1)
284 		{
285 			item = FindItemForAmmo( AMMO_METAL_BOLTS );
286 		}
287 		else
288 		{
289 			item = FindItemForAmmo( AMMO_BLASTER );
290 		}
291 		Drop_Item( self, item, 0, qtrue );
292 	}
293 	else if ( self->client->NPC_class == CLASS_MARK2 )
294 	{
295 
296 		if (Q_irand( 1, 2 )>1)
297 		{
298 			item = FindItemForAmmo( AMMO_METAL_BOLTS );
299 		}
300 		else
301 		{
302 			item = FindItemForAmmo( AMMO_POWERCELL );
303 		}
304 		Drop_Item( self, item, 0, qtrue );
305 	}
306 
307 	return dropped;//NOTE: presumes only drop one thing
308 }
309 
G_DropKey(gentity_t * self)310 void G_DropKey( gentity_t *self )
311 {//drop whatever security key I was holding
312 	gitem_t		*item = NULL;
313 	if ( !Q_stricmp( "goodie", self->message ) )
314 	{
315 		item = FindItemForInventory( INV_GOODIE_KEY );
316 	}
317 	else
318 	{
319 		item = FindItemForInventory( INV_SECURITY_KEY );
320 	}
321 	gentity_t	*dropped = Drop_Item( self, item, 0, qtrue );
322 	//Don't throw the key
323 	VectorClear( dropped->s.pos.trDelta );
324 	dropped->message = self->message;
325 	self->message = NULL;
326 }
327 
ObjectDie(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath)328 void ObjectDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
329 {
330 	if(self->target)
331 		G_UseTargets(self, attacker);
332 
333 	//remove my script_targetname
334 	G_FreeEntity( self );
335 }
336 /*
337 ==================
338 ExplodeDeath
339 ==================
340 */
341 
342 //FIXME: all hacked up...
343 
ExplodeDeath(gentity_t * self)344 void ExplodeDeath( gentity_t *self )
345 {
346 //	gentity_t	*tent;
347 	vec3_t		forward;
348 
349 	self->takedamage = qfalse;//stop chain reaction runaway loops
350 
351 	self->s.loopSound = 0;
352 
353 	VectorCopy( self->currentOrigin, self->s.pos.trBase );
354 
355 //	tent = G_TempEntity( self->s.origin, EV_FX_EXPLOSION );
356 	AngleVectors(self->s.angles, forward, NULL, NULL);  // FIXME: letting effect always shoot up?  Might be ok.
357 
358 	if ( self->fxID > 0 )
359 	{
360 		G_PlayEffect( self->fxID, self->currentOrigin, forward );
361 	}
362 //	else
363 //	{
364 //		CG_SurfaceExplosion( self->currentOrigin, forward, 20.0f, 12.0f, ((self->spawnflags&4)==qfalse) );	//FIXME: This needs to be consistent to all exploders!
365 //		G_Sound(self, self->sounds );
366 //	}
367 
368 	if(self->splashDamage > 0 && self->splashRadius > 0)
369 	{
370 		gentity_t *attacker = self;
371 		if ( self->owner )
372 		{
373 			attacker = self->owner;
374 		}
375 		G_RadiusDamage( self->currentOrigin, attacker, self->splashDamage, self->splashRadius,
376 				attacker, MOD_UNKNOWN );
377 	}
378 
379 	ObjectDie( self, self, self, 20, 0 );
380 }
381 
ExplodeDeath_Wait(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath,int dFlags,int hitLoc)382 void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc )
383 {
384 	self->e_DieFunc = dieF_NULL;
385 	self->nextthink = level.time + Q_irand(100, 500);
386 	self->e_ThinkFunc = thinkF_ExplodeDeath;
387 }
388 
ExplodeDeath(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath,int dFlags,int hitLoc)389 void ExplodeDeath( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc )
390 {
391 	self->currentOrigin[2] += 16; // me bad for hacking this.  should either do it in the effect file or make a custom explode death??
392 	ExplodeDeath( self );
393 }
394 
GoExplodeDeath(gentity_t * self,gentity_t * other,gentity_t * activator)395 void GoExplodeDeath( gentity_t *self, gentity_t *other, gentity_t *activator)
396 {
397 	G_ActivateBehavior(self,BSET_USE);
398 
399 	self->targetname = NULL;	//Make sure this entity cannot be told to explode again (recursive death fix)
400 
401 	ExplodeDeath( self );
402 }
403 
404 qboolean G_ActivateBehavior (gentity_t *self, int bset );
G_CheckVictoryScript(gentity_t * self)405 void G_CheckVictoryScript(gentity_t *self)
406 {
407 	if ( !G_ActivateBehavior( self, BSET_VICTORY ) )
408 	{
409 		if ( self->NPC && self->s.weapon == WP_SABER )
410 		{//Jedi taunt from within their AI
411 			self->NPC->blockedSpeechDebounceTime = 0;//get them ready to taunt
412 			return;
413 		}
414 		if ( self->client && self->client->NPC_class == CLASS_GALAKMECH )
415 		{
416 			self->wait = 1;
417 			TIMER_Set( self, "gloatTime", Q_irand( 5000, 8000 ) );
418 			self->NPC->blockedSpeechDebounceTime = 0;//get him ready to taunt
419 			return;
420 		}
421 		//FIXME: any way to not say this *right away*?  Wait for victim's death anim/scream to finish?
422 		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 ) )
423 		{//sometimes have the group commander speak instead
424 			self->NPC->group->commander->NPC->greetingDebounceTime = level.time + Q_irand( 2000, 5000 );
425 			//G_AddVoiceEvent( self->NPC->group->commander, Q_irand(EV_VICTORY1, EV_VICTORY3), 2000 );
426 		}
427 		else if ( self->NPC )
428 		{
429 			self->NPC->greetingDebounceTime = level.time + Q_irand( 2000, 5000 );
430 			//G_AddVoiceEvent( self, Q_irand(EV_VICTORY1, EV_VICTORY3), 2000 );
431 		}
432 	}
433 }
434 
OnSameTeam(gentity_t * ent1,gentity_t * ent2)435 qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 )
436 {
437 	if ( ent1->s.number < MAX_CLIENTS
438 		&& ent1->client
439 		&& ent1->client->playerTeam == TEAM_FREE )
440 	{//evil player *has* no allies
441 		return qfalse;
442 	}
443 	if ( ent2->s.number < MAX_CLIENTS
444 		&& ent2->client
445 		&& ent2->client->playerTeam == TEAM_FREE )
446 	{//evil player *has* no allies
447 		return qfalse;
448 	}
449 	if ( !ent1->client || !ent2->client )
450 	{
451 		if ( ent1->noDamageTeam )
452 		{
453 			if ( ent2->client && ent2->client->playerTeam == ent1->noDamageTeam )
454 			{
455 				return qtrue;
456 			}
457 			else if ( ent2->noDamageTeam == ent1->noDamageTeam )
458 			{
459 				if ( ent1->splashDamage && ent2->splashDamage && Q_stricmp("ambient_etherian_fliers", ent1->classname) != 0 )
460 				{//Barrels, exploding breakables and mines will blow each other up
461 					return qfalse;
462 				}
463 				else
464 				{
465 					return qtrue;
466 				}
467 			}
468 		}
469 		return qfalse;
470 	}
471 
472 	// shouldn't need this anymore, there were problems with certain droids, but now they have been labeled TEAM_ENEMY so this isn't needed
473 //	if ((( ent1->client->playerTeam == TEAM_IMPERIAL ) && ( ent1->client->playerTeam == TEAM_BOTS )) ||
474 //		(( ent1->client->playerTeam == TEAM_BOTS ) && ( ent1->client->playerTeam == TEAM_IMPERIAL )))
475 //	{
476 //		return qtrue;
477 //	}
478 
479 	return (qboolean)( ent1->client->playerTeam == ent2->client->playerTeam );
480 }
481 
482 
483 /*
484 -------------------------
485 G_AlertTeam
486 -------------------------
487 */
488 
G_AlertTeam(gentity_t * victim,gentity_t * attacker,float radius,float soundDist)489 void G_AlertTeam( gentity_t *victim, gentity_t *attacker, float radius, float soundDist )
490 {
491 	gentity_t	*radiusEnts[ 128 ];
492 	vec3_t		mins, maxs;
493 	int			numEnts;
494 	int			i;
495 	float		distSq, sndDistSq = (soundDist*soundDist);
496 
497 	if ( attacker == NULL || attacker->client == NULL )
498 		return;
499 
500 	//Setup the bbox to search in
501 	for ( i = 0; i < 3; i++ )
502 	{
503 		mins[i] = victim->currentOrigin[i] - radius;
504 		maxs[i] = victim->currentOrigin[i] + radius;
505 	}
506 
507 	//Get the number of entities in a given space
508 	numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, 128 );
509 
510 	//Cull this list
511 	for ( i = 0; i < numEnts; i++ )
512 	{
513 		//Validate clients
514 		if ( radiusEnts[i]->client == NULL )
515 			continue;
516 
517 		//only want NPCs
518 		if ( radiusEnts[i]->NPC == NULL )
519 			continue;
520 
521 		//Don't bother if they're ignoring enemies
522 		if ( radiusEnts[i]->svFlags & SVF_IGNORE_ENEMIES )
523 			continue;
524 
525 		//This NPC specifically flagged to ignore alerts
526 		if ( radiusEnts[i]->NPC->scriptFlags & SCF_IGNORE_ALERTS )
527 			continue;
528 
529 		//This NPC specifically flagged to ignore alerts
530 		if ( !(radiusEnts[i]->NPC->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
531 			continue;
532 
533 		//this ent does not participate in group AI
534 		if ( (radiusEnts[i]->NPC->scriptFlags&SCF_NO_GROUPS) )
535 			continue;
536 
537 		//Skip the requested avoid radiusEnts[i] if present
538 		if ( radiusEnts[i] == victim )
539 			continue;
540 
541 		//Skip the attacker
542 		if ( radiusEnts[i] == attacker )
543 			continue;
544 
545 		//Must be on the same team
546 		if ( radiusEnts[i]->client->playerTeam != victim->client->playerTeam )
547 			continue;
548 
549 		//Must be alive
550 		if ( radiusEnts[i]->health <= 0 )
551 			continue;
552 
553 		if ( radiusEnts[i]->enemy == NULL )
554 		{//only do this if they're not already mad at someone
555 			distSq = DistanceSquared( radiusEnts[i]->currentOrigin, victim->currentOrigin );
556 			if ( distSq > 16384 /*128 squared*/ && !gi.inPVS( victim->currentOrigin, radiusEnts[i]->currentOrigin ) )
557 			{//not even potentially visible/hearable
558 				continue;
559 			}
560 			//NOTE: this allows sound alerts to still go through doors/PVS if the teammate is within 128 of the victim...
561 			if ( soundDist <= 0 || distSq > sndDistSq )
562 			{//out of sound range
563 				if ( !InFOV( victim, radiusEnts[i], radiusEnts[i]->NPC->stats.hfov, radiusEnts[i]->NPC->stats.vfov )
564 					||  !NPC_ClearLOS( radiusEnts[i], victim->currentOrigin ) )
565 				{//out of FOV or no LOS
566 					continue;
567 				}
568 			}
569 
570 			//FIXME: This can have a nasty cascading effect if setup wrong...
571 			G_SetEnemy( radiusEnts[i], attacker );
572 		}
573 	}
574 }
575 
576 /*
577 -------------------------
578 G_DeathAlert
579 -------------------------
580 */
581 
582 #define	DEATH_ALERT_RADIUS			512
583 #define	DEATH_ALERT_SOUND_RADIUS	512
584 
G_DeathAlert(gentity_t * victim,gentity_t * attacker)585 void G_DeathAlert( gentity_t *victim, gentity_t *attacker )
586 {//FIXME: with all the other alert stuff, do we really need this?
587 	G_AlertTeam( victim, attacker, DEATH_ALERT_RADIUS, DEATH_ALERT_SOUND_RADIUS );
588 }
589 
590 /*
591 ----------------------------------------
592 DeathFX
593 
594 Applies appropriate special effects that occur while the entity is dying
595 Not to be confused with NPC_RemoveBodyEffects (NPC.cpp), which only applies effect when removing the body
596 ----------------------------------------
597 */
598 
DeathFX(gentity_t * ent)599 void DeathFX( gentity_t *ent )
600 {
601 	if ( !ent || !ent->client )
602 		return;
603 /*
604 	switch( ent->client->playerTeam )
605 	{
606 	case TEAM_BOTS:
607 		if (!Q_stricmp( ent->NPC_type, "mouse" ))
608 		{
609 			vec3_t		effectPos;
610 			VectorCopy( ent->currentOrigin, effectPos );
611 			effectPos[2] -= 20;
612 
613 			G_PlayEffect( "mouseexplosion1", effectPos );
614 			G_PlayEffect( "smaller_chunks", effectPos );
615 
616 		}
617 		else if (!Q_stricmp( ent->NPC_type, "probe" ))
618 		{
619 			vec3_t		effectPos;
620 			VectorCopy( ent->currentOrigin, effectPos );
621 			effectPos[2] += 50;
622 
623 			G_PlayEffect( "probeexplosion1", effectPos );
624 			G_PlayEffect( "small_chunks", effectPos );
625 		}
626 		else
627 		{
628 			vec3_t		effectPos;
629 			VectorCopy( ent->currentOrigin, effectPos );
630 			effectPos[2] -= 15;
631 			G_PlayEffect( "droidexplosion1", effectPos );
632 			G_PlayEffect( "small_chunks", effectPos );
633 		}
634 
635 		break;
636 
637 	default:
638 		break;
639 	}
640 */
641 	// team no longer indicates species/race.  NPC_class should be used to identify certain npc types
642 	vec3_t		effectPos, right;
643 	switch(ent->client->NPC_class)
644 	{
645 	case CLASS_MOUSE:
646 		VectorCopy( ent->currentOrigin, effectPos );
647 		effectPos[2] -= 20;
648 		G_PlayEffect( "env/small_explode", effectPos );
649 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/mouse/misc/death1" );
650 		break;
651 
652 	case CLASS_PROBE:
653 		VectorCopy( ent->currentOrigin, effectPos );
654 		effectPos[2] += 50;
655 		G_PlayEffect( "explosions/probeexplosion1", effectPos );
656 		break;
657 
658 	case CLASS_ATST:
659 		AngleVectors( ent->currentAngles, NULL, right, NULL );
660 		VectorMA( ent->currentOrigin, 20, right, effectPos );
661 		effectPos[2] += 180;
662 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
663 		VectorMA( effectPos, -40, right, effectPos );
664 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
665 		break;
666 
667 	case CLASS_SEEKER:
668 	case CLASS_REMOTE:
669 		G_PlayEffect( "env/small_explode", ent->currentOrigin );
670 		break;
671 
672 	case CLASS_GONK:
673 		VectorCopy( ent->currentOrigin, effectPos );
674 		effectPos[2] -= 5;
675 //		statusTextIndex = Q_irand( IGT_RESISTANCEISFUTILE, IGT_NAMEIS8OF12 );
676 		G_SoundOnEnt( ent, CHAN_AUTO, va("sound/chars/gonk/misc/death%d.wav",Q_irand( 1, 3 )) );
677 		G_PlayEffect( "env/med_explode", effectPos );
678 		break;
679 
680 	// should list all remaining droids here, hope I didn't miss any
681 	case CLASS_R2D2:
682 		VectorCopy( ent->currentOrigin, effectPos );
683 		effectPos[2] -= 10;
684 		G_PlayEffect( "env/med_explode", effectPos );
685 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/mark2/misc/mark2_explo" );
686 		break;
687 
688 	case CLASS_PROTOCOL://??
689 	case CLASS_R5D2:
690 		VectorCopy( ent->currentOrigin, effectPos );
691 		effectPos[2] -= 10;
692 		G_PlayEffect( "env/med_explode", effectPos );
693 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/mark2/misc/mark2_explo" );
694 		break;
695 
696 	case CLASS_MARK2:
697 		VectorCopy( ent->currentOrigin, effectPos );
698 		effectPos[2] -= 15;
699 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
700 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/mark2/misc/mark2_explo" );
701 		break;
702 
703 	case CLASS_INTERROGATOR:
704 		VectorCopy( ent->currentOrigin, effectPos );
705 		effectPos[2] -= 15;
706 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
707 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/interrogator/misc/int_droid_explo" );
708 		break;
709 
710 	case CLASS_MARK1:
711 		AngleVectors( ent->currentAngles, NULL, right, NULL );
712 		VectorMA( ent->currentOrigin, 10, right, effectPos );
713 		effectPos[2] -= 15;
714 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
715 		VectorMA( effectPos, -20, right, effectPos );
716 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
717 		VectorMA( effectPos, -20, right, effectPos );
718 		G_PlayEffect( "explosions/droidexplosion1", effectPos );
719 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/mark1/misc/mark1_explo" );
720 		break;
721 
722 	case CLASS_SENTRY:
723 		G_SoundOnEnt( ent, CHAN_AUTO, "sound/chars/sentry/misc/sentry_explo" );
724 		VectorCopy( ent->currentOrigin, effectPos );
725 		G_PlayEffect( "env/med_explode", effectPos );
726 		break;
727 
728 	default:
729 		break;
730 
731 	}
732 
733 }
734 
G_SetMissionStatusText(gentity_t * attacker,int mod)735 void G_SetMissionStatusText( gentity_t *attacker, int mod )
736 {
737 	if ( statusTextIndex >= 0 )
738 	{
739 		return;
740 	}
741 
742 	if ( mod == MOD_FALLING )
743 	{//fell to your death
744 		statusTextIndex = STAT_WATCHYOURSTEP;
745 	}
746 	else if ( mod == MOD_CRUSH )
747 	{//crushed
748 		statusTextIndex = STAT_JUDGEMENTMUCHDESIRED;
749 	}
750 	else if ( attacker && Q_stricmp( "trigger_hurt", attacker->classname ) == 0 )
751 	{//Killed by something that should have been clearly dangerous
752 //		statusTextIndex = Q_irand( IGT_JUDGEMENTDESIRED, IGT_JUDGEMENTMUCHDESIRED );
753 		statusTextIndex = STAT_JUDGEMENTMUCHDESIRED;
754 	}
755 	else if ( attacker && attacker->s.number != 0 && attacker->client && attacker->client->playerTeam == TEAM_PLAYER )
756 	{//killed by a teammate
757 		statusTextIndex = STAT_INSUBORDINATION;
758 	}
759 }
760 
G_MakeTeamVulnerable(void)761 void G_MakeTeamVulnerable( void )
762 {
763 	int i, newhealth;
764 	gentity_t *ent;
765 	gentity_t *self = &g_entities[0];
766 	if ( !self->client )
767 	{
768 		return;
769 	}
770 
771 //	for ( i = 0; i < globals.num_entities ; i++, ent++)
772 	for ( i = 0; i < globals.num_entities ; i++)
773 	{
774 		if(!PInUse(i))
775 			continue;
776 //		if ( !ent->inuse  )
777 //		{
778 //			continue;
779 //		}
780 //		if ( !ent )
781 //		{
782 //			continue;
783 //		}
784 		ent=&g_entities[i];
785 		if ( !ent->client  )
786 		{
787 			continue;
788 		}
789 		if ( ent->client->playerTeam != TEAM_PLAYER )
790 		{
791 			continue;
792 		}
793 		if ( !(ent->flags&FL_UNDYING) )
794 		{
795 			continue;
796 		}
797 		ent->flags &= ~FL_UNDYING;
798 		newhealth = Q_irand( 5, 40 );
799 		if ( ent->health > newhealth )
800 		{
801 			ent->health = newhealth;
802 		}
803 	}
804 }
805 
G_StartMatrixEffect(gentity_t * ent,int meFlags=0,int length=1000,float timeScale=0.0f,int spinTime=0)806 void G_StartMatrixEffect( gentity_t *ent, int meFlags = 0, int length = 1000, float timeScale = 0.0f, int spinTime = 0 )
807 {
808 	//FIXME: allow them to specify a different focal entity or point?
809 	if ( g_timescale->value != 1.0 || in_camera )
810 	{//already in some slow-mo mode or in_camera
811 		return;
812 	}
813 
814 	gentity_t	*matrix = G_Spawn();
815 	if ( matrix )
816 	{
817 		G_SetOrigin( matrix, ent->currentOrigin );
818 		gi.linkentity( matrix );
819 		matrix->s.otherEntityNum = ent->s.number;
820 		matrix->e_clThinkFunc = clThinkF_CG_MatrixEffect;
821 		matrix->s.eType = ET_THINKER;
822 		matrix->svFlags |= SVF_BROADCAST;// Broadcast to all clients
823 		matrix->s.time = level.time;
824 		matrix->s.eventParm = length;
825 		//now the cgame decides when to remove us... in case the framerate chugs so severely that it never finishes the effect before it removes itself!
826 		//matrix->e_ThinkFunc = thinkF_G_FreeEntity;
827 		//matrix->nextthink = level.time + length + 500;
828 		matrix->s.boltInfo = meFlags;
829 		matrix->s.time2 = spinTime;
830 		matrix->s.angles2[0] = timeScale;
831 	}
832 }
833 
G_JediInRoom(vec3_t from)834 qboolean G_JediInRoom( vec3_t from )
835 {
836 	gentity_t *ent;
837 	int i;
838 //	for ( i = 1, ent = &g_entities[1]; i < globals.num_entities; i++, ent++ )
839 	for ( i = 1; i < globals.num_entities; i++)
840 	{
841 		if(!PInUse(i))
842 			continue;
843 //		if ( !ent->inuse )
844 //		{
845 //			continue;
846 //		}
847 //		if ( !ent )
848 //		{
849 //			continue;
850 //		}
851 		ent = &g_entities[i];
852 		if ( !ent->NPC )
853 		{
854 			continue;
855 		}
856 		if ( ent->health <= 0 )
857 		{
858 			continue;
859 		}
860 		if ( ent->s.eFlags&EF_NODRAW )
861 		{
862 			continue;
863 		}
864 		if ( ent->s.weapon != WP_SABER )
865 		{
866 			continue;
867 		}
868 		if ( !gi.inPVS( ent->currentOrigin, from ) )
869 		{
870 			continue;
871 		}
872 		return qtrue;
873 	}
874 	return qfalse;
875 }
876 
G_GetHitLocFromSurfName(gentity_t * ent,const char * surfName,int * hitLoc,vec3_t point,vec3_t dir,vec3_t bladeDir,int mod,saberType_t saberType)877 qboolean G_GetHitLocFromSurfName( gentity_t *ent, const char *surfName, int *hitLoc, vec3_t point, vec3_t dir, vec3_t bladeDir, int mod, saberType_t saberType )
878 {
879 	qboolean dismember = qfalse;
880 
881 	*hitLoc = HL_NONE;
882 
883 	if ( !surfName || !surfName[0] )
884 	{
885 		return qfalse;
886 	}
887 
888 	if( !ent->client )
889 	{
890 		return qfalse;
891 	}
892 
893 	if ( ent->client
894 		&& ( ent->client->NPC_class == CLASS_R2D2
895 			|| ent->client->NPC_class == CLASS_R5D2
896 			|| ent->client->NPC_class == CLASS_GONK
897 			|| ent->client->NPC_class == CLASS_MOUSE
898 			|| ent->client->NPC_class == CLASS_SENTRY
899 			|| ent->client->NPC_class == CLASS_INTERROGATOR
900 			|| ent->client->NPC_class == CLASS_PROBE ) )
901 	{//we don't care about per-surface hit-locations or dismemberment for these guys
902 		return qfalse;
903 	}
904 
905 	if ( ent->client && (ent->client->NPC_class == CLASS_ATST) )
906 	{
907 		//FIXME: almost impossible to hit these... perhaps we should
908 		//		check for splashDamage and do radius damage to these parts?
909 		//		Or, if we ever get bbox G2 traces, that may fix it, too
910 		if (!Q_stricmp("head_light_blaster_cann",surfName))
911 		{
912 			*hitLoc = HL_ARM_LT;
913 		}
914 		else if (!Q_stricmp("head_concussion_charger",surfName))
915 		{
916 			*hitLoc = HL_ARM_RT;
917 		}
918 		return(qfalse);
919 	}
920 	else if ( ent->client && (ent->client->NPC_class == CLASS_MARK1) )
921 	{
922 		if (!Q_stricmp("l_arm",surfName))
923 		{
924 			*hitLoc = HL_ARM_LT;
925 		}
926 		else if (!Q_stricmp("r_arm",surfName))
927 		{
928 			*hitLoc = HL_ARM_RT;
929 		}
930 		else if (!Q_stricmp("torso_front",surfName))
931 		{
932 			*hitLoc = HL_CHEST;
933 		}
934 		else if (!Q_stricmp("torso_tube1",surfName))
935 		{
936 			*hitLoc = HL_GENERIC1;
937 		}
938 		else if (!Q_stricmp("torso_tube2",surfName))
939 		{
940 			*hitLoc = HL_GENERIC2;
941 		}
942 		else if (!Q_stricmp("torso_tube3",surfName))
943 		{
944 			*hitLoc = HL_GENERIC3;
945 		}
946 		else if (!Q_stricmp("torso_tube4",surfName))
947 		{
948 			*hitLoc = HL_GENERIC4;
949 		}
950 		else if (!Q_stricmp("torso_tube5",surfName))
951 		{
952 			*hitLoc = HL_GENERIC5;
953 		}
954 		else if (!Q_stricmp("torso_tube6",surfName))
955 		{
956 			*hitLoc = HL_GENERIC6;
957 		}
958 		return(qfalse);
959 	}
960 	else if ( ent->client && (ent->client->NPC_class == CLASS_MARK2) )
961 	{
962 		if (!Q_stricmp("torso_canister1",surfName))
963 		{
964 			*hitLoc = HL_GENERIC1;
965 		}
966 		else if (!Q_stricmp("torso_canister2",surfName))
967 		{
968 			*hitLoc = HL_GENERIC2;
969 		}
970 		else if (!Q_stricmp("torso_canister3",surfName))
971 		{
972 			*hitLoc = HL_GENERIC3;
973 		}
974 		return(qfalse);
975 	}
976 	else if ( ent->client && (ent->client->NPC_class == CLASS_GALAKMECH) )
977 	{
978 		if (!Q_stricmp("torso_antenna",surfName)||!Q_stricmp("torso_antenna_base",surfName))
979 		{
980 			*hitLoc = HL_GENERIC1;
981 		}
982 		else if (!Q_stricmp("torso_shield",surfName))
983 		{
984 			*hitLoc = HL_GENERIC2;
985 		}
986 		else
987 		{
988 			*hitLoc = HL_CHEST;
989 		}
990 		return(qfalse);
991 	}
992 
993 
994 	//FIXME: check the hitLoc and hitDir against the cap tag for the place
995 	//where the split will be- if the hit dir is roughly perpendicular to
996 	//the direction of the cap, then the split is allowed, otherwise we
997 	//hit it at the wrong angle and should not dismember...
998 	int	actualTime = (cg.time?cg.time:level.time);
999 	if ( !Q_stricmpn( "hips", surfName, 4 ) )
1000 	{//FIXME: test properly for legs
1001 		*hitLoc = HL_WAIST;
1002 		if ( ent->client != NULL && ent->ghoul2.size() )
1003 		{
1004 			mdxaBone_t	boltMatrix;
1005 			vec3_t	tagOrg, angles;
1006 
1007 			VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
1008 			if (ent->kneeLBolt>=0)
1009 			{
1010 				gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->kneeLBolt,
1011 								&boltMatrix, angles, ent->currentOrigin,
1012 								actualTime, NULL, ent->s.modelScale );
1013 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1014 				if ( DistanceSquared( point, tagOrg ) < 100 )
1015 				{//actually hit the knee
1016 					*hitLoc = HL_LEG_LT;
1017 				}
1018 			}
1019 			if (*hitLoc == HL_WAIST)
1020 			{
1021 				if (ent->kneeRBolt>=0)
1022 				{
1023 					gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->kneeRBolt,
1024 									&boltMatrix, angles, ent->currentOrigin,
1025 									actualTime, NULL, ent->s.modelScale );
1026 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1027 					if ( DistanceSquared( point, tagOrg ) < 100 )
1028 					{//actually hit the knee
1029 						*hitLoc = HL_LEG_RT;
1030 					}
1031 				}
1032 			}
1033 		}
1034 	}
1035 	else if ( !Q_stricmpn( "torso", surfName, 5 ) )
1036 	{
1037 		if ( !ent->client )
1038 		{
1039 			*hitLoc = HL_CHEST;
1040 		}
1041 		else
1042 		{
1043 			vec3_t	t_fwd, t_rt, t_up, dirToImpact;
1044 			float frontSide, rightSide, upSide;
1045 			AngleVectors( ent->client->renderInfo.torsoAngles, t_fwd, t_rt, t_up );
1046 			VectorSubtract( point, ent->client->renderInfo.torsoPoint, dirToImpact );
1047 			frontSide = DotProduct( t_fwd, dirToImpact );
1048 			rightSide = DotProduct( t_rt, dirToImpact );
1049 			upSide = DotProduct( t_up, dirToImpact );
1050 			if ( upSide < -10 )
1051 			{//hit at waist
1052 				*hitLoc = HL_WAIST;
1053 			}
1054 			else
1055 			{//hit on upper torso
1056 				if ( rightSide > 4 )
1057 				{
1058 					*hitLoc = HL_ARM_RT;
1059 				}
1060 				else if ( rightSide < -4 )
1061 				{
1062 					*hitLoc = HL_ARM_LT;
1063 				}
1064 				else if ( rightSide > 2 )
1065 				{
1066 					if ( frontSide > 0 )
1067 					{
1068 						*hitLoc = HL_CHEST_RT;
1069 					}
1070 					else
1071 					{
1072 						*hitLoc = HL_BACK_RT;
1073 					}
1074 				}
1075 				else if ( rightSide < -2 )
1076 				{
1077 					if ( frontSide > 0 )
1078 					{
1079 						*hitLoc = HL_CHEST_LT;
1080 					}
1081 					else
1082 					{
1083 						*hitLoc = HL_BACK_LT;
1084 					}
1085 				}
1086 				else if ( upSide > -3 && mod == MOD_SABER )
1087 				{
1088 					*hitLoc = HL_HEAD;
1089 				}
1090 				else if ( frontSide > 0 )
1091 				{
1092 					*hitLoc = HL_CHEST;
1093 				}
1094 				else
1095 				{
1096 					*hitLoc = HL_BACK;
1097 				}
1098 			}
1099 		}
1100 	}
1101 	else if ( !Q_stricmpn( "head", surfName, 4 ) )
1102 	{
1103 		*hitLoc = HL_HEAD;
1104 	}
1105 	else if ( !Q_stricmpn( "r_arm", surfName, 5 ) )
1106 	{
1107 		*hitLoc = HL_ARM_RT;
1108 		if ( ent->client != NULL && ent->ghoul2.size() )
1109 		{
1110 			mdxaBone_t	boltMatrix;
1111 			vec3_t	tagOrg, angles;
1112 
1113 			VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
1114 			if (ent->handRBolt>=0)
1115 			{
1116 				gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->handRBolt,
1117 								&boltMatrix, angles, ent->currentOrigin,
1118 								actualTime, NULL, ent->s.modelScale );
1119 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1120 				if ( DistanceSquared( point, tagOrg ) < 256 )
1121 				{//actually hit the hand
1122 					*hitLoc = HL_HAND_RT;
1123 				}
1124 			}
1125 		}
1126 	}
1127 	else if ( !Q_stricmpn( "l_arm", surfName, 5 ) )
1128 	{
1129 		*hitLoc = HL_ARM_LT;
1130 		if ( ent->client != NULL && ent->ghoul2.size() )
1131 		{
1132 			mdxaBone_t	boltMatrix;
1133 			vec3_t	tagOrg, angles;
1134 
1135 			VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
1136 			if (ent->handLBolt>=0)
1137 			{
1138 				gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->handLBolt,
1139 								&boltMatrix, angles, ent->currentOrigin,
1140 								actualTime, NULL, ent->s.modelScale );
1141 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1142 				if ( DistanceSquared( point, tagOrg ) < 256 )
1143 				{//actually hit the hand
1144 					*hitLoc = HL_HAND_LT;
1145 				}
1146 			}
1147 		}
1148 	}
1149 	else if ( !Q_stricmpn( "r_leg", surfName, 5 ) )
1150 	{
1151 		*hitLoc = HL_LEG_RT;
1152 		if ( ent->client != NULL && ent->ghoul2.size() )
1153 		{
1154 			mdxaBone_t	boltMatrix;
1155 			vec3_t	tagOrg, angles;
1156 
1157 			VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
1158 			if (ent->footRBolt>=0)
1159 			{
1160 				gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->footRBolt,
1161 								&boltMatrix, angles, ent->currentOrigin,
1162 								actualTime, NULL, ent->s.modelScale );
1163 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1164 				if ( DistanceSquared( point, tagOrg ) < 100 )
1165 				{//actually hit the foot
1166 					*hitLoc = HL_FOOT_RT;
1167 				}
1168 			}
1169 		}
1170 	}
1171 	else if ( !Q_stricmpn( "l_leg", surfName, 5 ) )
1172 	{
1173 		*hitLoc = HL_LEG_LT;
1174 		if ( ent->client != NULL && ent->ghoul2.size() )
1175 		{
1176 			mdxaBone_t	boltMatrix;
1177 			vec3_t	tagOrg, angles;
1178 
1179 			VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
1180 			if (ent->footLBolt>=0)
1181 			{
1182 				gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->footLBolt,
1183 								&boltMatrix, angles, ent->currentOrigin,
1184 								actualTime, NULL, ent->s.modelScale );
1185 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1186 				if ( DistanceSquared( point, tagOrg ) < 100 )
1187 				{//actually hit the foot
1188 					*hitLoc = HL_FOOT_LT;
1189 				}
1190 			}
1191 		}
1192 	}
1193 	else if ( mod == MOD_SABER && WP_BreakSaber( ent, surfName, saberType ) )
1194 	{//saber hit and broken
1195 		*hitLoc = HL_HAND_RT;
1196 	}
1197 	else if ( !Q_stricmpn( "r_hand", surfName, 6 ) || !Q_stricmpn( "w_", surfName, 2 ) )
1198 	{//right hand or weapon
1199 		//FIXME: if hit weapon, chance of breaking saber (if sabers.cfg entry shows it as breakable)
1200 		//			if breaks, remove saber and replace with the 2 replacement sabers (preserve color, length, etc.)
1201 		*hitLoc = HL_HAND_RT;
1202 	}
1203 	else if ( !Q_stricmpn( "l_hand", surfName, 6 ) )
1204 	{
1205 		*hitLoc = HL_HAND_LT;
1206 	}
1207 	else if ( ent->client && ent->client->ps.powerups[PW_GALAK_SHIELD] && !Q_stricmp( "force_shield", surfName ) )
1208 	{
1209 		*hitLoc = HL_GENERIC2;
1210 
1211 	}
1212 #ifdef _DEBUG
1213 	else
1214 	{
1215 		Com_Printf( "ERROR: surface %s does not belong to any hitLocation!!!\n", surfName );
1216 	}
1217 #endif //_DEBUG
1218 
1219 	if ( g_saberRealisticCombat->integer > 1
1220 		|| debug_subdivision->integer )
1221 	{
1222 		dismember = qtrue;
1223 	}
1224 	else if ( ent->client && ent->client->NPC_class == CLASS_PROTOCOL )
1225 	{
1226 		dismember = qtrue;
1227 	}
1228 	else if ( ent->client && ent->client->NPC_class == CLASS_ASSASSIN_DROID )
1229 	{
1230 		dismember = qtrue;
1231 	}
1232 	else if ( ent->client && ent->client->NPC_class == CLASS_SABER_DROID )
1233 	{
1234 		dismember = qtrue;
1235 	}
1236 	else if ( debug_subdivision->integer || !ent->client->dismembered )
1237 	{
1238 		if ( dir && (dir[0] || dir[1] || dir[2]) &&
1239 			bladeDir && (bladeDir[0] || bladeDir[1] || bladeDir[2]) )
1240 		{//we care about direction (presumably for dismemberment)
1241 			if (  g_dismemberProbabilities->value<=0.0f||G_Dismemberable( ent, *hitLoc ) )
1242 			{//the probability let us continue
1243 				const char *tagName = NULL;
1244 				float	aoa = 0.5f;
1245 				//dir must be roughly perpendicular to the hitLoc's cap bolt
1246 				switch ( *hitLoc )
1247 				{
1248 					case HL_LEG_RT:
1249 						tagName = "*hips_cap_r_leg";
1250 						break;
1251 					case HL_LEG_LT:
1252 						tagName = "*hips_cap_l_leg";
1253 						break;
1254 					case HL_WAIST:
1255 						tagName = "*hips_cap_torso";
1256 						aoa = 0.25f;
1257 						break;
1258 					case HL_CHEST_RT:
1259 					case HL_ARM_RT:
1260 					case HL_BACK_LT:
1261 						tagName = "*torso_cap_r_arm";
1262 						break;
1263 					case HL_CHEST_LT:
1264 					case HL_ARM_LT:
1265 					case HL_BACK_RT:
1266 						tagName = "*torso_cap_l_arm";
1267 						break;
1268 					case HL_HAND_RT:
1269 						tagName = "*r_arm_cap_r_hand";
1270 						break;
1271 					case HL_HAND_LT:
1272 						tagName = "*l_arm_cap_l_hand";
1273 						break;
1274 					case HL_HEAD:
1275 						tagName = "*torso_cap_head";
1276 						aoa = 0.25f;
1277 						break;
1278 					case HL_CHEST:
1279 					case HL_BACK:
1280 					case HL_FOOT_RT:
1281 					case HL_FOOT_LT:
1282 					default:
1283 						//no dismemberment possible with these, so no checks needed
1284 						break;
1285 				}
1286 				if ( tagName )
1287 				{
1288 					int tagBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], tagName );
1289 					if ( tagBolt != -1 )
1290 					{
1291 						mdxaBone_t	boltMatrix;
1292 						vec3_t	tagOrg, tagDir, angles;
1293 						VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
1294 						gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, tagBolt,
1295 										&boltMatrix, angles, ent->currentOrigin,
1296 										actualTime, NULL, ent->s.modelScale );
1297 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg );
1298 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, tagDir );
1299 						if ( DistanceSquared( point, tagOrg ) < 256 )
1300 						{//hit close
1301 							float dot = DotProduct( dir, tagDir );
1302 							if ( dot < aoa && dot > -aoa )
1303 							{//hit roughly perpendicular
1304 								dot = DotProduct( bladeDir, tagDir );
1305 								if ( dot < aoa && dot > -aoa )
1306 								{//blade was roughly perpendicular
1307 									dismember = qtrue;
1308 								}
1309 							}
1310 						}
1311 					}
1312 				}
1313 			}
1314 		}
1315 	}
1316 	return dismember;
1317 }
1318 
G_GetHitLocation(gentity_t * target,const vec3_t ppoint)1319 int G_GetHitLocation ( gentity_t *target, const vec3_t ppoint )
1320 {
1321 	vec3_t			point, point_dir;
1322 	vec3_t			forward, right, up;
1323 	vec3_t			tangles, tcenter;
1324 	float			udot, fdot, rdot;
1325 	int				Vertical, Forward, Lateral;
1326 	int				HitLoc;
1327 
1328 //get target forward, right and up
1329 	if(target->client)
1330 	{//ignore player's pitch and roll
1331 		VectorSet(tangles, 0, target->currentAngles[YAW], 0);
1332 	}
1333 
1334 	AngleVectors(tangles, forward, right, up);
1335 
1336 //get center of target
1337 	VectorAdd(target->absmin, target->absmax, tcenter);
1338 	VectorScale(tcenter, 0.5, tcenter);
1339 
1340 //get impact point
1341 	if(ppoint && !VectorCompare(ppoint, vec3_origin))
1342 	{
1343 		VectorCopy(ppoint, point);
1344 	}
1345 	else
1346 	{
1347 		return HL_NONE;
1348 	}
1349 
1350 /*
1351 //get impact dir
1352 	if(pdir && !VectorCompare(pdir, vec3_origin))
1353 	{
1354 		VectorCopy(pdir, dir);
1355 	}
1356 	else
1357 	{
1358 		return;
1359 	}
1360 
1361 //put point at controlled distance from center
1362 	VectorSubtract(point, tcenter, tempvec);
1363 	tempvec[2] = 0;
1364 	hdist = VectorLength(tempvec);
1365 
1366 	VectorMA(point, hdist - tradius, dir, point);
1367 	//now a point on the surface of a cylinder with a radius of tradius
1368 */
1369 	VectorSubtract(point, tcenter, point_dir);
1370 	VectorNormalize(point_dir);
1371 
1372 	//Get bottom to top (Vertical) position index
1373 	udot = DotProduct(up, point_dir);
1374 	if(udot>.800)
1375 		Vertical = 4;
1376 	else if(udot>.400)
1377 		Vertical = 3;
1378 	else if(udot>-.333)
1379 		Vertical = 2;
1380 	else if(udot>-.666)
1381 		Vertical = 1;
1382 	else
1383 		Vertical = 0;
1384 
1385 	//Get back to front (Forward) position index
1386 	fdot = DotProduct(forward, point_dir);
1387 	if(fdot>.666)
1388 		Forward = 4;
1389 	else if(fdot>.333)
1390 		Forward = 3;
1391 	else if(fdot>-.333)
1392 		Forward = 2;
1393 	else if(fdot>-.666)
1394 		Forward = 1;
1395 	else
1396 		Forward = 0;
1397 
1398 	//Get left to right (Lateral) position index
1399 	rdot = DotProduct(right, point_dir);
1400 	if(rdot>.666)
1401 		Lateral = 4;
1402 	else if(rdot>.333)
1403 		Lateral = 3;
1404 	else if(rdot>-.333)
1405 		Lateral = 2;
1406 	else if(rdot>-.666)
1407 		Lateral = 1;
1408 	else
1409 		Lateral = 0;
1410 
1411 	HitLoc = Vertical * 25 + Forward * 5 + Lateral;
1412 
1413 	if(HitLoc <= 10)
1414 	{//feet
1415 		if ( rdot > 0 )
1416 		{
1417 			return HL_FOOT_RT;
1418 		}
1419 		else
1420 		{
1421 			return HL_FOOT_LT;
1422 		}
1423 	}
1424 	else if(HitLoc <= 50)
1425 	{//legs
1426 		if ( rdot > 0 )
1427 		{
1428 			return HL_LEG_RT;
1429 		}
1430 		else
1431 		{
1432 			return HL_LEG_LT;
1433 		}
1434 	}
1435 	else if ( HitLoc == 56||HitLoc == 60||HitLoc == 61||HitLoc == 65||HitLoc == 66||HitLoc == 70 )
1436 	{//hands
1437 		if ( rdot > 0 )
1438 		{
1439 			return HL_HAND_RT;
1440 		}
1441 		else
1442 		{
1443 			return HL_HAND_LT;
1444 		}
1445 	}
1446 	else if ( HitLoc == 83||HitLoc == 87||HitLoc == 88||HitLoc == 92||HitLoc == 93||HitLoc == 97 )
1447 	{//arms
1448 		if ( rdot > 0 )
1449 		{
1450 			return HL_ARM_RT;
1451 		}
1452 		else
1453 		{
1454 			return HL_ARM_LT;
1455 		}
1456 	}
1457 	else if((HitLoc >= 107 && HitLoc <= 109)||
1458 		(HitLoc >= 112 && HitLoc <= 114)||
1459 		(HitLoc >= 117 && HitLoc <= 119))
1460 	{//head
1461 		return HL_HEAD;
1462 	}
1463 	else
1464 	{
1465 		if ( udot < 0.3 )
1466 		{
1467 			return HL_WAIST;
1468 		}
1469 		else if ( fdot < 0 )
1470 		{
1471 			if ( rdot > 0.4 )
1472 			{
1473 				return HL_BACK_RT;
1474 			}
1475 			else if ( rdot < -0.4 )
1476 			{
1477 				return HL_BACK_LT;
1478 			}
1479 			else
1480 			{
1481 				return HL_BACK;
1482 			}
1483 		}
1484 		else
1485 		{
1486 			if ( rdot > 0.3 )
1487 			{
1488 				return HL_CHEST_RT;
1489 			}
1490 			else if ( rdot < -0.3 )
1491 			{
1492 				return HL_CHEST_LT;
1493 			}
1494 			else
1495 			{
1496 				return HL_CHEST;
1497 			}
1498 		}
1499 	}
1500 	//return HL_NONE;
1501 }
1502 
G_PickPainAnim(gentity_t * self,const vec3_t point,int damage,int hitLoc=HL_NONE)1503 int G_PickPainAnim( gentity_t *self, const vec3_t point, int damage, int hitLoc = HL_NONE )
1504 {
1505 	if ( hitLoc == HL_NONE )
1506 	{
1507 		hitLoc = G_GetHitLocation( self, point );
1508 	}
1509 	switch( hitLoc )
1510 	{
1511 	case HL_FOOT_RT:
1512 		return BOTH_PAIN12;
1513 		//PAIN12 = right foot
1514 		break;
1515 	case HL_FOOT_LT:
1516 		return -1;
1517 		break;
1518 	case HL_LEG_RT:
1519 		if ( !Q_irand( 0, 1 ) )
1520 		{
1521 			return BOTH_PAIN11;
1522 		}
1523 		else
1524 		{
1525 			return BOTH_PAIN13;
1526 		}
1527 		//PAIN11 = twitch right leg
1528 		//PAIN13 = right knee
1529 		break;
1530 	case HL_LEG_LT:
1531 		return BOTH_PAIN14;
1532 		//PAIN14 = twitch left leg
1533 		break;
1534 	case HL_BACK_RT:
1535 		return BOTH_PAIN7;
1536 		//PAIN7 = med left shoulder
1537 		break;
1538 	case HL_BACK_LT:
1539 		return Q_irand( BOTH_PAIN15, BOTH_PAIN16 );
1540 		//PAIN15 = med right shoulder
1541 		//PAIN16 = twitch right shoulder
1542 		break;
1543 	case HL_BACK:
1544 		if ( !Q_irand( 0, 1 ) )
1545 		{
1546 			return BOTH_PAIN1;
1547 		}
1548 		else
1549 		{
1550 			return BOTH_PAIN5;
1551 		}
1552 		//PAIN1 = back
1553 		//PAIN5 = same as 1
1554 		break;
1555 	case HL_CHEST_RT:
1556 		return BOTH_PAIN3;
1557 		//PAIN3 = long, right shoulder
1558 		break;
1559 	case HL_CHEST_LT:
1560 		return BOTH_PAIN2;
1561 		//PAIN2 = long, left shoulder
1562 		break;
1563 	case HL_WAIST:
1564 	case HL_CHEST:
1565 		if ( !Q_irand( 0, 3 ) )
1566 		{
1567 			return BOTH_PAIN6;
1568 		}
1569 		else if ( !Q_irand( 0, 2 ) )
1570 		{
1571 			return BOTH_PAIN8;
1572 		}
1573 		else if ( !Q_irand( 0, 1 ) )
1574 		{
1575 			return BOTH_PAIN17;
1576 		}
1577 		else
1578 		{
1579 			return BOTH_PAIN18;
1580 		}
1581 		//PAIN6 = gut
1582 		//PAIN8 = chest
1583 		//PAIN17 = twitch crotch
1584 		//PAIN19 = med crotch
1585 		break;
1586 	case HL_ARM_RT:
1587 	case HL_HAND_RT:
1588 		return BOTH_PAIN9;
1589 		//PAIN9 = twitch right arm
1590 		break;
1591 	case HL_ARM_LT:
1592 	case HL_HAND_LT:
1593 		return BOTH_PAIN10;
1594 		//PAIN10 = twitch left arm
1595 		break;
1596 	case HL_HEAD:
1597 		return BOTH_PAIN4;
1598 		//PAIN4 = head
1599 		break;
1600 	default:
1601 		return -1;
1602 		break;
1603 	}
1604 }
1605 
1606 extern void G_BounceMissile( gentity_t *ent, trace_t *trace );
LimbThink(gentity_t * ent)1607 void LimbThink( gentity_t *ent )
1608 {//FIXME: just use object thinking?
1609 	vec3_t		origin;
1610 	trace_t		tr;
1611 
1612 
1613 	ent->nextthink = level.time + FRAMETIME;
1614 	if ( ent->owner
1615 		&& ent->owner->client
1616 		&& (ent->owner->client->ps.eFlags&EF_HELD_BY_RANCOR) )
1617 	{
1618 		ent->e_ThinkFunc = thinkF_G_FreeEntity;
1619 		return;
1620 	}
1621 
1622 	if ( ent->enemy )
1623 	{//alert people that I am a piece of one of their friends
1624 		AddSightEvent( ent->enemy, ent->currentOrigin, 384, AEL_DISCOVERED );
1625 	}
1626 
1627 	if ( ent->s.pos.trType == TR_STATIONARY )
1628 	{//stopped
1629 		if ( level.time > ent->s.apos.trTime + ent->s.apos.trDuration )
1630 		{
1631 			if (ent->owner && ent->owner->m_pVehicle)
1632 			{
1633 				ent->nextthink = level.time + Q_irand( 10000, 15000 );
1634 			}
1635 			else
1636 			{
1637 				ent->nextthink = level.time + Q_irand( 5000, 15000 );
1638 			}
1639 
1640 			ent->e_ThinkFunc = thinkF_G_FreeEntity;
1641 			//FIXME: these keep drawing for a frame or so after being freed?!  See them lerp to origin of world...
1642 		}
1643 		else
1644 		{
1645 			EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles );
1646 		}
1647 		return;
1648 	}
1649 
1650 	// get current position
1651 	EvaluateTrajectory( &ent->s.pos, level.time, origin );
1652 	// get current angles
1653 	EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles );
1654 
1655 	// trace a line from the previous position to the current position,
1656 	// ignoring interactions with the missile owner
1657 	gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin,
1658 		ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, (EG2_Collision)0, 0 );
1659 
1660 	VectorCopy( tr.endpos, ent->currentOrigin );
1661 	if ( tr.startsolid )
1662 	{
1663 		tr.fraction = 0;
1664 	}
1665 
1666 
1667 	gi.linkentity( ent );
1668 
1669 	if ( tr.fraction != 1 )
1670 	{
1671 		G_BounceMissile( ent, &tr );
1672 		if ( ent->s.pos.trType == TR_STATIONARY )
1673 		{//stopped, stop spinning
1674 			//lay flat
1675 			//pitch
1676 			VectorCopy( ent->currentAngles, ent->s.apos.trBase );
1677 			vec3_t	flatAngles;
1678 			if ( ent->s.angles2[0] == -1 )
1679 			{//any pitch is okay
1680 				flatAngles[0] = ent->currentAngles[0];
1681 			}
1682 			else
1683 			{//lay flat
1684 				if ( ent->owner
1685 					&& ent->owner->client
1686 					&& ent->owner->client->NPC_class == CLASS_PROTOCOL
1687 					&& ent->count == BOTH_DISMEMBER_TORSO1 )
1688 				{
1689 					if ( ent->currentAngles[0] > 0 || ent->currentAngles[0] < -180 )
1690 					{
1691 						flatAngles[0] = -90;
1692 					}
1693 					else
1694 					{
1695 						flatAngles[0] = 90;
1696 					}
1697 				}
1698 				else
1699 				{
1700 					if ( ent->currentAngles[0] > 90 || ent->currentAngles[0] < -90 )
1701 					{
1702 						flatAngles[0] = 180;
1703 					}
1704 					else
1705 					{
1706 						flatAngles[0] = 0;
1707 					}
1708 				}
1709 			}
1710 			//yaw
1711 			flatAngles[1] = ent->currentAngles[1];
1712 			//roll
1713 			if ( ent->s.angles2[2] == -1 )
1714 			{//any roll is okay
1715 				flatAngles[2] = ent->currentAngles[2];
1716 			}
1717 			else
1718 			{
1719 				if ( ent->currentAngles[2] > 90 || ent->currentAngles[2] < -90 )
1720 				{
1721 					flatAngles[2] = 180;
1722 				}
1723 				else
1724 				{
1725 					flatAngles[2] = 0;
1726 				}
1727 			}
1728 			VectorSubtract( flatAngles, ent->s.apos.trBase, ent->s.apos.trDelta );
1729 			for ( int i = 0; i < 3; i++ )
1730 			{
1731 				ent->s.apos.trDelta[i] = AngleNormalize180( ent->s.apos.trDelta[i] );
1732 			}
1733 			ent->s.apos.trTime = level.time;
1734 			ent->s.apos.trDuration = 1000;
1735 			ent->s.apos.trType = TR_LINEAR_STOP;
1736 			//VectorClear( ent->s.apos.trDelta );
1737 		}
1738 	}
1739 }
1740 
1741 float hitLocHealthPercentage[HL_MAX] =
1742 {
1743 	0.0f,	//HL_NONE = 0,
1744 	0.05f,	//HL_FOOT_RT,
1745 	0.05f,	//HL_FOOT_LT,
1746 	0.20f,	//HL_LEG_RT,
1747 	0.20f,	//HL_LEG_LT,
1748 	0.30f,	//HL_WAIST,
1749 	0.15f,	//HL_BACK_RT,
1750 	0.15f,	//HL_BACK_LT,
1751 	0.30f,	//HL_BACK,
1752 	0.15f,	//HL_CHEST_RT,
1753 	0.15f,	//HL_CHEST_LT,
1754 	0.30f,	//HL_CHEST,
1755 	0.05f,	//HL_ARM_RT,
1756 	0.05f,	//HL_ARM_LT,
1757 	0.01f,	//HL_HAND_RT,
1758 	0.01f,	//HL_HAND_LT,
1759 	0.10f,	//HL_HEAD
1760 	0.0f,	//HL_GENERIC1,
1761 	0.0f,	//HL_GENERIC2,
1762 	0.0f,	//HL_GENERIC3,
1763 	0.0f,	//HL_GENERIC4,
1764 	0.0f,	//HL_GENERIC5,
1765 	0.0f	//HL_GENERIC6
1766 };
1767 
1768 const char *hitLocName[HL_MAX] =
1769 {
1770 	"none",	//HL_NONE = 0,
1771 	"right foot",	//HL_FOOT_RT,
1772 	"left foot",	//HL_FOOT_LT,
1773 	"right leg",	//HL_LEG_RT,
1774 	"left leg",	//HL_LEG_LT,
1775 	"waist",	//HL_WAIST,
1776 	"back right shoulder",	//HL_BACK_RT,
1777 	"back left shoulder",	//HL_BACK_LT,
1778 	"back",	//HL_BACK,
1779 	"front right shouler",	//HL_CHEST_RT,
1780 	"front left shoulder",	//HL_CHEST_LT,
1781 	"chest",	//HL_CHEST,
1782 	"right arm",	//HL_ARM_RT,
1783 	"left arm",	//HL_ARM_LT,
1784 	"right hand",	//HL_HAND_RT,
1785 	"left hand",	//HL_HAND_LT,
1786 	"head",	//HL_HEAD
1787 	"generic1",	//HL_GENERIC1,
1788 	"generic2",	//HL_GENERIC2,
1789 	"generic3",	//HL_GENERIC3,
1790 	"generic4",	//HL_GENERIC4,
1791 	"generic5",	//HL_GENERIC5,
1792 	"generic6"	//HL_GENERIC6
1793 };
1794 
G_LimbLost(gentity_t * ent,int hitLoc)1795 qboolean G_LimbLost( gentity_t *ent, int hitLoc )
1796 {
1797 	switch ( hitLoc )
1798 	{
1799 	case HL_FOOT_RT:
1800 		if ( ent->locationDamage[HL_FOOT_RT] >= Q3_INFINITE )
1801 		{
1802 			return qtrue;
1803 		}
1804 		//NOTE: falls through
1805 	case HL_LEG_RT:
1806 		//NOTE: feet fall through
1807 		if ( ent->locationDamage[HL_LEG_RT] >= Q3_INFINITE )
1808 		{
1809 			return qtrue;
1810 		}
1811 		return qfalse;
1812 		break;
1813 
1814 	case HL_FOOT_LT:
1815 		if ( ent->locationDamage[HL_FOOT_LT] >= Q3_INFINITE )
1816 		{
1817 			return qtrue;
1818 		}
1819 		//NOTE: falls through
1820 	case HL_LEG_LT:
1821 		//NOTE: feet fall through
1822 		if ( ent->locationDamage[HL_LEG_LT] >= Q3_INFINITE )
1823 		{
1824 			return qtrue;
1825 		}
1826 		return qfalse;
1827 		break;
1828 
1829 	case HL_HAND_LT:
1830 		if ( ent->locationDamage[HL_HAND_LT] >= Q3_INFINITE )
1831 		{
1832 			return qtrue;
1833 		}
1834 		//NOTE: falls through
1835 	case HL_ARM_LT:
1836 	case HL_CHEST_LT:
1837 	case HL_BACK_RT:
1838 		//NOTE: hand falls through
1839 		if ( ent->locationDamage[HL_ARM_LT] >= Q3_INFINITE
1840 			|| ent->locationDamage[HL_CHEST_LT] >= Q3_INFINITE
1841 			|| ent->locationDamage[HL_BACK_RT] >= Q3_INFINITE
1842 			|| ent->locationDamage[HL_WAIST] >= Q3_INFINITE )
1843 		{
1844 			return qtrue;
1845 		}
1846 		return qfalse;
1847 		break;
1848 
1849 	case HL_HAND_RT:
1850 		if ( ent->locationDamage[HL_HAND_RT] >= Q3_INFINITE )
1851 		{
1852 			return qtrue;
1853 		}
1854 		//NOTE: falls through
1855 	case HL_ARM_RT:
1856 	case HL_CHEST_RT:
1857 	case HL_BACK_LT:
1858 		//NOTE: hand falls through
1859 		if ( ent->locationDamage[HL_ARM_RT] >= Q3_INFINITE
1860 			|| ent->locationDamage[HL_CHEST_RT] >= Q3_INFINITE
1861 			|| ent->locationDamage[HL_BACK_LT] >= Q3_INFINITE
1862 			|| ent->locationDamage[HL_WAIST] >= Q3_INFINITE )
1863 		{
1864 			return qtrue;
1865 		}
1866 		return qfalse;
1867 		break;
1868 
1869 	case HL_HEAD:
1870 		if ( ent->locationDamage[HL_HEAD] >= Q3_INFINITE )
1871 		{
1872 			return qtrue;
1873 		}
1874 		//NOTE: falls through
1875 	case HL_WAIST:
1876 		//NOTE: head falls through
1877 		if ( ent->locationDamage[HL_WAIST] >= Q3_INFINITE )
1878 		{
1879 			return qtrue;
1880 		}
1881 		return qfalse;
1882 	default:
1883 		return (qboolean)(ent->locationDamage[hitLoc]>=Q3_INFINITE);
1884 	}
1885 }
1886 
1887 extern qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, char *returnSurfName, int returnSize );
G_RemoveWeaponsWithLimbs(gentity_t * ent,gentity_t * limb,int limbAnim)1888 void G_RemoveWeaponsWithLimbs( gentity_t *ent, gentity_t *limb, int limbAnim )
1889 {
1890 	int		weaponModelNum = 0, checkAnim;
1891 	char	handName[MAX_QPATH];
1892 
1893 	for ( weaponModelNum = 0; weaponModelNum < MAX_INHAND_WEAPONS; weaponModelNum++ )
1894 	{
1895 		if ( ent->weaponModel[weaponModelNum] >= 0 )
1896 		{//have a weapon in this hand
1897 			if ( weaponModelNum == 0 && ent->client->ps.saberInFlight )
1898 			{//this is the right-hand weapon and it's a saber in-flight (i.e.: not in-hand, so no need to worry about it)
1899 				continue;
1900 			}
1901 			//otherwise, the corpse hasn't dropped their weapon
1902 			switch ( weaponModelNum )
1903 			{
1904 			case 0://right hand
1905 				checkAnim = BOTH_DISMEMBER_RARM;
1906 				G_GetRootSurfNameWithVariant( ent, "r_hand", handName, sizeof(handName) );
1907 				break;
1908 			case 1://left hand
1909 				checkAnim = BOTH_DISMEMBER_LARM;
1910 				G_GetRootSurfNameWithVariant( ent, "l_hand", handName, sizeof(handName) );
1911 				break;
1912 			default://not handled/valid
1913 				continue;
1914 				break;
1915 			}
1916 
1917 			if ( limbAnim == checkAnim || limbAnim == BOTH_DISMEMBER_TORSO1 )//either/both hands
1918 			{//FIXME: is this first check needed with this lower one?
1919 				if ( !gi.G2API_GetSurfaceRenderStatus( &limb->ghoul2[0], handName ) )
1920 				{//only copy the weapon over if the hand is actually on this limb...
1921 					//copy it to limb
1922 					if ( ent->s.weapon != WP_NONE )
1923 					{//only if they actually still have a weapon
1924 						limb->s.weapon = ent->s.weapon;
1925 						limb->weaponModel[weaponModelNum] = ent->weaponModel[weaponModelNum];
1926 					}//else - weaponModel is not -1 but don't have a weapon?  Oops, somehow G2 model wasn't removed?
1927 					//remove it on owner
1928 					if ( ent->weaponModel[weaponModelNum] > 0 )
1929 					{
1930 						gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel[weaponModelNum] );
1931 						ent->weaponModel[weaponModelNum] = -1;
1932 					}
1933 					if ( !ent->client->ps.saberInFlight )
1934 					{//saberent isn't flying through the air, it's in-hand and attached to player
1935 						if ( ent->client->ps.saberEntityNum != ENTITYNUM_NONE && ent->client->ps.saberEntityNum > 0 )
1936 						{//remove the owner ent's saber model and entity
1937 							if ( g_entities[ent->client->ps.saberEntityNum].inuse )
1938 							{
1939 								G_FreeEntity( &g_entities[ent->client->ps.saberEntityNum] );
1940 							}
1941 							ent->client->ps.saberEntityNum = ENTITYNUM_NONE;
1942 						}
1943 					}
1944 				}
1945 				else
1946 				{//the hand had already been removed
1947 					if ( ent->weaponModel[weaponModelNum] > 0 )
1948 					{//still a weapon associated with it, remove it from the limb
1949 						gi.G2API_RemoveGhoul2Model( limb->ghoul2, ent->weaponModel[weaponModelNum] );
1950 						limb->weaponModel[weaponModelNum] = -1;
1951 					}
1952 				}
1953 			}
1954 			else
1955 			{//this weapon isn't affected by this dismemberment
1956 				if ( ent->weaponModel[weaponModelNum] > 0 )
1957 				{//but a weapon was copied over to the limb, so remove it
1958 					gi.G2API_RemoveGhoul2Model( limb->ghoul2, ent->weaponModel[weaponModelNum] );
1959 					limb->weaponModel[weaponModelNum] = -1;
1960 				}
1961 			}
1962 		}
1963 	}
1964 }
1965 
G_Dismember(gentity_t * ent,vec3_t point,const char * limbBone,const char * rotateBone,const char * limbName,const char * limbCapName,const char * stubCapName,const char * limbTagName,const char * stubTagName,int limbAnim,float limbRollBase,float limbPitchBase,int damage,int hitLoc)1966 static qboolean G_Dismember( gentity_t *ent, vec3_t point,
1967 				 const char *limbBone, const char *rotateBone, const char *limbName,
1968 				 const char *limbCapName, const char *stubCapName, const char *limbTagName, const char *stubTagName,
1969 				 int limbAnim, float limbRollBase, float limbPitchBase,
1970 				 int damage, int hitLoc )
1971 {
1972 	//int newBolt;
1973 	vec3_t	dir, newPoint, limbAngles = {0,ent->client->ps.legsYaw,0};
1974 	gentity_t *limb;
1975 	trace_t	trace;
1976 
1977 	//make sure this limb hasn't been lopped off already!
1978 	if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], limbName ) == 0x00000100/*G2SURFACEFLAG_NODESCENDANTS*/ )
1979 	{//already lost this limb
1980 		//NOTE: we now check for off wth no decendants
1981 		//because the torso surface can be off with
1982 		//the torso variations on when this is one of
1983 		//our "choose your own jedi" models
1984 		return qfalse;
1985 	}
1986 
1987 	//NOTE: only reason I have this next part is because G2API_GetSurfaceRenderStatus is *not* working
1988 	if ( G_LimbLost( ent, hitLoc ) )
1989 	{//already lost this limb
1990 		return qfalse;
1991 	}
1992 
1993 	//FIXME: when timescale is high, can sometimes cut off a surf that includes a surf that was already cut off
1994 //0) create a limb ent
1995 	VectorCopy( point, newPoint );
1996 	newPoint[2] += 6;
1997 	limb = G_Spawn();
1998 	G_SetOrigin( limb, newPoint );
1999 	//VectorCopy(ent->currentAngles,limbAngles);
2000 	//G_SetAngles( limb, ent->currentAngles );
2001 	VectorCopy( newPoint, limb->s.pos.trBase );
2002 //1) copy the g2 instance of the victim into the limb
2003 	gi.G2API_CopyGhoul2Instance( ent->ghoul2, limb->ghoul2, 0 );
2004 	limb->playerModel = 0;//assumption!
2005 	limb->craniumBone = ent->craniumBone;
2006 	limb->cervicalBone = ent->cervicalBone;
2007 	limb->thoracicBone = ent->thoracicBone;
2008 	limb->upperLumbarBone = ent->upperLumbarBone;
2009 	limb->lowerLumbarBone = ent->lowerLumbarBone;
2010 	limb->hipsBone = ent->hipsBone;
2011 	limb->rootBone = ent->rootBone;
2012 //2) set the root surf on the limb
2013 	if ( limbTagName )
2014 	{//add smoke to cap tag
2015 		/*newBolt = gi.G2API_AddBolt( &limb->ghoul2[limb->playerModel], limbTagName );
2016 		if ( newBolt != -1 )
2017 		{
2018 			G_PlayEffect( G_EffectIndex("saber/limb_bolton"), limb->playerModel, newBolt, limb->s.number, newPoint);
2019 		}*/
2020 	}
2021 	/*
2022 	if ( limbBone && hitLoc == HL_HEAD )
2023 	{//stop the current anim on the limb?
2024 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "model_root" );
2025 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "motion" );
2026 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "upper_lumbar" );
2027 	}
2028 	*/
2029 	gi.G2API_StopBoneAnimIndex( &limb->ghoul2[limb->playerModel], limb->hipsBone );
2030 
2031 	gi.G2API_SetRootSurface( limb->ghoul2, limb->playerModel, limbName );
2032 	/*
2033 	if ( limbBone && hitLoc != HL_WAIST )
2034 	{//play the dismember anim on the limb?
2035 		//FIXME: screws up origin
2036 		animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations;
2037 		//play the proper dismember anim on the limb
2038 		gi.G2API_SetBoneAnim(&limb->ghoul2[limb->playerModel], 0, animations[limbAnim].firstFrame - 1,
2039 							animations[limbAnim].numFrames + animations[limbAnim].firstFrame - 1,
2040 							BONE_ANIM_OVERRIDE_FREEZE, 1, cg.time);
2041 	}
2042 	*/
2043 	if ( limbBone && hitLoc == HL_WAIST && ent->client->NPC_class == CLASS_PROTOCOL )
2044 	{//play the dismember anim on the limb?
2045 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "model_root" );
2046 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "motion" );
2047 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "pelvis" );
2048 		gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "upper_lumbar" );
2049 		//FIXME: screws up origin
2050 		animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations;
2051 		//play the proper dismember anim on the limb
2052 		gi.G2API_SetBoneAnim(&limb->ghoul2[limb->playerModel], 0, animations[limbAnim].firstFrame,
2053 							animations[limbAnim].numFrames + animations[limbAnim].firstFrame,
2054 							BONE_ANIM_OVERRIDE_FREEZE, 1, cg.time, -1, -1 );
2055 	}
2056 	if ( rotateBone )
2057 	{
2058  		gi.G2API_SetNewOrigin( &limb->ghoul2[0], gi.G2API_AddBolt( &limb->ghoul2[0], rotateBone ) );
2059 
2060 		//now let's try to position the limb at the *exact* right spot
2061 		int newBolt = gi.G2API_AddBolt( &ent->ghoul2[0], rotateBone );
2062 		if ( newBolt != -1 )
2063 		{
2064 			int	actualTime = (cg.time?cg.time:level.time);
2065 			mdxaBone_t	boltMatrix;
2066 			vec3_t	angles;
2067 
2068 			VectorSet( angles, 0, ent->currentAngles[YAW], 0 );
2069 			gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, newBolt,
2070 							&boltMatrix, angles, ent->currentOrigin,
2071 							actualTime, NULL, ent->s.modelScale );
2072 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, limb->s.origin );
2073 			G_SetOrigin( limb, limb->s.origin );
2074 			VectorCopy( limb->s.origin, limb->s.pos.trBase );
2075 			//angles, too
2076 			/*
2077 			vec3_t	limbF, limbR;
2078 			newBolt = gi.G2API_AddBolt( &ent->ghoul2[0], limbBone );
2079 			if ( newBolt != -1 )
2080 			{
2081 				gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, newBolt,
2082 								&boltMatrix, angles, ent->currentOrigin,
2083 								actualTime, NULL, ent->s.modelScale );
2084 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_X, limbF );
2085 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, limbR );
2086 				vectoangles( limbF, limbAngles );
2087 				vectoangles( limbR, angles );
2088 				limbAngles[YAW] += 180;
2089 				limbAngles[ROLL] = angles[PITCH]*-1.0f;
2090 			}
2091 			*/
2092 		}
2093 	}
2094 	if ( limbCapName )
2095 	{//turn on caps
2096 		gi.G2API_SetSurfaceOnOff( &limb->ghoul2[limb->playerModel], limbCapName, 0 );
2097 	}
2098 //3) turn off w/descendants that surf in original model
2099 //NOTE: we actually change the ent's stuff on the cgame side so that there is no 50ms lag
2100 //		this is neccessary because the Ghoul2 info does not have to go over the network,
2101 //		cgame looks at game's data.  The new limb ent will be delayed so we will see
2102 //		that a limb is missing before the chopped off limb appears.
2103 //		also, if the limb was going to start in solid, we can delete it and return
2104 	if ( stubTagName )
2105 	{//add smoke to cap surf, spawn effect
2106 		//do it later
2107 		limb->target = G_NewString( stubTagName );
2108 		/*
2109 		newBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], stubTagName );
2110 		if ( newBolt != -1 )
2111 		{
2112 			G_PlayEffect( "blaster/smoke_bolton", ent->playerModel, newBolt, ent->s.number);
2113 		}
2114 		*/
2115 	}
2116 	if ( limbName )
2117 	{
2118 		limb->target2 = G_NewString( limbName );
2119 		//gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], limbName, 0x00000100 );//G2SURFACEFLAG_NODESCENDANTS
2120 	}
2121 	if ( stubCapName )
2122 	{//turn on caps
2123 		limb->target3 = G_NewString( stubCapName );
2124 		//gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], stubCapName, 0 );
2125 	}
2126 	limb->count = limbAnim;
2127 //
2128 	limb->s.radius = 60;
2129 //4) toss the limb away
2130 	limb->classname = "limb";
2131 	limb->owner = ent;
2132 	limb->enemy = ent->enemy;
2133 
2134 	//remove weapons/move them to limb (if applicable in this dismemberment)
2135 	G_RemoveWeaponsWithLimbs( ent, limb, limbAnim );
2136 
2137 	limb->e_clThinkFunc = clThinkF_CG_Limb;
2138 	limb->e_ThinkFunc = thinkF_LimbThink;
2139 	limb->nextthink = level.time + FRAMETIME;
2140 	gi.linkentity( limb );
2141 	//need size, contents, clipmask
2142 	limb->svFlags = SVF_USE_CURRENT_ORIGIN;
2143 	limb->clipmask = MASK_SOLID;
2144 	limb->contents = CONTENTS_CORPSE;
2145 	VectorSet( limb->mins, -3.0f, -3.0f, -6.0f );
2146 	VectorSet( limb->maxs, 3.0f, 3.0f, 6.0f );
2147 
2148 	//make sure it doesn't start in solid
2149 	gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask, (EG2_Collision)0, 0 );
2150 	if ( trace.startsolid )
2151 	{
2152 		limb->s.pos.trBase[2] -= limb->mins[2];
2153 		gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask, (EG2_Collision)0, 0 );
2154 		if ( trace.startsolid )
2155 		{
2156 			limb->s.pos.trBase[2] += limb->mins[2];
2157 			gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask, (EG2_Collision)0, 0 );
2158 			if ( trace.startsolid )
2159 			{//stuck?  don't remove
2160 				G_FreeEntity( limb );
2161 				return qfalse;
2162 			}
2163 		}
2164 	}
2165 
2166 	//move it
2167 	VectorCopy( limb->s.pos.trBase, limb->currentOrigin );
2168 	gi.linkentity( limb );
2169 
2170 	limb->s.eType = ET_THINKER;//ET_GENERAL;
2171 	limb->physicsBounce = 0.2f;
2172 	limb->s.pos.trType = TR_GRAVITY;
2173 	limb->s.pos.trTime = level.time;								// move a bit on the very first frame
2174 	VectorSubtract( point, ent->currentOrigin, dir );
2175 	VectorNormalize( dir );
2176 	//no trDuration?
2177 	//spin it
2178 	//new way- try to preserve the exact angle and position of the limb as it was when attached
2179 	VectorSet( limb->s.angles2, limbPitchBase, 0, limbRollBase );
2180 	VectorCopy( limbAngles, limb->s.apos.trBase );
2181 	/*
2182 	//old way- just set an angle...
2183 	limb->s.apos.trBase[0] += limbPitchBase;
2184 	limb->s.apos.trBase[1] = ent->client->ps.viewangles[1];
2185 	limb->s.apos.trBase[2] += limbRollBase;
2186 	*/
2187 	limb->s.apos.trTime = level.time;
2188 	limb->s.apos.trType = TR_LINEAR;
2189 	VectorClear( limb->s.apos.trDelta );
2190 
2191 	if ( hitLoc == HL_HAND_RT || hitLoc == HL_HAND_LT )
2192 	{//hands fly farther
2193 		VectorMA( ent->client->ps.velocity, 200, dir, limb->s.pos.trDelta );
2194 		//make it bounce some
2195 		limb->s.eFlags |= EF_BOUNCE_HALF;
2196 		limb->s.apos.trDelta[0] = Q_irand( -300, 300 );
2197 		limb->s.apos.trDelta[1] = Q_irand( -800, 800 );
2198 	}
2199 	else if ( limbAnim == BOTH_DISMEMBER_HEAD1
2200 		|| limbAnim == BOTH_DISMEMBER_LARM
2201 		|| limbAnim == BOTH_DISMEMBER_RARM )
2202 	{//head and arms don't fly as far
2203 		limb->s.eFlags |= EF_BOUNCE_SHRAPNEL;
2204 		VectorMA( ent->client->ps.velocity, 150, dir, limb->s.pos.trDelta );
2205 		limb->s.apos.trDelta[0] = Q_irand( -200, 200 );
2206 		limb->s.apos.trDelta[1] = Q_irand( -400, 400 );
2207 	}
2208 	else// if ( limbAnim == BOTH_DISMEMBER_TORSO1 || limbAnim == BOTH_DISMEMBER_LLEG || limbAnim == BOTH_DISMEMBER_RLEG )
2209 	{//everything else just kinda falls off
2210 		limb->s.eFlags |= EF_BOUNCE_SHRAPNEL;
2211 		VectorMA( ent->client->ps.velocity, 100, dir, limb->s.pos.trDelta );
2212 		limb->s.apos.trDelta[0] = Q_irand( -100, 100 );
2213 		limb->s.apos.trDelta[1] = Q_irand( -200, 200 );
2214 	}
2215 	//roll? No, doesn't work...
2216 	//limb->s.apos.trDelta[2] = Q_irand( -300, 300 );//FIXME: this scales it down @ 80% and does weird stuff in timescale != 1.0
2217 	//limb->s.apos.trDelta[2] = limbRoll;
2218 
2219 	//preserve scale so giants don't have tiny limbs
2220 	VectorCopy( ent->s.modelScale, limb->s.modelScale );
2221 
2222 	//mark ent as dismembered
2223 	ent->locationDamage[hitLoc] = Q3_INFINITE;//mark this limb as gone
2224 	ent->client->dismembered = true;
2225 
2226 	//copy the custom RGB to the limb (for skin coloring)
2227 	limb->startRGBA[0] = ent->client->renderInfo.customRGBA[0];
2228 	limb->startRGBA[1] = ent->client->renderInfo.customRGBA[1];
2229 	limb->startRGBA[2] = ent->client->renderInfo.customRGBA[2];
2230 
2231 	return qtrue;
2232 }
2233 
G_Dismemberable(gentity_t * self,int hitLoc)2234 static qboolean G_Dismemberable( gentity_t *self, int hitLoc )
2235 {
2236 	if ( self->client->dismembered )
2237 	{//cannot dismember me right now
2238 		return qfalse;
2239 	}
2240 	if ( !debug_subdivision->integer && g_saberRealisticCombat->integer < 2 )
2241 	{
2242 		if ( g_dismemberProbabilities->value > 0.0f )
2243 		{//use the ent-specific dismemberProbabilities
2244 			float dismemberProb = 0;
2245 			// check which part of the body it is. Then check the npc's probability
2246 			// of that body part coming off, if it doesn't pass, return out.
2247 			switch ( hitLoc )
2248 			{
2249 			case HL_LEG_RT:
2250 			case HL_LEG_LT:
2251 				dismemberProb = self->client->dismemberProbLegs;
2252 				break;
2253 			case HL_WAIST:
2254 				dismemberProb = self->client->dismemberProbWaist;
2255 				break;
2256 			case HL_BACK_RT:
2257 			case HL_BACK_LT:
2258 			case HL_CHEST_RT:
2259 			case HL_CHEST_LT:
2260 			case HL_ARM_RT:
2261 			case HL_ARM_LT:
2262 				dismemberProb = self->client->dismemberProbArms;
2263 				break;
2264 			case HL_HAND_RT:
2265 			case HL_HAND_LT:
2266 				dismemberProb = self->client->dismemberProbHands;
2267 				break;
2268 			case HL_HEAD:
2269 				dismemberProb = self->client->dismemberProbHead;
2270 				break;
2271 			default:
2272 				return qfalse;
2273 				break;
2274 			}
2275 
2276 			//check probability of this happening on this npc
2277 			if ( floor((Q_flrand( 1, 100 )*g_dismemberProbabilities->value)) > dismemberProb*2.0f )//probabilities seemed really really low, had to crank them up
2278 			{
2279 				return qfalse;
2280 			}
2281 		}
2282 	}
2283 	return qtrue;
2284 }
2285 
G_Dismemberable2(gentity_t * self,int hitLoc)2286 static qboolean G_Dismemberable2( gentity_t *self, int hitLoc )
2287 {
2288 	if ( self->client->dismembered )
2289 	{//cannot dismember me right now
2290 		return qfalse;
2291 	}
2292 	if ( !debug_subdivision->integer && g_saberRealisticCombat->integer < 2 )
2293 	{
2294 		if ( g_dismemberProbabilities->value <= 0.0f )
2295 		{//add the passed-in damage to the locationDamage array, check to see if it's taken enough damage to actually dismember
2296 			if ( self->locationDamage[hitLoc] < (self->client->ps.stats[STAT_MAX_HEALTH]*hitLocHealthPercentage[hitLoc]) )
2297 			{//this location has not taken enough damage to dismember
2298 				return qfalse;
2299 			}
2300 		}
2301 	}
2302 	return qtrue;
2303 }
2304 
2305 #define	MAX_VARIANTS 8
G_GetRootSurfNameWithVariant(gentity_t * ent,const char * rootSurfName,char * returnSurfName,int returnSize)2306 qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, char *returnSurfName, int returnSize )
2307 {
2308 	if ( !gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], rootSurfName ) )
2309 	{//see if the basic name without variants is on
2310 		Q_strncpyz( returnSurfName, rootSurfName, returnSize );
2311 		return qtrue;
2312 	}
2313 	else
2314 	{//check variants
2315 		int i;
2316 		for ( i = 0; i < MAX_VARIANTS; i++ )
2317 		{
2318 			Com_sprintf( returnSurfName, returnSize, "%s%c", rootSurfName, 'a'+i );
2319 			if ( !gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], returnSurfName ) )
2320 			{
2321 				return qtrue;
2322 			}
2323 		}
2324 	}
2325 	Q_strncpyz( returnSurfName, rootSurfName, returnSize );
2326 	return qfalse;
2327 }
2328 
2329 extern qboolean G_StandardHumanoid( gentity_t *self );
G_DoDismemberment(gentity_t * self,vec3_t point,int mod,int damage,int hitLoc,qboolean force=qfalse)2330 qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force = qfalse )
2331 {
2332 //extern cvar_t	*g_iscensored;
2333 	// dismemberment -- FIXME: should have a check for how long npc has been dead so people can't
2334 	// continue to dismember a dead body long after it's been dead
2335 	//NOTE that you can only cut one thing off unless the debug_subdivisions is on
2336 	if ( ( g_dismemberment->integer || g_saberRealisticCombat->integer > 1 ) && mod == MOD_SABER )//only lightsaber
2337 	{//FIXME: don't do strcmps here
2338 		if ( G_StandardHumanoid( self )
2339 			&& (force||g_dismemberProbabilities->value>0.0f||G_Dismemberable2( self, hitLoc )) )
2340 		{//either it's a forced dismemberment or we're using probabilities (which are checked before this) or we've done enough damage to this location
2341 			//FIXME: check the hitLoc and hitDir against the cap tag for the place
2342 			//where the split will be- if the hit dir is roughly perpendicular to
2343 			//the direction of the cap, then the split is allowed, otherwise we
2344 			//hit it at the wrong angle and should not dismember...
2345 			const char	*limbBone = NULL, *rotateBone = NULL, *limbTagName = NULL, *stubTagName = NULL;
2346 			int		anim = -1;
2347 			float	limbRollBase = 0, limbPitchBase = 0;
2348 			qboolean doDismemberment = qfalse;
2349 			char	limbName[MAX_QPATH];
2350 			char	stubName[MAX_QPATH];
2351 			char	limbCapName[MAX_QPATH];
2352 			char	stubCapName[MAX_QPATH];
2353 
2354 			switch( hitLoc )//self->hitLoc
2355 			{
2356 			case HL_LEG_RT:
2357 				if ( g_dismemberment->integer > 1 )
2358 				{
2359 					doDismemberment = qtrue;
2360 					limbBone = "rtibia";
2361 					rotateBone = "rtalus";
2362 					G_GetRootSurfNameWithVariant( self, "r_leg", limbName, sizeof(limbName) );
2363 					G_GetRootSurfNameWithVariant( self, "hips", stubName, sizeof(stubName) );
2364 					Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
2365 					Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
2366 					limbTagName = "*r_leg_cap_hips";
2367 					stubTagName = "*hips_cap_r_leg";
2368 					anim = BOTH_DISMEMBER_RLEG;
2369 					limbRollBase = 0;
2370 					limbPitchBase = 0;
2371 				}
2372 				break;
2373 			case HL_LEG_LT:
2374 				if ( g_dismemberment->integer > 1 )
2375 				{
2376 					doDismemberment = qtrue;
2377 					limbBone = "ltibia";
2378 					rotateBone = "ltalus";
2379 					G_GetRootSurfNameWithVariant( self, "l_leg", limbName, sizeof(limbName) );
2380 					G_GetRootSurfNameWithVariant( self, "hips", stubName, sizeof(stubName) );
2381 					Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
2382 					Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_leg", stubName );
2383 					limbTagName = "*l_leg_cap_hips";
2384 					stubTagName = "*hips_cap_l_leg";
2385 					anim = BOTH_DISMEMBER_LLEG;
2386 					limbRollBase = 0;
2387 					limbPitchBase = 0;
2388 				}
2389 				break;
2390 			case HL_WAIST:
2391 				if ( g_dismemberment->integer > 2 &&
2392 					(!self->s.number||!self->message))
2393 				{
2394 					doDismemberment = qtrue;
2395 					limbBone = "pelvis";
2396 					rotateBone = "thoracic";
2397 					Q_strncpyz( limbName, "torso", sizeof( limbName ) );
2398 					Q_strncpyz( limbCapName, "torso_cap_hips", sizeof( limbCapName ) );
2399 					Q_strncpyz( stubCapName, "hips_cap_torso", sizeof( stubCapName ) );
2400 					limbTagName = "*torso_cap_hips";
2401 					stubTagName = "*hips_cap_torso";
2402 					anim = BOTH_DISMEMBER_TORSO1;
2403 					limbRollBase = 0;
2404 					limbPitchBase = 0;
2405 				}
2406 				break;
2407 			case HL_CHEST_RT:
2408 			case HL_ARM_RT:
2409 			case HL_BACK_RT:
2410 				if ( g_dismemberment->integer )
2411 				{
2412 					doDismemberment = qtrue;
2413 					limbBone = "rhumerus";
2414 					rotateBone = "rradius";
2415 					G_GetRootSurfNameWithVariant( self, "r_arm", limbName, sizeof(limbName) );
2416 					G_GetRootSurfNameWithVariant( self, "torso", stubName, sizeof(stubName) );
2417 					Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName );
2418 					Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_arm", stubName );
2419 					limbTagName = "*r_arm_cap_torso";
2420 					stubTagName = "*torso_cap_r_arm";
2421 					anim = BOTH_DISMEMBER_RARM;
2422 					limbRollBase = 0;
2423 					limbPitchBase = 0;
2424 				}
2425 				break;
2426 			case HL_CHEST_LT:
2427 			case HL_ARM_LT:
2428 			case HL_BACK_LT:
2429 				if ( g_dismemberment->integer &&
2430 					(!self->s.number||!self->message))
2431 				{//either the player or not carrying a key on my arm
2432 					doDismemberment = qtrue;
2433 					limbBone = "lhumerus";
2434 					rotateBone = "lradius";
2435 					G_GetRootSurfNameWithVariant( self, "l_arm", limbName, sizeof(limbName) );
2436 					G_GetRootSurfNameWithVariant( self, "torso", stubName, sizeof(stubName) );
2437 					Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName );
2438 					Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_arm", stubName );
2439 					limbTagName = "*l_arm_cap_torso";
2440 					stubTagName = "*torso_cap_l_arm";
2441 					anim = BOTH_DISMEMBER_LARM;
2442 					limbRollBase = 0;
2443 					limbPitchBase = 0;
2444 				}
2445 				break;
2446 			case HL_HAND_RT:
2447 				if ( g_dismemberment->integer )
2448 				{
2449 					doDismemberment = qtrue;
2450 					limbBone = "rradiusX";
2451 					rotateBone = "rhand";
2452 					G_GetRootSurfNameWithVariant( self, "r_hand", limbName, sizeof(limbName) );
2453 					G_GetRootSurfNameWithVariant( self, "r_arm", stubName, sizeof(stubName) );
2454 					Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_r_arm", limbName );
2455 					Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_hand", stubName );
2456 					limbTagName = "*r_hand_cap_r_arm";
2457 					stubTagName = "*r_arm_cap_r_hand";
2458 					anim = BOTH_DISMEMBER_RARM;
2459 					limbRollBase = 0;
2460 					limbPitchBase = 0;
2461 				}
2462 				break;
2463 			case HL_HAND_LT:
2464 				if ( g_dismemberment->integer )
2465 				{
2466 					doDismemberment = qtrue;
2467 					limbBone = "lradiusX";
2468 					rotateBone = "lhand";
2469 					G_GetRootSurfNameWithVariant( self, "l_hand", limbName, sizeof(limbName) );
2470 					G_GetRootSurfNameWithVariant( self, "l_arm", stubName, sizeof(stubName) );
2471 					Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_l_arm", limbName );
2472 					Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_hand", stubName );
2473 					limbTagName = "*l_hand_cap_l_arm";
2474 					stubTagName = "*l_arm_cap_l_hand";
2475 					anim = BOTH_DISMEMBER_LARM;
2476 					limbRollBase = 0;
2477 					limbPitchBase = 0;
2478 				}
2479 				break;
2480 			case HL_HEAD:
2481 				if ( g_dismemberment->integer > 2 )
2482 				{
2483 					doDismemberment = qtrue;
2484 					limbBone = "cervical";
2485 					rotateBone = "cranium";
2486 					Q_strncpyz( limbName, "head", sizeof( limbName ) );
2487 					Q_strncpyz( limbCapName, "head_cap_torso", sizeof( limbCapName ) );
2488 					Q_strncpyz( stubCapName, "torso_cap_head", sizeof( stubCapName ) );
2489 					limbTagName = "*head_cap_torso";
2490 					stubTagName = "*torso_cap_head";
2491 					anim = BOTH_DISMEMBER_HEAD1;
2492 					limbRollBase = -1;
2493 					limbPitchBase = -1;
2494 				}
2495 				break;
2496 			case HL_FOOT_RT:
2497 			case HL_FOOT_LT:
2498 			case HL_CHEST:
2499 			case HL_BACK:
2500 			default:
2501 				break;
2502 			}
2503 			if ( doDismemberment )
2504 			{
2505 				return G_Dismember( self, point, limbBone, rotateBone, limbName,
2506 					limbCapName, stubCapName, limbTagName, stubTagName,
2507 					anim, limbRollBase, limbPitchBase, damage, hitLoc );
2508 			}
2509 		}
2510 	}
2511 	return qfalse;
2512 }
2513 
G_CheckSpecialDeathAnim(gentity_t * self,vec3_t point,int damage,int mod,int hitLoc)2514 static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc )
2515 {
2516 	int deathAnim = -1;
2517 
2518 	if ( self->client->ps.legsAnim == BOTH_GETUP_BROLL_L
2519 		|| self->client->ps.legsAnim == BOTH_GETUP_BROLL_R )
2520 	{//rolling away to the side on our back
2521 		deathAnim = BOTH_DEATH_LYING_UP;
2522 	}
2523 	else if ( self->client->ps.legsAnim == BOTH_GETUP_FROLL_L
2524 		|| self->client->ps.legsAnim == BOTH_GETUP_FROLL_R )
2525 	{//rolling away to the side on our front
2526 		deathAnim = BOTH_DEATH_LYING_DN;
2527 	}
2528 	else if ( self->client->ps.legsAnim == BOTH_GETUP_BROLL_F
2529 		&& self->client->ps.legsAnimTimer > 350 )
2530 	{//kicking up
2531 		deathAnim = BOTH_DEATH_FALLING_UP;
2532 	}
2533 	else if ( self->client->ps.legsAnim == BOTH_GETUP_BROLL_B
2534 		&& self->client->ps.legsAnimTimer > 950 )
2535 	{//on back, rolling back to get up
2536 		deathAnim = BOTH_DEATH_LYING_UP;
2537 	}
2538 	else if ( self->client->ps.legsAnim == BOTH_GETUP_BROLL_B
2539 		&& self->client->ps.legsAnimTimer <= 950
2540 		&& self->client->ps.legsAnimTimer > 250 )
2541 	{//flipping over backwards
2542 		deathAnim = BOTH_FALLDEATH1LAND;
2543 	}
2544 	else if ( self->client->ps.legsAnim == BOTH_GETUP_FROLL_B
2545 		&& self->client->ps.legsAnimTimer <= 1100
2546 		&& self->client->ps.legsAnimTimer > 250 )
2547 	{//flipping over backwards
2548 		deathAnim = BOTH_FALLDEATH1LAND;
2549 	}
2550 	else if ( PM_InRoll( &self->client->ps ) )
2551 	{
2552 		deathAnim = BOTH_DEATH_ROLL;		//# Death anim from a roll
2553 	}
2554 	else if ( PM_FlippingAnim( self->client->ps.legsAnim ) )
2555 	{
2556 		deathAnim = BOTH_DEATH_FLIP;		//# Death anim from a flip
2557 	}
2558 	else if ( PM_SpinningAnim( self->client->ps.legsAnim ) )
2559 	{
2560 		float yawDiff = AngleNormalize180(AngleNormalize180(self->client->renderInfo.torsoAngles[YAW]) - AngleNormalize180(self->client->ps.viewangles[YAW]));
2561 		if ( yawDiff > 135 || yawDiff < -135 )
2562 		{
2563 			deathAnim = BOTH_DEATH_SPIN_180;	//# Death anim when facing backwards
2564 		}
2565 		else if ( yawDiff < -60 )
2566 		{
2567 			deathAnim = BOTH_DEATH_SPIN_90_R;	//# Death anim when facing 90 degrees right
2568 		}
2569 		else if ( yawDiff > 60 )
2570 		{
2571 			deathAnim = BOTH_DEATH_SPIN_90_L;	//# Death anim when facing 90 degrees left
2572 		}
2573 	}
2574 	else if ( PM_InKnockDown( &self->client->ps ) )
2575 	{//since these happen a lot, let's handle them case by case
2576 		int animLength = PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim );
2577 		if ( self->s.number < MAX_CLIENTS )
2578 		{
2579 			switch ( self->client->ps.legsAnim )
2580 			{
2581 			case BOTH_KNOCKDOWN1:
2582 			case BOTH_KNOCKDOWN2:
2583 			case BOTH_KNOCKDOWN3:
2584 			case BOTH_KNOCKDOWN4:
2585 			case BOTH_KNOCKDOWN5:
2586 			//case BOTH_PLAYER_PA_3_FLY:
2587 				animLength += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
2588 				break;
2589 			}
2590 		}
2591 		switch ( self->client->ps.legsAnim )
2592 		{
2593 		case BOTH_KNOCKDOWN1:
2594 			if ( animLength - self->client->ps.legsAnimTimer > 100 )
2595 			{//on our way down
2596 				if ( self->client->ps.legsAnimTimer > 600 )
2597 				{//still partially up
2598 					deathAnim = BOTH_DEATH_FALLING_UP;
2599 				}
2600 				else
2601 				{//down
2602 					deathAnim = BOTH_DEATH_LYING_UP;
2603 				}
2604 			}
2605 			break;
2606 		case BOTH_PLAYER_PA_3_FLY:
2607 		case BOTH_KNOCKDOWN2:
2608 			if ( animLength - self->client->ps.legsAnimTimer > 700 )
2609 			{//on our way down
2610 				if ( self->client->ps.legsAnimTimer > 600 )
2611 				{//still partially up
2612 					deathAnim = BOTH_DEATH_FALLING_UP;
2613 				}
2614 				else
2615 				{//down
2616 					deathAnim = BOTH_DEATH_LYING_UP;
2617 				}
2618 			}
2619 			break;
2620 		case BOTH_KNOCKDOWN3:
2621 			if ( animLength - self->client->ps.legsAnimTimer > 100 )
2622 			{//on our way down
2623 				if ( self->client->ps.legsAnimTimer > 1300 )
2624 				{//still partially up
2625 					deathAnim = BOTH_DEATH_FALLING_DN;
2626 				}
2627 				else
2628 				{//down
2629 					deathAnim = BOTH_DEATH_LYING_DN;
2630 				}
2631 			}
2632 			break;
2633 		case BOTH_KNOCKDOWN4:
2634 		case BOTH_RELEASED:
2635 			if ( animLength - self->client->ps.legsAnimTimer > 300 )
2636 			{//on our way down
2637 				if ( self->client->ps.legsAnimTimer > 350 )
2638 				{//still partially up
2639 					deathAnim = BOTH_DEATH_FALLING_UP;
2640 				}
2641 				else
2642 				{//down
2643 					deathAnim = BOTH_DEATH_LYING_UP;
2644 				}
2645 			}
2646 			else
2647 			{//crouch death
2648 				vec3_t fwd;
2649 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2650 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2651 				if ( thrown < -150 )
2652 				{
2653 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2654 				}
2655 				else
2656 				{
2657 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2658 				}
2659 			}
2660 			break;
2661 		case BOTH_KNOCKDOWN5:
2662 		case BOTH_LK_DL_ST_T_SB_1_L:
2663 			if ( self->client->ps.legsAnimTimer < 750 )
2664 			{//flat
2665 				deathAnim = BOTH_DEATH_LYING_DN;
2666 			}
2667 			break;
2668 		case BOTH_GETUP1:
2669 			if ( self->client->ps.legsAnimTimer < 350 )
2670 			{//standing up
2671 			}
2672 			else if ( self->client->ps.legsAnimTimer < 800 )
2673 			{//crouching
2674 				vec3_t fwd;
2675 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2676 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2677 				if ( thrown < -150 )
2678 				{
2679 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2680 				}
2681 				else
2682 				{
2683 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2684 				}
2685 			}
2686 			else
2687 			{//lying down
2688 				if ( animLength - self->client->ps.legsAnimTimer > 450 )
2689 				{//partially up
2690 					deathAnim = BOTH_DEATH_FALLING_UP;
2691 				}
2692 				else
2693 				{//down
2694 					deathAnim = BOTH_DEATH_LYING_UP;
2695 				}
2696 			}
2697 			break;
2698 		case BOTH_GETUP2:
2699 			if ( self->client->ps.legsAnimTimer < 150 )
2700 			{//standing up
2701 			}
2702 			else if ( self->client->ps.legsAnimTimer < 850 )
2703 			{//crouching
2704 				vec3_t fwd;
2705 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2706 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2707 				if ( thrown < -150 )
2708 				{
2709 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2710 				}
2711 				else
2712 				{
2713 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2714 				}
2715 			}
2716 			else
2717 			{//lying down
2718 				if ( animLength - self->client->ps.legsAnimTimer > 500 )
2719 				{//partially up
2720 					deathAnim = BOTH_DEATH_FALLING_UP;
2721 				}
2722 				else
2723 				{//down
2724 					deathAnim = BOTH_DEATH_LYING_UP;
2725 				}
2726 			}
2727 			break;
2728 		case BOTH_GETUP3:
2729 			if ( self->client->ps.legsAnimTimer < 250 )
2730 			{//standing up
2731 			}
2732 			else if ( self->client->ps.legsAnimTimer < 600 )
2733 			{//crouching
2734 				vec3_t fwd;
2735 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2736 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2737 				if ( thrown < -150 )
2738 				{
2739 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2740 				}
2741 				else
2742 				{
2743 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2744 				}
2745 			}
2746 			else
2747 			{//lying down
2748 				if ( animLength - self->client->ps.legsAnimTimer > 150 )
2749 				{//partially up
2750 					deathAnim = BOTH_DEATH_FALLING_DN;
2751 				}
2752 				else
2753 				{//down
2754 					deathAnim = BOTH_DEATH_LYING_DN;
2755 				}
2756 			}
2757 			break;
2758 		case BOTH_GETUP4:
2759 			if ( self->client->ps.legsAnimTimer < 250 )
2760 			{//standing up
2761 			}
2762 			else if ( self->client->ps.legsAnimTimer < 600 )
2763 			{//crouching
2764 				vec3_t fwd;
2765 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2766 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2767 				if ( thrown < -150 )
2768 				{
2769 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2770 				}
2771 				else
2772 				{
2773 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2774 				}
2775 			}
2776 			else
2777 			{//lying down
2778 				if ( animLength - self->client->ps.legsAnimTimer > 850 )
2779 				{//partially up
2780 					deathAnim = BOTH_DEATH_FALLING_DN;
2781 				}
2782 				else
2783 				{//down
2784 					deathAnim = BOTH_DEATH_LYING_UP;
2785 				}
2786 			}
2787 			break;
2788 		case BOTH_GETUP5:
2789 			if ( self->client->ps.legsAnimTimer > 850 )
2790 			{//lying down
2791 				if ( animLength - self->client->ps.legsAnimTimer > 1500 )
2792 				{//partially up
2793 					deathAnim = BOTH_DEATH_FALLING_DN;
2794 				}
2795 				else
2796 				{//down
2797 					deathAnim = BOTH_DEATH_LYING_DN;
2798 				}
2799 			}
2800 			break;
2801 		case BOTH_GETUP_CROUCH_B1:
2802 			if ( self->client->ps.legsAnimTimer < 800 )
2803 			{//crouching
2804 				vec3_t fwd;
2805 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2806 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2807 				if ( thrown < -150 )
2808 				{
2809 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2810 				}
2811 				else
2812 				{
2813 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2814 				}
2815 			}
2816 			else
2817 			{//lying down
2818 				if ( animLength - self->client->ps.legsAnimTimer > 400 )
2819 				{//partially up
2820 					deathAnim = BOTH_DEATH_FALLING_UP;
2821 				}
2822 				else
2823 				{//down
2824 					deathAnim = BOTH_DEATH_LYING_UP;
2825 				}
2826 			}
2827 			break;
2828 		case BOTH_GETUP_CROUCH_F1:
2829 			if ( self->client->ps.legsAnimTimer < 800 )
2830 			{//crouching
2831 				vec3_t fwd;
2832 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2833 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2834 				if ( thrown < -150 )
2835 				{
2836 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2837 				}
2838 				else
2839 				{
2840 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2841 				}
2842 			}
2843 			else
2844 			{//lying down
2845 				if ( animLength - self->client->ps.legsAnimTimer > 150 )
2846 				{//partially up
2847 					deathAnim = BOTH_DEATH_FALLING_DN;
2848 				}
2849 				else
2850 				{//down
2851 					deathAnim = BOTH_DEATH_LYING_DN;
2852 				}
2853 			}
2854 			break;
2855 		case BOTH_FORCE_GETUP_B1:
2856 			if ( self->client->ps.legsAnimTimer < 325 )
2857 			{//standing up
2858 			}
2859 			else if ( self->client->ps.legsAnimTimer < 725 )
2860 			{//spinning up
2861 				deathAnim = BOTH_DEATH_SPIN_180;	//# Death anim when facing backwards
2862 			}
2863 			else if ( self->client->ps.legsAnimTimer < 900 )
2864 			{//crouching
2865 				vec3_t fwd;
2866 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2867 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2868 				if ( thrown < -150 )
2869 				{
2870 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2871 				}
2872 				else
2873 				{
2874 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2875 				}
2876 			}
2877 			else
2878 			{//lying down
2879 				if ( animLength - self->client->ps.legsAnimTimer > 50 )
2880 				{//partially up
2881 					deathAnim = BOTH_DEATH_FALLING_UP;
2882 				}
2883 				else
2884 				{//down
2885 					deathAnim = BOTH_DEATH_LYING_UP;
2886 				}
2887 			}
2888 			break;
2889 		case BOTH_FORCE_GETUP_B2:
2890 			if ( self->client->ps.legsAnimTimer < 575 )
2891 			{//standing up
2892 			}
2893 			else if ( self->client->ps.legsAnimTimer < 875 )
2894 			{//spinning up
2895 				deathAnim = BOTH_DEATH_SPIN_180;	//# Death anim when facing backwards
2896 			}
2897 			else if ( self->client->ps.legsAnimTimer < 900 )
2898 			{//crouching
2899 				vec3_t fwd;
2900 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2901 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2902 				if ( thrown < -150 )
2903 				{
2904 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2905 				}
2906 				else
2907 				{
2908 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2909 				}
2910 			}
2911 			else
2912 			{//lying down
2913 				//partially up
2914 				deathAnim = BOTH_DEATH_FALLING_UP;
2915 			}
2916 			break;
2917 		case BOTH_FORCE_GETUP_B3:
2918 			if ( self->client->ps.legsAnimTimer < 150 )
2919 			{//standing up
2920 			}
2921 			else if ( self->client->ps.legsAnimTimer < 775 )
2922 			{//flipping
2923 				deathAnim = BOTH_DEATHBACKWARD2; //backflip
2924 			}
2925 			else
2926 			{//lying down
2927 				//partially up
2928 				deathAnim = BOTH_DEATH_FALLING_UP;
2929 			}
2930 			break;
2931 		case BOTH_FORCE_GETUP_B4:
2932 			if ( self->client->ps.legsAnimTimer < 325 )
2933 			{//standing up
2934 			}
2935 			else
2936 			{//lying down
2937 				if ( animLength - self->client->ps.legsAnimTimer > 150 )
2938 				{//partially up
2939 					deathAnim = BOTH_DEATH_FALLING_UP;
2940 				}
2941 				else
2942 				{//down
2943 					deathAnim = BOTH_DEATH_LYING_UP;
2944 				}
2945 			}
2946 			break;
2947 		case BOTH_FORCE_GETUP_B5:
2948 			if ( self->client->ps.legsAnimTimer < 550 )
2949 			{//standing up
2950 			}
2951 			else if ( self->client->ps.legsAnimTimer < 1025 )
2952 			{//kicking up
2953 				deathAnim = BOTH_DEATHBACKWARD2; //backflip
2954 			}
2955 			else
2956 			{//lying down
2957 				if ( animLength - self->client->ps.legsAnimTimer > 50 )
2958 				{//partially up
2959 					deathAnim = BOTH_DEATH_FALLING_UP;
2960 				}
2961 				else
2962 				{//down
2963 					deathAnim = BOTH_DEATH_LYING_UP;
2964 				}
2965 			}
2966 			break;
2967 		case BOTH_FORCE_GETUP_B6:
2968 			if ( self->client->ps.legsAnimTimer < 225 )
2969 			{//standing up
2970 			}
2971 			else if ( self->client->ps.legsAnimTimer < 425 )
2972 			{//crouching up
2973 				vec3_t fwd;
2974 				AngleVectors( self->currentAngles, fwd, NULL, NULL );
2975 				float	thrown = DotProduct( fwd, self->client->ps.velocity );
2976 				if ( thrown < -150 )
2977 				{
2978 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
2979 				}
2980 				else
2981 				{
2982 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
2983 				}
2984 			}
2985 			else if ( self->client->ps.legsAnimTimer < 825 )
2986 			{//flipping up
2987 				deathAnim = BOTH_DEATHFORWARD3; //backflip
2988 			}
2989 			else
2990 			{//lying down
2991 				if ( animLength - self->client->ps.legsAnimTimer > 225 )
2992 				{//partially up
2993 					deathAnim = BOTH_DEATH_FALLING_UP;
2994 				}
2995 				else
2996 				{//down
2997 					deathAnim = BOTH_DEATH_LYING_UP;
2998 				}
2999 			}
3000 			break;
3001 		case BOTH_FORCE_GETUP_F1:
3002 			if ( self->client->ps.legsAnimTimer < 275 )
3003 			{//standing up
3004 			}
3005 			else if ( self->client->ps.legsAnimTimer < 750 )
3006 			{//flipping
3007 				deathAnim = BOTH_DEATH14;
3008 			}
3009 			else
3010 			{//lying down
3011 				if ( animLength - self->client->ps.legsAnimTimer > 100 )
3012 				{//partially up
3013 					deathAnim = BOTH_DEATH_FALLING_DN;
3014 				}
3015 				else
3016 				{//down
3017 					deathAnim = BOTH_DEATH_LYING_DN;
3018 				}
3019 			}
3020 			break;
3021 		case BOTH_FORCE_GETUP_F2:
3022 			if ( self->client->ps.legsAnimTimer < 1200 )
3023 			{//standing
3024 			}
3025 			else
3026 			{//lying down
3027 				if ( animLength - self->client->ps.legsAnimTimer > 225 )
3028 				{//partially up
3029 					deathAnim = BOTH_DEATH_FALLING_DN;
3030 				}
3031 				else
3032 				{//down
3033 					deathAnim = BOTH_DEATH_LYING_DN;
3034 				}
3035 			}
3036 			break;
3037 		}
3038 	}
3039 	else if ( PM_InOnGroundAnim( &self->client->ps ) )
3040 	{
3041 		if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 )
3042 		{
3043 			deathAnim = BOTH_DEATH_LYING_UP;	//# Death anim when lying on back
3044 		}
3045 		else
3046 		{
3047 			deathAnim = BOTH_DEATH_LYING_DN;	//# Death anim when lying on front
3048 		}
3049 	}
3050 	else if ( PM_CrouchAnim( self->client->ps.legsAnim ) )
3051 	{
3052 		vec3_t fwd;
3053 		AngleVectors( self->currentAngles, fwd, NULL, NULL );
3054 		float	thrown = DotProduct( fwd, self->client->ps.velocity );
3055 		if ( thrown < -200 )
3056 		{
3057 			deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
3058 			if ( self->client->ps.velocity[2] > 0 && self->client->ps.velocity[2] < 100 )
3059 			{
3060 				self->client->ps.velocity[2] = 100;
3061 			}
3062 		}
3063 		else
3064 		{
3065 			deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
3066 		}
3067 	}
3068 
3069 	return deathAnim;
3070 }
3071 extern qboolean PM_FinishedCurrentLegsAnim( gentity_t *self );
G_PickDeathAnim(gentity_t * self,vec3_t point,int damage,int mod,int hitLoc)3072 static int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc )
3073 {//FIXME: play dead flop anims on body if in an appropriate _DEAD anim when this func is called
3074 	int deathAnim = -1;
3075 	if ( hitLoc == HL_NONE )
3076 	{
3077 		hitLoc = G_GetHitLocation( self, point );//self->hitLoc
3078 	}
3079 	//dead flops...if you are already playing a death animation, I guess it can just return directly
3080 	switch( self->client->ps.legsAnim )
3081 	{
3082 	case BOTH_DEATH1:		//# First Death anim
3083 	case BOTH_DEAD1:
3084 	case BOTH_DEATH2:			//# Second Death anim
3085 	case BOTH_DEAD2:
3086 	case BOTH_DEATH8:			//#
3087 	case BOTH_DEAD8:
3088 	case BOTH_DEATH13:			//#
3089 	case BOTH_DEAD13:
3090 	case BOTH_DEATH14:			//#
3091 	case BOTH_DEAD14:
3092 	case BOTH_DEATH16:			//#
3093 	case BOTH_DEAD16:
3094 	case BOTH_DEADBACKWARD1:		//# First thrown backward death finished pose
3095 	case BOTH_DEADBACKWARD2:		//# Second thrown backward death finished pose
3096 		//return -2;
3097 		//break;
3098 		if ( PM_FinishedCurrentLegsAnim( self ) )
3099 		{//done with the anim
3100 			deathAnim = BOTH_DEADFLOP2;
3101 		}
3102 		else
3103 		{
3104 			deathAnim = -2;
3105 		}
3106 		return deathAnim;
3107 		break;
3108 	case BOTH_DEADFLOP2:
3109 		//return -2;
3110 		return BOTH_DEADFLOP2;
3111 		break;
3112 	case BOTH_DEATH10:			//#
3113 	case BOTH_DEAD10:
3114 	case BOTH_DEATH15:			//#
3115 	case BOTH_DEAD15:
3116 	case BOTH_DEADFORWARD1:		//# First thrown forward death finished pose
3117 	case BOTH_DEADFORWARD2:		//# Second thrown forward death finished pose
3118 		//return -2;
3119 		//break;
3120 		if ( PM_FinishedCurrentLegsAnim( self ) )
3121 		{//done with the anim
3122 			deathAnim = BOTH_DEADFLOP1;
3123 		}
3124 		else
3125 		{
3126 			deathAnim = -2;
3127 		}
3128 		return deathAnim;
3129 		break;
3130 	case BOTH_DEADFLOP1:
3131 		//return -2;
3132 		return BOTH_DEADFLOP1;
3133 		break;
3134 	case BOTH_DEAD3:				//# Third Death finished pose
3135 	case BOTH_DEAD4:				//# Fourth Death finished pose
3136 	case BOTH_DEAD5:				//# Fifth Death finished pose
3137 	case BOTH_DEAD6:				//# Sixth Death finished pose
3138 	case BOTH_DEAD7:				//# Seventh Death finished pose
3139 	case BOTH_DEAD9:				//#
3140 	case BOTH_DEAD11:			//#
3141 	case BOTH_DEAD12:			//#
3142 	case BOTH_DEAD17:			//#
3143 	case BOTH_DEAD18:			//#
3144 	case BOTH_DEAD19:			//#
3145 	case BOTH_DEAD20:			//#
3146 	case BOTH_DEAD21:			//#
3147 	case BOTH_DEAD22:			//#
3148 	case BOTH_DEAD23:			//#
3149 	case BOTH_DEAD24:			//#
3150 	case BOTH_DEAD25:			//#
3151 	case BOTH_LYINGDEAD1:		//# Killed lying down death finished pose
3152 	case BOTH_STUMBLEDEAD1:		//# Stumble forward death finished pose
3153 	case BOTH_FALLDEAD1LAND:		//# Fall forward and splat death finished pose
3154 	case BOTH_DEATH3:			//# Third Death anim
3155 	case BOTH_DEATH4:			//# Fourth Death anim
3156 	case BOTH_DEATH5:			//# Fifth Death anim
3157 	case BOTH_DEATH6:			//# Sixth Death anim
3158 	case BOTH_DEATH7:			//# Seventh Death anim
3159 	case BOTH_DEATH9:			//#
3160 	case BOTH_DEATH11:			//#
3161 	case BOTH_DEATH12:			//#
3162 	case BOTH_DEATH17:			//#
3163 	case BOTH_DEATH18:			//#
3164 	case BOTH_DEATH19:			//#
3165 	case BOTH_DEATH20:			//#
3166 	case BOTH_DEATH21:			//#
3167 	case BOTH_DEATH22:			//#
3168 	case BOTH_DEATH23:			//#
3169 	case BOTH_DEATH24:			//#
3170 	case BOTH_DEATH25:			//#
3171 	case BOTH_DEATHFORWARD1:		//# First Death in which they get thrown forward
3172 	case BOTH_DEATHFORWARD2:		//# Second Death in which they get thrown forward
3173 	case BOTH_DEATHFORWARD3:		//# Second Death in which they get thrown forward
3174 	case BOTH_DEATHBACKWARD1:	//# First Death in which they get thrown backward
3175 	case BOTH_DEATHBACKWARD2:	//# Second Death in which they get thrown backward
3176 	case BOTH_DEATH1IDLE:		//# Idle while close to death
3177 	case BOTH_LYINGDEATH1:		//# Death to play when killed lying down
3178 	case BOTH_STUMBLEDEATH1:		//# Stumble forward and fall face first death
3179 	case BOTH_FALLDEATH1:		//# Fall forward off a high cliff and splat death - start
3180 	case BOTH_FALLDEATH1INAIR:	//# Fall forward off a high cliff and splat death - loop
3181 	case BOTH_FALLDEATH1LAND:	//# Fall forward off a high cliff and splat death - hit bottom
3182 		return -2;
3183 		break;
3184 	case BOTH_DEATH_ROLL:		//# Death anim from a roll
3185 	case BOTH_DEATH_FLIP:		//# Death anim from a flip
3186 	case BOTH_DEATH_SPIN_90_R:	//# Death anim when facing 90 degrees right
3187 	case BOTH_DEATH_SPIN_90_L:	//# Death anim when facing 90 degrees left
3188 	case BOTH_DEATH_SPIN_180:	//# Death anim when facing backwards
3189 	case BOTH_DEATH_LYING_UP:	//# Death anim when lying on back
3190 	case BOTH_DEATH_LYING_DN:	//# Death anim when lying on front
3191 	case BOTH_DEATH_FALLING_DN:	//# Death anim when falling on face
3192 	case BOTH_DEATH_FALLING_UP:	//# Death anim when falling on back
3193 	case BOTH_DEATH_CROUCHED:	//# Death anim when crouched
3194 	case BOTH_RIGHTHANDCHOPPEDOFF:
3195 		return -2;
3196 		break;
3197 	}
3198 	// Not currently playing a death animation, so try and get an appropriate one now.
3199 	if ( deathAnim == -1 )
3200 	{
3201 		deathAnim = G_CheckSpecialDeathAnim( self, point, damage, mod, hitLoc );
3202 
3203 		if ( deathAnim == -1 )
3204 		{//base on hitLoc
3205 			vec3_t fwd;
3206 			AngleVectors( self->currentAngles, fwd, NULL, NULL );
3207 			float	thrown = DotProduct( fwd, self->client->ps.velocity );
3208 			//death anims
3209 			switch( hitLoc )
3210 			{
3211 			case HL_FOOT_RT:
3212 				if ( !Q_irand( 0, 2 ) && thrown < 250 )
3213 				{
3214 					deathAnim = BOTH_DEATH24;//right foot trips up, spin
3215 				}
3216 				else if ( !Q_irand( 0, 1 ) )
3217 				{
3218 					if ( !Q_irand( 0, 1 ) )
3219 					{
3220 						deathAnim = BOTH_DEATH4;//back: forward
3221 					}
3222 					else
3223 					{
3224 						deathAnim = BOTH_DEATH16;//same as 1
3225 					}
3226 				}
3227 				else
3228 				{
3229 					deathAnim = BOTH_DEATH5;//same as 4
3230 				}
3231 				break;
3232 			case HL_FOOT_LT:
3233 				if ( !Q_irand( 0, 2 ) && thrown < 250 )
3234 				{
3235 					deathAnim = BOTH_DEATH25;//left foot trips up, spin
3236 				}
3237 				else if ( !Q_irand( 0, 1 ) )
3238 				{
3239 					if ( !Q_irand( 0, 1 ) )
3240 					{
3241 						deathAnim = BOTH_DEATH4;//back: forward
3242 					}
3243 					else
3244 					{
3245 						deathAnim = BOTH_DEATH16;//same as 1
3246 					}
3247 				}
3248 				else
3249 				{
3250 					deathAnim = BOTH_DEATH5;//same as 4
3251 				}
3252 				break;
3253 			case HL_LEG_RT:
3254 				if ( !Q_irand( 0, 2 ) && thrown < 250 )
3255 				{
3256 					deathAnim = BOTH_DEATH3;//right leg collapse
3257 				}
3258 				else if ( !Q_irand( 0, 1 ) )
3259 				{
3260 					deathAnim = BOTH_DEATH5;//same as 4
3261 				}
3262 				else
3263 				{
3264 					if ( !Q_irand( 0, 1 ) )
3265 					{
3266 						deathAnim = BOTH_DEATH4;//back: forward
3267 					}
3268 					else
3269 					{
3270 						deathAnim = BOTH_DEATH16;//same as 1
3271 					}
3272 				}
3273 				break;
3274 			case HL_LEG_LT:
3275 				if ( !Q_irand( 0, 2 ) && thrown < 250 )
3276 				{
3277 					deathAnim = BOTH_DEATH7;//left leg collapse
3278 				}
3279 				else if ( !Q_irand( 0, 1 ) )
3280 				{
3281 					deathAnim = BOTH_DEATH5;//same as 4
3282 				}
3283 				else
3284 				{
3285 					if ( !Q_irand( 0, 1 ) )
3286 					{
3287 						deathAnim = BOTH_DEATH4;//back: forward
3288 					}
3289 					else
3290 					{
3291 						deathAnim = BOTH_DEATH16;//same as 1
3292 					}
3293 				}
3294 				break;
3295 			case HL_BACK:
3296 				if ( fabs(thrown) < 50 || (fabs(thrown) < 200&&!Q_irand(0,3)) )
3297 				{
3298 					if ( Q_irand( 0, 1 ) )
3299 					{
3300 						deathAnim = BOTH_DEATH17;//head/back: croak
3301 					}
3302 					else
3303 					{
3304 						deathAnim = BOTH_DEATH10;//back: bend back, fall forward
3305 					}
3306 				}
3307 				else
3308 				{
3309 					if ( !Q_irand( 0, 2 ) )
3310 					{
3311 						deathAnim = BOTH_DEATH4;//back: forward
3312 					}
3313 					else if ( !Q_irand( 0, 1 ) )
3314 					{
3315 						deathAnim = BOTH_DEATH5;//back: forward
3316 					}
3317 					else
3318 					{
3319 						deathAnim = BOTH_DEATH16;//same as 1
3320 					}
3321 				}
3322 				break;
3323 			case HL_HAND_RT:
3324 			case HL_CHEST_RT:
3325 			case HL_ARM_RT:
3326 			case HL_BACK_LT:
3327 				if ( (damage <= self->max_health*0.25&&Q_irand(0,1)) || (fabs(thrown)<200&&!Q_irand(0,2)) || !Q_irand( 0, 10 ) )
3328 				{
3329 					if ( Q_irand( 0, 1 ) )
3330 					{
3331 						deathAnim = BOTH_DEATH9;//chest right: snap, fall forward
3332 					}
3333 					else
3334 					{
3335 						deathAnim = BOTH_DEATH20;//chest right: snap, fall forward
3336 					}
3337 				}
3338 				else if ( (damage <= self->max_health*0.5&&Q_irand(0,1)) || !Q_irand( 0, 10 ) )
3339 				{
3340 					deathAnim = BOTH_DEATH3;//chest right: back
3341 				}
3342 				else if ( (damage <= self->max_health*0.75&&Q_irand(0,1)) || !Q_irand( 0, 10 ) )
3343 				{
3344 					deathAnim = BOTH_DEATH6;//chest right: spin
3345 				}
3346 				else
3347 				{
3348 					//TEMP HACK: play spinny deaths less often
3349 					if ( Q_irand( 0, 1 ) )
3350 					{
3351 						deathAnim = BOTH_DEATH8;//chest right: spin high
3352 					}
3353 					else
3354 					{
3355 						switch ( Q_irand( 0, 3 ) )
3356 						{
3357 						default:
3358 						case 0:
3359 							deathAnim = BOTH_DEATH9;//chest right: snap, fall forward
3360 							break;
3361 						case 1:
3362 							deathAnim = BOTH_DEATH3;//chest right: back
3363 							break;
3364 						case 2:
3365 							deathAnim = BOTH_DEATH6;//chest right: spin
3366 							break;
3367 						case 3:
3368 							deathAnim = BOTH_DEATH20;//chest right: spin
3369 							break;
3370 						}
3371 					}
3372 				}
3373 				break;
3374 			case HL_CHEST_LT:
3375 			case HL_ARM_LT:
3376 			case HL_HAND_LT:
3377 			case HL_BACK_RT:
3378 				if ( (damage <= self->max_health*0.25&&Q_irand(0,1)) || (fabs(thrown)<200&&!Q_irand(0,2)) || !Q_irand(0, 10) )
3379 				{
3380 					if ( Q_irand( 0, 1 ) )
3381 					{
3382 						deathAnim = BOTH_DEATH11;//chest left: snap, fall forward
3383 					}
3384 					else
3385 					{
3386 						deathAnim = BOTH_DEATH21;//chest left: snap, fall forward
3387 					}
3388 				}
3389 				else if ( (damage <= self->max_health*0.5&&Q_irand(0,1)) || !Q_irand(0, 10) )
3390 				{
3391 					deathAnim = BOTH_DEATH7;//chest left: back
3392 				}
3393 				else if ( (damage <= self->max_health*0.75&&Q_irand(0,1)) || !Q_irand(0, 10) )
3394 				{
3395 					deathAnim = BOTH_DEATH12;//chest left: spin
3396 				}
3397 				else
3398 				{
3399 					//TEMP HACK: play spinny deaths less often
3400 					if ( Q_irand( 0, 1 ) )
3401 					{
3402 						deathAnim = BOTH_DEATH14;//chest left: spin high
3403 					}
3404 					else
3405 					{
3406 						switch ( Q_irand( 0, 3 ) )
3407 						{
3408 						default:
3409 						case 0:
3410 							deathAnim = BOTH_DEATH11;//chest left: snap, fall forward
3411 							break;
3412 						case 1:
3413 							deathAnim = BOTH_DEATH7;//chest left: back
3414 							break;
3415 						case 2:
3416 							deathAnim = BOTH_DEATH12;//chest left: spin
3417 							break;
3418 						case 3:
3419 							deathAnim = BOTH_DEATH21;//chest left: spin
3420 							break;
3421 						}
3422 					}
3423 				}
3424 				break;
3425 			case HL_CHEST:
3426 			case HL_WAIST:
3427 				if ( (damage <= self->max_health*0.25&&Q_irand(0,1)) || thrown > -50 )
3428 				{
3429 					if ( !Q_irand( 0, 1 ) )
3430 					{
3431 						deathAnim = BOTH_DEATH18;//gut: fall right
3432 					}
3433 					else
3434 					{
3435 						deathAnim = BOTH_DEATH19;//gut: fall left
3436 					}
3437 				}
3438 				else if ( (damage <= self->max_health*0.5&&!Q_irand(0,1)) || (fabs(thrown)<200&&!Q_irand(0,3)) )
3439 				{
3440 					if ( Q_irand( 0, 2 ) )
3441 					{
3442 						deathAnim = BOTH_DEATH2;//chest: backward short
3443 					}
3444 					else if ( Q_irand( 0, 1 ) )
3445 					{
3446 						deathAnim = BOTH_DEATH22;//chest: backward short
3447 					}
3448 					else
3449 					{
3450 						deathAnim = BOTH_DEATH23;//chest: backward short
3451 					}
3452 				}
3453 				else if ( thrown < -300 && Q_irand( 0, 1 ) )
3454 				{
3455 					if ( Q_irand( 0, 1 ) )
3456 					{
3457 						deathAnim = BOTH_DEATHBACKWARD1;//chest: fly back
3458 					}
3459 					else
3460 					{
3461 						deathAnim = BOTH_DEATHBACKWARD2;//chest: flip back
3462 					}
3463 				}
3464 				else if ( thrown < -200 && Q_irand( 0, 1 ) )
3465 				{
3466 					deathAnim = BOTH_DEATH15;//chest: roll backward
3467 				}
3468 				else
3469 				{
3470 					deathAnim = BOTH_DEATH1;//chest: backward med
3471 				}
3472 				break;
3473 			case HL_HEAD:
3474 				if ( damage <= self->max_health*0.5 && Q_irand(0,2) )
3475 				{
3476 					deathAnim = BOTH_DEATH17;//head/back: croak
3477 				}
3478 				else
3479 				{
3480 					if ( Q_irand( 0, 2 ) )
3481 					{
3482 						deathAnim = BOTH_DEATH13;//head: stumble, fall back
3483 					}
3484 					else
3485 					{
3486 						deathAnim = BOTH_DEATH10;//head: stumble, fall back
3487 					}
3488 				}
3489 				break;
3490 			default:
3491 				break;
3492 			}
3493 		}
3494 	}
3495 
3496 	// Validate.....
3497 	if ( deathAnim == -1 || !PM_HasAnimation( self, deathAnim ))
3498 	{
3499 		if ( deathAnim == BOTH_DEADFLOP1
3500 			|| deathAnim == BOTH_DEADFLOP2 )
3501 		{//if don't have deadflop, don't do anything
3502 			deathAnim = -1;
3503 		}
3504 		else
3505 		{
3506 			// I guess we'll take what we can get.....
3507 			deathAnim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH25 );
3508 		}
3509 	}
3510 
3511 	return deathAnim;
3512 }
3513 
G_CheckLedgeDive(gentity_t * self,float checkDist,const vec3_t checkVel,qboolean tryOpposite,qboolean tryPerp)3514 int G_CheckLedgeDive( gentity_t *self, float checkDist, const vec3_t checkVel, qboolean tryOpposite, qboolean tryPerp )
3515 {
3516 	//		Intelligent Ledge-Diving Deaths:
3517 	//		If I'm an NPC, check for nearby ledges and fall off it if possible
3518 	//		How should/would/could this interact with knockback if we already have some?
3519 	//		Ideally - apply knockback if there are no ledges or a ledge in that dir
3520 	//		But if there is a ledge and it's not in the dir of my knockback, fall off the ledge instead
3521 	if ( !self || !self->client )
3522 	{
3523 		return 0;
3524 	}
3525 
3526 	vec3_t	fallForwardDir, fallRightDir;
3527 	vec3_t	angles = {0};
3528 	int		cliff_fall = 0;
3529 
3530 	if ( checkVel && !VectorCompare( checkVel, vec3_origin ) )
3531 	{//already moving in a dir
3532 		angles[1] = vectoyaw( self->client->ps.velocity );
3533 		AngleVectors( angles, fallForwardDir, fallRightDir, NULL );
3534 	}
3535 	else
3536 	{//try forward first
3537 		angles[1] = self->client->ps.viewangles[1];
3538 		AngleVectors( angles, fallForwardDir, fallRightDir, NULL );
3539 	}
3540 	VectorNormalize( fallForwardDir );
3541 	float fallDist = G_CheckForLedge( self, fallForwardDir, checkDist );
3542 	if ( fallDist >= 128 )
3543 	{
3544 		VectorClear( self->client->ps.velocity );
3545 		G_Throw( self, fallForwardDir, 85 );
3546 		self->client->ps.velocity[2] = 100;
3547 		self->client->ps.groundEntityNum = ENTITYNUM_NONE;
3548 	}
3549 	else if ( tryOpposite )
3550 	{
3551 		VectorScale( fallForwardDir, -1, fallForwardDir );
3552 		fallDist = G_CheckForLedge( self, fallForwardDir, checkDist );
3553 		if ( fallDist >= 128 )
3554 		{
3555 			VectorClear( self->client->ps.velocity );
3556 			G_Throw( self, fallForwardDir, 85 );
3557 			self->client->ps.velocity[2] = 100;
3558 			self->client->ps.groundEntityNum = ENTITYNUM_NONE;
3559 		}
3560 	}
3561 	if ( !cliff_fall && tryPerp )
3562 	{//try sides
3563 		VectorNormalize( fallRightDir );
3564 		fallDist = G_CheckForLedge( self, fallRightDir, checkDist );
3565 		if ( fallDist >= 128 )
3566 		{
3567 			VectorClear( self->client->ps.velocity );
3568 			G_Throw( self, fallRightDir, 85 );
3569 			self->client->ps.velocity[2] = 100;
3570 		}
3571 		else
3572 		{
3573 			VectorScale( fallRightDir, -1, fallRightDir );
3574 			fallDist = G_CheckForLedge( self, fallRightDir, checkDist );
3575 			if ( fallDist >= 128 )
3576 			{
3577 				VectorClear( self->client->ps.velocity );
3578 				G_Throw( self, fallRightDir, 85 );
3579 				self->client->ps.velocity[2] = 100;
3580 			}
3581 		}
3582 	}
3583 	if ( fallDist >= 256 )
3584 	{
3585 		cliff_fall = 2;
3586 	}
3587 	else if ( fallDist >= 128 )
3588 	{
3589 		cliff_fall = 1;
3590 	}
3591 	return cliff_fall;
3592 }
3593 
3594 /*
3595 ==================
3596 player_die
3597 ==================
3598 */
3599 void NPC_SetAnim(gentity_t	*ent,int setAnimParts,int anim,int setAnimFlags, int iBlend);
3600 extern void AI_DeleteSelfFromGroup( gentity_t *self );
3601 extern void AI_GroupMemberKilled( gentity_t *self );
3602 extern qboolean FlyingCreature( gentity_t *ent );
3603 extern void G_DrivableATSTDie( gentity_t *self );
3604 extern void JET_FlyStop( gentity_t *self );
3605 extern void VehicleExplosionDelay( gentity_t *self );
3606 extern void NPC_LeaveTroop(gentity_t* actor);
3607 extern void Rancor_DropVictim( gentity_t *self );
3608 extern void Wampa_DropVictim( gentity_t *self );
3609 extern void WP_StopForceHealEffects( gentity_t *self );
player_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath,int dflags,int hitLoc)3610 void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath, int dflags, int hitLoc )
3611 {
3612 	int			anim;
3613 	int			contents;
3614 	qboolean	deathScript = qfalse;
3615 	qboolean	lastInGroup = qfalse;
3616 	qboolean	specialAnim = qfalse;
3617 	qboolean	holdingSaber = qfalse;
3618 	int			cliff_fall = 0;
3619 
3620 	//FIXME: somehow people are sometimes not completely dying???
3621 	if ( self->client->ps.pm_type == PM_DEAD && (meansOfDeath != MOD_SNIPER || (self->flags & FL_DISINTEGRATED)) )
3622 	{//do dismemberment/twitching
3623 		if ( self->client->NPC_class == CLASS_MARK1 )
3624 		{
3625 			DeathFX(self);
3626 			self->takedamage = qfalse;
3627 			self->client->ps.eFlags |= EF_NODRAW;
3628 			self->contents = 0;
3629 			// G_FreeEntity( self ); // Is this safe?  I can't see why we'd mark it nodraw and then just leave it around??
3630 			self->e_ThinkFunc = thinkF_G_FreeEntity;
3631 			self->nextthink = level.time + FRAMETIME;
3632 		}
3633 		else
3634 		{
3635 			anim = G_PickDeathAnim( self, self->pos1, damage, meansOfDeath, hitLoc );
3636 			if ( dflags & DAMAGE_DISMEMBER )
3637 			{
3638 				G_DoDismemberment( self, self->pos1, meansOfDeath, damage, hitLoc );
3639 			}
3640 			if ( anim >= 0 )
3641 			{
3642 				NPC_SetAnim(self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
3643 			}
3644 		}
3645 		return;
3646 	}
3647 
3648 	// If the entity is in a vehicle.
3649 	if ( self->client && self->client->NPC_class != CLASS_VEHICLE && self->s.m_iVehicleNum != 0 )
3650 	{
3651 		Vehicle_t *pVeh = g_entities[self->s.m_iVehicleNum].m_pVehicle;
3652 		if (pVeh)
3653 		{
3654 			if ( pVeh->m_pOldPilot != self
3655 				&& pVeh->m_pPilot != self )
3656 			{//whaaa?  I'm not on this bike?  er....
3657 				assert(!!"How did we get to this point?");
3658 			}
3659 			else
3660 			{	// Get thrown out.
3661 				pVeh->m_pVehicleInfo->Eject( pVeh, self, qtrue );
3662 
3663 				// Now Send The Vehicle Flying To It's Death
3664 				if (pVeh->m_pVehicleInfo->type==VH_SPEEDER && pVeh->m_pParentEntity && pVeh->m_pParentEntity->client)
3665 				{
3666 					gentity_t*	parent = pVeh->m_pParentEntity;
3667 					float		CurSpeed = VectorLength(parent->client->ps.velocity);
3668 
3669 					// If Moving
3670 					//-----------
3671 					if (CurSpeed>(pVeh->m_pVehicleInfo->speedMax*0.5f))
3672 					{
3673 						// Send The Bike Out Of Control
3674 						//------------------------------
3675 						pVeh->m_pVehicleInfo->StartDeathDelay(pVeh, 10000);
3676 						pVeh->m_ulFlags		|= (VEH_OUTOFCONTROL);
3677 						VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3);
3678 
3679 
3680 						// Try To Accelerate A Slowing Moving Vehicle To Full Speed
3681 						//----------------------------------------------------------
3682 						if (CurSpeed<(pVeh->m_pVehicleInfo->speedMax*0.9f))
3683 						{
3684 							VectorNormalize(parent->pos3);
3685 							if (fabsf(parent->pos3[2])<0.3f)
3686 							{
3687 								VectorScale(parent->pos3, (pVeh->m_pVehicleInfo->speedMax * 1.25f), parent->pos3);
3688 							}
3689 							else
3690 							{
3691 								VectorClear(parent->pos3);
3692 							}
3693 						}
3694 
3695 						// Throw The Pilot
3696 						//----------------
3697 						if (parent->pos3[0] || parent->pos3[1])
3698 						{
3699 							vec3_t	throwDir;
3700 
3701 							VectorCopy(parent->client->ps.velocity, throwDir);
3702 							VectorNormalize(throwDir);
3703 							throwDir[2] += 0.3f;	// up a little
3704 
3705 							self->client->noRagTime = -1;	// no ragdoll for you
3706 							CurSpeed /= 10.0f;
3707 							if (CurSpeed<50.0)
3708 							{
3709 								CurSpeed = 50.0f;
3710 							}
3711 							if (throwDir[2]<0.0f)
3712 							{
3713 								throwDir[2] = fabsf(throwDir[2]);
3714 							}
3715 							if (fabsf(throwDir[0])<0.2f)
3716 							{
3717 								throwDir[0] = Q_flrand(-0.5f, 0.5f);
3718 							}
3719 							if (fabsf(throwDir[1])<0.2f)
3720 							{
3721 								throwDir[1] = Q_flrand(-0.5f, 0.5f);
3722 							}
3723 							G_Throw(self, throwDir, CurSpeed);
3724 						}
3725 					}
3726 				}
3727 			}
3728 		}
3729 		else
3730 		{
3731 			assert(!!"How did we get to this point?");
3732 		}
3733 	}
3734 
3735 #ifndef FINAL_BUILD
3736 	if ( d_saberCombat->integer && attacker && attacker->client )
3737 	{
3738 		gi.Printf( S_COLOR_YELLOW"combatant %s died, killer anim = %s\n", self->targetname, animTable[attacker->client->ps.torsoAnim].name );
3739 	}
3740 #endif//FINAL_BUILD
3741 	if ( self->NPC )
3742 	{
3743 		if (NAV::HasPath(self))
3744 		{
3745 			NAV::ClearPath(self);
3746 		}
3747 		if (self->NPC->troop)
3748 		{
3749 			NPC_LeaveTroop(self);
3750 		}
3751 		// STEER_TODO: Do we need to free the steer user too?
3752 
3753 		//clear charmed
3754 		G_CheckCharmed( self );
3755 
3756 		// Remove The Bubble Shield From The Assassin Droid
3757 		if (self->client && self->client->NPC_class==CLASS_ASSASSIN_DROID && (self->flags&FL_SHIELDED))
3758 		{
3759 			self->flags &= ~FL_SHIELDED;
3760 			self->client->ps.stats[STAT_ARMOR] = 0;
3761 			self->client->ps.powerups[PW_GALAK_SHIELD] = 0;
3762 			gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "force_shield", TURN_OFF );
3763 		}
3764 
3765 		if (self->client && self->client->NPC_class==CLASS_HOWLER)
3766 		{
3767 			G_StopEffect( G_EffectIndex( "howler/sonic" ), self->playerModel, self->genericBolt1, self->s.number );
3768 		}
3769 
3770 
3771 
3772 		if ( self->client && Jedi_WaitingAmbush( self ) )
3773 		{//ambushing trooper
3774 			self->client->noclip = false;
3775 		}
3776 		NPC_FreeCombatPoint( self->NPC->combatPoint );
3777 		if ( self->NPC->group )
3778 		{
3779 			lastInGroup = (qboolean)(self->NPC->group->numGroup < 2);
3780 			AI_GroupMemberKilled( self );
3781 			AI_DeleteSelfFromGroup( self );
3782 		}
3783 
3784 		if ( self->NPC->tempGoal )
3785 		{
3786 			G_FreeEntity( self->NPC->tempGoal );
3787 			self->NPC->tempGoal = NULL;
3788 		}
3789 		if ( self->s.eFlags & EF_LOCKED_TO_WEAPON )
3790 		{
3791 			// dumb, just get the NPC out of the chair
3792 extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd );
3793 
3794 			usercmd_t cmd, *ad_cmd;
3795 
3796 			memset( &cmd, 0, sizeof( usercmd_t ));
3797 
3798 			//gentity_t *old = self->owner;
3799 
3800 			if ( self->owner )
3801 			{
3802 				self->owner->s.frame = self->owner->startFrame = self->owner->endFrame = 0;
3803 				self->owner->svFlags &= ~SVF_ANIMATING;
3804 			}
3805 
3806 			cmd.buttons |= BUTTON_USE;
3807 			ad_cmd = &cmd;
3808 			RunEmplacedWeapon( self, &ad_cmd );
3809 			//self->owner = old;
3810 		}
3811 		if ( self->client->NPC_class == CLASS_BOBAFETT
3812 			|| self->client->NPC_class == CLASS_ROCKETTROOPER )
3813 		{
3814 			if ( self->client->moveType == MT_FLYSWIM )
3815 			{
3816 				JET_FlyStop( self );
3817 			}
3818 		}
3819 		if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
3820 		{
3821 			self->client->ps.eFlags &= ~EF_SPOTLIGHT;
3822 		}
3823 		if ( self->client->NPC_class == CLASS_SAND_CREATURE )
3824 		{
3825 			self->client->ps.eFlags &= ~EF_NODRAW;
3826 			self->s.eFlags &= ~EF_NODRAW;
3827 		}
3828 		if ( self->client->NPC_class == CLASS_RANCOR )
3829 		{
3830 			if ( self->count )
3831 			{
3832 				Rancor_DropVictim( self );
3833 			}
3834 		}
3835 		if ( self->client->NPC_class == CLASS_WAMPA )
3836 		{
3837 			if ( self->count )
3838 			{
3839 				if ( self->activator && attacker == self->activator && meansOfDeath == MOD_SABER )
3840 				{
3841 					self->client->dismembered = false;
3842 					//FIXME: the limb should just disappear, cuz I ate it
3843 					G_DoDismemberment( self, self->currentOrigin, MOD_SABER, 1000, HL_ARM_RT, qtrue );
3844 				}
3845 				Wampa_DropVictim( self );
3846 			}
3847 		}
3848 		if ( (self->NPC->aiFlags&NPCAI_HEAL_ROSH) )
3849 		{
3850 			if ( self->client->leader )
3851 			{
3852 				self->client->leader->flags &= ~FL_UNDYING;
3853 				if ( self->client->leader->client )
3854 				{
3855 					self->client->leader->client->ps.forcePowersKnown &= ~FORCE_POWERS_ROSH_FROM_TWINS;
3856 				}
3857 			}
3858 		}
3859 		if ( (self->client->ps.stats[STAT_WEAPONS]&(1<<WP_SCEPTER)) )
3860 		{
3861 			G_StopEffect( G_EffectIndex( "scepter/beam_warmup.efx" ), self->weaponModel[1], self->genericBolt1, self->s.number );
3862 			G_StopEffect( G_EffectIndex( "scepter/beam.efx" ), self->weaponModel[1], self->genericBolt1, self->s.number );
3863 			G_StopEffect( G_EffectIndex( "scepter/slam_warmup.efx" ), self->weaponModel[1], self->genericBolt1, self->s.number );
3864 			self->s.loopSound = 0;
3865 		}
3866 	}
3867 	if ( attacker && attacker->NPC && attacker->NPC->group && attacker->NPC->group->enemy == self )
3868 	{
3869 		attacker->NPC->group->enemy = NULL;
3870 	}
3871 	if ( self->s.weapon == WP_SABER )
3872 	{
3873 		holdingSaber = qtrue;
3874 	}
3875 	if ( self->client->ps.saberEntityNum != ENTITYNUM_NONE && self->client->ps.saberEntityNum > 0 )
3876 	{
3877 		if ( self->client->ps.saberInFlight )
3878 		{//just drop it
3879 			self->client->ps.saber[0].Deactivate();
3880 		}
3881 		else
3882 		{
3883 			if ( g_saberPickuppableDroppedSabers->integer )
3884 			{//always drop your sabers
3885 				TossClientItems( self );
3886 				self->client->ps.weapon = self->s.weapon = WP_NONE;
3887 			}
3888 			else if ( (
3889 					(hitLoc != HL_HAND_RT&&hitLoc !=HL_CHEST_RT&&hitLoc!=HL_ARM_RT&&hitLoc!=HL_BACK_LT)
3890 					|| self->client->dismembered
3891 					|| meansOfDeath != MOD_SABER
3892 				  )//if might get hand cut off, leave saber in hand
3893 				&& holdingSaber
3894 				&& ( Q_irand( 0, 1 )
3895 					|| meansOfDeath == MOD_EXPLOSIVE
3896 					|| meansOfDeath == MOD_REPEATER_ALT
3897 					|| meansOfDeath == MOD_FLECHETTE_ALT
3898 					|| meansOfDeath == MOD_ROCKET
3899 					|| meansOfDeath == MOD_ROCKET_ALT
3900 					|| meansOfDeath == MOD_CONC
3901 					|| meansOfDeath == MOD_CONC_ALT
3902 					|| meansOfDeath == MOD_THERMAL
3903 					|| meansOfDeath == MOD_THERMAL_ALT
3904 					|| meansOfDeath == MOD_DETPACK
3905 					|| meansOfDeath == MOD_LASERTRIP
3906 					|| meansOfDeath == MOD_LASERTRIP_ALT
3907 					|| meansOfDeath == MOD_MELEE
3908 					|| meansOfDeath == MOD_FORCE_GRIP
3909 					|| meansOfDeath == MOD_KNOCKOUT
3910 					|| meansOfDeath == MOD_CRUSH
3911 					|| meansOfDeath == MOD_IMPACT
3912 					|| meansOfDeath == MOD_FALLING
3913 					|| meansOfDeath == MOD_EXPLOSIVE_SPLASH ) )
3914 			{//drop it
3915 				TossClientItems( self );
3916 				self->client->ps.weapon = self->s.weapon = WP_NONE;
3917 			}
3918 			else
3919 			{//just free it
3920 				if ( g_entities[self->client->ps.saberEntityNum].inuse )
3921 				{
3922 					G_FreeEntity( &g_entities[self->client->ps.saberEntityNum] );
3923 				}
3924 				self->client->ps.saberEntityNum = ENTITYNUM_NONE;
3925 			}
3926 		}
3927 	}
3928 	if ( self->client->NPC_class == CLASS_SHADOWTROOPER )
3929 	{//drop a force crystal
3930 		if ( Q_stricmpn("shadowtrooper", self->NPC_type, 13 ) == 0 )
3931 		{
3932 			gitem_t		*item;
3933 			item = FindItemForAmmo( AMMO_FORCE );
3934 			Drop_Item( self, item, 0, qtrue );
3935 		}
3936 	}
3937 	//Use any target we had
3938 	if ( meansOfDeath != MOD_KNOCKOUT )
3939 	{
3940 		G_UseTargets( self, self );
3941 	}
3942 
3943 	if ( attacker )
3944 	{
3945 		if ( attacker->client && !attacker->s.number )
3946 		{
3947 			if ( self->client )
3948 			{//killed a client
3949 				if ( self->client->playerTeam == TEAM_ENEMY
3950 					|| self->client->playerTeam == TEAM_FREE
3951 					|| (self->NPC && self->NPC->charmedTime > level.time) )
3952 				{//killed an enemy
3953 					attacker->client->sess.missionStats.enemiesKilled++;
3954 				}
3955 			}
3956 			if ( attacker != self )
3957 			{
3958 				G_TrackWeaponUsage( attacker, inflictor, 30, meansOfDeath );
3959 			}
3960 		}
3961 		G_CheckVictoryScript(attacker);
3962 		//player killing a jedi with a lightsaber spawns a matrix-effect entity
3963 		if ( d_slowmodeath->integer )
3964 		{
3965 			if ( !self->s.number )
3966 			{//what the hell, always do slow-mo when player dies
3967 				//FIXME: don't do this when crushed to death?
3968 				if ( meansOfDeath == MOD_FALLING && self->client->ps.groundEntityNum == ENTITYNUM_NONE )
3969 				{//falling to death, have not hit yet
3970 					G_StartMatrixEffect( self, (MEF_NO_VERTBOB|MEF_HIT_GROUND_STOP|MEF_MULTI_SPIN), 10000, 0.25f );
3971 				}
3972 				else if ( meansOfDeath != MOD_CRUSH )
3973 				{//for all deaths except being crushed
3974 					G_StartMatrixEffect( self );
3975 				}
3976 			}
3977 			else if ( d_slowmodeath->integer < 4 )
3978 			{//any jedi killed by player-saber
3979 				if ( d_slowmodeath->integer < 3 )
3980 				{//must be the last jedi in the room
3981 					if ( !G_JediInRoom( attacker->currentOrigin ) )
3982 					{
3983 						lastInGroup = qtrue;
3984 					}
3985 					else
3986 					{
3987 						lastInGroup = qfalse;
3988 					}
3989 				}
3990 				if ( !attacker->s.number
3991 					&& (holdingSaber||self->client->NPC_class==CLASS_WAMPA)
3992 					&& meansOfDeath == MOD_SABER
3993 					&& attacker->client
3994 					&& attacker->client->ps.weapon == WP_SABER
3995 					&& !attacker->client->ps.saberInFlight //FIXME: if dualSabers, should still do slowmo if this killing blow was struck with the left-hand saber...
3996 					&& (d_slowmodeath->integer > 2||lastInGroup) )//either slow mo death level 3 (any jedi) or 2 and I was the last jedi in the room
3997 				{//Matrix!
3998 					if ( attacker->client->ps.torsoAnim == BOTH_A6_SABERPROTECT )
3999 					{//don't override the range and vertbob
4000 						G_StartMatrixEffect( self, (MEF_NO_RANGEVAR|MEF_NO_VERTBOB) );
4001 					}
4002 					else
4003 					{
4004 						G_StartMatrixEffect( self );
4005 					}
4006 				}
4007 			}
4008 			else
4009 			{//all player-saber kills
4010 				if ( !attacker->s.number
4011 					&& meansOfDeath == MOD_SABER
4012 					&& attacker->client
4013 					&& attacker->client->ps.weapon == WP_SABER
4014 					&& !attacker->client->ps.saberInFlight
4015 					&& (d_slowmodeath->integer > 4||lastInGroup||holdingSaber||self->client->NPC_class==CLASS_WAMPA))//either slow mo death level 5 (any enemy) or 4 and I was the last in my group or I'm a saber user
4016 				{//Matrix!
4017 					if ( attacker->client->ps.torsoAnim == BOTH_A6_SABERPROTECT )
4018 					{//don't override the range and vertbob
4019 						G_StartMatrixEffect( self, (MEF_NO_RANGEVAR|MEF_NO_VERTBOB) );
4020 					}
4021 					else
4022 					{
4023 						G_StartMatrixEffect( self );
4024 					}
4025 				}
4026 			}
4027 		}
4028 	}
4029 
4030 	self->enemy = attacker;
4031 	self->client->renderInfo.lookTarget = ENTITYNUM_NONE;
4032 
4033 	self->client->ps.persistant[PERS_KILLED]++;
4034 	if ( self->client->playerTeam == TEAM_PLAYER )
4035 	{//FIXME: just HazTeam members in formation on away missions?
4036 		//or more controlled- via deathscripts?
4037 		// Don't count player
4038 		if (( &g_entities[0] != NULL && g_entities[0].client ) && (self->s.number != 0))
4039 		{//add to the number of teammates lost
4040 			g_entities[0].client->ps.persistant[PERS_TEAMMATES_KILLED]++;
4041 		}
4042 		else	// Player died, fire off scoreboard soon
4043 		{
4044 			cg.missionStatusDeadTime = level.time + 1000;	// Too long?? Too short??
4045 			cg.zoomMode = 0; // turn off zooming when we die
4046 		}
4047 	}
4048 
4049 	if ( self->s.number == 0 && attacker )
4050 	{
4051 //		G_SetMissionStatusText( attacker, meansOfDeath );
4052 		//TEST: If player killed, unmark all teammates from being undying so they can buy it too
4053 		//NOTE: we want this to happen ONLY on our squad ONLY on missions... in the tutorial or on voyager levels this could be really weird.
4054 		G_MakeTeamVulnerable();
4055 	}
4056 
4057 	if ( attacker && attacker->client)
4058 	{
4059 		if ( attacker == self || OnSameTeam (self, attacker ) )
4060 		{
4061 			AddScore( attacker, -1 );
4062 		}
4063 		else
4064 		{
4065 			AddScore( attacker, 1 );
4066 		}
4067 	}
4068 	else
4069 	{
4070 		AddScore( self, -1 );
4071 	}
4072 
4073 	// if client is in a nodrop area, don't drop anything
4074 	contents = gi.pointcontents( self->currentOrigin, -1 );
4075 	if ( !holdingSaber
4076 		//&& self->s.number != 0
4077 		&& !( contents & CONTENTS_NODROP )
4078 		&& meansOfDeath != MOD_SNIPER
4079 		&& (!self->client||self->client->NPC_class!=CLASS_GALAKMECH))
4080 	{
4081 		TossClientItems( self );
4082 	}
4083 
4084 	if ( meansOfDeath == MOD_SNIPER )
4085 	{//I was disintegrated
4086 		if ( self->message )
4087 		{//I was holding a key
4088 			//drop the key
4089 			G_DropKey( self );
4090 		}
4091 	}
4092 
4093 	if ( holdingSaber )
4094 	{//never drop a lightsaber!
4095 		if ( self->client->ps.SaberActive() )
4096 		{
4097 			self->client->ps.SaberDeactivate();
4098 			G_SoundIndexOnEnt( self, CHAN_AUTO, self->client->ps.saber[0].soundOff );
4099 		}
4100 	}
4101 	else if ( self->s.weapon != WP_BRYAR_PISTOL )
4102 	{//since player can't pick up bryar pistols, never drop those
4103 		self->s.weapon = WP_NONE;
4104 		G_RemoveWeaponModels( self );
4105 	}
4106 
4107 	self->s.powerups &= ~PW_REMOVE_AT_DEATH;//removes everything but electricity and force push
4108 
4109 	//FIXME: do this on a callback?  So people can't walk through long death anims?
4110 	//Maybe set on last frame?  Would be cool for big blocking corpses if the never got set?
4111 	//self->contents = CONTENTS_CORPSE;//now done a second after death
4112 	/*
4113 	self->takedamage = qfalse;	// no gibbing
4114 	if ( self->client->playerTeam == TEAM_PARASITE )
4115 	{
4116 		self->contents = CONTENTS_NONE; // FIXME: temp fix
4117 	}
4118 	else
4119 	{
4120 		self->contents = CONTENTS_CORPSE;
4121 		self->maxs[2] = -8;
4122 	}
4123 	*/
4124 	if ( !self->s.number )
4125 	{//player
4126 		self->contents = CONTENTS_CORPSE;
4127 		self->maxs[2] = -8;
4128 	}
4129 	self->clipmask&=~(CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);//so dead NPC can fly off ledges
4130 
4131 	//FACING==========================================================
4132 	if ( attacker && self->s.number == 0 )
4133 	{
4134 		self->client->ps.stats[STAT_DEAD_YAW] = AngleNormalize180( self->client->ps.viewangles[YAW] );
4135 	}
4136 	self->currentAngles[PITCH] = 0;
4137 	self->currentAngles[ROLL] = 0;
4138 	if ( self->NPC )
4139 	{
4140 		self->NPC->desiredYaw = 0;
4141 		self->NPC->desiredPitch = 0;
4142 		self->NPC->confusionTime = 0;
4143 		self->NPC->charmedTime = 0;
4144 		if ( self->ghoul2.size() )
4145 		{
4146 			if ( self->chestBolt != -1 )
4147 			{
4148 				G_StopEffect("force/rage2", self->playerModel, self->chestBolt, self->s.number );
4149 			}
4150 			if ( self->headBolt != -1 )
4151 			{
4152 				G_StopEffect("force/confusion", self->playerModel, self->headBolt, self->s.number );
4153 			}
4154 			WP_StopForceHealEffects( self );
4155 		}
4156 	}
4157 	VectorCopy( self->currentAngles, self->client->ps.viewangles );
4158 	//FACING==========================================================
4159 	if ( player && player->client && player->client->ps.viewEntity == self->s.number )
4160 	{//I was the player's viewentity and I died, kick him back to his normal view
4161 		G_ClearViewEntity( player );
4162 	}
4163 	else if ( !self->s.number && self->client->ps.viewEntity > 0 && self->client->ps.viewEntity < ENTITYNUM_NONE )
4164 	{
4165 		G_ClearViewEntity( self );
4166 	}
4167 	else if ( !self->s.number && self->client->ps.viewEntity > 0 && self->client->ps.viewEntity < ENTITYNUM_NONE )
4168 	{
4169 		G_ClearViewEntity( self );
4170 	}
4171 
4172 	self->s.loopSound = 0;
4173 
4174 	// remove powerups
4175 	memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );
4176 
4177 	if ( (self->client->ps.eFlags&EF_HELD_BY_RANCOR)
4178 		|| (self->client->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
4179 		|| (self->client->ps.eFlags&EF_HELD_BY_WAMPA) )
4180 	{//do nothing special here
4181 	}
4182 	else if ( self->client->NPC_class == CLASS_MARK1 )
4183 	{
4184 		Mark1_die( self, inflictor, attacker, damage, meansOfDeath, dflags, hitLoc );
4185 	}
4186 	else if ( self->client->NPC_class == CLASS_INTERROGATOR )
4187 	{
4188 		Interrogator_die( self, inflictor, attacker, damage, meansOfDeath, dflags, hitLoc );
4189 	}
4190 	else if ( self->client->NPC_class == CLASS_GALAKMECH )
4191 	{//FIXME: need keyframed explosions?
4192 		NPC_SetAnim( self, SETANIM_BOTH, BOTH_DEATH1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
4193 		G_AddEvent( self, Q_irand(EV_DEATH1, EV_DEATH3), self->health );
4194 	}
4195 	else if ( self->client->NPC_class == CLASS_ATST )
4196 	{//FIXME: need keyframed explosions
4197 		if ( !self->s.number )
4198 		{
4199 			G_DrivableATSTDie( self );
4200 		}
4201 		anim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH25 );	//initialize to good data
4202 		if ( anim != -1 )
4203 		{
4204 			NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
4205 		}
4206 	}
4207 	else if ( self->s.number && self->message && meansOfDeath != MOD_SNIPER )
4208 	{//imp with a key on his arm
4209 		//pick a death anim that leaves key visible
4210 		switch ( Q_irand( 0, 3 ) )
4211 		{
4212 		case 0:
4213 			anim = BOTH_DEATH4;
4214 			break;
4215 		case 1:
4216 			anim = BOTH_DEATH21;
4217 			break;
4218 		case 2:
4219 			anim = BOTH_DEATH17;
4220 			break;
4221 		case 3:
4222 		default:
4223 			anim = BOTH_DEATH18;
4224 			break;
4225 		}
4226 		//FIXME: verify we have this anim?
4227 		NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
4228 		if ( meansOfDeath == MOD_KNOCKOUT || meansOfDeath == MOD_MELEE )
4229 		{
4230 			G_AddEvent( self, EV_JUMP, 0 );
4231 		}
4232 		else if ( meansOfDeath == MOD_FORCE_DRAIN )
4233 		{
4234 			G_AddEvent( self, EV_WATER_DROWN, 0 );
4235 		}
4236 		else if ( meansOfDeath == MOD_GAS )
4237 		{
4238 			G_AddEvent( self, EV_WATER_DROWN, 0 );
4239 		}
4240 		else
4241 		{
4242 			G_AddEvent( self, Q_irand(EV_DEATH1, EV_DEATH3), self->health );
4243 		}
4244 	}
4245 	else if ( meansOfDeath == MOD_FALLING || (self->client->ps.legsAnim == BOTH_FALLDEATH1INAIR && self->client->ps.torsoAnim == BOTH_FALLDEATH1INAIR) || (self->client->ps.legsAnim == BOTH_FALLDEATH1 && self->client->ps.torsoAnim == BOTH_FALLDEATH1) )
4246 	{
4247 		//FIXME: no good way to predict you're going to fall to your death... need falling bushes/triggers?
4248 		if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE //in the air
4249 			&& self->client->ps.velocity[2] < 0 //falling
4250 			&& self->client->ps.legsAnim != BOTH_FALLDEATH1INAIR //not already in falling loop
4251 			&& self->client->ps.torsoAnim != BOTH_FALLDEATH1INAIR )//not already in falling loop
4252 		{
4253 			NPC_SetAnim(self, SETANIM_BOTH, BOTH_FALLDEATH1INAIR, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4254 			if ( !self->NPC )
4255 			{
4256 				G_SoundOnEnt( self, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN
4257 			}
4258 			else if (!(self->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
4259 			{
4260 				G_SoundOnEnt( self, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN
4261 				//so we don't do this again
4262 				self->NPC->aiFlags |= NPCAI_DIE_ON_IMPACT;
4263 				//self->client->ps.gravity *= 0.5;//Fall a bit slower
4264 				self->client->ps.friction = 1;
4265 			}
4266 		}
4267 		else
4268 		{
4269 			int	deathAnim = BOTH_FALLDEATH1LAND;
4270 			if ( PM_InOnGroundAnim( &self->client->ps ) )
4271 			{
4272 				if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 )
4273 				{
4274 					deathAnim = BOTH_DEATH_LYING_UP;	//# Death anim when lying on back
4275 				}
4276 				else
4277 				{
4278 					deathAnim = BOTH_DEATH_LYING_DN;	//# Death anim when lying on front
4279 				}
4280 			}
4281 			else if ( PM_InKnockDown( &self->client->ps ) )
4282 			{
4283 				if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 )
4284 				{
4285 					deathAnim = BOTH_DEATH_FALLING_UP;	//# Death anim when falling on back
4286 				}
4287 				else
4288 				{
4289 					deathAnim = BOTH_DEATH_FALLING_DN;	//# Death anim when falling on face
4290 				}
4291 			}
4292 			NPC_SetAnim(self, SETANIM_BOTH, deathAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4293 			//HMM: check for nodrop?
4294 			G_SoundOnEnt( self, CHAN_BODY, "sound/player/fallsplat.wav" );
4295 			if ( gi.VoiceVolume[self->s.number]
4296 				&& self->NPC && (self->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
4297 			{//I was talking, so cut it off... with a jump sound?
4298 				G_SoundOnEnt( self, CHAN_VOICE_ATTEN, "*pain100.wav" );
4299 			}
4300 		}
4301 	}
4302 	else
4303 	{// normal death
4304 		anim = G_CheckSpecialDeathAnim( self, self->pos1, damage, meansOfDeath, hitLoc );
4305 		if ( anim == -1 )
4306 		{
4307 			if ( PM_InOnGroundAnim( &self->client->ps ) && PM_HasAnimation( self, BOTH_LYINGDEATH1 ) )
4308 			{//on ground, need different death anim
4309 				anim = BOTH_LYINGDEATH1;
4310 			}
4311 			else if ( meansOfDeath == MOD_TRIGGER_HURT && (self->s.powerups&(1<<PW_SHOCKED)) )
4312 			{//electrocuted
4313 				anim = BOTH_DEATH17;
4314 			}
4315 			else if ( meansOfDeath == MOD_WATER || meansOfDeath == MOD_GAS || meansOfDeath == MOD_FORCE_DRAIN )
4316 			{//drowned
4317 				anim = BOTH_DEATH17;
4318 			}
4319 			else if ( meansOfDeath != MOD_SNIPER //disintegrates
4320 				&& meansOfDeath != MOD_CONC_ALT )//does its own death throw
4321 			{
4322 				cliff_fall = G_CheckLedgeDive( self, 128, self->client->ps.velocity, qtrue, qfalse );
4323 				if ( cliff_fall == 2 )
4324 				{
4325 					if ( !FlyingCreature( self ) && g_gravity->value > 0 )
4326 					{
4327 						if ( !self->NPC )
4328 						{
4329 							G_SoundOnEnt( self, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN
4330 						}
4331 						else if (!(self->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
4332 						{
4333 							G_SoundOnEnt( self, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN
4334 							self->NPC->aiFlags |= NPCAI_DIE_ON_IMPACT;
4335 							self->client->ps.friction = 0;
4336 						}
4337 					}
4338 				}
4339 				if ( self->client->ps.pm_time > 0 && self->client->ps.pm_flags & PMF_TIME_KNOCKBACK && self->client->ps.velocity[2] > 0 )
4340 				{
4341 					float	thrown, dot;
4342 					vec3_t	throwdir, forward;
4343 
4344 					AngleVectors(self->currentAngles, forward, NULL, NULL);
4345 					thrown = VectorNormalize2(self->client->ps.velocity, throwdir);
4346 					dot = DotProduct(forward, throwdir);
4347 					if ( thrown > 100 )
4348 					{
4349 						if ( dot > 0.3 )
4350 						{//falling forward
4351 							if ( cliff_fall == 2 && PM_HasAnimation( self, BOTH_FALLDEATH1 ) )
4352 							{
4353 								anim = BOTH_FALLDEATH1;
4354 							}
4355 							else
4356 							{
4357 								switch ( Q_irand( 0, 7 ) )
4358 								{
4359 								case 0:
4360 								case 1:
4361 									anim = BOTH_DEATH4;
4362 									break;
4363 								case 2:
4364 									anim = BOTH_DEATH16;
4365 									break;
4366 								case 3:
4367 								case 4:
4368 								case 5:
4369 									anim = BOTH_DEATH5;
4370 									break;
4371 								case 6:
4372 									anim = BOTH_DEATH8;
4373 									break;
4374 								case 7:
4375 									anim = BOTH_DEATH14;
4376 									break;
4377 								}
4378 								if ( PM_HasAnimation( self, anim ))
4379 								{
4380 									self->client->ps.gravity *= 0.8;
4381 									self->client->ps.friction = 0;
4382 									if ( self->client->ps.velocity[2] > 0 && self->client->ps.velocity[2] < 100 )
4383 									{
4384 										self->client->ps.velocity[2] = 100;
4385 									}
4386 								}
4387 								else
4388 								{
4389 									anim = -1;
4390 								}
4391 							}
4392 						}
4393 						else if ( dot < -0.3 )
4394 						{
4395 							if ( thrown >= 250 && !Q_irand( 0, 3 ) )
4396 							{
4397 								if ( Q_irand( 0, 1 ) )
4398 								{
4399 									anim = BOTH_DEATHBACKWARD1;
4400 								}
4401 								else
4402 								{
4403 									anim = BOTH_DEATHBACKWARD2;
4404 								}
4405 							}
4406 							else
4407 							{
4408 								switch ( Q_irand( 0, 7 ) )
4409 								{
4410 								case 0:
4411 								case 1:
4412 									anim = BOTH_DEATH1;
4413 									break;
4414 								case 2:
4415 								case 3:
4416 									anim = BOTH_DEATH2;
4417 									break;
4418 								case 4:
4419 								case 5:
4420 									anim = BOTH_DEATH22;
4421 									break;
4422 								case 6:
4423 								case 7:
4424 									anim = BOTH_DEATH23;
4425 									break;
4426 								}
4427 							}
4428 							if ( PM_HasAnimation( self, anim ) )
4429 							{
4430 								self->client->ps.gravity *= 0.8;
4431 								self->client->ps.friction = 0;
4432 								if ( self->client->ps.velocity[2] > 0 && self->client->ps.velocity[2] < 100 )
4433 								{
4434 									self->client->ps.velocity[2] = 100;
4435 								}
4436 							}
4437 							else
4438 							{
4439 								anim = -1;
4440 							}
4441 						}
4442 						else
4443 						{//falling to one of the sides
4444 							if ( cliff_fall == 2 && PM_HasAnimation( self, BOTH_FALLDEATH1 ) )
4445 							{
4446 								anim = BOTH_FALLDEATH1;
4447 								if ( self->client->ps.velocity[2] > 0 && self->client->ps.velocity[2] < 100 )
4448 								{
4449 									self->client->ps.velocity[2] = 100;
4450 								}
4451 							}
4452 						}
4453 					}
4454 				}
4455 			}
4456 		}
4457 		else
4458 		{
4459 			specialAnim = qtrue;
4460 		}
4461 
4462 		if ( anim == -1 )
4463 		{
4464 			if ( meansOfDeath == MOD_ELECTROCUTE
4465 				|| (meansOfDeath == MOD_CRUSH && self->s.eFlags&EF_FORCE_GRIPPED)
4466 				|| (meansOfDeath == MOD_FORCE_DRAIN && self->s.eFlags&EF_FORCE_DRAINED))
4467 			{//electrocuted or choked to death
4468 				anim = BOTH_DEATH17;
4469 			}
4470 			else
4471 			{
4472 				anim = G_PickDeathAnim( self, self->pos1, damage, meansOfDeath, hitLoc );
4473 			}
4474 		}
4475 		if ( anim == -1 )
4476 		{
4477 			anim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH25 );	//initialize to good data
4478 			//TEMP HACK: these spinny deaths should happen less often
4479 			if ( ( anim == BOTH_DEATH8 || anim == BOTH_DEATH14 ) && Q_irand( 0, 1 ) )
4480 			{
4481 				anim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH25 );	//initialize to good data
4482 			}
4483 		}
4484 		else if ( !PM_HasAnimation( self, anim ) )
4485 		{//crap, still missing an anim, so pick one that we do have
4486             anim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH25 );	//initialize to good data
4487 		}
4488 
4489 
4490 		if ( meansOfDeath == MOD_KNOCKOUT )
4491 		{
4492 			//FIXME: knock-out sound, and don't remove me
4493 			G_AddEvent( self, EV_JUMP, 0 );
4494 			G_UseTargets2( self, self, self->target2 );
4495 			G_AlertTeam( self, attacker, 512, 32 );
4496 			if ( self->NPC )
4497 			{//stick around for a while
4498 				self->NPC->timeOfDeath = level.time + 10000;
4499 			}
4500 		}
4501 		else if ( meansOfDeath == MOD_GAS || meansOfDeath == MOD_FORCE_DRAIN )
4502 		{
4503 			G_AddEvent( self, EV_WATER_DROWN, 0 );
4504 			G_AlertTeam( self, attacker, 512, 32 );
4505 			if ( self->NPC )
4506 			{//stick around for a while
4507 				self->NPC->timeOfDeath = level.time + 10000;
4508 			}
4509 		}
4510 		else if ( meansOfDeath == MOD_SNIPER )
4511 		{
4512 			gentity_t	*tent;
4513 			vec3_t		spot;
4514 
4515 			VectorCopy( self->currentOrigin, spot );
4516 
4517 			self->flags |= FL_DISINTEGRATED;
4518 			self->svFlags |= SVF_BROADCAST;
4519 			tent = G_TempEntity( spot, EV_DISINTEGRATION );
4520 			tent->s.eventParm = PW_DISRUPTION;
4521 			tent->svFlags |= SVF_BROADCAST;
4522 			tent->owner = self;
4523 
4524 			G_AlertTeam( self, attacker, 512, 88 );
4525 
4526 			if ( self->playerModel >= 0 )
4527 			{
4528 				// don't let 'em animate
4529 				gi.G2API_PauseBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, cg.time );
4530 				gi.G2API_PauseBoneAnimIndex( &self->ghoul2[self->playerModel], self->motionBone, cg.time );
4531 				gi.G2API_PauseBoneAnimIndex( &self->ghoul2[self->playerModel], self->lowerLumbarBone, cg.time );
4532 				anim = -1;
4533 			}
4534 
4535 			//not solid anymore
4536 			self->contents = 0;
4537 			self->maxs[2] = -8;
4538 
4539 			if ( self->NPC )
4540 			{
4541 				//need to pad deathtime some to stick around long enough for death effect to play
4542 				self->NPC->timeOfDeath = level.time + 2000;
4543 			}
4544 		}
4545 		else
4546 		{
4547 			if ( hitLoc == HL_HEAD
4548 				&& !(dflags&DAMAGE_RADIUS)
4549 				&& meansOfDeath!=MOD_REPEATER_ALT
4550 				&& meansOfDeath!=MOD_FLECHETTE_ALT
4551 				&& meansOfDeath!=MOD_ROCKET
4552 				&& meansOfDeath!=MOD_ROCKET_ALT
4553 				&& meansOfDeath!=MOD_CONC
4554 				&& meansOfDeath!=MOD_THERMAL
4555 				&& meansOfDeath!=MOD_THERMAL_ALT
4556 				&& meansOfDeath!=MOD_DETPACK
4557 				&& meansOfDeath!=MOD_LASERTRIP
4558 				&& meansOfDeath!=MOD_LASERTRIP_ALT
4559 				&& meansOfDeath!=MOD_EXPLOSIVE
4560 				&& meansOfDeath!=MOD_EXPLOSIVE_SPLASH )
4561 			{//no sound when killed by headshot (explosions don't count)
4562 				G_AlertTeam( self, attacker, 512, 0 );
4563 				if ( gi.VoiceVolume[self->s.number] )
4564 				{//I was talking, so cut it off... with a jump sound?
4565 					G_SoundOnEnt( self, CHAN_VOICE, "*jump1.wav" );
4566 				}
4567 			}
4568 			else
4569 			{
4570 				if ( (self->client->ps.eFlags&EF_FORCE_GRIPPED) || (self->client->ps.eFlags&EF_FORCE_DRAINED) )
4571 				{//killed while gripped - no loud scream
4572 					G_AlertTeam( self, attacker, 512, 32 );
4573 				}
4574 				else if ( cliff_fall != 2 )
4575 				{
4576 					if ( meansOfDeath == MOD_KNOCKOUT || meansOfDeath == MOD_MELEE )
4577 					{
4578 						G_AddEvent( self, EV_JUMP, 0 );
4579 					}
4580 					else if ( meansOfDeath == MOD_GAS || meansOfDeath == MOD_FORCE_DRAIN )
4581 					{
4582 						G_AddEvent( self, EV_WATER_DROWN, 0 );
4583 					}
4584 					else
4585 					{
4586 						G_AddEvent( self, Q_irand(EV_DEATH1, EV_DEATH3), self->health );
4587 					}
4588 					G_DeathAlert( self, attacker );
4589 				}
4590 				else
4591 				{//screaming death is louder
4592 					G_AlertTeam( self, attacker, 512, 1024 );
4593 				}
4594 			}
4595 		}
4596 
4597 		if ( attacker && attacker->s.number == 0 )
4598 		{//killed by player
4599 			//FIXME: this should really be wherever my body comes to rest...
4600 			AddSightEvent( attacker, self->currentOrigin, 384, AEL_DISCOVERED, 10 );
4601 			//FIXME: danger event so that others will run away from this area since it's obviously dangerous
4602 		}
4603 
4604 		if ( anim >= 0 )//can be -1 if it fails, -2 if it's already in a death anim
4605 		{
4606 			NPC_SetAnim(self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4607 		}
4608 	}
4609 
4610 	//do any dismemberment if there's any to do...
4611 	if ( (dflags&DAMAGE_DISMEMBER)
4612 		&& G_DoDismemberment( self, self->pos1, meansOfDeath, damage, hitLoc )
4613 		&& !specialAnim )
4614 	{//we did dismemberment and our death anim is okay to override
4615 		if ( hitLoc == HL_HAND_RT && self->locationDamage[hitLoc] >= Q3_INFINITE && cliff_fall != 2 && self->client->ps.groundEntityNum != ENTITYNUM_NONE )
4616 		{//just lost our right hand and we're on the ground, use the special anim
4617 			NPC_SetAnim( self, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
4618 		}
4619 	}
4620 
4621 	// don't allow player to respawn for a few seconds
4622 	self->client->respawnTime = level.time + 2000;//self->client->ps.legsAnimTimer;
4623 
4624 //rww - RAGDOLL_BEGIN
4625 	if (gi.Cvar_VariableIntegerValue("broadsword"))
4626 	{
4627 		if ( self->client && (!self->NPC || !G_StandardHumanoid( self ) ) )
4628 		{
4629 			PM_SetLegsAnimTimer( self, &self->client->ps.legsAnimTimer, -1 );
4630 			PM_SetTorsoAnimTimer( self, &self->client->ps.torsoAnimTimer, -1 );
4631 		}
4632 	}
4633 	else
4634 	{
4635 		if ( self->client )
4636 		{
4637 			PM_SetLegsAnimTimer( self, &self->client->ps.legsAnimTimer, -1 );
4638 			PM_SetTorsoAnimTimer( self, &self->client->ps.torsoAnimTimer, -1 );
4639 		}
4640 	}
4641 //rww - RAGDOLL_END
4642 
4643 	//Flying creatures should drop when killed
4644 	//FIXME: This may screw up certain things that expect to float even while dead <?>
4645 	self->svFlags &= ~SVF_CUSTOM_GRAVITY;
4646 
4647 	self->client->ps.pm_type = PM_DEAD;
4648 	self->client->ps.pm_flags &= ~PMF_STUCK_TO_WALL;
4649 	//need to update STAT_HEALTH here because ClientThink_real for self may happen before STAT_HEALTH is updated from self->health and pmove will stomp death anim with a move anim
4650 	self->client->ps.stats[STAT_HEALTH] = self->health;
4651 
4652 	if ( self->NPC )
4653 	{//If an NPC, make sure we start running our scripts again- this gets set to infinite while we fall to our deaths
4654 		self->NPC->nextBStateThink = level.time;
4655 	}
4656 
4657 	if ( G_ActivateBehavior( self, BSET_DEATH ) )
4658 	{
4659 		deathScript = qtrue;
4660 	}
4661 
4662 	if ( self->NPC && (self->NPC->scriptFlags&SCF_FFDEATH) )
4663 	{
4664 		if ( G_ActivateBehavior( self, BSET_FFDEATH ) )
4665 		{//FIXME: should running this preclude running the normal deathscript?
4666 			deathScript = qtrue;
4667 		}
4668 		G_UseTargets2( self, self, self->target4 );
4669 	}
4670 
4671 	if ( !deathScript && !(self->svFlags&SVF_KILLED_SELF) )
4672 	{
4673 		//Should no longer run scripts
4674 		//WARNING!!! DO NOT DO THIS WHILE RUNNING A SCRIPT, ICARUS WILL HANDLE IT, but it's bad
4675 		Quake3Game()->FreeEntity( self );
4676 	}
4677 
4678 	// Free up any timers we may have on us.
4679 	TIMER_Clear( self->s.number );
4680 
4681 	// Set pending objectives to failed
4682 	OBJ_SetPendingObjectives(self);
4683 
4684 	gi.linkentity (self);
4685 
4686 	self->bounceCount = -1; // This is a cheap hack for optimizing the pointcontents check in deadthink
4687 	if ( self->NPC )
4688 	{
4689 		self->NPC->timeOfDeath = level.time;//this will change - used for debouncing post-death events
4690 		self->s.time = level.time;//this will not chage- this is actual time of death
4691 	}
4692 
4693 	// Start any necessary death fx for this entity
4694 	DeathFX( self );
4695 }
4696 
G_CheckForStrongAttackMomentum(gentity_t * self)4697 qboolean G_CheckForStrongAttackMomentum( gentity_t *self )
4698 {//see if our saber attack has too much momentum to be interrupted
4699 	if ( PM_PowerLevelForSaberAnim( &self->client->ps ) > FORCE_LEVEL_2 )
4700 	{//strong attacks can't be interrupted
4701 		if ( PM_InAnimForSaberMove( self->client->ps.torsoAnim, self->client->ps.saberMove ) )
4702 		{//our saberMove was not already interupted by some other anim (like pain)
4703 			if ( PM_SaberInStart( self->client->ps.saberMove ) )
4704 			{
4705 				float animLength = PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.torsoAnim );
4706 				if ( animLength - self->client->ps.torsoAnimTimer > 750 )
4707 				{//start anim is already 3/4 of a second into it, can't interrupt it now
4708 					return qtrue;
4709 				}
4710 			}
4711 			else if ( PM_SaberInReturn( self->client->ps.saberMove ) )
4712 			{
4713 				if ( self->client->ps.torsoAnimTimer > 750 )
4714 				{//still have a good amount of time left in the return anim, can't interrupt it
4715 					return qtrue;
4716 				}
4717 			}
4718 			else
4719 			{//cannot interrupt actual transitions and attacks
4720 				return qtrue;
4721 			}
4722 		}
4723 	}
4724 	return qfalse;
4725 }
4726 
PlayerPain(gentity_t * self,gentity_t * inflictor,gentity_t * other,const vec3_t point,int damage,int mod,int hitLoc)4727 void PlayerPain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod, int hitLoc )
4728 {
4729 	if ( self->client->NPC_class == CLASS_ATST )
4730 	{//different kind of pain checking altogether
4731 		G_ATSTCheckPain( self, other, point, damage, mod, hitLoc );
4732 		int blasterTest = gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "head_light_blaster_cann" );
4733 		int chargerTest = gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "head_concussion_charger" );
4734 		if ( blasterTest && chargerTest )
4735 		{//lost both side guns
4736 			//take away that weapon
4737 			self->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_ATST_SIDE );
4738 			//switch to primary guns
4739 			if ( self->client->ps.weapon == WP_ATST_SIDE )
4740 			{
4741 				CG_ChangeWeapon( WP_ATST_MAIN );
4742 			}
4743 		}
4744 	}
4745 	else
4746 	{
4747 		// play an apropriate pain sound
4748 		if ( level.time > self->painDebounceTime && !(self->flags & FL_GODMODE) )
4749 		{//first time hit this frame and not in godmode
4750 			self->client->ps.damageEvent++;
4751 			if ( !Q3_TaskIDPending( self, TID_CHAN_VOICE ) )
4752 			{
4753 				if ( self->client->damage_blood )
4754 				{//took damage myself, not just armor
4755 					if ( mod == MOD_GAS )
4756 					{
4757 						//SIGH... because our choke sounds are inappropriately long, I have to debounce them in code!
4758 						if ( TIMER_Done( self, "gasChokeSound" ) )
4759 						{
4760 							TIMER_Set( self, "gasChokeSound", Q_irand( 1000, 2000 ) );
4761 							G_SpeechEvent( self, Q_irand(EV_CHOKE1, EV_CHOKE3) );
4762 						}
4763 						if ( self->painDebounceTime <= level.time )
4764 						{
4765 							self->painDebounceTime = level.time + 50;
4766 						}
4767 					}
4768 					else
4769 					{
4770 						G_AddEvent( self, EV_PAIN, self->health );
4771 					}
4772 				}
4773 			}
4774 		}
4775 		if ( damage != -1 && (mod==MOD_MELEE || damage==0/*fake damage*/ || (Q_irand( 0, 10 ) <= damage && self->client->damage_blood)) )
4776 		{//-1 == don't play pain anim
4777 			if ( ( ((mod==MOD_SABER||mod==MOD_MELEE)&&self->client->damage_blood) || mod == MOD_CRUSH ) && (self->s.weapon == WP_SABER||self->s.weapon==WP_MELEE||cg.renderingThirdPerson) )//FIXME: not only if using saber, but if in third person at all?  But then 1st/third person functionality is different...
4778 			{//FIXME: only strong-level saber attacks should make me play pain anim?
4779 				if ( !G_CheckForStrongAttackMomentum( self ) && !PM_SpinningSaberAnim( self->client->ps.legsAnim )
4780 					&& !PM_SaberInSpecialAttack( self->client->ps.torsoAnim )
4781 					&& !PM_InKnockDown( &self->client->ps ) )
4782 				{//strong attacks and spins cannot be interrupted by pain, no pain when in knockdown
4783 					int	parts = SETANIM_BOTH;
4784 					if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE &&
4785 						!PM_SpinningSaberAnim( self->client->ps.legsAnim ) &&
4786 						!PM_FlippingAnim( self->client->ps.legsAnim ) &&
4787 						!PM_InSpecialJump( self->client->ps.legsAnim ) &&
4788 						!PM_RollingAnim( self->client->ps.legsAnim )&&
4789 						!PM_CrouchAnim( self->client->ps.legsAnim )&&
4790 						!PM_RunningAnim( self->client->ps.legsAnim ))
4791 					{//if on a surface and not in a spin or flip, play full body pain
4792 						parts = SETANIM_BOTH;
4793 					}
4794 					else
4795 					{//play pain just in torso
4796 						parts = SETANIM_TORSO;
4797 					}
4798 					if ( self->painDebounceTime < level.time )
4799 					{
4800 						//temp HACK: these are the only 2 pain anims that look good when holding a saber
4801 						NPC_SetAnim( self, parts, PM_PickAnim( self, BOTH_PAIN2, BOTH_PAIN3 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
4802 						self->client->ps.saberMove = LS_READY;//don't finish whatever saber move you may have been in
4803 						//WTF - insn't working
4804 						if ( self->health < 10 && d_slowmodeath->integer > 5 )
4805 						{
4806 							G_StartMatrixEffect( self );
4807 						}
4808 					}
4809 					if ( (parts == SETANIM_BOTH && damage > 30) || (self->painDebounceTime>level.time&&damage>10))
4810 					{//took a lot of damage in 1 hit //or took 2 hits in quick succession
4811 						self->aimDebounceTime = level.time + self->client->ps.torsoAnimTimer;
4812 						self->client->ps.pm_time = self->client->ps.torsoAnimTimer;
4813 						self->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
4814 					}
4815 					self->client->ps.weaponTime = self->client->ps.torsoAnimTimer;
4816 					self->attackDebounceTime = level.time + self->client->ps.torsoAnimTimer;
4817 				}
4818 				self->painDebounceTime = level.time + self->client->ps.torsoAnimTimer;
4819 			}
4820 		}
4821 	}
4822 	if ( mod != MOD_GAS && self->painDebounceTime <= level.time )
4823 	{
4824 		self->painDebounceTime = level.time + 700;
4825 	}
4826 }
4827 /*
4828 ================
4829 CheckArmor
4830 ================
4831 */
CheckArmor(gentity_t * ent,int damage,int dflags,int mod)4832 int CheckArmor (gentity_t *ent, int damage, int dflags, int mod)
4833 {
4834 	gclient_t	*client;
4835 	int			save;
4836 	int			count;
4837 
4838 	if (!damage)
4839 		return 0;
4840 
4841 	client = ent->client;
4842 
4843 	if (!client)
4844 		return 0;
4845 
4846 	if ( (dflags&DAMAGE_NO_ARMOR) )
4847 	{
4848 		// If this isn't a vehicle, leave.
4849 		if ( client->NPC_class != CLASS_VEHICLE )
4850 		{
4851 			return 0;
4852 		}
4853 	}
4854 
4855 	if (client->NPC_class==CLASS_ASSASSIN_DROID)
4856 	{
4857 		// The Assassin Always Completely Ignores These Damage Types
4858 		//-----------------------------------------------------------
4859 		if (mod==MOD_GAS ||	mod==MOD_IMPACT || mod==MOD_LAVA || mod==MOD_SLIME || mod==MOD_WATER ||
4860 			mod==MOD_FORCE_GRIP || mod==MOD_FORCE_DRAIN || mod==MOD_SEEKER || mod==MOD_MELEE ||
4861 			mod==MOD_BOWCASTER || mod==MOD_BRYAR || mod==MOD_BRYAR_ALT || mod==MOD_BLASTER || mod==MOD_BLASTER_ALT ||
4862 			mod==MOD_SNIPER || mod==MOD_BOWCASTER || mod==MOD_BOWCASTER_ALT || mod==MOD_REPEATER || mod==MOD_REPEATER_ALT)
4863 		{
4864 			return damage;
4865 		}
4866 
4867 		// The Assassin Always Takes Half Of These Damage Types
4868 		//------------------------------------------------------
4869 		if (mod==MOD_GAS ||	mod==MOD_IMPACT || mod==MOD_LAVA || mod==MOD_SLIME || mod==MOD_WATER)
4870 		{
4871 			return damage/2;
4872 		}
4873 
4874 		// If The Shield Is Not On, No Additional Protection
4875 		//---------------------------------------------------
4876 		if (!(ent->flags&FL_SHIELDED))
4877 		{
4878 			// He Does Ignore Half Saber Damage, Even Shield Down
4879 			//----------------------------------------------------
4880 			if (mod==MOD_SABER)
4881 			{
4882 				return (int)((float)(damage)*0.75f);
4883 			}
4884 			return 0;
4885 		}
4886 
4887 		// If The Shield Is Up, He Ignores These Damage Types
4888 		//----------------------------------------------------
4889 		if (mod==MOD_SABER || mod==MOD_FLECHETTE || mod==MOD_FLECHETTE_ALT || mod==MOD_DISRUPTOR)
4890 		{
4891 			return damage;
4892 		}
4893 
4894 		// The Demp Completely Destroys The Shield
4895 		//-----------------------------------------
4896 		if (mod==MOD_DEMP2 || mod==MOD_DEMP2_ALT)
4897 		{
4898 			client->ps.stats[STAT_ARMOR] = 0;
4899 			return 0;
4900 		}
4901 
4902 		// Otherwise, The Shield Absorbs As Much Damage As Possible
4903 		//----------------------------------------------------------
4904 		int	previousArmor			  = client->ps.stats[STAT_ARMOR];
4905 		client->ps.stats[STAT_ARMOR] -= damage;
4906 		if (client->ps.stats[STAT_ARMOR]<0)
4907 		{
4908 			client->ps.stats[STAT_ARMOR] = 0;
4909 		}
4910 		return (previousArmor - client->ps.stats[STAT_ARMOR]);
4911 	}
4912 
4913 
4914 
4915 	if ( client->NPC_class == CLASS_GALAKMECH)
4916 	{//special case
4917 		if ( client->ps.stats[STAT_ARMOR] <= 0 )
4918 		{//no shields
4919 			client->ps.powerups[PW_GALAK_SHIELD] = 0;
4920 			return 0;
4921 		}
4922 		else
4923 		{//shields take all the damage
4924 			client->ps.stats[STAT_ARMOR] -= damage;
4925 			if ( client->ps.stats[STAT_ARMOR] <= 0 )
4926 			{
4927 				client->ps.powerups[PW_GALAK_SHIELD] = 0;
4928 				client->ps.stats[STAT_ARMOR] = 0;
4929 			}
4930 			return damage;
4931 		}
4932 	}
4933 	else
4934 	{
4935 		// armor
4936 		count = client->ps.stats[STAT_ARMOR];
4937 
4938 		// No damage to entity until armor is at less than 50% strength
4939 		if (count > (client->ps.stats[STAT_MAX_HEALTH]/2)) // MAX_HEALTH is considered max armor. Or so I'm told.
4940 		{
4941 			save = damage;
4942 		}
4943 		else
4944 		{
4945 			if ( !ent->s.number && client->NPC_class == CLASS_ATST )
4946 			{//player in ATST... armor takes *all* the damage
4947 				save = damage;
4948 			}
4949 			else
4950 			{
4951 				save = ceil( (float) damage * ARMOR_PROTECTION );
4952 			}
4953 		}
4954 
4955 		//Always round up
4956 		if (damage == 1)
4957 		{
4958 			if ( client->ps.stats[STAT_ARMOR] > 0 )
4959 				client->ps.stats[STAT_ARMOR] -= save;
4960 			//WTF? returns false 0 if armor absorbs only 1 point of damage
4961 			return 0;
4962 		}
4963 
4964 		if (save >= count)
4965 			save = count;
4966 
4967 		if (!save)
4968 			return 0;
4969 
4970 		client->ps.stats[STAT_ARMOR] -= save;
4971 
4972 		return save;
4973 	}
4974 }
4975 
4976 extern void NPC_SetPainEvent( gentity_t *self );
4977 extern qboolean Boba_StopKnockdown( gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown = qfalse );
4978 extern qboolean Jedi_StopKnockdown( gentity_t *self, gentity_t *pusher, const vec3_t pushDir );
G_Knockdown(gentity_t * self,gentity_t * attacker,const vec3_t pushDir,float strength,qboolean breakSaberLock)4979 void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock )
4980 {
4981 	if ( !self || !self->client || !attacker || !attacker->client )
4982 	{
4983 		return;
4984 	}
4985 
4986 	if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
4987 	{
4988 		return;
4989 	}
4990 
4991 	if ( Boba_StopKnockdown( self, attacker, pushDir ) )
4992 	{
4993 		return;
4994 	}
4995 	else if ( Jedi_StopKnockdown( self, attacker, pushDir ) )
4996 	{//They can sometimes backflip instead of be knocked down
4997 		return;
4998 	}
4999 	else if ( PM_LockedAnim( self->client->ps.legsAnim ) )
5000 	{//stuck doing something else
5001 		return;
5002 	}
5003 	else if ( Rosh_BeingHealed( self ) )
5004 	{
5005 		return;
5006 	}
5007 
5008 	//break out of a saberLock?
5009 	if ( self->client->ps.saberLockTime > level.time )
5010 	{
5011 		if ( breakSaberLock )
5012 		{
5013 			self->client->ps.saberLockTime = 0;
5014 			self->client->ps.saberLockEnemy = ENTITYNUM_NONE;
5015 		}
5016 		else
5017 		{
5018 			return;
5019 		}
5020 	}
5021 
5022 	if ( self->health > 0 )
5023 	{
5024 		if ( !self->s.number )
5025 		{
5026 			NPC_SetPainEvent( self );
5027 		}
5028 		else
5029 		{
5030 			GEntity_PainFunc( self, attacker, attacker, self->currentOrigin, 0, MOD_MELEE );
5031 		}
5032 
5033 		G_CheckLedgeDive( self, 72, pushDir, qfalse, qfalse );
5034 
5035 		if ( !PM_SpinningSaberAnim( self->client->ps.legsAnim )
5036 			&& !PM_FlippingAnim( self->client->ps.legsAnim )
5037 			&& !PM_RollingAnim( self->client->ps.legsAnim )
5038 			&& !PM_InKnockDown( &self->client->ps ) )
5039 		{
5040 			int knockAnim = BOTH_KNOCKDOWN1;//default knockdown
5041 			if ( !self->s.number && ( strength < 300 ) )//!g_spskill->integer ||
5042 			{//player only knocked down if pushed *hard*
5043 				return;
5044 			}
5045 			else if ( PM_CrouchAnim( self->client->ps.legsAnim ) )
5046 			{//crouched knockdown
5047 				knockAnim = BOTH_KNOCKDOWN4;
5048 			}
5049 			else
5050 			{//plain old knockdown
5051 				vec3_t pLFwd, pLAngles = {0,self->client->ps.viewangles[YAW],0};
5052 				AngleVectors( pLAngles, pLFwd, NULL, NULL );
5053 				if ( DotProduct( pLFwd, pushDir ) > 0.2f )
5054 				{//pushing him from behind
5055 					knockAnim = BOTH_KNOCKDOWN3;
5056 				}
5057 				else
5058 				{//pushing him from front
5059 					knockAnim = BOTH_KNOCKDOWN1;
5060 				}
5061 			}
5062 			if ( knockAnim == BOTH_KNOCKDOWN1 && strength > 150 )
5063 			{//push *hard*
5064 				knockAnim = BOTH_KNOCKDOWN2;
5065 			}
5066 			NPC_SetAnim( self, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
5067 			if ( self->s.number >= MAX_CLIENTS )
5068 			{//randomize getup times
5069 				int addTime = Q_irand( -200, 200 );
5070 				self->client->ps.legsAnimTimer += addTime;
5071 				self->client->ps.torsoAnimTimer += addTime;
5072 			}
5073 			else
5074 			{//player holds extra long so you have more time to decide to do the quick getup
5075 				if ( PM_KnockDownAnim( self->client->ps.legsAnim ) )
5076 				{
5077 					self->client->ps.legsAnimTimer += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
5078 					self->client->ps.torsoAnimTimer += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
5079 				}
5080 			}
5081 		}
5082 	}
5083 }
5084 
G_CheckKnockdown(gentity_t * targ,gentity_t * attacker,vec3_t newDir,int dflags,int mod)5085 void G_CheckKnockdown( gentity_t *targ, gentity_t *attacker, vec3_t newDir, int dflags, int mod )
5086 {
5087 	if ( !targ || !attacker )
5088 	{
5089 		return;
5090 	}
5091 	if ( !(dflags&DAMAGE_RADIUS) )
5092 	{//not inherently explosive damage, check mod
5093 		if ( mod!=MOD_REPEATER_ALT
5094 			&&mod!=MOD_FLECHETTE_ALT
5095 			&&mod!=MOD_ROCKET
5096 			&&mod!=MOD_ROCKET_ALT
5097 			&&mod!=MOD_CONC
5098 			&&mod!=MOD_CONC_ALT
5099 			&&mod!=MOD_THERMAL
5100 			&&mod!=MOD_THERMAL_ALT
5101 			&&mod!=MOD_DETPACK
5102 			&&mod!=MOD_LASERTRIP
5103 			&&mod!=MOD_LASERTRIP_ALT
5104 			&&mod!=MOD_EXPLOSIVE
5105 			&&mod!=MOD_EXPLOSIVE_SPLASH )
5106 		{
5107 			return;
5108 		}
5109 	}
5110 
5111 	if ( !targ->client || targ->client->NPC_class == CLASS_PROTOCOL || !G_StandardHumanoid( targ ) )
5112 	{
5113 		return;
5114 	}
5115 
5116 	if ( targ->client->ps.groundEntityNum == ENTITYNUM_NONE )
5117 	{//already in air
5118 		return;
5119 	}
5120 
5121 	if ( !targ->s.number )
5122 	{//player less likely to be knocked down
5123 		if ( !g_spskill->integer )
5124 		{//never in easy
5125 			return;
5126 		}
5127 		if ( !cg.renderingThirdPerson || cg.zoomMode )
5128 		{//never if not in chase camera view (so more likely with saber out)
5129 			return;
5130 		}
5131 		if ( g_spskill->integer == 1 )
5132 		{//33% chance on medium
5133 			if ( Q_irand( 0, 2 ) )
5134 			{
5135 				return;
5136 			}
5137 		}
5138 		else
5139 		{//50% chance on hard
5140 			if ( Q_irand( 0, 1 ) )
5141 			{
5142 				return;
5143 			}
5144 		}
5145 	}
5146 
5147 	float strength = VectorLength( targ->client->ps.velocity );
5148 	if ( targ->client->ps.velocity[2] > 100 && strength > Q_irand( 150, 350 ) )//600 ) )
5149 	{//explosive concussion possibly do a knockdown?
5150 		G_Knockdown( targ, attacker, newDir, strength, qtrue );
5151 	}
5152 }
5153 
G_ApplyKnockback(gentity_t * targ,vec3_t newDir,float knockback)5154 void G_ApplyKnockback( gentity_t *targ, vec3_t newDir, float knockback )
5155 {
5156 	vec3_t	kvel;
5157 	float	mass;
5158 
5159 	if ( targ
5160 		&& targ->client
5161 		&& ( targ->client->NPC_class == CLASS_ATST
5162 			|| targ->client->NPC_class == CLASS_RANCOR
5163 			|| targ->client->NPC_class == CLASS_SAND_CREATURE
5164 			|| targ->client->NPC_class == CLASS_WAMPA) )
5165 	{//much to large to *ever* throw
5166 		return;
5167 	}
5168 
5169 	//--- TEMP TEST
5170 	if ( newDir[2] <= 0.0f )
5171 	{
5172 
5173 		newDir[2] += (( 0.0f - newDir[2] ) * 1.2f );
5174 	}
5175 
5176 	knockback *= 2.0f;
5177 
5178 	if ( knockback > 120 )
5179 	{
5180 		knockback = 120;
5181 	}
5182 	//--- TEMP TEST
5183 
5184 	if ( targ->physicsBounce > 0 )	//overide the mass
5185 		mass = targ->physicsBounce;
5186 	else
5187 		mass = 200;
5188 
5189 	if ( g_gravity->value > 0 )
5190 	{
5191 		VectorScale( newDir, g_knockback->value * (float)knockback / mass * 0.8, kvel );
5192 		kvel[2] = newDir[2] * ( g_knockback->value * (float)knockback ) / ( mass * 1.5 ) + 20;
5193 	}
5194 	else
5195 	{
5196 		VectorScale( newDir, g_knockback->value * (float)knockback / mass, kvel );
5197 	}
5198 
5199 	if ( targ->client )
5200 	{
5201 		VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );
5202 	}
5203 	else if ( targ->s.pos.trType != TR_STATIONARY && targ->s.pos.trType != TR_LINEAR_STOP && targ->s.pos.trType != TR_NONLINEAR_STOP )
5204 	{
5205 		VectorAdd( targ->s.pos.trDelta, kvel, targ->s.pos.trDelta );
5206 		VectorCopy( targ->currentOrigin, targ->s.pos.trBase );
5207 		targ->s.pos.trTime = level.time;
5208 	}
5209 
5210 	// set the timer so that the other client can't cancel
5211 	// out the movement immediately
5212 	if ( targ->client && !targ->client->ps.pm_time )
5213 	{
5214 		int		t;
5215 
5216 		t = knockback * 2;
5217 		if ( t < 50 ) {
5218 			t = 50;
5219 		}
5220 		if ( t > 200 ) {
5221 			t = 200;
5222 		}
5223 		targ->client->ps.pm_time = t;
5224 		targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
5225 	}
5226 }
5227 
G_CheckForLedge(gentity_t * self,vec3_t fallCheckDir,float checkDist)5228 static int G_CheckForLedge( gentity_t *self, vec3_t fallCheckDir, float checkDist )
5229 {
5230 	vec3_t	start, end;
5231 	trace_t	tr;
5232 
5233 	VectorMA( self->currentOrigin, checkDist, fallCheckDir, end );
5234 	//Should have clip burshes masked out by now and have bbox resized to death size
5235 	gi.trace( &tr, self->currentOrigin, self->mins, self->maxs, end, self->s.number, self->clipmask, (EG2_Collision)0, 0 );
5236 	if ( tr.allsolid || tr.startsolid )
5237 	{
5238 		return 0;
5239 	}
5240 	VectorCopy( tr.endpos, start );
5241 	VectorCopy( start, end );
5242 	end[2] -= 256;
5243 
5244 	gi.trace( &tr, start, self->mins, self->maxs, end, self->s.number, self->clipmask, (EG2_Collision)0, 0 );
5245 	if ( tr.allsolid || tr.startsolid )
5246 	{
5247 		return 0;
5248 	}
5249 	if ( tr.fraction >= 1.0 )
5250 	{
5251 		return (start[2]-tr.endpos[2]);
5252 	}
5253 	return 0;
5254 }
5255 
G_FriendlyFireReaction(gentity_t * self,gentity_t * other,int dflags)5256 static void G_FriendlyFireReaction( gentity_t *self, gentity_t *other, int dflags )
5257 {
5258 	if ( (!player->client->ps.viewEntity || other->s.number != player->client->ps.viewEntity))
5259 	{//hit by a teammate
5260 		if ( other != self->enemy && self != other->enemy )
5261 		{//we weren't already enemies
5262 			if ( self->enemy || other->enemy || (other->s.number&&other->s.number!=player->client->ps.viewEntity) )
5263 			{//if one of us actually has an enemy already, it's okay, just an accident OR wasn't hit by player or someone controlled by player OR player hit ally and didn't get 25% chance of getting mad (FIXME:accumulate anger+base on diff?)
5264 				return;
5265 			}
5266 			else if ( self->NPC && !other->s.number )//should be assumed, but...
5267 			{//dammit, stop that!
5268 				if ( !(dflags&DAMAGE_RADIUS) )
5269 				{
5270 					//if it's radius damage, ignore it
5271 					if ( self->NPC->ffireDebounce < level.time )
5272 					{
5273 						//FIXME: way something?  NEED DIALOGUE
5274 						self->NPC->ffireCount++;
5275 						//Com_Printf( "incr: %d < %d\n", self->NPC->ffireCount, 3+((2-g_spskill->integer)*2) );
5276 						self->NPC->ffireDebounce = level.time + 500;
5277 					}
5278 				}
5279 			}
5280 		}
5281 	}
5282 }
5283 
5284 float damageModifier[HL_MAX] =
5285 {
5286 	1.0f,	//HL_NONE,
5287 	0.25f,	//HL_FOOT_RT,
5288 	0.25f,	//HL_FOOT_LT,
5289 	0.75f,	//HL_LEG_RT,
5290 	0.75f,	//HL_LEG_LT,
5291 	1.0f,	//HL_WAIST,
5292 	1.0f,	//HL_BACK_RT,
5293 	1.0f,	//HL_BACK_LT,
5294 	1.0f,	//HL_BACK,
5295 	1.0f,	//HL_CHEST_RT,
5296 	1.0f,	//HL_CHEST_LT,
5297 	1.0f,	//HL_CHEST,
5298 	0.5f,	//HL_ARM_RT,
5299 	0.5f,	//HL_ARM_LT,
5300 	0.25f,	//HL_HAND_RT,
5301 	0.25f,	//HL_HAND_LT,
5302 	2.0f,	//HL_HEAD,
5303 	1.0f,	//HL_GENERIC1,
5304 	1.0f,	//HL_GENERIC2,
5305 	1.0f,	//HL_GENERIC3,
5306 	1.0f,	//HL_GENERIC4,
5307 	1.0f,	//HL_GENERIC5,
5308 	1.0f,	//HL_GENERIC6,
5309 };
5310 
G_TrackWeaponUsage(gentity_t * self,gentity_t * inflictor,int add,int mod)5311 void G_TrackWeaponUsage( gentity_t *self, gentity_t *inflictor, int add, int mod )
5312 {
5313 	if ( !self || !self->client || self->s.number )
5314 	{//player only
5315 		return;
5316 	}
5317 	int weapon = WP_NONE;
5318 	//FIXME: need to check the MOD to find out what weapon (if *any*) actually did the killing
5319 	if ( inflictor && !inflictor->client && mod != MOD_SABER && inflictor->lastEnemy && inflictor->lastEnemy != self )
5320 	{//a missile that was reflected, ie: not owned by me originally
5321 		if ( inflictor->owner == self && self->s.weapon == WP_SABER )
5322 		{//we reflected it
5323 			weapon = WP_SABER;
5324 		}
5325 	}
5326 	if ( weapon == WP_NONE )
5327 	{
5328 		switch ( mod )
5329 		{
5330 		case MOD_SABER:
5331 			weapon = WP_SABER;
5332 			break;
5333 		case MOD_BRYAR:
5334 		case MOD_BRYAR_ALT:
5335 			weapon = WP_BRYAR_PISTOL;
5336 			break;
5337 		case MOD_BLASTER:
5338 		case MOD_BLASTER_ALT:
5339 			weapon = WP_BLASTER;
5340 			break;
5341 		case MOD_DISRUPTOR:
5342 		case MOD_SNIPER:
5343 			weapon = WP_DISRUPTOR;
5344 			break;
5345 		case MOD_BOWCASTER:
5346 		case MOD_BOWCASTER_ALT:
5347 			weapon = WP_BOWCASTER;
5348 			break;
5349 		case MOD_REPEATER:
5350 		case MOD_REPEATER_ALT:
5351 			weapon = WP_REPEATER;
5352 			break;
5353 		case MOD_DEMP2:
5354 		case MOD_DEMP2_ALT:
5355 			weapon = WP_DEMP2;
5356 			break;
5357 		case MOD_FLECHETTE:
5358 		case MOD_FLECHETTE_ALT:
5359 			weapon = WP_FLECHETTE;
5360 			break;
5361 		case MOD_ROCKET:
5362 		case MOD_ROCKET_ALT:
5363 			weapon = WP_ROCKET_LAUNCHER;
5364 			break;
5365 		case MOD_CONC:
5366 		case MOD_CONC_ALT:
5367 			weapon = WP_CONCUSSION;
5368 			break;
5369 		case MOD_THERMAL:
5370 		case MOD_THERMAL_ALT:
5371 			weapon = WP_THERMAL;
5372 			break;
5373 		case MOD_DETPACK:
5374 			weapon = WP_DET_PACK;
5375 			break;
5376 		case MOD_LASERTRIP:
5377 		case MOD_LASERTRIP_ALT:
5378 			weapon = WP_TRIP_MINE;
5379 			break;
5380 		case MOD_MELEE:
5381 			if ( self->s.weapon == WP_STUN_BATON )
5382 			{
5383 				weapon = WP_STUN_BATON;
5384 			}
5385 			else if ( self->s.weapon == WP_MELEE )
5386 			{
5387 				weapon = WP_MELEE;
5388 			}
5389 			break;
5390 		}
5391 	}
5392 	if ( weapon != WP_NONE )
5393 	{
5394 		self->client->sess.missionStats.weaponUsed[weapon] += add;
5395 	}
5396 }
5397 
G_NonLocationSpecificDamage(int meansOfDeath)5398 qboolean G_NonLocationSpecificDamage( int meansOfDeath )
5399 {
5400 	if ( meansOfDeath == MOD_EXPLOSIVE
5401 		|| meansOfDeath == MOD_REPEATER_ALT
5402 		|| meansOfDeath == MOD_FLECHETTE_ALT
5403 		|| meansOfDeath == MOD_ROCKET
5404 		|| meansOfDeath == MOD_ROCKET_ALT
5405 		|| meansOfDeath == MOD_CONC
5406 		|| meansOfDeath == MOD_THERMAL
5407 		|| meansOfDeath == MOD_THERMAL_ALT
5408 		|| meansOfDeath == MOD_DETPACK
5409 		|| meansOfDeath == MOD_LASERTRIP
5410 		|| meansOfDeath == MOD_LASERTRIP_ALT
5411 		|| meansOfDeath == MOD_MELEE
5412 		|| meansOfDeath == MOD_FORCE_GRIP
5413 		|| meansOfDeath == MOD_KNOCKOUT
5414 		|| meansOfDeath == MOD_CRUSH
5415 		|| meansOfDeath == MOD_EXPLOSIVE_SPLASH )
5416 	{
5417 		return qtrue;
5418 	}
5419 	return qfalse;
5420 }
5421 
G_ImmuneToGas(gentity_t * ent)5422 qboolean G_ImmuneToGas( gentity_t *ent )
5423 {
5424 	if ( !ent || !ent->client )
5425 	{//only effects living clients
5426 		return qtrue;
5427 	}
5428 	if ( ent->s.weapon == WP_NOGHRI_STICK//assumes user is immune
5429 		|| ent->client->NPC_class == CLASS_HAZARD_TROOPER
5430 		|| ent->client->NPC_class == CLASS_ATST
5431 		|| ent->client->NPC_class == CLASS_GONK
5432 		|| ent->client->NPC_class == CLASS_SAND_CREATURE
5433 		|| ent->client->NPC_class == CLASS_INTERROGATOR
5434 		|| ent->client->NPC_class == CLASS_MARK1
5435 		|| ent->client->NPC_class == CLASS_MARK2
5436 		|| ent->client->NPC_class == CLASS_GALAKMECH
5437 		|| ent->client->NPC_class == CLASS_MOUSE
5438 		|| ent->client->NPC_class == CLASS_PROBE			// droid
5439 		|| ent->client->NPC_class == CLASS_PROTOCOL			// droid
5440 		|| ent->client->NPC_class == CLASS_R2D2				// droid
5441 		|| ent->client->NPC_class == CLASS_R5D2				// droid
5442 		|| ent->client->NPC_class == CLASS_REMOTE
5443 		|| ent->client->NPC_class == CLASS_SEEKER			// droid
5444 		|| ent->client->NPC_class == CLASS_SENTRY
5445 		|| ent->client->NPC_class == CLASS_SWAMPTROOPER
5446 		|| ent->client->NPC_class == CLASS_TUSKEN
5447 		|| ent->client->NPC_class == CLASS_BOBAFETT
5448 		|| ent->client->NPC_class == CLASS_ROCKETTROOPER
5449 		|| ent->client->NPC_class == CLASS_SABER_DROID
5450 		|| ent->client->NPC_class == CLASS_ASSASSIN_DROID
5451 		|| ent->client->NPC_class == CLASS_HAZARD_TROOPER
5452 		|| ent->client->NPC_class == CLASS_VEHICLE )
5453 	{
5454 		return qtrue;
5455 	}
5456 	return qfalse;
5457 }
5458 
5459 extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
5460 extern void G_StartRoll( gentity_t *ent, int anim );
5461 extern void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt );
5462 
5463 /*
5464 ============
5465 T_Damage
5466 
5467 targ		entity that is being damaged
5468 inflictor	entity that is causing the damage
5469 attacker	entity that caused the inflictor to damage targ
5470 	example: targ=monster, inflictor=rocket, attacker=player
5471 
5472 dir			direction of the attack for knockback
5473 point		point at which the damage is being inflicted, used for headshots
5474 damage		amount of damage being inflicted
5475 knockback	force to be applied against targ as a result of the damage
5476 
5477 inflictor, attacker, dir, and point can be NULL for environmental effects
5478 
5479 dflags		these flags are used to control how T_Damage works
5480 	DAMAGE_RADIUS			damage was indirect (from a nearby explosion)
5481 	DAMAGE_NO_ARMOR			armor does not protect from this damage
5482 	DAMAGE_NO_KNOCKBACK		do not affect velocity, just view angles
5483 	DAMAGE_NO_PROTECTION	kills godmode, armor, everything
5484 	DAMAGE_NO_HIT_LOC		Damage not based on hit location
5485 ============
5486 */
G_Damage(gentity_t * targ,gentity_t * inflictor,gentity_t * attacker,const vec3_t dir,const vec3_t point,int damage,int dflags,int mod,int hitLoc)5487 void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const vec3_t dir, const vec3_t point, int damage, int dflags, int mod, int hitLoc )
5488 {
5489 	gclient_t	*client;
5490 	int			take;
5491 	int			asave = 0;
5492 	int			knockback;
5493 	vec3_t		newDir;
5494 	qboolean	alreadyDead = qfalse;
5495 
5496 	if (!targ->takedamage) {
5497 		if ( targ->client //client
5498 			&& targ->client->NPC_class == CLASS_SAND_CREATURE//sand creature
5499 			&& targ->activator//something in our mouth
5500 			&& targ->activator == inflictor )//inflictor of damage is the thing in our mouth
5501 		{//being damaged by the thing in our mouth, allow the damage
5502 		}
5503 		else
5504 		{
5505 			return;
5506 		}
5507 	}
5508 
5509 	if ( targ->health <= 0 && !targ->client )
5510 	{	// allow corpses to be disintegrated
5511 		if( mod != MOD_SNIPER || (targ->flags & FL_DISINTEGRATED) )
5512 		return;
5513 	}
5514 
5515 	// if we are the player and we are locked to an emplaced gun, we have to reroute damage to the gun....sigh.
5516 	if ( targ->s.eFlags & EF_LOCKED_TO_WEAPON
5517 		&& targ->s.number == 0
5518 		&& targ->owner
5519 		&& !targ->owner->bounceCount //not an EWeb
5520 		&& !( targ->owner->flags & FL_GODMODE ))
5521 	{
5522 		// swapping the gun into our place to absorb our damage
5523 		targ = targ->owner;
5524 	}
5525 
5526 	if ( (targ->flags&FL_SHIELDED) && mod != MOD_SABER  && !targ->client)
5527 	{//magnetically protected, this thing can only be damaged by lightsabers
5528 		return;
5529 	}
5530 
5531 	if ( (targ->flags&FL_DMG_BY_SABER_ONLY) && mod != MOD_SABER )
5532 	{//can only be damaged by lightsabers (but no shield... yeah, it's redundant, but whattayagonnado?)
5533 		return;
5534 	}
5535 
5536 	if (( targ->flags & FL_DMG_BY_HEAVY_WEAP_ONLY ) && !( dflags & DAMAGE_HEAVY_WEAP_CLASS ))
5537 	{
5538 		// can only be damaged by an heavy type weapon...but impacting missile was in the heavy weap class...so we just aren't taking damage from this missile
5539 		return;
5540 	}
5541 
5542 	if ( (targ->svFlags&SVF_BBRUSH)
5543 		|| (!targ->client && Q_stricmp( "misc_model_breakable", targ->classname ) == 0 ) )//FIXME: flag misc_model_breakables?
5544 	{//breakable brush or misc_model_breakable
5545 		if ( targ->NPC_targetname )
5546 		{//only a certain attacker can destroy this
5547 			if ( !attacker
5548 				|| !attacker->targetname
5549 				|| Q_stricmp( targ->NPC_targetname, attacker->targetname ) != 0 )
5550 			{//and it's not this one, do nothing
5551 				return;
5552 			}
5553 		}
5554 	}
5555 
5556 	if ( targ->client && targ->client->NPC_class == CLASS_ATST )
5557 	{
5558 		// extra checks can be done here
5559 		if ( mod == MOD_SNIPER
5560 			|| mod == MOD_DISRUPTOR
5561 			|| mod == MOD_CONC_ALT )
5562 		{
5563 			// disruptor does not hurt an atst
5564 			return;
5565 		}
5566 	}
5567 	if ( targ->client
5568 		&& targ->client->NPC_class == CLASS_RANCOR
5569 		&& (!attacker||!attacker->client||attacker->client->NPC_class!=CLASS_RANCOR) )
5570 	{
5571 		// I guess always do 10 points of damage...feel free to tweak as needed
5572 		if ( damage < 10 )
5573 		{//ignore piddly little damage
5574 			damage = 0;
5575 		}
5576 		else if ( damage >= 10 )
5577 		{
5578 			damage = 10;
5579 		}
5580 	}
5581 	else if ( mod == MOD_SABER )
5582 	{//sabers do less damage to mark1's and atst's, and to hazard troopers and assassin droids
5583 		if ( targ->client )
5584 		{
5585 			if ( targ->client->NPC_class == CLASS_ATST
5586 				|| targ->client->NPC_class == CLASS_MARK1 )
5587 			{
5588 				// I guess always do 5 points of damage...feel free to tweak as needed
5589 				if ( damage > 5 )
5590 				{
5591 					damage = 5;
5592 				}
5593 			}
5594 			/*
5595 			//NOTE: a more controlled way to do the class-specific saber immunities, if we want
5596 			else if ( targ->client->NPC_class == CLASS_ASSASSIN_DROID )
5597 			{//takes 2 hits to kill on easy, 3 on medium, 4 on hard
5598 				int maxDamage = ceil((float)targ->max_health/(2.0f+g_spskill->value));
5599 				if ( damage > maxDamage )
5600 				{
5601 					damage = maxDamage;
5602 				}
5603 			}
5604 			else if ( targ->client->NPC_class == CLASS_HAZARD_TROOPER )
5605 			{//takes 3 hits to kill on easy, 4 on medium, 5 on hard
5606 				int maxDamage = ceil((float)targ->max_health/(3.0f+g_spskill->value));
5607 				if ( damage > maxDamage )
5608 				{
5609 					damage = maxDamage;
5610 				}
5611 			}
5612 			*/
5613 		}
5614 	}
5615 
5616 	if ( !inflictor ) {
5617 		inflictor = &g_entities[ENTITYNUM_WORLD];
5618 	}
5619 	if ( !attacker ) {
5620 		attacker = &g_entities[ENTITYNUM_WORLD];
5621 	}
5622 
5623 	// no more weakling allies!
5624 //	if ( attacker->s.number != 0 && damage >= 2 && targ->s.number != 0 && attacker->client && attacker->client->playerTeam == TEAM_PLAYER )
5625 //	{//player-helpers do only half damage to enemies
5626 //		damage = ceil((float)damage/2.0f);
5627 //	}
5628 
5629 	client = targ->client;
5630 
5631 	if ( client ) {
5632 		if ( client->noclip && !targ->s.number ) {
5633 			return;
5634 		}
5635 	}
5636 
5637 	if ( mod == MOD_GAS )
5638 	{//certain NPCs are immune
5639 		if ( G_ImmuneToGas( targ ) )
5640 		{//immune
5641 			return;
5642 		}
5643 		dflags |= DAMAGE_NO_ARMOR;
5644 	}
5645 	if ( dflags&DAMAGE_NO_DAMAGE )
5646 	{
5647 		damage = 0;
5648 	}
5649 
5650 	if ( dir == NULL )
5651 	{
5652 		dflags |= DAMAGE_NO_KNOCKBACK;
5653 	}
5654 	else
5655 	{
5656 		VectorNormalize2( dir, newDir );
5657 	}
5658 
5659 	if ( targ->s.number != 0 )
5660 	{//not the player
5661 		if ( (targ->flags&FL_GODMODE) || (targ->flags&FL_UNDYING) )
5662 		{//have god or undying on, so ignore no protection flag
5663 			dflags &= ~DAMAGE_NO_PROTECTION;
5664 		}
5665 	}
5666 
5667 	if ( client && PM_InOnGroundAnim( &client->ps ))
5668 	{
5669 		dflags |= DAMAGE_NO_KNOCKBACK;
5670 	}
5671 	if ( !attacker->s.number && targ->client && attacker->client && targ->client->playerTeam == attacker->client->playerTeam )
5672 	{//player doesn't do knockback against allies unless he kills them
5673 		dflags |= DAMAGE_DEATH_KNOCKBACK;
5674 	}
5675 
5676 	if (targ->client &&  (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT) )
5677 	{
5678 		TIMER_Set(targ, "DEMP2_StunTime", Q_irand(1000, 2000));
5679 	}
5680 
5681 	if ((client) &&
5682 		(mod==MOD_DEMP2 || mod==MOD_DEMP2_ALT) &&
5683 		(
5684 			client->NPC_class == CLASS_SABER_DROID ||
5685 			client->NPC_class == CLASS_ASSASSIN_DROID ||
5686 			client->NPC_class == CLASS_GONK ||
5687 			client->NPC_class == CLASS_MOUSE ||
5688 			client->NPC_class == CLASS_PROBE ||
5689 			client->NPC_class == CLASS_PROTOCOL ||
5690 			client->NPC_class == CLASS_R2D2 ||
5691 			client->NPC_class == CLASS_R5D2 ||
5692 			client->NPC_class == CLASS_SEEKER ||
5693 			client->NPC_class == CLASS_INTERROGATOR
5694 		)
5695 	   )
5696 	{
5697 		damage *= 7;
5698 	}
5699 
5700 	if ( client && client->NPC_class == CLASS_HAZARD_TROOPER )
5701 	{
5702 		if ( mod == MOD_SABER
5703 			&& damage>0
5704 			&& !(dflags&DAMAGE_NO_PROTECTION) )
5705 		{
5706 			damage /= 10;
5707 		}
5708 	}
5709 
5710 	if ( client
5711 		&& client->NPC_class == CLASS_GALAKMECH
5712 		&& !(dflags&DAMAGE_NO_PROTECTION) )
5713 	{//hit Galak
5714 		if ( client->ps.stats[STAT_ARMOR] > 0 )
5715 		{//shields are up
5716 			dflags &= ~DAMAGE_NO_ARMOR;//always affect armor
5717 			if ( mod == MOD_ELECTROCUTE
5718 				|| mod == MOD_DEMP2
5719 				|| mod == MOD_DEMP2_ALT )
5720 			{//shield protects us from this
5721 				damage = 0;
5722 			}
5723 		}
5724 		else
5725 		{//shields down
5726 			if ( mod == MOD_MELEE
5727 				|| (mod == MOD_CRUSH && attacker && attacker->client) )
5728 			{//Galak takes no impact damage
5729 				return;
5730 			}
5731 			if ( (dflags & DAMAGE_RADIUS)
5732 				|| mod == MOD_REPEATER_ALT
5733 				|| mod == MOD_FLECHETTE_ALT
5734 				|| mod == MOD_ROCKET
5735 				|| mod == MOD_ROCKET_ALT
5736 				|| mod == MOD_CONC
5737 				|| mod == MOD_THERMAL
5738 				|| mod == MOD_THERMAL_ALT
5739 				|| mod == MOD_DETPACK
5740 				|| mod == MOD_LASERTRIP
5741 				|| mod == MOD_LASERTRIP_ALT
5742 				|| mod == MOD_EXPLOSIVE_SPLASH
5743 				|| mod == MOD_ENERGY_SPLASH
5744 				|| mod == MOD_SABER )
5745 			{//galak without shields takes quarter damage from explosives and lightsaber
5746 				damage = ceil((float)damage/4.0f);
5747 			}
5748 		}
5749 	}
5750 
5751 	if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
5752 	{
5753 		if ( client )
5754 		{
5755 			if ( client->NPC_class == CLASS_PROTOCOL || client->NPC_class == CLASS_SEEKER ||
5756 				client->NPC_class == CLASS_R2D2 || client->NPC_class == CLASS_R5D2 ||
5757 				client->NPC_class == CLASS_MOUSE || client->NPC_class == CLASS_GONK )
5758 			{
5759 				// DEMP2 does more damage to these guys.
5760 				damage *= 2;
5761 			}
5762 			else if ( client->NPC_class == CLASS_PROBE || client->NPC_class == CLASS_INTERROGATOR ||
5763 						client->NPC_class == CLASS_MARK1 || client->NPC_class == CLASS_MARK2 || client->NPC_class == CLASS_SENTRY ||
5764 						client->NPC_class == CLASS_ATST )
5765 			{
5766 				// DEMP2 does way more damage to these guys.
5767 				damage *= 5;
5768 			}
5769 		}
5770 		else if ( targ->s.weapon == WP_TURRET )
5771 		{
5772 			damage *= 6;// more damage to turret things
5773 		}
5774 	}
5775 
5776 	if (targ
5777 		&& targ->client
5778 		&& !(dflags&DAMAGE_NO_PROTECTION)
5779 		&& !(dflags&DAMAGE_DIE_ON_IMPACT) )//falling to you death ignores force protect and force rage (but obeys godmode and undying flags)
5780 	{//force protections
5781 		//rage
5782 		if ( (targ->client->ps.forcePowersActive & (1 << FP_RAGE)))
5783 		{
5784 			damage = floor((float)damage/(float)(targ->client->ps.forcePowerLevel[FP_RAGE]*2));
5785 		}
5786 		//protect
5787 		if ( (targ->client->ps.forcePowersActive & (1 << FP_PROTECT)) )
5788 		{
5789 			/*
5790 			qboolean doSound = qfalse;
5791 			switch ( targ->client->ps.forcePowerLevel[FP_PROTECT] )
5792 			{
5793 			case FORCE_LEVEL_3:
5794 				//NOTE: purposely falls through
5795 				switch ( mod )
5796 				{
5797 				case MOD_REPEATER_ALT:
5798 				case MOD_FLECHETTE_ALT:
5799 				case MOD_ROCKET:
5800 				case MOD_ROCKET_ALT:
5801 				case MOD_CONC:
5802 				case MOD_THERMAL:
5803 				case MOD_THERMAL_ALT:
5804 				case MOD_DETPACK:
5805 				case MOD_LASERTRIP:
5806 				case MOD_LASERTRIP_ALT:
5807 				case MOD_EMPLACED:
5808 				case MOD_EXPLOSIVE:
5809 				case MOD_EXPLOSIVE_SPLASH:
5810 				case MOD_CRUSH:
5811 					doSound = (Q_irand(0,4)==0);
5812 					damage = floor((float)damage/(float)(targ->client->ps.forcePowerLevel[FP_PROTECT]-1));
5813 					break;
5814 				}
5815 			case FORCE_LEVEL_2:
5816 				//NOTE: purposely falls through
5817 				switch ( mod )
5818 				{
5819 				case MOD_SABER:
5820 				case MOD_DISRUPTOR:
5821 				case MOD_SNIPER:
5822 				case MOD_CONC_ALT:
5823 				case MOD_BOWCASTER:
5824 				case MOD_BOWCASTER_ALT:
5825 				case MOD_DEMP2:
5826 				case MOD_DEMP2_ALT:
5827 				case MOD_ENERGY:
5828 				case MOD_ENERGY_SPLASH:
5829 				case MOD_ELECTROCUTE:
5830 					doSound = (Q_irand(0,4)==0);
5831 					damage = floor((float)damage/(float)(targ->client->ps.forcePowerLevel[FP_PROTECT]));
5832 					break;
5833 				}
5834 			case FORCE_LEVEL_1:
5835 				switch ( mod )
5836 				{
5837 				case MOD_BRYAR:
5838 				case MOD_BRYAR_ALT:
5839 				case MOD_BLASTER:
5840 				case MOD_BLASTER_ALT:
5841 				case MOD_REPEATER:
5842 				case MOD_FLECHETTE:
5843 				case MOD_WATER:
5844 				case MOD_SLIME:
5845 				case MOD_LAVA:
5846 				case MOD_FALLING:
5847 					doSound = (Q_irand(0,4)==0);
5848 					damage = floor((float)damage/(float)(targ->client->ps.forcePowerLevel[FP_PROTECT]+1));
5849 					break;
5850 				}
5851 				break;
5852 			}
5853 			*/
5854 			//New way: just cut all physical damage by force level
5855 			if ( mod == MOD_FALLING
5856 				&& targ->NPC
5857 				&& (targ->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
5858 			{//if falling to your death, protect can't save you
5859 			}
5860 			else
5861 			{
5862 				qboolean doSound = qfalse;
5863 				switch ( mod )
5864 				{
5865 				case MOD_CRUSH:
5866 					if ( attacker && attacker->client )
5867 					{//need to still be crushed by AT-ST
5868 						break;
5869 					}
5870 				case MOD_REPEATER_ALT:
5871 				case MOD_FLECHETTE_ALT:
5872 				case MOD_ROCKET:
5873 				case MOD_ROCKET_ALT:
5874 				case MOD_CONC:
5875 				case MOD_THERMAL:
5876 				case MOD_THERMAL_ALT:
5877 				case MOD_DETPACK:
5878 				case MOD_LASERTRIP:
5879 				case MOD_LASERTRIP_ALT:
5880 				case MOD_EMPLACED:
5881 				case MOD_EXPLOSIVE:
5882 				case MOD_EXPLOSIVE_SPLASH:
5883 				case MOD_SABER:
5884 				case MOD_DISRUPTOR:
5885 				case MOD_SNIPER:
5886 				case MOD_CONC_ALT:
5887 				case MOD_BOWCASTER:
5888 				case MOD_BOWCASTER_ALT:
5889 				case MOD_DEMP2:
5890 				case MOD_DEMP2_ALT:
5891 				case MOD_ENERGY:
5892 				case MOD_ENERGY_SPLASH:
5893 				case MOD_ELECTROCUTE:
5894 				case MOD_BRYAR:
5895 				case MOD_BRYAR_ALT:
5896 				case MOD_BLASTER:
5897 				case MOD_BLASTER_ALT:
5898 				case MOD_REPEATER:
5899 				case MOD_FLECHETTE:
5900 				case MOD_WATER:
5901 				case MOD_SLIME:
5902 				case MOD_LAVA:
5903 				case MOD_FALLING:
5904 				case MOD_MELEE:
5905 					doSound = (qboolean)(Q_irand(0,4)==0);
5906 					switch ( targ->client->ps.forcePowerLevel[FP_PROTECT] )
5907 					{
5908 					case FORCE_LEVEL_4:
5909 						//je suis invincible!!!
5910 						if ( targ->client
5911 							&& attacker->client
5912 							&& targ->client->playerTeam == attacker->client->playerTeam
5913 							&& (!targ->NPC || !targ->NPC->charmedTime) )
5914 						{//complain, but don't turn on them
5915 							G_FriendlyFireReaction( targ, attacker, dflags );
5916 						}
5917 						return;
5918 						break;
5919 					case FORCE_LEVEL_3:
5920 						//one-tenth damage
5921 						if ( damage <= 1 )
5922 						{
5923 							damage = 0;
5924 						}
5925 						else
5926 						{
5927 							damage = ceil((float)damage*0.25f);//was 0.1f);
5928 						}
5929 						break;
5930 					case FORCE_LEVEL_2:
5931 						//half damage
5932 						if ( damage <= 1 )
5933 						{
5934 							damage = 0;
5935 						}
5936 						else
5937 						{
5938 							damage = ceil((float)damage*0.5f);
5939 						}
5940 						break;
5941 					case FORCE_LEVEL_1:
5942 						//three-quarters damage
5943 						if ( damage <= 1 )
5944 						{
5945 							damage = 0;
5946 						}
5947 						else
5948 						{
5949 							damage = ceil((float)damage*0.75f);
5950 						}
5951 						break;
5952 					}
5953 					break;
5954 				}
5955 				if ( doSound )
5956 				{
5957 					//make protect sound
5958 					G_SoundOnEnt( targ, CHAN_ITEM, "sound/weapons/force/protecthit.wav" );
5959 				}
5960 			}
5961 		}
5962 		//absorb
5963 		/*
5964 		if ( (targ->client->ps.forcePowersActive & (1 << FP_ABSORB)) )
5965 		{
5966 			if ( mod == MOD_FORCE_LIGHTNING
5967 				|| mod == MOD_FORCE_GRIP
5968 				|| mod == MOD_FORCE_DRAIN )
5969 			{
5970 				int absorbed = targ->client->ps.forcePowerLevel[FP_ABSORB]*5;
5971 				damage -= absorbed;
5972 				if ( damage < 0 )
5973 				{
5974 					absorbed += damage;
5975 					damage = 0;
5976 				}
5977 				//absorb the energy
5978 				//make absorb sound
5979 				G_SoundOnEnt( targ, CHAN_ITEM, "sound/weapons/force/absorbhit.wav" );
5980 				targ->client->ps.forcePower += absorbed;
5981 			}
5982 		}
5983 		*/
5984 	}
5985 
5986 	knockback = damage;
5987 
5988 	//Attempt to apply extra knockback
5989 	if ( dflags & DAMAGE_EXTRA_KNOCKBACK )
5990 	{
5991 		knockback *= 2;
5992 	}
5993 
5994 	if ( knockback > 200 ) {
5995 		knockback = 200;
5996 	}
5997 
5998 	if ( targ->client
5999 		&& (targ->client->ps.forcePowersActive&(1<<FP_PROTECT))
6000 		&& targ->client->ps.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_3 )
6001 	{//pretend there was no damage?
6002 		knockback = 0;
6003 	}
6004 	else if ( mod == MOD_CRUSH )
6005 	{
6006 		knockback = 0;
6007 	}
6008 	else if ( targ->flags & FL_NO_KNOCKBACK )
6009 	{
6010 		knockback = 0;
6011 	}
6012 	else if ( targ->NPC
6013 		&& 	targ->NPC->jumpState == JS_JUMPING )
6014 	{
6015 		knockback = 0;
6016 	}
6017 	else if ( attacker->s.number >= MAX_CLIENTS//an NPC fired
6018 		&& targ->client //hit a client
6019 		&& attacker->client //attacker is a client
6020 		&& targ->client->playerTeam == attacker->client->playerTeam )//on same team
6021 	{//crap, ignore knockback
6022 		knockback = 0;
6023 	}
6024 	else if ( dflags & DAMAGE_NO_KNOCKBACK )
6025 	{
6026 		knockback = 0;
6027 	}
6028 
6029 	if ( (dflags&DAMAGE_SABER_KNOCKBACK1) )
6030 	{
6031 		if ( attacker && attacker->client )
6032 		{
6033 			knockback *= attacker->client->ps.saber[0].knockbackScale;
6034 		}
6035 	}
6036 	if ( (dflags&DAMAGE_SABER_KNOCKBACK1_B2) )
6037 	{
6038 		if ( attacker && attacker->client )
6039 		{
6040 			knockback *= attacker->client->ps.saber[0].knockbackScale2;
6041 		}
6042 	}
6043 	if ( (dflags&DAMAGE_SABER_KNOCKBACK2) )
6044 	{
6045 		if ( attacker && attacker->client )
6046 		{
6047 			knockback *= attacker->client->ps.saber[1].knockbackScale;
6048 		}
6049 	}
6050 	if ( (dflags&DAMAGE_SABER_KNOCKBACK2_B2) )
6051 	{
6052 		if ( attacker && attacker->client )
6053 		{
6054 			knockback *= attacker->client->ps.saber[1].knockbackScale2;
6055 		}
6056 	}
6057 	// figure momentum add, even if the damage won't be taken
6058 	if ( knockback && !(dflags&DAMAGE_DEATH_KNOCKBACK) ) //&& targ->client
6059 	{
6060 		G_ApplyKnockback( targ, newDir, knockback );
6061 		G_CheckKnockdown( targ, attacker, newDir, dflags, mod );
6062 	}
6063 
6064 	// check for godmode, completely getting out of the damage
6065 	if ( ( (targ->flags&FL_GODMODE) || (targ->client&&targ->client->ps.powerups[PW_INVINCIBLE]>level.time) )
6066 		&& !(dflags&DAMAGE_NO_PROTECTION) )
6067 	{
6068 		if ( targ->client
6069 			&& attacker->client
6070 			&& targ->client->playerTeam == attacker->client->playerTeam
6071 			&& (!targ->NPC || !targ->NPC->charmedTime) )
6072 		{//complain, but don't turn on them
6073 			G_FriendlyFireReaction( targ, attacker, dflags );
6074 		}
6075 		return;
6076 	}
6077 
6078 	// Check for team damage
6079 	/*
6080 	if ( targ != attacker && !(dflags&DAMAGE_IGNORE_TEAM) && OnSameTeam (targ, attacker)  )
6081 	{//on same team
6082 		if ( !targ->client )
6083 		{//a non-player object should never take damage from an ent on the same team
6084 			return;
6085 		}
6086 
6087 		if ( attacker->client && attacker->client->playerTeam == targ->noDamageTeam )
6088 		{//NPC or player shot an object on his own team
6089 			return;
6090 		}
6091 
6092 		if ( attacker->s.number != 0 && targ->s.number != 0 &&//player not involved in any way in this exchange
6093 			attacker->client && targ->client &&//two NPCs
6094 			attacker->client->playerTeam == targ->client->playerTeam ) //on the same team
6095 		{//NPCs on same team don't hurt each other
6096 			return;
6097 		}
6098 
6099 		if ( targ->s.number == 0 &&//player was hit
6100 			attacker->client && targ->client &&//by an NPC
6101 			attacker->client->playerTeam == TEAM_PLAYER ) //on the same team
6102 		{
6103 			if ( attacker->enemy != targ )//by accident
6104 			{//do no damage, no armor loss, no reaction, run no scripts
6105 				return;
6106 			}
6107 		}
6108 	}
6109 	*/
6110 
6111 	// add to the attacker's hit counter
6112 	if ( attacker->client && targ != attacker && targ->health > 0 ) {
6113 		if ( OnSameTeam( targ, attacker ) ) {
6114 //			attacker->client->ps.persistant[PERS_HITS] -= damage;
6115 		} else {
6116 //			attacker->client->ps.persistant[PERS_HITS] += damage;
6117 		}
6118 	}
6119 
6120 	take = damage;
6121 
6122 	//FIXME: Do not use this method of difficulty changing
6123 	// Scale the amount of damage given to the player based on the skill setting
6124 	/*
6125 	if ( targ->s.number == 0 && targ != attacker )
6126 	{
6127 		take *= ( g_spskill->integer + 1) * 0.75;
6128 	}
6129 
6130 	if ( take < 1 ) {
6131 		take = 1;
6132 	}
6133 	*/
6134 	if ( client )
6135 	{
6136 		//don't lose armor if on same team
6137 		// save some from armor
6138 		asave = CheckArmor (targ, take, dflags, mod);
6139 		if ( !asave )
6140 		{//nothing was absorbed (or just ran out?)
6141 		}
6142 		else if ( targ->client->NPC_class != CLASS_VEHICLE )
6143 		{//vehicles don't have personal shields
6144 			targ->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
6145 			if ( targ->client->ps.stats[STAT_ARMOR] <= 0 )
6146 			{//all out of armor
6147 				//remove Galak's shield
6148 				targ->client->ps.powerups[PW_BATTLESUIT] = 0;
6149 			}
6150 		}
6151 
6152 		if (mod==MOD_SLIME || mod==MOD_LAVA)
6153 		{
6154 			// Hazard Troopers Don't Mind Acid Rain
6155 			if (targ->client->NPC_class == CLASS_HAZARD_TROOPER
6156 				&& !(dflags&DAMAGE_NO_PROTECTION) )
6157 			{
6158 				take = 0;
6159 			}
6160 
6161 			if (mod==MOD_SLIME)
6162 			{
6163   				trace_t		testTrace;
6164 				vec3_t		testDirection;
6165 				vec3_t		testStartPos;
6166 				vec3_t		testEndPos;
6167 				//int			numPuffs = Q_irand(1, 2);
6168 
6169 				//for (int i=0; i<numPuffs; i++)
6170 				{
6171  					testDirection[0] = (Q_flrand(0.0f, 1.0f) * 0.5f) - 0.25f;
6172 					testDirection[1] = (Q_flrand(0.0f, 1.0f) * 0.5f) - 0.25f;
6173 					testDirection[2] = 1.0f;
6174 					VectorMA(targ->currentOrigin, 60.0f, testDirection, testStartPos);
6175 					VectorCopy(targ->currentOrigin, testEndPos);
6176 					testEndPos[0] += (Q_flrand(0.0f, 1.0f) * 8.0f) - 4.0f;
6177 					testEndPos[1] += (Q_flrand(0.0f, 1.0f) * 8.0f) - 4.0f;
6178 					testEndPos[2] += (Q_flrand(0.0f, 1.0f) * 8.0f);
6179 
6180 					gi.trace (&testTrace, testStartPos, NULL, NULL, testEndPos, ENTITYNUM_NONE, MASK_SHOT, G2_COLLIDE, 0);
6181 
6182 					if (!testTrace.startsolid &&
6183 						!testTrace.allsolid &&
6184 						testTrace.entityNum==targ->s.number &&
6185 						testTrace.G2CollisionMap[0].mEntityNum!=-1)
6186 					{
6187 						G_PlayEffect( "world/acid_fizz", testTrace.G2CollisionMap[0].mCollisionPosition );
6188 					}
6189 //					CG_DrawEdge(testStartPos,	testEndPos,	EDGE_IMPACT_POSSIBLE);
6190 					float chanceOfFizz = gi.WE_GetChanceOfSaberFizz();
6191 					TIMER_Set(targ, "AcidPainDebounce", 200 + (10000.0f * Q_flrand(0.0f, 1.0f) * chanceOfFizz));
6192 					hitLoc = HL_CHEST;
6193 				}
6194 			}
6195 		}
6196 
6197 		take -= asave;
6198 
6199 		if ( targ->client->NPC_class == CLASS_VEHICLE )
6200 		{
6201 			if ( targ->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
6202 			{
6203 				//((CVehicleNPC *)targ->NPC)->m_ulFlags |= CVehicleNPC::VEH_BUCKING;
6204 			}
6205 
6206 			if ( (damage > 0) &&											// Actually took some damage
6207 				 (mod!=MOD_SABER) &&										// and damage didn't come from a saber
6208 				 (targ->m_pVehicle->m_pVehicleInfo->type==VH_SPEEDER) &&	// and is a speeder
6209 			//	 (targ->client->ps.speed > 30.0f) &&						// and is moving
6210 				 (attacker) &&												// and there is an attacker
6211 				 (attacker->client) &&										// who is a client
6212 				 (attacker->s.number<MAX_CLIENTS) &&						// who is the player
6213 				 (G_IsRidingVehicle(attacker))								// who is riding a bike
6214 				 )
6215 			{
6216 				vec3_t	vehFwd;
6217 				vec3_t	actorFwd;
6218 				AngleVectors(targ->currentAngles, actorFwd, 0, 0);
6219 
6220 
6221 				Vehicle_t*	pVeh = G_IsRidingVehicle(attacker);
6222 				VectorCopy(pVeh->m_pParentEntity->client->ps.velocity, vehFwd);
6223 				VectorNormalize(vehFwd);
6224 
6225 				if (DotProduct(vehFwd, actorFwd)>0.5)
6226 				{
6227 					damage *= 10.0f;
6228 				}
6229 			}
6230 
6231 			if ( (damage > 0) &&												// Actually took some damage
6232 				 (mod==MOD_SABER) &&										// If Attacked By A Saber
6233 				 (targ->m_pVehicle->m_pVehicleInfo->type==VH_SPEEDER) &&	// and is a speeder
6234 				!(targ->m_pVehicle->m_ulFlags & VEH_OUTOFCONTROL) &&			// and is not already spinning
6235 				 (targ->client->ps.speed > 30.0f) &&						// and is moving
6236 				 (attacker==inflictor || Q_irand(0, 30)==0)					// and EITHER saber is held, or 1 in 30 chance of hitting when thrown
6237 				)
6238 			{
6239 				Vehicle_t*	pVeh = targ->m_pVehicle;
6240 				gentity_t*	parent = pVeh->m_pParentEntity;
6241 				float		CurSpeed = VectorLength(parent->client->ps.velocity);
6242 				pVeh->m_iArmor	= 0;	// Remove all remaining Armor
6243 				pVeh->m_pVehicleInfo->StartDeathDelay(pVeh, 10000);
6244 				pVeh->m_ulFlags |= (VEH_OUTOFCONTROL|VEH_SPINNING);
6245 				VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3);
6246 				if (CurSpeed<pVeh->m_pVehicleInfo->speedMax)
6247 				{
6248 					VectorNormalize(parent->pos3);
6249 					if (CurSpeed<pVeh->m_pVehicleInfo->speedMax)
6250 					{
6251 						VectorNormalize(parent->pos3);
6252 						if (fabsf(parent->pos3[2])<0.25f)
6253 						{
6254 							VectorScale(parent->pos3, (pVeh->m_pVehicleInfo->speedMax * 1.25f), parent->pos3);
6255 						}
6256 						else
6257 						{
6258 							VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3);
6259 						}
6260 					}
6261 				}
6262 
6263 
6264 				// TODO: Play Huge Spark Effect & Start Rolling Sound
6265 	 			if (attacker==inflictor && (!G_IsRidingVehicle(attacker) || Q_irand(0, 3)==0))
6266 				{
6267 		 			attacker->lastEnemy = targ;
6268 					G_StartMatrixEffect(attacker, MEF_LOOK_AT_ENEMY|MEF_NO_RANGEVAR|MEF_NO_VERTBOB|MEF_NO_SPIN, 1000);
6269 					if (!G_IsRidingVehicle(attacker))
6270 					{
6271 						G_StartRoll(attacker, (Q_irand(0,1)==0)?(BOTH_ROLL_L):(BOTH_ROLL_R));
6272 					}
6273 				}
6274 
6275 				if (targ->m_pVehicle->m_pPilot && targ->m_pVehicle->m_pPilot->s.number>=MAX_CLIENTS)
6276 				{
6277 					G_SoundOnEnt(targ->m_pVehicle->m_pPilot, CHAN_VOICE, "*falling1.wav" );
6278 				}
6279 
6280 
6281 
6282 				// DISMEMBER THE FRONT PART OF THE MODEL
6283 				{
6284 					trace_t	trace;
6285 
6286 					gentity_t *limb = G_Spawn();
6287 
6288 
6289 					// Setup Basic Limb Entity Properties
6290 					//------------------------------------
6291 					limb->s.radius		= 60;
6292 					limb->s.eType		= ET_THINKER;
6293 					limb->s.eFlags	   |= EF_BOUNCE_HALF;
6294 					limb->classname		= "limb";
6295 					limb->owner			= targ;
6296 					limb->enemy			= targ->enemy;
6297 					limb->svFlags		= SVF_USE_CURRENT_ORIGIN;
6298 					limb->playerModel	= 0;
6299 					limb->clipmask		= MASK_SOLID;
6300 					limb->contents		= CONTENTS_CORPSE;
6301 					limb->e_clThinkFunc = clThinkF_CG_Limb;
6302 					limb->e_ThinkFunc	= thinkF_LimbThink;
6303 					limb->nextthink		= level.time + FRAMETIME;
6304 					limb->physicsBounce = 0.2f;
6305 					limb->craniumBone	= targ->craniumBone;
6306 					limb->cervicalBone	= targ->cervicalBone;
6307 					limb->thoracicBone	= targ->thoracicBone;
6308 					limb->upperLumbarBone = targ->upperLumbarBone;
6309 					limb->lowerLumbarBone = targ->lowerLumbarBone;
6310 					limb->hipsBone		= targ->hipsBone;
6311 					limb->rootBone		= targ->rootBone;
6312 
6313 
6314 					// Calculate The Location Of The New Limb
6315 					//----------------------------------------
6316 					G_SetOrigin( limb, targ->currentOrigin );
6317 
6318 					VectorCopy( targ->currentOrigin, limb->s.pos.trBase );
6319 					VectorSet( limb->mins, -3.0f, -3.0f, -6.0f );
6320 					VectorSet( limb->maxs, 3.0f, 3.0f, 6.0f );
6321 					VectorCopy( targ->s.modelScale, limb->s.modelScale );
6322 
6323 
6324 
6325 
6326 					//copy the g2 instance of the victim into the limb
6327 					//-------------------------------------------------
6328  					gi.G2API_CopyGhoul2Instance(targ->ghoul2, limb->ghoul2, -1);
6329 					gi.G2API_SetRootSurface(limb->ghoul2, limb->playerModel, "lfront");
6330 					gi.G2API_SetSurfaceOnOff(&targ->ghoul2[targ->playerModel], "lfront", TURN_OFF);
6331 					animation_t *animations = level.knownAnimFileSets[targ->client->clientInfo.animFileIndex].animations;
6332 
6333 					//play the proper dismember anim on the limb
6334 					gi.G2API_SetBoneAnim(&limb->ghoul2[limb->playerModel], 0, animations[BOTH_A1_BL_TR].firstFrame,
6335 							animations[BOTH_A1_BL_TR].numFrames + animations[BOTH_A1_BL_TR].firstFrame,
6336 							BONE_ANIM_OVERRIDE_FREEZE, 1, level.time, -1, -1 );
6337 
6338 
6339 					// Check For Start In Solid
6340 					//--------------------------
6341 					gi.linkentity( limb );
6342 					gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask, (EG2_Collision)0, 0 );
6343 					if ( trace.startsolid )
6344 					{
6345 						limb->s.pos.trBase[2] -= limb->mins[2];
6346 						gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask, (EG2_Collision)0, 0 );
6347 						if ( trace.startsolid )
6348 						{
6349 							limb->s.pos.trBase[2] += limb->mins[2];
6350 							gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask, (EG2_Collision)0, 0 );
6351 
6352 						}
6353 					}
6354 
6355 					// If Started In Solid, Remove
6356 					//-----------------------------
6357 					if ( trace.startsolid )
6358 					{
6359 						G_FreeEntity( limb );
6360 					}
6361 
6362 					// Otherwise, Send It Flying
6363 					//---------------------------
6364 					else
6365 					{
6366 						VectorCopy( limb->s.pos.trBase, limb->currentOrigin );
6367 						VectorScale( targ->client->ps.velocity, 1.0f, limb->s.pos.trDelta );
6368 						limb->s.pos.trType		= TR_GRAVITY;
6369 						limb->s.pos.trTime		= level.time;
6370 
6371 						VectorCopy( targ->currentAngles, limb->s.apos.trBase );
6372 						VectorClear( limb->s.apos.trDelta );
6373 						limb->s.apos.trTime		= level.time;
6374 						limb->s.apos.trType		= TR_LINEAR;
6375 						limb->s.apos.trDelta[0] = Q_irand( -300, 300 );
6376 						limb->s.apos.trDelta[1] = Q_irand( -800, 800 );
6377 
6378 						gi.linkentity( limb );
6379 					}
6380 				}
6381 			}
6382 
6383 			targ->m_pVehicle->m_iShields = targ->client->ps.stats[STAT_ARMOR];
6384 			targ->m_pVehicle->m_iArmor -= take;
6385 			if ( targ->m_pVehicle->m_iArmor < 0 )
6386 			{
6387 				targ->m_pVehicle->m_iArmor = 0;
6388 			}
6389 			if ( ( targ->m_pVehicle->m_iArmor <= 0 )
6390 				&& targ->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
6391 			{//vehicle all out of armor
6392 				Vehicle_t *pVeh = targ->m_pVehicle;
6393 				if (dflags&DAMAGE_IMPACT_DIE)
6394 				{
6395 					// kill it now
6396 					pVeh->m_pVehicleInfo->StartDeathDelay( pVeh, -1/* -1 causes instant death */ );
6397 				}
6398 				else
6399 				{
6400 					if ( pVeh->m_iDieTime == 0 )
6401 					{//just start the flaming effect and explosion delay, if it's not going already...
6402 						pVeh->m_pVehicleInfo->StartDeathDelay( pVeh, Q_irand( 4000, 5500 ) );
6403 					}
6404 				}
6405 			}
6406 			else if (targ->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL)
6407 			{
6408 				take = 0;
6409 			}
6410 		}
6411 	}
6412 	if ( !(dflags&DAMAGE_NO_HIT_LOC) || !(dflags&DAMAGE_RADIUS))
6413 	{
6414 		if ( !G_NonLocationSpecificDamage( mod ) )
6415 		{//certain kinds of damage don't care about hitlocation
6416 			take = ceil( (float)take*damageModifier[hitLoc] );
6417 		}
6418 	}
6419 
6420 	if ( g_debugDamage->integer ) {
6421 		gi.Printf( "[%d]client:%i health:%i damage:%i armor:%i hitloc:%s\n", level.time, targ->s.number, targ->health, take, asave, hitLocName[hitLoc] );
6422 	}
6423 
6424 	// add to the damage inflicted on a player this frame
6425 	// the total will be turned into screen blends and view angle kicks
6426 	// at the end of the frame
6427 	if ( client ) {
6428 		client->ps.persistant[PERS_ATTACKER] = attacker->s.number;	//attack can be the world ent
6429 		client->damage_armor += asave;
6430 		client->damage_blood += take;
6431 		if ( dir ) {	//can't check newdir since it's local, newdir is dir normalized
6432 			VectorCopy ( newDir, client->damage_from );
6433 			client->damage_fromWorld = false;
6434 		} else {
6435 			VectorCopy ( targ->currentOrigin, client->damage_from );
6436 			client->damage_fromWorld = true;
6437 		}
6438 	}
6439 
6440 	// do the damage
6441 	if ( targ->health <= 0 )
6442 	{
6443 		alreadyDead = qtrue;
6444 	}
6445 
6446 	// Undying If:
6447 	//--------------------------------------------------------------------------
6448 	qboolean targUndying = (qboolean)(
6449 		!alreadyDead &&
6450 		!(dflags & DAMAGE_NO_PROTECTION) &&
6451 		((targ->flags&FL_UNDYING) ||
6452 			(dflags&DAMAGE_NO_KILL) ||
6453 			((targ->client) &&
6454 				(targ->client->ps.forcePowersActive & (1 << FP_RAGE)) &&
6455 				!(dflags&DAMAGE_NO_PROTECTION) &&
6456 				!(dflags&DAMAGE_DIE_ON_IMPACT))));
6457 
6458 	if ( targ->client
6459 		&& targ->client->NPC_class == CLASS_WAMPA
6460 		&& targ->count
6461 		&& take >= targ->health )
6462 	{//wampa holding someone, don't die unless you can release them!
6463 		qboolean removeArm = qfalse;
6464 		if ( targ->activator
6465 			&& attacker == targ->activator
6466 			&& mod == MOD_SABER )
6467 		{
6468 			removeArm = qtrue;
6469 		}
6470 		if ( Wampa_CheckDropVictim( targ, qtrue ) )
6471 		{//released our victim
6472 			if ( removeArm )
6473 			{
6474 				targ->client->dismembered = false;
6475 				//FIXME: the limb should just disappear, cuz I ate it
6476 				G_DoDismemberment( targ, targ->currentOrigin, MOD_SABER, 1000, HL_ARM_RT, qtrue );
6477 			}
6478 		}
6479 		else
6480 		{//couldn't release him
6481 			targUndying = qtrue;
6482 		}
6483 	}
6484 
6485 	if ( attacker && attacker->client && !attacker->s.number )
6486 	{
6487 		if ( !alreadyDead )
6488 		{
6489 			int add;
6490 			if ( take > targ->health )
6491 			{
6492 				add = targ->health;
6493 			}
6494 			else
6495 			{
6496 				add = take;
6497 			}
6498 			add += asave;
6499 			add = ceil(add/10.0f);
6500 			if ( attacker != targ )
6501 			{
6502 				G_TrackWeaponUsage( attacker, inflictor, add, mod );
6503 			}
6504 		}
6505 	}
6506 
6507 	if ( take || (dflags&DAMAGE_NO_DAMAGE) )
6508 	{
6509 		if ( !targ->client || !attacker->client )
6510 		{
6511 			targ->health = targ->health - take;
6512 			if (targ->health < 0)
6513 			{
6514 				targ->health = 0;
6515 			}
6516 			if ( targUndying  )
6517 			{
6518 				if(targ->health < 1)
6519 				{
6520 					G_ActivateBehavior( targ, BSET_DEATH );
6521 					targ->health = 1;
6522 				}
6523 			}
6524 		}
6525 		else
6526 		{//two clients
6527 			team_t		targTeam = TEAM_FREE;
6528 			team_t		attackerTeam = TEAM_FREE;
6529 
6530 			if ( player->client->ps.viewEntity && targ->s.number == player->client->ps.viewEntity )
6531 			{
6532 				targTeam = player->client->playerTeam;
6533 			}
6534 			else if ( targ->client ) {
6535 				targTeam = targ->client->playerTeam;
6536 			}
6537 			else {
6538 				targTeam = targ->noDamageTeam;
6539 			}
6540 		//	if ( targTeam == TEAM_DISGUISE ) {
6541 		//		targTeam = TEAM_PLAYER;
6542 		//	}
6543 			if ( player->client->ps.viewEntity && attacker->s.number == player->client->ps.viewEntity )
6544 			{
6545 				attackerTeam = player->client->playerTeam;
6546 			}
6547 			else if ( attacker->client ) {
6548 				attackerTeam = attacker->client->playerTeam;
6549 			}
6550 			else {
6551 				attackerTeam = attacker->noDamageTeam;
6552 			}
6553 		//	if ( attackerTeam == TEAM_DISGUISE ) {
6554 		//		attackerTeam = TEAM_PLAYER;
6555 		//	}
6556 
6557 			if ( targTeam != attackerTeam
6558 				|| (targ->s.number < MAX_CLIENTS && targTeam == TEAM_FREE)//evil player hit
6559 				|| (attacker && attacker->s.number < MAX_CLIENTS && attackerTeam == TEAM_FREE) )//evil player attacked
6560 			{//on opposite team
6561 				targ->health = targ->health - take;
6562 
6563 				//MCG - Falling should never kill player- only if a trigger_hurt does so.
6564 				if ( mod == MOD_FALLING && targ->s.number == 0 && targ->health < 1 )
6565 				{
6566 					targ->health = 1;
6567 				}
6568 				else if (targ->health < 0)
6569 				{
6570 					targ->health = 0;
6571 				}
6572 
6573 				if (targUndying)
6574 				{
6575 					if ( targ->health < 1 )
6576 					{
6577 						if ( targ->NPC == NULL || !(targ->NPC->aiFlags&NPCAI_ROSH) || !Rosh_TwinPresent( targ ) )
6578 						{//NOTE: Rosh won't run his deathscript until he doesn't have the twins to heal him
6579 							G_ActivateBehavior( targ, BSET_DEATH );
6580 						}
6581 						targ->health = 1;
6582 					}
6583 				}
6584 				else if ( targ->health < 1 && attacker->client )
6585 				{	// The player or NPC just killed an enemy so increment the kills counter
6586 					attacker->client->ps.persistant[PERS_ENEMIES_KILLED]++;
6587 				}
6588 			}
6589 			else if ( targTeam == TEAM_PLAYER )
6590 			{//on the same team, and target is an ally
6591 				qboolean takeDamage = qtrue;
6592 				qboolean yellAtAttacker = qtrue;
6593 
6594 				//1) player doesn't take damage from teammates unless they're angry at him
6595 				if ( targ->s.number == 0 )
6596 				{//the player
6597 					if ( attacker->enemy != targ && attacker != targ )
6598 					{//an NPC shot the player by accident
6599 						takeDamage = qfalse;
6600 					}
6601 				}
6602 				//2) NPCs don't take any damage from player during combat
6603 				else
6604 				{//an NPC
6605 					if ( ((dflags & DAMAGE_RADIUS)) && !(dflags&DAMAGE_IGNORE_TEAM) )
6606 					{//An NPC got hit by player and this is during combat or it was slash damage
6607 						//NOTE: though it's not realistic to have teammates not take splash damage,
6608 						//		even when not in combat, it feels really bad to have them able to
6609 						//		actually be killed by the player's splash damage
6610 						takeDamage = qfalse;
6611 					}
6612 
6613 					if ( (dflags & DAMAGE_RADIUS) )
6614 					{//you're fighting and it's just radius damage, so don't even mention it
6615 						yellAtAttacker = qfalse;
6616 					}
6617 				}
6618 
6619 				if ( takeDamage )
6620 				{
6621 					targ->health = targ->health - take;
6622 					if ( !alreadyDead && ((((targ->flags&FL_UNDYING)||targ->client->ps.forcePowersActive & (1 << FP_RAGE)) && !(dflags&DAMAGE_NO_PROTECTION) && attacker->s.number != 0) || (dflags&DAMAGE_NO_KILL) ) )
6623 					{//guy is marked undying and we're not the player or we're in combat
6624 						if ( targ->health < 1 )
6625 						{
6626 							G_ActivateBehavior( targ, BSET_DEATH );
6627 
6628 							targ->health = 1;
6629 						}
6630 					}
6631 					else if ( !alreadyDead && ((((targ->flags&FL_UNDYING)||targ->client->ps.forcePowersActive & (1 << FP_RAGE)) && !(dflags&DAMAGE_NO_PROTECTION) && !attacker->s.number && !targ->s.number) || (dflags&DAMAGE_NO_KILL)) )
6632 					{// player is undying and he's attacking himself, don't let him die
6633 						if ( targ->health < 1 )
6634 						{
6635 							G_ActivateBehavior( targ, BSET_DEATH );
6636 
6637 							targ->health = 1;
6638 						}
6639 					}
6640 					else if ( targ->health < 0 )
6641 					{
6642 						targ->health = 0;
6643 						if ( attacker->s.number == 0 && targ->NPC )
6644 						{
6645 							targ->NPC->scriptFlags |= SCF_FFDEATH;
6646 						}
6647 					}
6648 				}
6649 
6650 				if ( yellAtAttacker )
6651 				{
6652 					if ( !targ->NPC || !targ->NPC->charmedTime )
6653 					{
6654 						G_FriendlyFireReaction( targ, attacker, dflags );
6655 					}
6656 				}
6657 			}
6658 			else
6659 			{
6660 
6661 			}
6662 		}
6663 
6664 		if ( targ->client ) {
6665 			targ->client->ps.stats[STAT_HEALTH] = targ->health;
6666 			g_lastClientDamaged = targ;
6667 		}
6668 
6669 		//TEMP HACK FOR PLAYER LOOK AT ENEMY CODE
6670 		//FIXME: move this to a player pain func?
6671 		if ( targ->s.number == 0 )
6672 		{
6673 			if ( !targ->enemy //player does not have an enemy yet
6674 				|| targ->enemy->s.weapon != WP_SABER //or player's enemy is not a jedi
6675 				|| attacker->s.weapon == WP_SABER )//and attacker is a jedi
6676 				//keep enemy jedi over shooters
6677 			{
6678 				if ( attacker->enemy == targ || !OnSameTeam( targ, attacker ) )
6679 				{//don't set player's enemy to teammates that hit him by accident
6680 					targ->enemy = attacker;
6681 				}
6682 				NPC_SetLookTarget( targ, attacker->s.number, level.time+1000 );
6683 			}
6684 		}
6685 		else if ( attacker->s.number == 0 && (!targ->NPC || !targ->NPC->timeOfDeath) && (mod == MOD_SABER || attacker->s.weapon != WP_SABER || !attacker->enemy || attacker->enemy->s.weapon != WP_SABER) )//keep enemy jedi over shooters
6686 		{//this looks dumb when they're on the ground and you keep hitting them, so only do this when first kill them
6687 			if ( !OnSameTeam( targ, attacker ) )
6688 			{//don't set player's enemy to teammates that he hits by accident
6689 				attacker->enemy = targ;
6690 			}
6691 			NPC_SetLookTarget( attacker, targ->s.number, level.time+1000 );
6692 		}
6693 		//TEMP HACK FOR PLAYER LOOK AT ENEMY CODE
6694 
6695 		//add up the damage to the location
6696 		if ( targ->client )
6697 		{
6698 			if ( targ->locationDamage[hitLoc] < Q3_INFINITE )
6699 			{
6700 				targ->locationDamage[hitLoc] += take;
6701 			}
6702 		}
6703 
6704 
6705 		if ( targ->health > 0 && targ->NPC && targ->NPC->surrenderTime > level.time )
6706 		{//he was surrendering, goes down with one hit
6707 			if (!targ->client || targ->client->NPC_class!=CLASS_BOBAFETT)
6708 			{
6709 				targ->health = 0;
6710 			}
6711 		}
6712 
6713 		if ( targ->health <= 0 )
6714 		{
6715 			if ( knockback && (dflags&DAMAGE_DEATH_KNOCKBACK) )//&& targ->client
6716 			{//only do knockback on death
6717 				if ( mod == MOD_FLECHETTE )
6718 				{//special case because this is shotgun-ish damage, we need to multiply the knockback
6719 					knockback *= 12;//*6 for 6 flechette shots
6720 				}
6721 				G_ApplyKnockback( targ, newDir, knockback );
6722 			}
6723 
6724 			/*
6725 			if ( client )
6726 				targ->flags |= FL_NO_KNOCKBACK;
6727 			*/
6728 
6729 			if (targ->health < -999)
6730 				targ->health = -999;
6731 
6732 			// If we are a breaking glass brush, store the damage point so we can do cool things with it.
6733 			if ( targ->svFlags & SVF_GLASS_BRUSH )
6734 			{
6735 				VectorCopy( point, targ->pos1 );
6736 				VectorCopy( dir, targ->pos2 );
6737 			}
6738 			if ( targ->client )
6739 			{//HACK
6740 				if ( point )
6741 				{
6742 					VectorCopy( point, targ->pos1 );
6743 				}
6744 				else
6745 				{
6746 					VectorCopy( targ->currentOrigin, targ->pos1 );
6747 				}
6748 			}
6749 			if ( !alreadyDead && !targ->enemy )
6750 			{//just killed and didn't have an enemy before
6751 				targ->enemy = attacker;
6752 			}
6753 
6754 			GEntity_DieFunc( targ, inflictor, attacker, take, mod, dflags, hitLoc );
6755 		}
6756 		else
6757 		{
6758 			GEntity_PainFunc( targ, inflictor, attacker, point, take, mod, hitLoc );
6759 			if ( targ->s.number == 0 )
6760 			{//player run painscript
6761 				G_ActivateBehavior( targ, BSET_PAIN );
6762 				if ( targ->health <= 25 )
6763 				{
6764 					G_ActivateBehavior( targ, BSET_FLEE );
6765 				}
6766 			}
6767 		}
6768 	}
6769 }
6770 
6771 
6772 /*
6773 ============
6774 CanDamage
6775 
6776 Returns qtrue if the inflictor can directly damage the target.  Used for
6777 explosions and melee attacks.
6778 ============
6779 */
CanDamage(gentity_t * targ,const vec3_t origin)6780 qboolean CanDamage (gentity_t *targ, const vec3_t origin) {
6781 	vec3_t	dest;
6782 	trace_t	tr;
6783 	vec3_t	midpoint;
6784 	qboolean cantHitEnt = qtrue;
6785 
6786 	if ( (targ->contents&MASK_SOLID) )
6787 	{//can hit it
6788 		if ( targ->s.solid == SOLID_BMODEL )
6789 		{//but only if it's a brushmodel
6790 			cantHitEnt = qfalse;
6791 		}
6792 	}
6793 
6794 	// use the midpoint of the bounds instead of the origin, because
6795 	// bmodels may have their origin at 0,0,0
6796 	VectorAdd (targ->absmin, targ->absmax, midpoint);
6797 	VectorScale (midpoint, 0.5, midpoint);
6798 
6799 	VectorCopy (midpoint, dest);
6800 	/*
6801 	vec3_t blah;
6802 	VectorCopy( origin, blah);
6803 	G_DebugLine(blah, dest, 5000, 0x0000ff, qtrue );
6804 	*/
6805 	gi.trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, (EG2_Collision)0, 0);
6806 	if (( tr.fraction == 1.0 && cantHitEnt) || tr.entityNum == targ->s.number ) // if we also test the entitynum's we can bust up bbrushes better!
6807 		return qtrue;
6808 
6809 	// this should probably check in the plane of projection,
6810 	// rather than in world coordinate, and also include Z
6811 	VectorCopy (midpoint, dest);
6812 	dest[0] += 15.0;
6813 	dest[1] += 15.0;
6814 	gi.trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, (EG2_Collision)0, 0);
6815 	if (( tr.fraction == 1.0 && cantHitEnt) || tr.entityNum == targ->s.number )
6816 		return qtrue;
6817 
6818 	VectorCopy (midpoint, dest);
6819 	dest[0] += 15.0;
6820 	dest[1] -= 15.0;
6821 	gi.trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, (EG2_Collision)0, 0);
6822 	if (( tr.fraction == 1.0 && cantHitEnt) || tr.entityNum == targ->s.number )
6823 		return qtrue;
6824 
6825 	VectorCopy (midpoint, dest);
6826 	dest[0] -= 15.0;
6827 	dest[1] += 15.0;
6828 	gi.trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, (EG2_Collision)0, 0);
6829 	if (( tr.fraction == 1.0 && cantHitEnt) || tr.entityNum == targ->s.number )
6830 		return qtrue;
6831 
6832 	VectorCopy (midpoint, dest);
6833 	dest[0] -= 15.0;
6834 	dest[1] -= 15.0;
6835 	gi.trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, (EG2_Collision)0, 0);
6836 	if (( tr.fraction == 1.0 && cantHitEnt) || tr.entityNum == targ->s.number )
6837 		return qtrue;
6838 
6839 
6840 	return qfalse;
6841 }
6842 
6843 extern	void	Boba_DustFallNear(const vec3_t origin, int dustcount);
6844 extern	void	G_GetMassAndVelocityForEnt( gentity_t *ent, float *mass, vec3_t velocity );
6845 /*
6846 ============
6847 G_RadiusDamage
6848 ============
6849 */
G_RadiusDamage(const vec3_t origin,gentity_t * attacker,float damage,float radius,gentity_t * ignore,int mod)6850 void G_RadiusDamage ( const vec3_t origin, gentity_t *attacker, float damage, float radius,
6851 					 gentity_t *ignore, int mod) {
6852 	float		points, dist;
6853 	gentity_t	*ent;
6854 	gentity_t	*entityList[MAX_GENTITIES];
6855 	int			numListedEntities;
6856 	vec3_t		mins, maxs;
6857 	vec3_t		v;
6858 	vec3_t		dir;
6859 	int			i, e;
6860 	int			dFlags = DAMAGE_RADIUS;
6861 
6862 	if ( radius < 1 ) {
6863 		radius = 1;
6864 	}
6865 
6866 	for ( i = 0 ; i < 3 ; i++ ) {
6867 		mins[i] = origin[i] - radius;
6868 		maxs[i] = origin[i] + radius;
6869 	}
6870 
6871 	if (mod==MOD_ROCKET)
6872 	{
6873 		Boba_DustFallNear(origin, 10);
6874 	}
6875 
6876 	if ( mod == MOD_GAS )
6877 	{
6878 		dFlags |= DAMAGE_NO_KNOCKBACK;
6879 	}
6880 
6881 	numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
6882 
6883 	for ( e = 0 ; e < numListedEntities ; e++ ) {
6884 		ent = entityList[ e ];
6885 
6886 		if ( ent == ignore )
6887 			continue;
6888 		if ( !ent->takedamage )
6889 			continue;
6890 		if ( !ent->contents )
6891 			continue;
6892 
6893 		// find the distance from the edge of the bounding box
6894 		for ( i = 0 ; i < 3 ; i++ ) {
6895 			if ( origin[i] < ent->absmin[i] ) {
6896 				v[i] = ent->absmin[i] - origin[i];
6897 			} else if ( origin[i] > ent->absmax[i] ) {
6898 				v[i] = origin[i] - ent->absmax[i];
6899 			} else {
6900 				v[i] = 0;
6901 			}
6902 		}
6903 
6904 		dist = VectorLength( v );
6905 		if ( dist >= radius ) {
6906 			continue;
6907 		}
6908 
6909 		points = damage * ( 1.0 - dist / radius );
6910 
6911 		// Lessen damage to vehicles that are moving away from the explosion
6912 		if (ent->client && (ent->client->NPC_class==CLASS_VEHICLE || G_IsRidingVehicle(ent)))
6913 		{
6914 			gentity_t*	bike = ent;
6915 
6916 			if (G_IsRidingVehicle(ent) && ent->owner)
6917 			{
6918 				bike = ent->owner;
6919 			}
6920 
6921 			vec3_t	vehMoveDirection;
6922 			float	vehMoveSpeed;
6923 
6924 			vec3_t	explosionDirection;
6925 			float	explosionDirectionSimilarity;
6926 
6927 			float	mass;
6928 			G_GetMassAndVelocityForEnt( bike, &mass, vehMoveDirection );
6929 			vehMoveSpeed		= VectorNormalize(vehMoveDirection);
6930 			if (vehMoveSpeed>300.0f)
6931 			{
6932 				VectorSubtract(bike->currentOrigin, origin, explosionDirection);
6933 				VectorNormalize(explosionDirection);
6934 
6935 				explosionDirectionSimilarity = DotProduct(vehMoveDirection, explosionDirection);
6936 				if (explosionDirectionSimilarity>0.0f)
6937 				{
6938 					points *= (1.0f - explosionDirectionSimilarity);
6939 				}
6940 			}
6941 		}
6942 
6943 		if (CanDamage (ent, origin))
6944 		{//FIXME: still do a little damage in in PVS and close?
6945 			if ( ent->svFlags & (SVF_GLASS_BRUSH|SVF_BBRUSH) )
6946 			{
6947 				VectorAdd( ent->absmin, ent->absmax, v );
6948 				VectorScale( v, 0.5f, v );
6949 			}
6950 			else
6951 			{
6952 				VectorCopy( ent->currentOrigin, v );
6953 			}
6954 
6955 			VectorSubtract( v, origin, dir);
6956 			// push the center of mass higher than the origin so players
6957 			// get knocked into the air more
6958 			dir[2] += 24;
6959 
6960 			if ( ent->svFlags & SVF_GLASS_BRUSH )
6961 			{
6962 				if ( points > 1.0f )
6963 				{
6964 					// we want to cap this at some point, otherwise it just gets crazy
6965 					if ( points > 6.0f )
6966 					{
6967 						VectorScale( dir, 6.0f, dir );
6968 					}
6969 					else
6970 					{
6971 						VectorScale( dir, points, dir );
6972 					}
6973 				}
6974 
6975 				ent->splashRadius = radius;// * ( 1.0 - dist / radius );
6976 			}
6977 
6978 			G_Damage (ent, NULL, attacker, dir, origin, (int)points, dFlags, mod);
6979 		}
6980 	}
6981 }
6982