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 "b_local.h"
27 #include "bg_saga.h"
28 
29 extern int G_ShipSurfaceForSurfName( const char *surfaceName );
30 extern qboolean G_FlyVehicleDestroySurface( gentity_t *veh, int surface );
31 extern void G_VehicleSetDamageLocFlags( gentity_t *veh, int impactDir, int deathPoint );
32 extern void G_VehUpdateShields( gentity_t *targ );
33 extern void G_LetGoOfWall( gentity_t *ent );
34 extern void BG_ClearRocketLock( playerState_t *ps );
35 //rww - pd
36 void BotDamageNotification(gclient_t *bot, gentity_t *attacker);
37 //end rww
38 
39 void ThrowSaberToAttacker(gentity_t *self, gentity_t *attacker);
40 
ObjectDie(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath)41 void ObjectDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
42 {
43 	if(self->target)
44 	{
45 		G_UseTargets(self, attacker);
46 	}
47 
48 	//remove my script_targetname
49 	G_FreeEntity( self );
50 }
51 
G_HeavyMelee(gentity_t * attacker)52 qboolean G_HeavyMelee( gentity_t *attacker )
53 {
54 	if (level.gametype == GT_SIEGE
55 		&& attacker
56 		&& attacker->client
57 		&& attacker->client->siegeClass != -1
58 		&& (bgSiegeClasses[attacker->client->siegeClass].classflags & (1<<CFL_HEAVYMELEE)) )
59 	{
60 		return qtrue;
61 	}
62 	return qfalse;
63 }
64 
G_GetHitLocation(gentity_t * target,vec3_t ppoint)65 int G_GetHitLocation(gentity_t *target, vec3_t ppoint)
66 {
67 	vec3_t			point, point_dir;
68 	vec3_t			forward, right, up;
69 	vec3_t			tangles, tcenter;
70 //	float			tradius;
71 	float			udot, fdot, rdot;
72 	int				Vertical, Forward, Lateral;
73 	int				HitLoc;
74 
75 	// Get target forward, right and up.
76 	if(target->client)
77 	{
78 		// Ignore player's pitch and roll.
79 		VectorSet(tangles, 0, target->r.currentAngles[YAW], 0);
80 	}
81 
82 	AngleVectors(tangles, forward, right, up);
83 
84 	// Get center of target.
85 	VectorAdd(target->r.absmin, target->r.absmax, tcenter);
86 	VectorScale(tcenter, 0.5, tcenter);
87 
88 	// Get radius width of target.
89 //	tradius = (fabs(target->r.maxs[0]) + fabs(target->r.maxs[1]) + fabs(target->r.mins[0]) + fabs(target->r.mins[1]))/4;
90 
91 	// Get impact point.
92 	if(ppoint && !VectorCompare(ppoint, vec3_origin))
93 	{
94 		VectorCopy(ppoint, point);
95 	}
96 	else
97 	{
98 		return HL_NONE;
99 	}
100 
101 /*
102 //get impact dir
103 	if(pdir && !VectorCompare(pdir, vec3_origin))
104 	{
105 		VectorCopy(pdir, dir);
106 	}
107 	else
108 	{
109 		return;
110 	}
111 
112 //put point at controlled distance from center
113 	VectorSubtract(point, tcenter, tempvec);
114 	tempvec[2] = 0;
115 	hdist = VectorLength(tempvec);
116 
117 	VectorMA(point, hdist - tradius, dir, point);
118 	//now a point on the surface of a cylinder with a radius of tradius
119 */
120 	VectorSubtract(point, tcenter, point_dir);
121 	VectorNormalize(point_dir);
122 
123 	// Get bottom to top (vertical) position index
124 	udot = DotProduct(up, point_dir);
125 	if(udot>.800)
126 	{
127 		Vertical = 4;
128 	}
129 	else if(udot>.400)
130 	{
131 		Vertical = 3;
132 	}
133 	else if(udot>-.333)
134 	{
135 		Vertical = 2;
136 	}
137 	else if(udot>-.666)
138 	{
139 		Vertical = 1;
140 	}
141 	else
142 	{
143 		Vertical = 0;
144 	}
145 
146 	// Get back to front (forward) position index.
147 	fdot = DotProduct(forward, point_dir);
148 	if(fdot>.666)
149 	{
150 		Forward = 4;
151 	}
152 	else if(fdot>.333)
153 	{
154 		Forward = 3;
155 	}
156 	else if(fdot>-.333)
157 	{
158 		Forward = 2;
159 	}
160 	else if(fdot>-.666)
161 	{
162 		Forward = 1;
163 	}
164 	else
165 	{
166 		Forward = 0;
167 	}
168 
169 	// Get left to right (lateral) position index.
170 	rdot = DotProduct(right, point_dir);
171 	if(rdot>.666)
172 	{
173 		Lateral = 4;
174 	}
175 	else if(rdot>.333)
176 	{
177 		Lateral = 3;
178 	}
179 	else if(rdot>-.333)
180 	{
181 		Lateral = 2;
182 	}
183 	else if(rdot>-.666)
184 	{
185 		Lateral = 1;
186 	}
187 	else
188 	{
189 		Lateral = 0;
190 	}
191 
192 	HitLoc = Vertical * 25 + Forward * 5 + Lateral;
193 
194 	if(HitLoc <= 10)
195 	{
196 		// Feet.
197 		if ( rdot > 0 )
198 		{
199 			return HL_FOOT_RT;
200 		}
201 		else
202 		{
203 			return HL_FOOT_LT;
204 		}
205 	}
206 	else if(HitLoc <= 50)
207 	{
208 		// Legs.
209 		if ( rdot > 0 )
210 		{
211 			return HL_LEG_RT;
212 		}
213 		else
214 		{
215 			return HL_LEG_LT;
216 		}
217 	}
218 	else if(HitLoc == 56||HitLoc == 60||HitLoc == 61||HitLoc == 65||HitLoc == 66||HitLoc == 70)
219 	{
220 		// Hands.
221 		if ( rdot > 0 )
222 		{
223 			return HL_HAND_RT;
224 		}
225 		else
226 		{
227 			return HL_HAND_LT;
228 		}
229 	}
230 	else if(HitLoc == 83||HitLoc == 87||HitLoc == 88||HitLoc == 92||HitLoc == 93||HitLoc == 97)
231 	{
232 		// Arms.
233 		if ( rdot > 0 )
234 		{
235 			return HL_ARM_RT;
236 		}
237 		else
238 		{
239 			return HL_ARM_LT;
240 		}
241 	}
242 	else if((HitLoc >= 107 && HitLoc <= 109)||(HitLoc >= 112 && HitLoc <= 114)||(HitLoc >= 117 && HitLoc <= 119))
243 	{
244 		// Head.
245 		return HL_HEAD;
246 	}
247 	else
248 	{
249 		if(udot < 0.3)
250 		{
251 			return HL_WAIST;
252 		}
253 		else if(fdot < 0)
254 		{
255 			if(rdot > 0.4)
256 			{
257 				return HL_BACK_RT;
258 			}
259 			else if(rdot < -0.4)
260 			{
261 				return HL_BACK_LT;
262 			}
263 			else if(fdot < 0)
264 			{
265 				return HL_BACK;
266 			}
267 		}
268 		else
269 		{
270 			if(rdot > 0.3)
271 			{
272 				return HL_CHEST_RT;
273 			}
274 			else if(rdot < -0.3)
275 			{
276 				return HL_CHEST_LT;
277 			}
278 			else if(fdot < 0)
279 			{
280 				return HL_CHEST;
281 			}
282 		}
283 	}
284 	return HL_NONE;
285 }
286 
287 /*
288 int G_PickPainAnim( gentity_t *self, vec3_t point, int damage )
289 {
290 	switch( G_GetHitLocation( self, point ) )
291 	{
292 	case HL_FOOT_RT:
293 		return BOTH_PAIN12;
294 		//PAIN12 = right foot
295 		break;
296 	case HL_FOOT_LT:
297 		return -1;
298 		break;
299 	case HL_LEG_RT:
300 		if ( !Q_irand( 0, 1 ) )
301 		{
302 			return BOTH_PAIN11;
303 		}
304 		else
305 		{
306 			return BOTH_PAIN13;
307 		}
308 		//PAIN11 = twitch right leg
309 		//PAIN13 = right knee
310 		break;
311 	case HL_LEG_LT:
312 		return BOTH_PAIN14;
313 		//PAIN14 = twitch left leg
314 		break;
315 	case HL_BACK_RT:
316 		return BOTH_PAIN7;
317 		//PAIN7 = med left shoulder
318 		break;
319 	case HL_BACK_LT:
320 		return Q_irand( BOTH_PAIN15, BOTH_PAIN16 );
321 		//PAIN15 = med right shoulder
322 		//PAIN16 = twitch right shoulder
323 		break;
324 	case HL_BACK:
325 		if ( !Q_irand( 0, 1 ) )
326 		{
327 			return BOTH_PAIN1;
328 		}
329 		else
330 		{
331 			return BOTH_PAIN5;
332 		}
333 		//PAIN1 = back
334 		//PAIN5 = same as 1
335 		break;
336 	case HL_CHEST_RT:
337 		return BOTH_PAIN3;
338 		//PAIN3 = long, right shoulder
339 		break;
340 	case HL_CHEST_LT:
341 		return BOTH_PAIN2;
342 		//PAIN2 = long, left shoulder
343 		break;
344 	case HL_WAIST:
345 	case HL_CHEST:
346 		if ( !Q_irand( 0, 3 ) )
347 		{
348 			return BOTH_PAIN6;
349 		}
350 		else if ( !Q_irand( 0, 2 ) )
351 		{
352 			return BOTH_PAIN8;
353 		}
354 		else if ( !Q_irand( 0, 1 ) )
355 		{
356 			return BOTH_PAIN17;
357 		}
358 		else
359 		{
360 			return BOTH_PAIN19;
361 		}
362 		//PAIN6 = gut
363 		//PAIN8 = chest
364 		//PAIN17 = twitch crotch
365 		//PAIN19 = med crotch
366 		break;
367 	case HL_ARM_RT:
368 	case HL_HAND_RT:
369 		return BOTH_PAIN9;
370 		//PAIN9 = twitch right arm
371 		break;
372 	case HL_ARM_LT:
373 	case HL_HAND_LT:
374 		return BOTH_PAIN10;
375 		//PAIN10 = twitch left arm
376 		break;
377 	case HL_HEAD:
378 		return BOTH_PAIN4;
379 		//PAIN4 = head
380 		break;
381 	default:
382 		return -1;
383 		break;
384 	}
385 }
386 */
387 
ExplodeDeath(gentity_t * self)388 void ExplodeDeath( gentity_t *self )
389 {
390 //	gentity_t	*tent;
391 	vec3_t		forward;
392 
393 	self->takedamage = qfalse;//stop chain reaction runaway loops
394 
395 	self->s.loopSound = 0;
396 	self->s.loopIsSoundset = qfalse;
397 
398 	VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
399 
400 //	tent = G_TempEntity( self->s.origin, EV_FX_EXPLOSION );
401 	AngleVectors(self->s.angles, forward, NULL, NULL);
402 
403 /*
404 	if ( self->fxID > 0 )
405 	{
406 		G_PlayEffect( self->fxID, self->r.currentOrigin, forward );
407 	}
408 	else
409 	*/
410 
411 	{
412 //		CG_SurfaceExplosion( self->r.currentOrigin, forward, 20.0f, 12.0f, ((self->spawnflags&4)==qfalse) );	//FIXME: This needs to be consistent to all exploders!
413 //		G_Sound(self, self->sounds );
414 	}
415 
416 	if(self->splashDamage > 0 && self->splashRadius > 0)
417 	{
418 		gentity_t *attacker = self;
419 		if ( self->parent )
420 		{
421 			attacker = self->parent;
422 		}
423 		G_RadiusDamage( self->r.currentOrigin, attacker, self->splashDamage, self->splashRadius,
424 				attacker, NULL, MOD_UNKNOWN );
425 	}
426 
427 	ObjectDie( self, self, self, 20, 0 );
428 }
429 
430 
431 /*
432 ============
433 ScorePlum
434 ============
435 */
ScorePlum(gentity_t * ent,vec3_t origin,int score)436 void ScorePlum( gentity_t *ent, vec3_t origin, int score ) {
437 	gentity_t *plum;
438 
439 	plum = G_TempEntity( origin, EV_SCOREPLUM );
440 	// only send this temp entity to a single client
441 	plum->r.svFlags |= SVF_SINGLECLIENT;
442 	plum->r.singleClient = ent->s.number;
443 	//
444 	plum->s.otherEntityNum = ent->s.number;
445 	plum->s.time = score;
446 }
447 
448 /*
449 ============
450 AddScore
451 
452 Adds score to both the client and his team
453 ============
454 */
455 extern qboolean g_dontPenalizeTeam; //g_cmds.c
AddScore(gentity_t * ent,vec3_t origin,int score)456 void AddScore( gentity_t *ent, vec3_t origin, int score )
457 {
458 	/*
459 	if (level.gametype == GT_SIEGE)
460 	{ //no scoring in this gametype at all.
461 		return;
462 	}
463 	*/
464 
465 	if ( !ent->client ) {
466 		return;
467 	}
468 	// no scoring during pre-match warmup
469 	if ( level.warmupTime ) {
470 		return;
471 	}
472 	// show score plum
473 	//ScorePlum(ent, origin, score);
474 	//
475 	ent->client->ps.persistant[PERS_SCORE] += score;
476 	if ( level.gametype == GT_TEAM && !g_dontPenalizeTeam )
477 		level.teamScores[ ent->client->ps.persistant[PERS_TEAM] ] += score;
478 	CalculateRanks();
479 }
480 
481 /*
482 =================
483 TossClientItems
484 
485 rww - Toss the weapon away from the player in the specified direction
486 =================
487 */
TossClientWeapon(gentity_t * self,vec3_t direction,float speed)488 void TossClientWeapon(gentity_t *self, vec3_t direction, float speed)
489 {
490 	vec3_t vel;
491 	gitem_t *item;
492 	gentity_t *launched;
493 	int weapon = self->s.weapon;
494 	int ammoSub;
495 
496 	if (level.gametype == GT_SIEGE)
497 	{ //no dropping weaps
498 		return;
499 	}
500 
501 	if (weapon <= WP_BRYAR_PISTOL)
502 	{ //can't have this
503 		return;
504 	}
505 
506 	if (weapon == WP_EMPLACED_GUN ||
507 		weapon == WP_TURRET)
508 	{
509 		return;
510 	}
511 
512 	// find the item type for this weapon
513 	item = BG_FindItemForWeapon( weapon );
514 
515 	ammoSub = (self->client->ps.ammo[weaponData[weapon].ammoIndex] - bg_itemlist[BG_GetItemIndexByTag(weapon, IT_WEAPON)].quantity);
516 
517 	if (ammoSub < 0)
518 	{
519 		int ammoQuan = item->quantity;
520 		ammoQuan -= (-ammoSub);
521 
522 		if (ammoQuan <= 0)
523 		{ //no ammo
524 			return;
525 		}
526 	}
527 
528 	vel[0] = direction[0]*speed;
529 	vel[1] = direction[1]*speed;
530 	vel[2] = direction[2]*speed;
531 
532 	launched = LaunchItem(item, self->client->ps.origin, vel);
533 
534 	launched->s.generic1 = self->s.number;
535 	launched->s.powerups = level.time + 1500;
536 
537 	launched->count = bg_itemlist[BG_GetItemIndexByTag(weapon, IT_WEAPON)].quantity;
538 
539 	self->client->ps.ammo[weaponData[weapon].ammoIndex] -= bg_itemlist[BG_GetItemIndexByTag(weapon, IT_WEAPON)].quantity;
540 
541 	if (self->client->ps.ammo[weaponData[weapon].ammoIndex] < 0)
542 	{
543 		launched->count -= (-self->client->ps.ammo[weaponData[weapon].ammoIndex]);
544 		self->client->ps.ammo[weaponData[weapon].ammoIndex] = 0;
545 	}
546 
547 	if ((self->client->ps.ammo[weaponData[weapon].ammoIndex] < 1 && weapon != WP_DET_PACK) ||
548 		(weapon != WP_THERMAL && weapon != WP_DET_PACK && weapon != WP_TRIP_MINE))
549 	{
550 		int i = 0;
551 		int weap = -1;
552 
553 		self->client->ps.stats[STAT_WEAPONS] &= ~(1 << weapon);
554 
555 		while (i < WP_NUM_WEAPONS)
556 		{
557 			if ((self->client->ps.stats[STAT_WEAPONS] & (1 << i)) && i != WP_NONE)
558 			{ //this one's good
559 				weap = i;
560 				break;
561 			}
562 			i++;
563 		}
564 
565 		if (weap != -1)
566 		{
567 			self->s.weapon = weap;
568 			self->client->ps.weapon = weap;
569 		}
570 		else
571 		{
572 			self->s.weapon = 0;
573 			self->client->ps.weapon = 0;
574 		}
575 
576 		G_AddEvent(self, EV_NOAMMO, weapon);
577 	}
578 }
579 
580 /*
581 =================
582 TossClientItems
583 
584 Toss the weapon and powerups for the killed player
585 =================
586 */
TossClientItems(gentity_t * self)587 void TossClientItems( gentity_t *self ) {
588 	gitem_t		*item;
589 	int			weapon;
590 	float		angle;
591 	int			i;
592 	gentity_t	*drop;
593 
594 	if (level.gametype == GT_SIEGE)
595 	{ //just don't drop anything then
596 		return;
597 	}
598 
599 	// drop the weapon if not a gauntlet or machinegun
600 	weapon = self->s.weapon;
601 
602 	// make a special check to see if they are changing to a new
603 	// weapon that isn't the mg or gauntlet.  Without this, a client
604 	// can pick up a weapon, be killed, and not drop the weapon because
605 	// their weapon change hasn't completed yet and they are still holding the MG.
606 	if ( weapon == WP_BRYAR_PISTOL) {
607 		if ( self->client->ps.weaponstate == WEAPON_DROPPING ) {
608 			weapon = self->client->pers.cmd.weapon;
609 		}
610 		if ( !( self->client->ps.stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
611 			weapon = WP_NONE;
612 		}
613 	}
614 
615 	self->s.bolt2 = weapon;
616 
617 	if ( weapon > WP_BRYAR_PISTOL &&
618 		weapon != WP_EMPLACED_GUN &&
619 		weapon != WP_TURRET &&
620 		self->client->ps.ammo[ weaponData[weapon].ammoIndex ] ) {
621 		gentity_t *te;
622 
623 		// find the item type for this weapon
624 		item = BG_FindItemForWeapon( weapon );
625 
626 		// tell all clients to remove the weapon model on this guy until he respawns
627 		te = G_TempEntity( vec3_origin, EV_DESTROY_WEAPON_MODEL );
628 		te->r.svFlags |= SVF_BROADCAST;
629 		te->s.eventParm = self->s.number;
630 
631 		// spawn the item
632 		Drop_Item( self, item, 0 );
633 	}
634 
635 	// drop all the powerups if not in teamplay
636 	if ( level.gametype != GT_TEAM && level.gametype != GT_SIEGE ) {
637 		angle = 45;
638 		for ( i = 1 ; i < PW_NUM_POWERUPS ; i++ ) {
639 			if ( self->client->ps.powerups[ i ] > level.time ) {
640 				item = BG_FindItemForPowerup( i );
641 				if ( !item ) {
642 					continue;
643 				}
644 				drop = Drop_Item( self, item, angle );
645 				// decide how many seconds it has left
646 				drop->count = ( self->client->ps.powerups[ i ] - level.time ) / 1000;
647 				if ( drop->count < 1 ) {
648 					drop->count = 1;
649 				}
650 				angle += 45;
651 			}
652 		}
653 	}
654 }
655 
656 
657 /*
658 ==================
659 LookAtKiller
660 ==================
661 */
LookAtKiller(gentity_t * self,gentity_t * inflictor,gentity_t * attacker)662 void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker ) {
663 	vec3_t		dir;
664 
665 	if ( attacker && attacker != self )
666 		VectorSubtract (attacker->s.pos.trBase, self->s.pos.trBase, dir);
667 	else if ( inflictor && inflictor != self )
668 		VectorSubtract (inflictor->s.pos.trBase, self->s.pos.trBase, dir);
669 	else {
670 		self->client->ps.stats[STAT_DEAD_YAW] = self->s.angles[YAW];
671 		return;
672 	}
673 
674 	self->client->ps.stats[STAT_DEAD_YAW] = vectoyaw ( dir );
675 }
676 
677 /*
678 ==================
679 GibEntity
680 ==================
681 */
GibEntity(gentity_t * self,int killer)682 void GibEntity( gentity_t *self, int killer ) {
683 	G_AddEvent( self, EV_GIB_PLAYER, killer );
684 	self->takedamage = qfalse;
685 	self->s.eType = ET_INVISIBLE;
686 	self->r.contents = 0;
687 }
688 
BodyRid(gentity_t * ent)689 void BodyRid(gentity_t *ent)
690 {
691 	trap->UnlinkEntity( (sharedEntity_t *)ent );
692 	ent->physicsObject = qfalse;
693 }
694 
695 /*
696 ==================
697 body_die
698 ==================
699 */
body_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath)700 void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
701 	// NOTENOTE No gibbing right now, this is star wars.
702 	qboolean doDisint = qfalse;
703 
704 	if (self->s.eType == ET_NPC)
705 	{ //well, just rem it then, so long as it's done with its death anim and it's not a standard weapon.
706 		if ( self->client && self->client->ps.torsoTimer <= 0 &&
707 			 (meansOfDeath == MOD_UNKNOWN ||
708 			  meansOfDeath == MOD_WATER ||
709 			  meansOfDeath == MOD_SLIME ||
710 			  meansOfDeath == MOD_LAVA ||
711 			  meansOfDeath == MOD_CRUSH ||
712 			  meansOfDeath == MOD_TELEFRAG ||
713 			  meansOfDeath == MOD_FALLING ||
714 			  meansOfDeath == MOD_SUICIDE ||
715 			  meansOfDeath == MOD_TARGET_LASER ||
716 			  meansOfDeath == MOD_TRIGGER_HURT) )
717 		{
718 			self->think = G_FreeEntity;
719 			self->nextthink = level.time;
720 		}
721 		return;
722 	}
723 
724 	if (self->health < (GIB_HEALTH+1))
725 	{
726 		self->health = GIB_HEALTH+1;
727 
728 		if (self->client && (level.time - self->client->respawnTime) < 2000)
729 		{
730 			doDisint = qfalse;
731 		}
732 		else
733 		{
734 			doDisint = qtrue;
735 		}
736 	}
737 
738 	if (self->client && (self->client->ps.eFlags & EF_DISINTEGRATION))
739 	{
740 		return;
741 	}
742 	else if (self->s.eFlags & EF_DISINTEGRATION)
743 	{
744 		return;
745 	}
746 
747 	if (doDisint)
748 	{
749 		if (self->client)
750 		{
751 			self->client->ps.eFlags |= EF_DISINTEGRATION;
752 			VectorCopy(self->client->ps.origin, self->client->ps.lastHitLoc);
753 		}
754 		else
755 		{
756 			self->s.eFlags |= EF_DISINTEGRATION;
757 			VectorCopy(self->r.currentOrigin, self->s.origin2);
758 
759 			//since it's the corpse entity, tell it to "remove" itself
760 			self->think = BodyRid;
761 			self->nextthink = level.time + 1000;
762 		}
763 		return;
764 	}
765 }
766 
767 
768 // these are just for logging, the client prints its own messages
769 char	*modNames[MOD_MAX] = {
770 	"MOD_UNKNOWN",
771 	"MOD_STUN_BATON",
772 	"MOD_MELEE",
773 	"MOD_SABER",
774 	"MOD_BRYAR_PISTOL",
775 	"MOD_BRYAR_PISTOL_ALT",
776 	"MOD_BLASTER",
777 	"MOD_TURBLAST",
778 	"MOD_DISRUPTOR",
779 	"MOD_DISRUPTOR_SPLASH",
780 	"MOD_DISRUPTOR_SNIPER",
781 	"MOD_BOWCASTER",
782 	"MOD_REPEATER",
783 	"MOD_REPEATER_ALT",
784 	"MOD_REPEATER_ALT_SPLASH",
785 	"MOD_DEMP2",
786 	"MOD_DEMP2_ALT",
787 	"MOD_FLECHETTE",
788 	"MOD_FLECHETTE_ALT_SPLASH",
789 	"MOD_ROCKET",
790 	"MOD_ROCKET_SPLASH",
791 	"MOD_ROCKET_HOMING",
792 	"MOD_ROCKET_HOMING_SPLASH",
793 	"MOD_THERMAL",
794 	"MOD_THERMAL_SPLASH",
795 	"MOD_TRIP_MINE_SPLASH",
796 	"MOD_TIMED_MINE_SPLASH",
797 	"MOD_DET_PACK_SPLASH",
798 	"MOD_VEHICLE",
799 	"MOD_CONC",
800 	"MOD_CONC_ALT",
801 	"MOD_FORCE_DARK",
802 	"MOD_SENTRY",
803 	"MOD_WATER",
804 	"MOD_SLIME",
805 	"MOD_LAVA",
806 	"MOD_CRUSH",
807 	"MOD_TELEFRAG",
808 	"MOD_FALLING",
809 	"MOD_SUICIDE",
810 	"MOD_TARGET_LASER",
811 	"MOD_TRIGGER_HURT"
812 };
813 
814 
815 /*
816 ==================
817 CheckAlmostCapture
818 ==================
819 */
CheckAlmostCapture(gentity_t * self,gentity_t * attacker)820 void CheckAlmostCapture( gentity_t *self, gentity_t *attacker ) {
821 #if 0
822 	gentity_t	*ent;
823 	vec3_t		dir;
824 	char		*classname;
825 
826 	// if this player was carrying a flag
827 	if ( self->client->ps.powerups[PW_REDFLAG] ||
828 		self->client->ps.powerups[PW_BLUEFLAG] ||
829 		self->client->ps.powerups[PW_NEUTRALFLAG] ) {
830 		// get the goal flag this player should have been going for
831 		if ( level.gametype == GT_CTF || level.gametype == GT_CTY ) {
832 			if ( self->client->sess.sessionTeam == TEAM_BLUE ) {
833 				classname = "team_CTF_blueflag";
834 			}
835 			else {
836 				classname = "team_CTF_redflag";
837 			}
838 		}
839 		else {
840 			if ( self->client->sess.sessionTeam == TEAM_BLUE ) {
841 				classname = "team_CTF_redflag";
842 			}
843 			else {
844 				classname = "team_CTF_blueflag";
845 			}
846 		}
847 		ent = NULL;
848 		do
849 		{
850 			ent = G_Find(ent, FOFS(classname), classname);
851 		} while (ent && (ent->flags & FL_DROPPED_ITEM));
852 		// if we found the destination flag and it's not picked up
853 		if (ent && !(ent->r.svFlags & SVF_NOCLIENT) ) {
854 			// if the player was *very* close
855 			VectorSubtract( self->client->ps.origin, ent->s.origin, dir );
856 			if ( VectorLength(dir) < 200 ) {
857 				self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT;
858 				if ( attacker->client ) {
859 					attacker->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_HOLYSHIT;
860 				}
861 			}
862 		}
863 	}
864 #endif
865 }
866 
G_InKnockDown(playerState_t * ps)867 qboolean G_InKnockDown( playerState_t *ps )
868 {
869 	switch ( (ps->legsAnim) )
870 	{
871 	case BOTH_KNOCKDOWN1:
872 	case BOTH_KNOCKDOWN2:
873 	case BOTH_KNOCKDOWN3:
874 	case BOTH_KNOCKDOWN4:
875 	case BOTH_KNOCKDOWN5:
876 		return qtrue;
877 		break;
878 	case BOTH_GETUP1:
879 	case BOTH_GETUP2:
880 	case BOTH_GETUP3:
881 	case BOTH_GETUP4:
882 	case BOTH_GETUP5:
883 	case BOTH_FORCE_GETUP_F1:
884 	case BOTH_FORCE_GETUP_F2:
885 	case BOTH_FORCE_GETUP_B1:
886 	case BOTH_FORCE_GETUP_B2:
887 	case BOTH_FORCE_GETUP_B3:
888 	case BOTH_FORCE_GETUP_B4:
889 	case BOTH_FORCE_GETUP_B5:
890 		return qtrue;
891 		break;
892 	}
893 	return qfalse;
894 }
895 
G_CheckSpecialDeathAnim(gentity_t * self,vec3_t point,int damage,int mod,int hitLoc)896 static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc )
897 {
898 	int deathAnim = -1;
899 
900 	if ( BG_InRoll( &self->client->ps, self->client->ps.legsAnim ) )
901 	{
902 		deathAnim = BOTH_DEATH_ROLL;		//# Death anim from a roll
903 	}
904 	else if ( BG_FlippingAnim( self->client->ps.legsAnim ) )
905 	{
906 		deathAnim = BOTH_DEATH_FLIP;		//# Death anim from a flip
907 	}
908 	else if ( G_InKnockDown( &self->client->ps ) )
909 	{//since these happen a lot, let's handle them case by case
910 		int animLength = bgAllAnims[self->localAnimIndex].anims[self->client->ps.legsAnim].numFrames * fabs((float)(bgHumanoidAnimations[self->client->ps.legsAnim].frameLerp));
911 		switch ( self->client->ps.legsAnim )
912 		{
913 		case BOTH_KNOCKDOWN1:
914 			if ( animLength - self->client->ps.legsTimer > 100 )
915 			{//on our way down
916 				if ( self->client->ps.legsTimer > 600 )
917 				{//still partially up
918 					deathAnim = BOTH_DEATH_FALLING_UP;
919 				}
920 				else
921 				{//down
922 					deathAnim = BOTH_DEATH_LYING_UP;
923 				}
924 			}
925 			break;
926 		case BOTH_KNOCKDOWN2:
927 			if ( animLength - self->client->ps.legsTimer > 700 )
928 			{//on our way down
929 				if ( self->client->ps.legsTimer > 600 )
930 				{//still partially up
931 					deathAnim = BOTH_DEATH_FALLING_UP;
932 				}
933 				else
934 				{//down
935 					deathAnim = BOTH_DEATH_LYING_UP;
936 				}
937 			}
938 			break;
939 		case BOTH_KNOCKDOWN3:
940 			if ( animLength - self->client->ps.legsTimer > 100 )
941 			{//on our way down
942 				if ( self->client->ps.legsTimer > 1300 )
943 				{//still partially up
944 					deathAnim = BOTH_DEATH_FALLING_DN;
945 				}
946 				else
947 				{//down
948 					deathAnim = BOTH_DEATH_LYING_DN;
949 				}
950 			}
951 			break;
952 		case BOTH_KNOCKDOWN4:
953 			if ( animLength - self->client->ps.legsTimer > 300 )
954 			{//on our way down
955 				if ( self->client->ps.legsTimer > 350 )
956 				{//still partially up
957 					deathAnim = BOTH_DEATH_FALLING_UP;
958 				}
959 				else
960 				{//down
961 					deathAnim = BOTH_DEATH_LYING_UP;
962 				}
963 			}
964 			else
965 			{//crouch death
966 				vec3_t fwd;
967 				float thrown = 0;
968 
969 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
970 				thrown = DotProduct( fwd, self->client->ps.velocity );
971 
972 				if ( thrown < -150 )
973 				{
974 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
975 				}
976 				else
977 				{
978 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
979 				}
980 			}
981 			break;
982 		case BOTH_KNOCKDOWN5:
983 			if ( self->client->ps.legsTimer < 750 )
984 			{//flat
985 				deathAnim = BOTH_DEATH_LYING_DN;
986 			}
987 			break;
988 		case BOTH_GETUP1:
989 			if ( self->client->ps.legsTimer < 350 )
990 			{//standing up
991 			}
992 			else if ( self->client->ps.legsTimer < 800 )
993 			{//crouching
994 				vec3_t fwd;
995 				float thrown = 0;
996 
997 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
998 				thrown = DotProduct( fwd, self->client->ps.velocity );
999 				if ( thrown < -150 )
1000 				{
1001 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1002 				}
1003 				else
1004 				{
1005 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1006 				}
1007 			}
1008 			else
1009 			{//lying down
1010 				if ( animLength - self->client->ps.legsTimer > 450 )
1011 				{//partially up
1012 					deathAnim = BOTH_DEATH_FALLING_UP;
1013 				}
1014 				else
1015 				{//down
1016 					deathAnim = BOTH_DEATH_LYING_UP;
1017 				}
1018 			}
1019 			break;
1020 		case BOTH_GETUP2:
1021 			if ( self->client->ps.legsTimer < 150 )
1022 			{//standing up
1023 			}
1024 			else if ( self->client->ps.legsTimer < 850 )
1025 			{//crouching
1026 				vec3_t fwd;
1027 				float thrown = 0;
1028 
1029 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1030 				thrown = DotProduct( fwd, self->client->ps.velocity );
1031 
1032 				if ( thrown < -150 )
1033 				{
1034 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1035 				}
1036 				else
1037 				{
1038 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1039 				}
1040 			}
1041 			else
1042 			{//lying down
1043 				if ( animLength - self->client->ps.legsTimer > 500 )
1044 				{//partially up
1045 					deathAnim = BOTH_DEATH_FALLING_UP;
1046 				}
1047 				else
1048 				{//down
1049 					deathAnim = BOTH_DEATH_LYING_UP;
1050 				}
1051 			}
1052 			break;
1053 		case BOTH_GETUP3:
1054 			if ( self->client->ps.legsTimer < 250 )
1055 			{//standing up
1056 			}
1057 			else if ( self->client->ps.legsTimer < 600 )
1058 			{//crouching
1059 				vec3_t fwd;
1060 				float thrown = 0;
1061 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1062 				thrown = DotProduct( fwd, self->client->ps.velocity );
1063 
1064 				if ( thrown < -150 )
1065 				{
1066 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1067 				}
1068 				else
1069 				{
1070 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1071 				}
1072 			}
1073 			else
1074 			{//lying down
1075 				if ( animLength - self->client->ps.legsTimer > 150 )
1076 				{//partially up
1077 					deathAnim = BOTH_DEATH_FALLING_DN;
1078 				}
1079 				else
1080 				{//down
1081 					deathAnim = BOTH_DEATH_LYING_DN;
1082 				}
1083 			}
1084 			break;
1085 		case BOTH_GETUP4:
1086 			if ( self->client->ps.legsTimer < 250 )
1087 			{//standing up
1088 			}
1089 			else if ( self->client->ps.legsTimer < 600 )
1090 			{//crouching
1091 				vec3_t fwd;
1092 				float thrown = 0;
1093 
1094 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1095 				thrown = DotProduct( fwd, self->client->ps.velocity );
1096 
1097 				if ( thrown < -150 )
1098 				{
1099 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1100 				}
1101 				else
1102 				{
1103 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1104 				}
1105 			}
1106 			else
1107 			{//lying down
1108 				if ( animLength - self->client->ps.legsTimer > 850 )
1109 				{//partially up
1110 					deathAnim = BOTH_DEATH_FALLING_DN;
1111 				}
1112 				else
1113 				{//down
1114 					deathAnim = BOTH_DEATH_LYING_UP;
1115 				}
1116 			}
1117 			break;
1118 		case BOTH_GETUP5:
1119 			if ( self->client->ps.legsTimer > 850 )
1120 			{//lying down
1121 				if ( animLength - self->client->ps.legsTimer > 1500 )
1122 				{//partially up
1123 					deathAnim = BOTH_DEATH_FALLING_DN;
1124 				}
1125 				else
1126 				{//down
1127 					deathAnim = BOTH_DEATH_LYING_DN;
1128 				}
1129 			}
1130 			break;
1131 		case BOTH_GETUP_CROUCH_B1:
1132 			if ( self->client->ps.legsTimer < 800 )
1133 			{//crouching
1134 				vec3_t fwd;
1135 				float thrown = 0;
1136 
1137 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1138 				thrown = DotProduct( fwd, self->client->ps.velocity );
1139 
1140 				if ( thrown < -150 )
1141 				{
1142 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1143 				}
1144 				else
1145 				{
1146 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1147 				}
1148 			}
1149 			else
1150 			{//lying down
1151 				if ( animLength - self->client->ps.legsTimer > 400 )
1152 				{//partially up
1153 					deathAnim = BOTH_DEATH_FALLING_UP;
1154 				}
1155 				else
1156 				{//down
1157 					deathAnim = BOTH_DEATH_LYING_UP;
1158 				}
1159 			}
1160 			break;
1161 		case BOTH_GETUP_CROUCH_F1:
1162 			if ( self->client->ps.legsTimer < 800 )
1163 			{//crouching
1164 				vec3_t fwd;
1165 				float thrown = 0;
1166 
1167 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1168 				thrown = DotProduct( fwd, self->client->ps.velocity );
1169 
1170 				if ( thrown < -150 )
1171 				{
1172 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1173 				}
1174 				else
1175 				{
1176 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1177 				}
1178 			}
1179 			else
1180 			{//lying down
1181 				if ( animLength - self->client->ps.legsTimer > 150 )
1182 				{//partially up
1183 					deathAnim = BOTH_DEATH_FALLING_DN;
1184 				}
1185 				else
1186 				{//down
1187 					deathAnim = BOTH_DEATH_LYING_DN;
1188 				}
1189 			}
1190 			break;
1191 		case BOTH_FORCE_GETUP_B1:
1192 			if ( self->client->ps.legsTimer < 325 )
1193 			{//standing up
1194 			}
1195 			else if ( self->client->ps.legsTimer < 725 )
1196 			{//spinning up
1197 				deathAnim = BOTH_DEATH_SPIN_180;	//# Death anim when facing backwards
1198 			}
1199 			else if ( self->client->ps.legsTimer < 900 )
1200 			{//crouching
1201 				vec3_t fwd;
1202 				float thrown = 0;
1203 
1204 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1205 				thrown = DotProduct( fwd, self->client->ps.velocity );
1206 
1207 				if ( thrown < -150 )
1208 				{
1209 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1210 				}
1211 				else
1212 				{
1213 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1214 				}
1215 			}
1216 			else
1217 			{//lying down
1218 				if ( animLength - self->client->ps.legsTimer > 50 )
1219 				{//partially up
1220 					deathAnim = BOTH_DEATH_FALLING_UP;
1221 				}
1222 				else
1223 				{//down
1224 					deathAnim = BOTH_DEATH_LYING_UP;
1225 				}
1226 			}
1227 			break;
1228 		case BOTH_FORCE_GETUP_B2:
1229 			if ( self->client->ps.legsTimer < 575 )
1230 			{//standing up
1231 			}
1232 			else if ( self->client->ps.legsTimer < 875 )
1233 			{//spinning up
1234 				deathAnim = BOTH_DEATH_SPIN_180;	//# Death anim when facing backwards
1235 			}
1236 			else if ( self->client->ps.legsTimer < 900 )
1237 			{//crouching
1238 				vec3_t fwd;
1239 				float thrown = 0;
1240 
1241 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1242 				thrown = DotProduct( fwd, self->client->ps.velocity );
1243 
1244 				if ( thrown < -150 )
1245 				{
1246 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1247 				}
1248 				else
1249 				{
1250 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1251 				}
1252 			}
1253 			else
1254 			{//lying down
1255 				//partially up
1256 				deathAnim = BOTH_DEATH_FALLING_UP;
1257 			}
1258 			break;
1259 		case BOTH_FORCE_GETUP_B3:
1260 			if ( self->client->ps.legsTimer < 150 )
1261 			{//standing up
1262 			}
1263 			else if ( self->client->ps.legsTimer < 775 )
1264 			{//flipping
1265 				deathAnim = BOTH_DEATHBACKWARD2; //backflip
1266 			}
1267 			else
1268 			{//lying down
1269 				//partially up
1270 				deathAnim = BOTH_DEATH_FALLING_UP;
1271 			}
1272 			break;
1273 		case BOTH_FORCE_GETUP_B4:
1274 			if ( self->client->ps.legsTimer < 325 )
1275 			{//standing up
1276 			}
1277 			else
1278 			{//lying down
1279 				if ( animLength - self->client->ps.legsTimer > 150 )
1280 				{//partially up
1281 					deathAnim = BOTH_DEATH_FALLING_UP;
1282 				}
1283 				else
1284 				{//down
1285 					deathAnim = BOTH_DEATH_LYING_UP;
1286 				}
1287 			}
1288 			break;
1289 		case BOTH_FORCE_GETUP_B5:
1290 			if ( self->client->ps.legsTimer < 550 )
1291 			{//standing up
1292 			}
1293 			else if ( self->client->ps.legsTimer < 1025 )
1294 			{//kicking up
1295 				deathAnim = BOTH_DEATHBACKWARD2; //backflip
1296 			}
1297 			else
1298 			{//lying down
1299 				if ( animLength - self->client->ps.legsTimer > 50 )
1300 				{//partially up
1301 					deathAnim = BOTH_DEATH_FALLING_UP;
1302 				}
1303 				else
1304 				{//down
1305 					deathAnim = BOTH_DEATH_LYING_UP;
1306 				}
1307 			}
1308 			break;
1309 		case BOTH_FORCE_GETUP_B6:
1310 			if ( self->client->ps.legsTimer < 225 )
1311 			{//standing up
1312 			}
1313 			else if ( self->client->ps.legsTimer < 425 )
1314 			{//crouching up
1315 				vec3_t fwd;
1316 				float thrown = 0;
1317 
1318 				AngleVectors( self->client->ps.viewangles, fwd, NULL, NULL );
1319 				thrown = DotProduct( fwd, self->client->ps.velocity );
1320 
1321 				if ( thrown < -150 )
1322 				{
1323 					deathAnim = BOTH_DEATHBACKWARD1;	//# Death anim when crouched and thrown back
1324 				}
1325 				else
1326 				{
1327 					deathAnim = BOTH_DEATH_CROUCHED;	//# Death anim when crouched
1328 				}
1329 			}
1330 			else if ( self->client->ps.legsTimer < 825 )
1331 			{//flipping up
1332 				deathAnim = BOTH_DEATHFORWARD3; //backflip
1333 			}
1334 			else
1335 			{//lying down
1336 				if ( animLength - self->client->ps.legsTimer > 225 )
1337 				{//partially up
1338 					deathAnim = BOTH_DEATH_FALLING_UP;
1339 				}
1340 				else
1341 				{//down
1342 					deathAnim = BOTH_DEATH_LYING_UP;
1343 				}
1344 			}
1345 			break;
1346 		case BOTH_FORCE_GETUP_F1:
1347 			if ( self->client->ps.legsTimer < 275 )
1348 			{//standing up
1349 			}
1350 			else if ( self->client->ps.legsTimer < 750 )
1351 			{//flipping
1352 				deathAnim = BOTH_DEATH14;
1353 			}
1354 			else
1355 			{//lying down
1356 				if ( animLength - self->client->ps.legsTimer > 100 )
1357 				{//partially up
1358 					deathAnim = BOTH_DEATH_FALLING_DN;
1359 				}
1360 				else
1361 				{//down
1362 					deathAnim = BOTH_DEATH_LYING_DN;
1363 				}
1364 			}
1365 			break;
1366 		case BOTH_FORCE_GETUP_F2:
1367 			if ( self->client->ps.legsTimer < 1200 )
1368 			{//standing
1369 			}
1370 			else
1371 			{//lying down
1372 				if ( animLength - self->client->ps.legsTimer > 225 )
1373 				{//partially up
1374 					deathAnim = BOTH_DEATH_FALLING_DN;
1375 				}
1376 				else
1377 				{//down
1378 					deathAnim = BOTH_DEATH_LYING_DN;
1379 				}
1380 			}
1381 			break;
1382 		}
1383 	}
1384 
1385 	return deathAnim;
1386 }
1387 
G_PickDeathAnim(gentity_t * self,vec3_t point,int damage,int mod,int hitLoc)1388 int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc )
1389 {//FIXME: play dead flop anims on body if in an appropriate _DEAD anim when this func is called
1390 	int deathAnim = -1;
1391 	int max_health;
1392 	int legAnim = 0;
1393 	vec3_t objVelocity;
1394 
1395 	if (!self || !self->client)
1396 	{
1397 		if (!self || self->s.eType != ET_NPC)
1398 		{ //g2animent
1399 			return 0;
1400 		}
1401 	}
1402 
1403 	if (self->client)
1404 	{
1405 		max_health = self->client->ps.stats[STAT_MAX_HEALTH];
1406 
1407 		if (self->client->inSpaceIndex && self->client->inSpaceIndex != ENTITYNUM_NONE)
1408 		{
1409 			return BOTH_CHOKE3;
1410 		}
1411 	}
1412 	else
1413 	{
1414 		max_health = 60;
1415 	}
1416 
1417 	if (self->client)
1418 	{
1419 		VectorCopy(self->client->ps.velocity, objVelocity);
1420 	}
1421 	else
1422 	{
1423 		VectorCopy(self->s.pos.trDelta, objVelocity);
1424 	}
1425 
1426 	if ( hitLoc == HL_NONE )
1427 	{
1428 		hitLoc = G_GetHitLocation( self, point );//self->hitLoc
1429 	}
1430 
1431 	if (self->client)
1432 	{
1433 		legAnim = self->client->ps.legsAnim;
1434 	}
1435 	else
1436 	{
1437 		legAnim = self->s.legsAnim;
1438 	}
1439 
1440 	if (gGAvoidDismember)
1441 	{
1442 		return BOTH_RIGHTHANDCHOPPEDOFF;
1443 	}
1444 
1445 	//dead flops
1446 	switch( legAnim )
1447 	{
1448 	case BOTH_DEATH1:		//# First Death anim
1449 	case BOTH_DEAD1:
1450 	case BOTH_DEATH2:			//# Second Death anim
1451 	case BOTH_DEAD2:
1452 	case BOTH_DEATH8:			//#
1453 	case BOTH_DEAD8:
1454 	case BOTH_DEATH13:			//#
1455 	case BOTH_DEAD13:
1456 	case BOTH_DEATH14:			//#
1457 	case BOTH_DEAD14:
1458 	case BOTH_DEATH16:			//#
1459 	case BOTH_DEAD16:
1460 	case BOTH_DEADBACKWARD1:		//# First thrown backward death finished pose
1461 	case BOTH_DEADBACKWARD2:		//# Second thrown backward death finished pose
1462 		deathAnim = -2;
1463 		break;
1464 		/*
1465 		if ( PM_FinishedCurrentLegsAnim( self ) )
1466 		{//done with the anim
1467 			deathAnim = BOTH_DEADFLOP2;
1468 		}
1469 		else
1470 		{
1471 			deathAnim = -2;
1472 		}
1473 		break;
1474 	case BOTH_DEADFLOP2:
1475 		deathAnim = BOTH_DEADFLOP2;
1476 		break;
1477 		*/
1478 	case BOTH_DEATH10:			//#
1479 	case BOTH_DEAD10:
1480 	case BOTH_DEATH15:			//#
1481 	case BOTH_DEAD15:
1482 	case BOTH_DEADFORWARD1:		//# First thrown forward death finished pose
1483 	case BOTH_DEADFORWARD2:		//# Second thrown forward death finished pose
1484 		deathAnim = -2;
1485 		break;
1486 		/*
1487 		if ( PM_FinishedCurrentLegsAnim( self ) )
1488 		{//done with the anim
1489 			deathAnim = BOTH_DEADFLOP1;
1490 		}
1491 		else
1492 		{
1493 			deathAnim = -2;
1494 		}
1495 		break;
1496 		*/
1497 	case BOTH_DEADFLOP1:
1498 		deathAnim = -2;
1499 		//deathAnim = BOTH_DEADFLOP1;
1500 		break;
1501 	case BOTH_DEAD3:				//# Third Death finished pose
1502 	case BOTH_DEAD4:				//# Fourth Death finished pose
1503 	case BOTH_DEAD5:				//# Fifth Death finished pose
1504 	case BOTH_DEAD6:				//# Sixth Death finished pose
1505 	case BOTH_DEAD7:				//# Seventh Death finished pose
1506 	case BOTH_DEAD9:				//#
1507 	case BOTH_DEAD11:			//#
1508 	case BOTH_DEAD12:			//#
1509 	case BOTH_DEAD17:			//#
1510 	case BOTH_DEAD18:			//#
1511 	case BOTH_DEAD19:			//#
1512 	case BOTH_LYINGDEAD1:		//# Killed lying down death finished pose
1513 	case BOTH_STUMBLEDEAD1:		//# Stumble forward death finished pose
1514 	case BOTH_FALLDEAD1LAND:		//# Fall forward and splat death finished pose
1515 	case BOTH_DEATH3:			//# Third Death anim
1516 	case BOTH_DEATH4:			//# Fourth Death anim
1517 	case BOTH_DEATH5:			//# Fifth Death anim
1518 	case BOTH_DEATH6:			//# Sixth Death anim
1519 	case BOTH_DEATH7:			//# Seventh Death anim
1520 	case BOTH_DEATH9:			//#
1521 	case BOTH_DEATH11:			//#
1522 	case BOTH_DEATH12:			//#
1523 	case BOTH_DEATH17:			//#
1524 	case BOTH_DEATH18:			//#
1525 	case BOTH_DEATH19:			//#
1526 	case BOTH_DEATHFORWARD1:		//# First Death in which they get thrown forward
1527 	case BOTH_DEATHFORWARD2:		//# Second Death in which they get thrown forward
1528 	case BOTH_DEATHBACKWARD1:	//# First Death in which they get thrown backward
1529 	case BOTH_DEATHBACKWARD2:	//# Second Death in which they get thrown backward
1530 	case BOTH_DEATH1IDLE:		//# Idle while close to death
1531 	case BOTH_LYINGDEATH1:		//# Death to play when killed lying down
1532 	case BOTH_STUMBLEDEATH1:		//# Stumble forward and fall face first death
1533 	case BOTH_FALLDEATH1:		//# Fall forward off a high cliff and splat death - start
1534 	case BOTH_FALLDEATH1INAIR:	//# Fall forward off a high cliff and splat death - loop
1535 	case BOTH_FALLDEATH1LAND:	//# Fall forward off a high cliff and splat death - hit bottom
1536 		deathAnim = -2;
1537 		break;
1538 	}
1539 	if ( deathAnim == -1 )
1540 	{
1541 		if (self->client)
1542 		{
1543 			deathAnim = G_CheckSpecialDeathAnim( self, point, damage, mod, hitLoc );
1544 		}
1545 
1546 		if (deathAnim == -1)
1547 		{
1548 			//death anims
1549 			switch( hitLoc )
1550 			{
1551 			case HL_FOOT_RT:
1552 			case HL_FOOT_LT:
1553 				if ( mod == MOD_SABER && !Q_irand( 0, 2 ) )
1554 				{
1555 					return BOTH_DEATH10;//chest: back flip
1556 				}
1557 				else if ( !Q_irand( 0, 2 ) )
1558 				{
1559 					deathAnim = BOTH_DEATH4;//back: forward
1560 				}
1561 				else if ( !Q_irand( 0, 1 ) )
1562 				{
1563 					deathAnim = BOTH_DEATH5;//same as 4
1564 				}
1565 				else
1566 				{
1567 					deathAnim = BOTH_DEATH15;//back: forward
1568 				}
1569 				break;
1570 			case HL_LEG_RT:
1571 				if ( !Q_irand( 0, 2 ) )
1572 				{
1573 					deathAnim = BOTH_DEATH4;//back: forward
1574 				}
1575 				else if ( !Q_irand( 0, 1 ) )
1576 				{
1577 					deathAnim = BOTH_DEATH5;//same as 4
1578 				}
1579 				else
1580 				{
1581 					deathAnim = BOTH_DEATH15;//back: forward
1582 				}
1583 				break;
1584 			case HL_LEG_LT:
1585 				if ( !Q_irand( 0, 2 ) )
1586 				{
1587 					deathAnim = BOTH_DEATH4;//back: forward
1588 				}
1589 				else if ( !Q_irand( 0, 1 ) )
1590 				{
1591 					deathAnim = BOTH_DEATH5;//same as 4
1592 				}
1593 				else
1594 				{
1595 					deathAnim = BOTH_DEATH15;//back: forward
1596 				}
1597 				break;
1598 			case HL_BACK:
1599 				if ( !VectorLengthSquared( objVelocity ) )
1600 				{
1601 					deathAnim = BOTH_DEATH17;//head/back: croak
1602 				}
1603 				else
1604 				{
1605 					if ( !Q_irand( 0, 2 ) )
1606 					{
1607 						deathAnim = BOTH_DEATH4;//back: forward
1608 					}
1609 					else if ( !Q_irand( 0, 1 ) )
1610 					{
1611 						deathAnim = BOTH_DEATH5;//same as 4
1612 					}
1613 					else
1614 					{
1615 						deathAnim = BOTH_DEATH15;//back: forward
1616 					}
1617 				}
1618 				break;
1619 			case HL_CHEST_RT:
1620 			case HL_ARM_RT:
1621 			case HL_HAND_RT:
1622 			case HL_BACK_RT:
1623 				if ( damage <= max_health*0.25 )
1624 				{
1625 					deathAnim = BOTH_DEATH9;//chest right: snap, fall forward
1626 				}
1627 				else if ( damage <= max_health*0.5 )
1628 				{
1629 					deathAnim = BOTH_DEATH3;//chest right: back
1630 				}
1631 				else if ( damage <= max_health*0.75 )
1632 				{
1633 					deathAnim = BOTH_DEATH6;//chest right: spin
1634 				}
1635 				else
1636 				{
1637 					//TEMP HACK: play spinny deaths less often
1638 					if ( Q_irand( 0, 1 ) )
1639 					{
1640 						deathAnim = BOTH_DEATH8;//chest right: spin high
1641 					}
1642 					else
1643 					{
1644 						switch ( Q_irand( 0, 2 ) )
1645 						{
1646 						default:
1647 						case 0:
1648 							deathAnim = BOTH_DEATH9;//chest right: snap, fall forward
1649 							break;
1650 						case 1:
1651 							deathAnim = BOTH_DEATH3;//chest right: back
1652 							break;
1653 						case 2:
1654 							deathAnim = BOTH_DEATH6;//chest right: spin
1655 							break;
1656 						}
1657 					}
1658 				}
1659 				break;
1660 			case HL_CHEST_LT:
1661 			case HL_ARM_LT:
1662 			case HL_HAND_LT:
1663 			case HL_BACK_LT:
1664 				if ( damage <= max_health*0.25 )
1665 				{
1666 					deathAnim = BOTH_DEATH11;//chest left: snap, fall forward
1667 				}
1668 				else if ( damage <= max_health*0.5 )
1669 				{
1670 					deathAnim = BOTH_DEATH7;//chest left: back
1671 				}
1672 				else if ( damage <= max_health*0.75 )
1673 				{
1674 					deathAnim = BOTH_DEATH12;//chest left: spin
1675 				}
1676 				else
1677 				{
1678 					//TEMP HACK: play spinny deaths less often
1679 					if ( Q_irand( 0, 1 ) )
1680 					{
1681 						deathAnim = BOTH_DEATH14;//chest left: spin high
1682 					}
1683 					else
1684 					{
1685 						switch ( Q_irand( 0, 2 ) )
1686 						{
1687 						default:
1688 						case 0:
1689 							deathAnim = BOTH_DEATH11;//chest left: snap, fall forward
1690 							break;
1691 						case 1:
1692 							deathAnim = BOTH_DEATH7;//chest left: back
1693 							break;
1694 						case 2:
1695 							deathAnim = BOTH_DEATH12;//chest left: spin
1696 							break;
1697 						}
1698 					}
1699 				}
1700 				break;
1701 			case HL_CHEST:
1702 			case HL_WAIST:
1703 				if ( damage <= max_health*0.25 || !VectorLengthSquared( objVelocity ) )
1704 				{
1705 					if ( !Q_irand( 0, 1 ) )
1706 					{
1707 						deathAnim = BOTH_DEATH18;//gut: fall right
1708 					}
1709 					else
1710 					{
1711 						deathAnim = BOTH_DEATH19;//gut: fall left
1712 					}
1713 				}
1714 				else if ( damage <= max_health*0.5 )
1715 				{
1716 					deathAnim = BOTH_DEATH2;//chest: backward short
1717 				}
1718 				else if ( damage <= max_health*0.75 )
1719 				{
1720 					if ( !Q_irand( 0, 1 ) )
1721 					{
1722 						deathAnim = BOTH_DEATH1;//chest: backward med
1723 					}
1724 					else
1725 					{
1726 						deathAnim = BOTH_DEATH16;//same as 1
1727 					}
1728 				}
1729 				else
1730 				{
1731 					deathAnim = BOTH_DEATH10;//chest: back flip
1732 				}
1733 				break;
1734 			case HL_HEAD:
1735 				if ( damage <= max_health*0.5 )
1736 				{
1737 					deathAnim = BOTH_DEATH17;//head/back: croak
1738 				}
1739 				else
1740 				{
1741 					deathAnim = BOTH_DEATH13;//head: stumble, fall back
1742 				}
1743 				break;
1744 			default:
1745 				break;
1746 			}
1747 		}
1748 	}
1749 
1750 	// Validate.....
1751 	if ( deathAnim == -1 || !BG_HasAnimation( self->localAnimIndex, deathAnim ))
1752 	{
1753 		// I guess we'll take what we can get.....
1754 		deathAnim = BG_PickAnim( self->localAnimIndex, BOTH_DEATH1, BOTH_DEATH25 );
1755 	}
1756 
1757 	return deathAnim;
1758 }
1759 
G_GetJediMaster(void)1760 gentity_t *G_GetJediMaster(void)
1761 {
1762 	int i = 0;
1763 	gentity_t *ent;
1764 
1765 	while (i < MAX_CLIENTS)
1766 	{
1767 		ent = &g_entities[i];
1768 
1769 		if (ent && ent->inuse && ent->client && ent->client->ps.isJediMaster)
1770 		{
1771 			return ent;
1772 		}
1773 
1774 		i++;
1775 	}
1776 
1777 	return NULL;
1778 }
1779 
1780 /*
1781 -------------------------
1782 G_AlertTeam
1783 -------------------------
1784 */
1785 
G_AlertTeam(gentity_t * victim,gentity_t * attacker,float radius,float soundDist)1786 void G_AlertTeam( gentity_t *victim, gentity_t *attacker, float radius, float soundDist )
1787 {
1788 	int			radiusEnts[ 128 ];
1789 	gentity_t	*check;
1790 	vec3_t		mins, maxs;
1791 	int			numEnts;
1792 	int			i;
1793 	float		distSq, sndDistSq = (soundDist*soundDist);
1794 
1795 	if ( attacker == NULL || attacker->client == NULL )
1796 		return;
1797 
1798 	//Setup the bbox to search in
1799 	for ( i = 0; i < 3; i++ )
1800 	{
1801 		mins[i] = victim->r.currentOrigin[i] - radius;
1802 		maxs[i] = victim->r.currentOrigin[i] + radius;
1803 	}
1804 
1805 	//Get the number of entities in a given space
1806 	numEnts = trap->EntitiesInBox( mins, maxs, radiusEnts, 128 );
1807 
1808 	//Cull this list
1809 	for ( i = 0; i < numEnts; i++ )
1810 	{
1811 		check = &g_entities[radiusEnts[i]];
1812 
1813 		//Validate clients
1814 		if ( check->client == NULL )
1815 			continue;
1816 
1817 		//only want NPCs
1818 		if ( check->NPC == NULL )
1819 			continue;
1820 
1821 		//Don't bother if they're ignoring enemies
1822 //		if ( check->svFlags & SVF_IGNORE_ENEMIES )
1823 //			continue;
1824 
1825 		//This NPC specifically flagged to ignore alerts
1826 		if ( check->NPC->scriptFlags & SCF_IGNORE_ALERTS )
1827 			continue;
1828 
1829 		//This NPC specifically flagged to ignore alerts
1830 		if ( !(check->NPC->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
1831 			continue;
1832 
1833 		//this ent does not participate in group AI
1834 		if ( (check->NPC->scriptFlags&SCF_NO_GROUPS) )
1835 			continue;
1836 
1837 		//Skip the requested avoid check if present
1838 		if ( check == victim )
1839 			continue;
1840 
1841 		//Skip the attacker
1842 		if ( check == attacker )
1843 			continue;
1844 
1845 		//Must be on the same team
1846 		if ( check->client->playerTeam != victim->client->playerTeam )
1847 			continue;
1848 
1849 		//Must be alive
1850 		if ( check->health <= 0 )
1851 			continue;
1852 
1853 		if ( check->enemy == NULL )
1854 		{//only do this if they're not already mad at someone
1855 			distSq = DistanceSquared( check->r.currentOrigin, victim->r.currentOrigin );
1856 			if ( distSq > 16384 /*128 squared*/ && !trap->InPVS( victim->r.currentOrigin, check->r.currentOrigin ) )
1857 			{//not even potentially visible/hearable
1858 				continue;
1859 			}
1860 			//NOTE: this allows sound alerts to still go through doors/PVS if the teammate is within 128 of the victim...
1861 			if ( soundDist <= 0 || distSq > sndDistSq )
1862 			{//out of sound range
1863 				if ( !InFOV( victim, check, check->NPC->stats.hfov, check->NPC->stats.vfov )
1864 					||  !NPC_ClearLOS2( check, victim->r.currentOrigin ) )
1865 				{//out of FOV or no LOS
1866 					continue;
1867 				}
1868 			}
1869 
1870 			//FIXME: This can have a nasty cascading effect if setup wrong...
1871 			G_SetEnemy( check, attacker );
1872 		}
1873 	}
1874 }
1875 
1876 /*
1877 -------------------------
1878 G_DeathAlert
1879 -------------------------
1880 */
1881 
1882 #define	DEATH_ALERT_RADIUS			512
1883 #define	DEATH_ALERT_SOUND_RADIUS	512
1884 
G_DeathAlert(gentity_t * victim,gentity_t * attacker)1885 void G_DeathAlert( gentity_t *victim, gentity_t *attacker )
1886 {//FIXME: with all the other alert stuff, do we really need this?
1887 	G_AlertTeam( victim, attacker, DEATH_ALERT_RADIUS, DEATH_ALERT_SOUND_RADIUS );
1888 }
1889 
1890 /*
1891 ----------------------------------------
1892 DeathFX
1893 
1894 Applies appropriate special effects that occur while the entity is dying
1895 Not to be confused with NPC_RemoveBodyEffects (NPC.cpp), which only applies effect when removing the body
1896 ----------------------------------------
1897 */
1898 
DeathFX(gentity_t * ent)1899 void DeathFX( gentity_t *ent )
1900 {
1901 	vec3_t		effectPos, right;
1902 	vec3_t		defaultDir;
1903 
1904 	if ( !ent || !ent->client )
1905 		return;
1906 
1907 	VectorSet(defaultDir, 0, 0, 1);
1908 
1909 	// team no longer indicates species/race.  NPC_class should be used to identify certain npc types
1910 	switch(ent->client->NPC_class)
1911 	{
1912 	case CLASS_MOUSE:
1913 		VectorCopy( ent->r.currentOrigin, effectPos );
1914 		effectPos[2] -= 20;
1915 		G_PlayEffectID( G_EffectIndex("env/small_explode"), effectPos, defaultDir );
1916 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mouse/misc/death1") );
1917 		break;
1918 
1919 	case CLASS_PROBE:
1920 		VectorCopy( ent->r.currentOrigin, effectPos );
1921 		effectPos[2] += 50;
1922 		G_PlayEffectID( G_EffectIndex("explosions/probeexplosion1"), effectPos, defaultDir );
1923 		break;
1924 
1925 	case CLASS_ATST:
1926 		AngleVectors( ent->r.currentAngles, NULL, right, NULL );
1927 		VectorMA( ent->r.currentOrigin, 20, right, effectPos );
1928 		effectPos[2] += 180;
1929 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1930 		VectorMA( effectPos, -40, right, effectPos );
1931 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1932 		break;
1933 
1934 	case CLASS_SEEKER:
1935 	case CLASS_REMOTE:
1936 		G_PlayEffectID( G_EffectIndex("env/small_explode"), ent->r.currentOrigin, defaultDir );
1937 		break;
1938 
1939 	case CLASS_GONK:
1940 		VectorCopy( ent->r.currentOrigin, effectPos );
1941 		effectPos[2] -= 5;
1942 //		statusTextIndex = Q_irand( IGT_RESISTANCEISFUTILE, IGT_NAMEIS8OF12 );
1943 		G_Sound( ent, CHAN_AUTO, G_SoundIndex(va("sound/chars/gonk/misc/death%d.wav",Q_irand( 1, 3 ))) );
1944 		G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
1945 		break;
1946 
1947 	// should list all remaining droids here, hope I didn't miss any
1948 	case CLASS_R2D2:
1949 		VectorCopy( ent->r.currentOrigin, effectPos );
1950 		effectPos[2] -= 10;
1951 		G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
1952 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_explo") );
1953 		break;
1954 
1955 	case CLASS_PROTOCOL: //c3p0
1956 	case CLASS_R5D2:
1957 		VectorCopy( ent->r.currentOrigin, effectPos );
1958 		effectPos[2] -= 10;
1959 		G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
1960 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_explo") );
1961 		break;
1962 
1963 	case CLASS_MARK2:
1964 		VectorCopy( ent->r.currentOrigin, effectPos );
1965 		effectPos[2] -= 15;
1966 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1967 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_explo") );
1968 		break;
1969 
1970 	case CLASS_INTERROGATOR:
1971 		VectorCopy( ent->r.currentOrigin, effectPos );
1972 		effectPos[2] -= 15;
1973 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1974 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/interrogator/misc/int_droid_explo") );
1975 		break;
1976 
1977 	case CLASS_MARK1:
1978 		AngleVectors( ent->r.currentAngles, NULL, right, NULL );
1979 		VectorMA( ent->r.currentOrigin, 10, right, effectPos );
1980 		effectPos[2] -= 15;
1981 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1982 		VectorMA( effectPos, -20, right, effectPos );
1983 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1984 		VectorMA( effectPos, -20, right, effectPos );
1985 		G_PlayEffectID( G_EffectIndex("explosions/droidexplosion1"), effectPos, defaultDir );
1986 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_explo") );
1987 		break;
1988 
1989 	case CLASS_SENTRY:
1990 		G_Sound( ent, CHAN_AUTO, G_SoundIndex("sound/chars/sentry/misc/sentry_explo") );
1991 		VectorCopy( ent->r.currentOrigin, effectPos );
1992 		G_PlayEffectID( G_EffectIndex("env/med_explode"), effectPos, defaultDir );
1993 		break;
1994 
1995 	default:
1996 		break;
1997 
1998 	}
1999 
2000 }
2001 
G_CheckVictoryScript(gentity_t * self)2002 void G_CheckVictoryScript(gentity_t *self)
2003 {
2004 	if ( !G_ActivateBehavior( self, BSET_VICTORY ) )
2005 	{
2006 		if ( self->NPC && self->s.weapon == WP_SABER )
2007 		{//Jedi taunt from within their AI
2008 			self->NPC->blockedSpeechDebounceTime = 0;//get them ready to taunt
2009 			return;
2010 		}
2011 		if ( self->client && self->client->NPC_class == CLASS_GALAKMECH )
2012 		{
2013 			self->wait = 1;
2014 			TIMER_Set( self, "gloatTime", Q_irand( 5000, 8000 ) );
2015 			self->NPC->blockedSpeechDebounceTime = 0;//get him ready to taunt
2016 			return;
2017 		}
2018 		//FIXME: any way to not say this *right away*?  Wait for victim's death anim/scream to finish?
2019 		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 ) )
2020 		{//sometimes have the group commander speak instead
2021 			self->NPC->group->commander->NPC->greetingDebounceTime = level.time + Q_irand( 2000, 5000 );
2022 			//G_AddVoiceEvent( self->NPC->group->commander, Q_irand(EV_VICTORY1, EV_VICTORY3), 2000 );
2023 		}
2024 		else if ( self->NPC )
2025 		{
2026 			self->NPC->greetingDebounceTime = level.time + Q_irand( 2000, 5000 );
2027 			//G_AddVoiceEvent( self, Q_irand(EV_VICTORY1, EV_VICTORY3), 2000 );
2028 		}
2029 	}
2030 }
2031 
G_AddPowerDuelScore(int team,int score)2032 void G_AddPowerDuelScore(int team, int score)
2033 {
2034 	int i = 0;
2035 	gentity_t *check;
2036 
2037 	while (i < MAX_CLIENTS)
2038 	{
2039 		check = &g_entities[i];
2040 		if (check->inuse && check->client &&
2041 			check->client->pers.connected == CON_CONNECTED && !check->client->iAmALoser &&
2042 			check->client->ps.stats[STAT_HEALTH] > 0 &&
2043 			check->client->sess.sessionTeam != TEAM_SPECTATOR &&
2044 			check->client->sess.duelTeam == team)
2045 		{ //found a living client on the specified team
2046 			check->client->sess.wins += score;
2047 			ClientUserinfoChanged(check->s.number);
2048 		}
2049 		i++;
2050 	}
2051 }
2052 
G_AddPowerDuelLoserScore(int team,int score)2053 void G_AddPowerDuelLoserScore(int team, int score)
2054 {
2055 	int i = 0;
2056 	gentity_t *check;
2057 
2058 	while (i < MAX_CLIENTS)
2059 	{
2060 		check = &g_entities[i];
2061 		if (check->inuse && check->client &&
2062 			check->client->pers.connected == CON_CONNECTED &&
2063 			(check->client->iAmALoser || (check->client->ps.stats[STAT_HEALTH] <= 0 && check->client->sess.sessionTeam != TEAM_SPECTATOR)) &&
2064 			check->client->sess.duelTeam == team)
2065 		{ //found a living client on the specified team
2066 			check->client->sess.losses += score;
2067 			ClientUserinfoChanged(check->s.number);
2068 		}
2069 		i++;
2070 	}
2071 }
2072 
2073 /*
2074 ==================
2075 player_die
2076 ==================
2077 */
2078 extern stringID_table_t animTable[MAX_ANIMATIONS+1];
2079 
2080 extern void AI_DeleteSelfFromGroup( gentity_t *self );
2081 extern void AI_GroupMemberKilled( gentity_t *self );
2082 extern void Boba_FlyStop( gentity_t *self );
2083 extern qboolean Jedi_WaitingAmbush( gentity_t *self );
2084 void CheckExitRules( void );
2085 extern void Rancor_DropVictim( gentity_t *self );
2086 
2087 extern qboolean g_dontFrickinCheck;
2088 extern qboolean g_endPDuel;
2089 extern qboolean g_noPDuelCheck;
2090 extern void saberReactivate(gentity_t *saberent, gentity_t *saberOwner);
2091 extern void saberBackToOwner(gentity_t *saberent);
player_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int meansOfDeath)2092 void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
2093 	gentity_t	*ent;
2094 	int			anim;
2095 	int			killer;
2096 	int			i;
2097 	char		*killerName, *obit;
2098 	qboolean	wasJediMaster = qfalse;
2099 	int			sPMType = 0;
2100 	char		buf[512] = {0};
2101 
2102 	if ( self->client->ps.pm_type == PM_DEAD ) {
2103 		return;
2104 	}
2105 
2106 	if ( level.intermissiontime ) {
2107 		return;
2108 	}
2109 
2110 	if ( !attacker )
2111 		return;
2112 
2113 	//check player stuff
2114 	g_dontFrickinCheck = qfalse;
2115 
2116 	if (level.gametype == GT_POWERDUEL)
2117 	{ //don't want to wait til later in the frame if this is the case
2118 		CheckExitRules();
2119 
2120 		if ( level.intermissiontime )
2121 		{
2122 			return;
2123 		}
2124 	}
2125 
2126 	if (self->s.eType == ET_NPC &&
2127 		self->s.NPC_class == CLASS_VEHICLE &&
2128 		self->m_pVehicle &&
2129 		!self->m_pVehicle->m_pVehicleInfo->explosionDelay &&
2130 		(self->m_pVehicle->m_pPilot || self->m_pVehicle->m_iNumPassengers > 0 || self->m_pVehicle->m_pDroidUnit))
2131 	{ //kill everyone on board in the name of the attacker... if the vehicle has no death delay
2132 		gentity_t *murderer = NULL;
2133 		gentity_t *killEnt;
2134 
2135 		if (self->client->ps.otherKillerTime >= level.time)
2136 		{ //use the last attacker
2137 			murderer = &g_entities[self->client->ps.otherKiller];
2138 			if (!murderer->inuse || !murderer->client)
2139 			{
2140 				murderer = NULL;
2141 			}
2142 			else
2143 			{
2144 				if (murderer->s.number >= MAX_CLIENTS &&
2145 					murderer->s.eType == ET_NPC &&
2146 					murderer->s.NPC_class == CLASS_VEHICLE &&
2147 					murderer->m_pVehicle &&
2148 					murderer->m_pVehicle->m_pPilot)
2149 				{
2150 					gentity_t *murderPilot = &g_entities[murderer->m_pVehicle->m_pPilot->s.number];
2151 					if (murderPilot->inuse && murderPilot->client)
2152 					{ //give the pilot of the offending vehicle credit for the kill
2153 						murderer = murderPilot;
2154 					}
2155 				}
2156 			}
2157 		}
2158 		else if (attacker && attacker->inuse && attacker->client)
2159 		{
2160 			if (attacker->s.number >= MAX_CLIENTS &&
2161 				attacker->s.eType == ET_NPC &&
2162 				attacker->s.NPC_class == CLASS_VEHICLE &&
2163 				attacker->m_pVehicle &&
2164 				attacker->m_pVehicle->m_pPilot)
2165 			{ //set vehicles pilot's killer as murderer
2166 				murderer = &g_entities[attacker->m_pVehicle->m_pPilot->s.number];
2167 				if (murderer->inuse && murderer->client &&murderer->client->ps.otherKillerTime >= level.time)
2168 				{
2169 					murderer = &g_entities[murderer->client->ps.otherKiller];
2170 					if (!murderer->inuse || !murderer->client)
2171 					{
2172 						murderer = NULL;
2173 					}
2174 				}
2175 				else
2176 				{
2177 					murderer = NULL;
2178 				}
2179 			}
2180 			else
2181 			{
2182 				murderer = &g_entities[attacker->s.number];
2183 			}
2184 		}
2185 		else if (self->m_pVehicle->m_pPilot)
2186 		{
2187 			murderer = (gentity_t *)self->m_pVehicle->m_pPilot;
2188 			if (!murderer->inuse || !murderer->client)
2189 			{
2190 				murderer = NULL;
2191 			}
2192 		}
2193 
2194 		//no valid murderer.. just use self I guess
2195 		if (!murderer)
2196 		{
2197 			murderer = self;
2198 		}
2199 
2200 		if ( self->m_pVehicle->m_pVehicleInfo->hideRider )
2201 		{//pilot is *inside* me, so kill him, too
2202 			killEnt = (gentity_t *)self->m_pVehicle->m_pPilot;
2203 			if (killEnt && killEnt->inuse && killEnt->client)
2204 			{
2205 				G_Damage(killEnt, murderer, murderer, NULL, killEnt->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_BLASTER);
2206 			}
2207 			if ( self->m_pVehicle->m_pVehicleInfo )
2208 			{//FIXME: this wile got stuck in an endless loop, that's BAD!!  This method SUCKS (not initting "i", not incrementing it or using it directly, all sorts of badness), so I'm rewriting it
2209 				//while (i < self->m_pVehicle->m_iNumPassengers)
2210 				int numPass = self->m_pVehicle->m_iNumPassengers;
2211 				for ( i = 0; i < numPass && self->m_pVehicle->m_iNumPassengers; i++ )
2212 				{//go through and eject the last passenger
2213 					killEnt = (gentity_t *)self->m_pVehicle->m_ppPassengers[self->m_pVehicle->m_iNumPassengers-1];
2214 					if ( killEnt )
2215 					{
2216 						self->m_pVehicle->m_pVehicleInfo->Eject(self->m_pVehicle, (bgEntity_t *)killEnt, qtrue);
2217 						if ( killEnt->inuse && killEnt->client )
2218 						{
2219 							G_Damage(killEnt, murderer, murderer, NULL, killEnt->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_BLASTER);
2220 						}
2221 					}
2222 				}
2223 			}
2224 		}
2225 		killEnt = (gentity_t *)self->m_pVehicle->m_pDroidUnit;
2226 		if (killEnt && killEnt->inuse && killEnt->client)
2227 		{
2228 			killEnt->flags &= ~FL_UNDYING;
2229 			G_Damage(killEnt, murderer, murderer, NULL, killEnt->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_BLASTER);
2230 		}
2231 	}
2232 
2233 	self->client->ps.emplacedIndex = 0;
2234 
2235 	G_BreakArm(self, 0); //unbreak anything we have broken
2236 	self->client->ps.saberEntityNum = self->client->saberStoredIndex; //in case we died while our saber was knocked away.
2237 
2238 	if (self->client->ps.weapon == WP_SABER && self->client->saberKnockedTime)
2239 	{
2240 		gentity_t *saberEnt = &g_entities[self->client->ps.saberEntityNum];
2241 		//trap->Print("DEBUG: Running saber cleanup for %s\n", self->client->pers.netname);
2242 		self->client->saberKnockedTime = 0;
2243 		saberReactivate(saberEnt, self);
2244 		saberEnt->r.contents = CONTENTS_LIGHTSABER;
2245 		saberEnt->think = saberBackToOwner;
2246 		saberEnt->nextthink = level.time;
2247 		G_RunObject(saberEnt);
2248 	}
2249 
2250 	self->client->bodyGrabIndex = ENTITYNUM_NONE;
2251 	self->client->bodyGrabTime = 0;
2252 
2253 	if (self->client->holdingObjectiveItem > 0)
2254 	{ //carrying a siege objective item - make sure it updates and removes itself from us now in case this is an instant death-respawn situation
2255 		gentity_t *objectiveItem = &g_entities[self->client->holdingObjectiveItem];
2256 
2257 		if (objectiveItem->inuse && objectiveItem->think)
2258 		{
2259             objectiveItem->think(objectiveItem);
2260 		}
2261 	}
2262 
2263 	if ( (self->client->inSpaceIndex && self->client->inSpaceIndex != ENTITYNUM_NONE) ||
2264 		 (self->client->ps.eFlags2 & EF2_SHIP_DEATH) )
2265 	{
2266 		self->client->noCorpse = qtrue;
2267 	}
2268 
2269 	if ( self->client->NPC_class != CLASS_VEHICLE
2270 		&& self->client->ps.m_iVehicleNum )
2271 	{ //I'm riding a vehicle
2272 		//tell it I'm getting off
2273 		gentity_t *veh = &g_entities[self->client->ps.m_iVehicleNum];
2274 
2275 		if (veh->inuse && veh->client && veh->m_pVehicle)
2276 		{
2277 			veh->m_pVehicle->m_pVehicleInfo->Eject(veh->m_pVehicle, (bgEntity_t *)self, qtrue);
2278 
2279 			if (veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
2280 			{ //go into "die in ship" mode with flag
2281 				self->client->ps.eFlags2 |= EF2_SHIP_DEATH;
2282 
2283 				//put me over where my vehicle exploded
2284 				G_SetOrigin(self, veh->client->ps.origin);
2285 				VectorCopy(veh->client->ps.origin, self->client->ps.origin);
2286 			}
2287 		}
2288 		//droids throw heads if they haven't yet
2289 		switch(self->client->NPC_class)
2290 		{
2291 		case CLASS_R2D2:
2292 			if ( !trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" ) )
2293 			{
2294 				vec3_t	up;
2295 				AngleVectors( self->r.currentAngles, NULL, NULL, up );
2296 				G_PlayEffectID( G_EffectIndex("chunks/r2d2head_veh"), self->r.currentOrigin, up );
2297 			}
2298 			break;
2299 
2300 		case CLASS_R5D2:
2301 			if ( !trap->G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" ) )
2302 			{
2303 				vec3_t	up;
2304 				AngleVectors( self->r.currentAngles, NULL, NULL, up );
2305 				G_PlayEffectID( G_EffectIndex("chunks/r5d2head_veh"), self->r.currentOrigin, up );
2306 			}
2307 			break;
2308 		default:
2309 			break;
2310 		}
2311 	}
2312 
2313 	if ( self->NPC )
2314 	{
2315 		if ( self->client && Jedi_WaitingAmbush( self ) )
2316 		{//ambushing trooper
2317 			self->client->noclip = qfalse;
2318 		}
2319 		NPC_FreeCombatPoint( self->NPC->combatPoint, qfalse );
2320 		if ( self->NPC->group )
2321 		{
2322 			//lastInGroup = (self->NPC->group->numGroup < 2);
2323 			AI_GroupMemberKilled( self );
2324 			AI_DeleteSelfFromGroup( self );
2325 		}
2326 
2327 		if ( self->NPC->tempGoal )
2328 		{
2329 			G_FreeEntity( self->NPC->tempGoal );
2330 			self->NPC->tempGoal = NULL;
2331 		}
2332 		/*
2333 		if ( self->s.eFlags & EF_LOCKED_TO_WEAPON )
2334 		{
2335 			// dumb, just get the NPC out of the chair
2336 extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd );
2337 
2338 			usercmd_t cmd, *ad_cmd;
2339 
2340 			memset( &cmd, 0, sizeof( usercmd_t ));
2341 
2342 			//gentity_t *old = self->owner;
2343 
2344 			if ( self->owner )
2345 			{
2346 				self->owner->s.frame = self->owner->startFrame = self->owner->endFrame = 0;
2347 				self->owner->svFlags &= ~SVF_ANIMATING;
2348 			}
2349 
2350 			cmd.buttons |= BUTTON_USE;
2351 			ad_cmd = &cmd;
2352 			RunEmplacedWeapon( self, &ad_cmd );
2353 			//self->owner = old;
2354 		}
2355 		*/
2356 		if ( self->client->NPC_class == CLASS_BOBAFETT && self->client->ps.eFlags2 & EF2_FLYING )
2357 			Boba_FlyStop( self );
2358 		if ( self->s.NPC_class == CLASS_RANCOR )
2359 			Rancor_DropVictim( self );
2360 	}
2361 	if ( attacker && attacker->NPC && attacker->NPC->group && attacker->NPC->group->enemy == self )
2362 	{
2363 		attacker->NPC->group->enemy = NULL;
2364 	}
2365 
2366 	//Cheap method until/if I decide to put fancier stuff in (e.g. sabers falling out of hand and slowly
2367 	//holstering on death like sp)
2368 	if (self->client->ps.weapon == WP_SABER &&
2369 		!self->client->ps.saberHolstered &&
2370 		self->client->ps.saberEntityNum)
2371 	{
2372 		if (!self->client->ps.saberInFlight &&
2373 			self->client->saber[0].soundOff)
2374 		{
2375 			G_Sound(self, CHAN_AUTO, self->client->saber[0].soundOff);
2376 		}
2377 		if (self->client->saber[1].soundOff &&
2378 			self->client->saber[1].model[0])
2379 		{
2380 			G_Sound(self, CHAN_AUTO, self->client->saber[1].soundOff);
2381 		}
2382 	}
2383 
2384 	//Use any target we had
2385 	G_UseTargets( self, self );
2386 
2387 	if (g_slowmoDuelEnd.integer && (level.gametype == GT_DUEL || level.gametype == GT_POWERDUEL) && attacker && attacker->inuse && attacker->client)
2388 	{
2389 		if (!gDoSlowMoDuel)
2390 		{
2391 			gDoSlowMoDuel = qtrue;
2392 			gSlowMoDuelTime = level.time;
2393 		}
2394 	}
2395 	/*
2396 	else if (self->NPC && attacker && attacker->client && attacker->s.number < MAX_CLIENTS && !gDoSlowMoDuel)
2397 	{
2398 		gDoSlowMoDuel = qtrue;
2399 		gSlowMoDuelTime = level.time;
2400 	}
2401 	*/
2402 
2403 	//Make sure the jetpack is turned off.
2404 	Jetpack_Off(self);
2405 
2406 	self->client->ps.heldByClient = 0;
2407 	self->client->beingThrown = 0;
2408 	self->client->doingThrow = 0;
2409 	BG_ClearRocketLock( &self->client->ps );
2410 	self->client->isHacking = 0;
2411 	self->client->ps.hackingTime = 0;
2412 
2413 	if (inflictor && inflictor->activator && !inflictor->client && !attacker->client &&
2414 		inflictor->activator->client && inflictor->activator->inuse &&
2415 		inflictor->s.weapon == WP_TURRET)
2416 	{
2417 		attacker = inflictor->activator;
2418 	}
2419 
2420 	if (self->client && self->client->ps.isJediMaster)
2421 	{
2422 		wasJediMaster = qtrue;
2423 	}
2424 
2425 	//if he was charging or anything else, kill the sound
2426 	G_MuteSound(self->s.number, CHAN_WEAPON);
2427 
2428 	//FIXME: this may not be enough
2429 	if ( level.gametype == GT_SIEGE && meansOfDeath == MOD_TEAM_CHANGE )
2430 		RemoveDetpacks( self );
2431 	else
2432 		BlowDetpacks(self); //blow detpacks if they're planted
2433 
2434 	self->client->ps.fd.forceDeactivateAll = 1;
2435 
2436 	if ((self == attacker || (attacker && !attacker->client)) &&
2437 		(meansOfDeath == MOD_CRUSH || meansOfDeath == MOD_FALLING || meansOfDeath == MOD_TRIGGER_HURT || meansOfDeath == MOD_UNKNOWN) &&
2438 		self->client->ps.otherKillerTime > level.time)
2439 	{
2440 		attacker = &g_entities[self->client->ps.otherKiller];
2441 	}
2442 
2443 	// check for an almost capture
2444 	CheckAlmostCapture( self, attacker );
2445 
2446 	self->client->ps.pm_type = PM_DEAD;
2447 	self->client->ps.pm_flags &= ~PMF_STUCK_TO_WALL;
2448 
2449 	if ( attacker ) {
2450 		killer = attacker->s.number;
2451 		if ( attacker->client ) {
2452 			killerName = attacker->client->pers.netname;
2453 		} else {
2454 			killerName = "<non-client>";
2455 		}
2456 	} else {
2457 		killer = ENTITYNUM_WORLD;
2458 		killerName = "<world>";
2459 	}
2460 
2461 	if ( killer < 0 || killer >= MAX_CLIENTS ) {
2462 		killer = ENTITYNUM_WORLD;
2463 		killerName = "<world>";
2464 	}
2465 
2466 	if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) {
2467 		obit = "<bad obituary>";
2468 	} else {
2469 		obit = modNames[ meansOfDeath ];
2470 	}
2471 
2472 	// log the victim and attacker's names with the method of death
2473 	Com_sprintf( buf, sizeof( buf ), "Kill: %i %i %i: %s killed ", killer, self->s.number, meansOfDeath, killerName );
2474 	if ( self->s.eType == ET_NPC ) {
2475 		// check for named NPCs
2476 		if ( self->targetname )
2477 			Q_strcat( buf, sizeof( buf ), va( "%s (%s) by %s\n", self->NPC_type, self->targetname, obit ) );
2478 		else
2479 			Q_strcat( buf, sizeof( buf ), va( "%s by %s\n", self->NPC_type, obit ) );
2480 	}
2481 	else
2482 		Q_strcat( buf, sizeof( buf ), va( "%s by %s\n", self->client->pers.netname, obit ) );
2483 	G_LogPrintf( "%s", buf );
2484 
2485 	if ( g_austrian.integer
2486 		&& level.gametype == GT_DUEL
2487 		&& level.numPlayingClients >= 2 )
2488 	{
2489 		int spawnTime = (level.clients[level.sortedClients[0]].respawnTime > level.clients[level.sortedClients[1]].respawnTime) ? level.clients[level.sortedClients[0]].respawnTime : level.clients[level.sortedClients[1]].respawnTime;
2490 		G_LogPrintf("Duel Kill Details:\n");
2491 		G_LogPrintf("Kill Time: %d\n", level.time-spawnTime );
2492 		G_LogPrintf("victim: %s, hits on enemy %d\n", self->client->pers.netname, self->client->ps.persistant[PERS_HITS] );
2493 		if ( attacker && attacker->client )
2494 		{
2495 			G_LogPrintf("killer: %s, hits on enemy %d, health: %d\n", attacker->client->pers.netname, attacker->client->ps.persistant[PERS_HITS], attacker->health );
2496 			//also - if MOD_SABER, list the animation and saber style
2497 			if ( meansOfDeath == MOD_SABER )
2498 			{
2499 				G_LogPrintf("killer saber style: %d, killer saber anim %s\n", attacker->client->ps.fd.saberAnimLevel, animTable[(attacker->client->ps.torsoAnim)].name );
2500 			}
2501 		}
2502 	}
2503 
2504 	G_LogWeaponKill(killer, meansOfDeath);
2505 	G_LogWeaponDeath(self->s.number, self->s.weapon);
2506 	if (attacker && attacker->client && attacker->inuse)
2507 	{
2508 		G_LogWeaponFrag(killer, self->s.number);
2509 	}
2510 
2511 	// broadcast the death event to everyone
2512 	if (self->s.eType != ET_NPC && !g_noPDuelCheck)
2513 	{
2514 		ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
2515 		ent->s.eventParm = meansOfDeath;
2516 		ent->s.otherEntityNum = self->s.number;
2517 		ent->s.otherEntityNum2 = killer;
2518 		ent->r.svFlags = SVF_BROADCAST;	// send to everyone
2519 		ent->s.isJediMaster = wasJediMaster;
2520 	}
2521 
2522 	self->enemy = attacker;
2523 
2524 	self->client->ps.persistant[PERS_KILLED]++;
2525 
2526 	if (self == attacker)
2527 	{
2528 		self->client->ps.fd.suicides++;
2529 	}
2530 
2531 	if (attacker && attacker->client) {
2532 		attacker->client->lastkilled_client = self->s.number;
2533 
2534 		G_CheckVictoryScript(attacker);
2535 
2536 		if ( attacker == self || OnSameTeam (self, attacker ) ) {
2537 			if (level.gametype == GT_DUEL)
2538 			{ //in duel, if you kill yourself, the person you are dueling against gets a kill for it
2539 				int otherClNum = -1;
2540 				if (level.sortedClients[0] == self->s.number)
2541 				{
2542 					otherClNum = level.sortedClients[1];
2543 				}
2544 				else if (level.sortedClients[1] == self->s.number)
2545 				{
2546 					otherClNum = level.sortedClients[0];
2547 				}
2548 
2549 				if (otherClNum >= 0 && otherClNum < MAX_CLIENTS &&
2550 					g_entities[otherClNum].inuse && g_entities[otherClNum].client &&
2551 					otherClNum != attacker->s.number)
2552 				{
2553 					AddScore( &g_entities[otherClNum], self->r.currentOrigin, 1 );
2554 				}
2555 				else
2556 				{
2557 					AddScore( attacker, self->r.currentOrigin, -1 );
2558 				}
2559 			}
2560 			else
2561 			{
2562 				AddScore( attacker, self->r.currentOrigin, -1 );
2563 			}
2564 			if (level.gametype == GT_JEDIMASTER)
2565 			{
2566 				if (self->client && self->client->ps.isJediMaster)
2567 				{ //killed ourself so return the saber to the original position
2568 				  //(to avoid people jumping off ledges and making the saber
2569 				  //unreachable for 60 seconds)
2570 					ThrowSaberToAttacker(self, NULL);
2571 					self->client->ps.isJediMaster = qfalse;
2572 				}
2573 			}
2574 		} else {
2575 			if (level.gametype == GT_JEDIMASTER)
2576 			{
2577 				if ((attacker->client && attacker->client->ps.isJediMaster) ||
2578 					(self->client && self->client->ps.isJediMaster))
2579 				{
2580 					AddScore( attacker, self->r.currentOrigin, 1 );
2581 
2582 					if (self->client && self->client->ps.isJediMaster)
2583 					{
2584 						ThrowSaberToAttacker(self, attacker);
2585 						self->client->ps.isJediMaster = qfalse;
2586 					}
2587 				}
2588 				else
2589 				{
2590 					gentity_t *jmEnt = G_GetJediMaster();
2591 
2592 					if (jmEnt && jmEnt->client)
2593 					{
2594 						AddScore( jmEnt, self->r.currentOrigin, 1 );
2595 					}
2596 				}
2597 			}
2598 			else
2599 			{
2600 				AddScore( attacker, self->r.currentOrigin, 1 );
2601 			}
2602 
2603 			if( meansOfDeath == MOD_STUN_BATON ) {
2604 
2605 				// play humiliation on player
2606 				attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
2607 
2608 				attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
2609 
2610 				// also play humiliation on target
2611 				self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD;
2612 			}
2613 
2614 			// check for two kills in a short amount of time
2615 			// if this is close enough to the last kill, give a reward sound
2616 			if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) {
2617 				// play excellent on player
2618 				attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
2619 
2620 				attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
2621 			}
2622 			attacker->client->lastKillTime = level.time;
2623 
2624 		}
2625 	} else {
2626 		if (self->client && self->client->ps.isJediMaster)
2627 		{ //killed ourself so return the saber to the original position
2628 		  //(to avoid people jumping off ledges and making the saber
2629 		  //unreachable for 60 seconds)
2630 			ThrowSaberToAttacker(self, NULL);
2631 			self->client->ps.isJediMaster = qfalse;
2632 		}
2633 
2634 		if (level.gametype == GT_DUEL)
2635 		{ //in duel, if you kill yourself, the person you are dueling against gets a kill for it
2636 			int otherClNum = -1;
2637 			if (level.sortedClients[0] == self->s.number)
2638 			{
2639 				otherClNum = level.sortedClients[1];
2640 			}
2641 			else if (level.sortedClients[1] == self->s.number)
2642 			{
2643 				otherClNum = level.sortedClients[0];
2644 			}
2645 
2646 			if (otherClNum >= 0 && otherClNum < MAX_CLIENTS &&
2647 				g_entities[otherClNum].inuse && g_entities[otherClNum].client &&
2648 				otherClNum != self->s.number)
2649 			{
2650 				AddScore( &g_entities[otherClNum], self->r.currentOrigin, 1 );
2651 			}
2652 			else
2653 			{
2654 				AddScore( self, self->r.currentOrigin, -1 );
2655 			}
2656 		}
2657 		else
2658 		{
2659 			AddScore( self, self->r.currentOrigin, -1 );
2660 		}
2661 	}
2662 
2663 	// Add team bonuses
2664 	Team_FragBonuses(self, inflictor, attacker);
2665 
2666 	// if I committed suicide, the flag does not fall, it returns.
2667 	if (meansOfDeath == MOD_SUICIDE) {
2668 		if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {		// only happens in One Flag CTF
2669 			Team_ReturnFlag( TEAM_FREE );
2670 			self->client->ps.powerups[PW_NEUTRALFLAG] = 0;
2671 		}
2672 		else if ( self->client->ps.powerups[PW_REDFLAG] ) {		// only happens in standard CTF
2673 			Team_ReturnFlag( TEAM_RED );
2674 			self->client->ps.powerups[PW_REDFLAG] = 0;
2675 		}
2676 		else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {	// only happens in standard CTF
2677 			Team_ReturnFlag( TEAM_BLUE );
2678 			self->client->ps.powerups[PW_BLUEFLAG] = 0;
2679 		}
2680 	}
2681 
2682 	if (!self->client->ps.fallingToDeath) {
2683 		if (self->s.eType != ET_NPC)
2684 		{
2685 			TossClientItems( self );
2686 		}
2687 	}
2688 	else {
2689 		if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {		// only happens in One Flag CTF
2690 			Team_ReturnFlag( TEAM_FREE );
2691 			self->client->ps.powerups[PW_NEUTRALFLAG] = 0;
2692 		}
2693 		else if ( self->client->ps.powerups[PW_REDFLAG] ) {		// only happens in standard CTF
2694 			Team_ReturnFlag( TEAM_RED );
2695 			self->client->ps.powerups[PW_REDFLAG] = 0;
2696 		}
2697 		else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {	// only happens in standard CTF
2698 			Team_ReturnFlag( TEAM_BLUE );
2699 			self->client->ps.powerups[PW_BLUEFLAG] = 0;
2700 		}
2701 	}
2702 
2703 	if ( MOD_TEAM_CHANGE == meansOfDeath )
2704 	{
2705 		// Give them back a point since they didn't really die.
2706 		AddScore( self, self->r.currentOrigin, 1 );
2707 	}
2708 	else
2709 	{
2710 		Cmd_Score_f( self );		// show scores
2711 	}
2712 
2713 	// send updated scores to any clients that are following this one,
2714 	// or they would get stale scoreboards
2715 	for ( i = 0 ; i < level.maxclients ; i++ ) {
2716 		gclient_t *cl = &level.clients[i];
2717 		if ( cl->pers.connected != CON_CONNECTED ) {
2718 			continue;
2719 		}
2720 		if ( cl->sess.sessionTeam != TEAM_SPECTATOR ) {
2721 			continue;
2722 		}
2723 		if ( cl->sess.spectatorClient == self->s.number ) {
2724 			Cmd_Score_f( g_entities + i );
2725 		}
2726 	}
2727 
2728 	self->takedamage = qtrue;	// can still be gibbed
2729 
2730 	self->s.weapon = WP_NONE;
2731 	self->s.powerups = 0;
2732 	if (self->s.eType != ET_NPC)
2733 	{ //handled differently for NPCs
2734 		self->r.contents = CONTENTS_CORPSE;
2735 	}
2736 	self->client->ps.zoomMode = 0;	// Turn off zooming when we die
2737 
2738 	//rww - 07/19/02 - I removed this because it isn't working and it's ugly (for people on the outside)
2739 	/*
2740 	self->s.angles[0] = 0;
2741 	self->s.angles[2] = 0;
2742 	LookAtKiller (self, inflictor, attacker);
2743 
2744 	VectorCopy( self->s.angles, self->client->ps.viewangles );
2745 	*/
2746 
2747 	self->s.loopSound = 0;
2748 	self->s.loopIsSoundset = qfalse;
2749 
2750 	if (self->s.eType != ET_NPC)
2751 	{ //handled differently for NPCs
2752 		self->r.maxs[2] = -8;
2753 	}
2754 
2755 	// don't allow respawn until the death anim is done
2756 	// g_forcerespawn may force spawning at some later time
2757 	self->client->respawnTime = level.time + 1700;
2758 
2759 	// remove powerups
2760 	memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );
2761 
2762 	self->client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
2763 	self->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
2764 
2765 	// NOTENOTE No gib deaths right now, this is star wars.
2766 	/*
2767 	// never gib in a nodrop
2768 	if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE)
2769 	{
2770 		// gib death
2771 		GibEntity( self, killer );
2772 	}
2773 	else
2774 	*/
2775 	{
2776 		// normal death
2777 
2778 		static int deathAnim;
2779 
2780 		anim = G_PickDeathAnim(self, self->pos1, damage, meansOfDeath, HL_NONE);
2781 
2782 		if (anim >= 1)
2783 		{ //Some droids don't have death anims
2784 			// for the no-blood option, we need to prevent the health
2785 			// from going to gib level
2786 			if ( self->health <= GIB_HEALTH ) {
2787 				self->health = GIB_HEALTH+1;
2788 			}
2789 
2790 			self->client->respawnTime = level.time + 1000;//((self->client->animations[anim].numFrames*40)/(50.0f / self->client->animations[anim].frameLerp))+300;
2791 
2792 			sPMType = self->client->ps.pm_type;
2793 			self->client->ps.pm_type = PM_NORMAL; //don't want pm type interfering with our setanim calls.
2794 
2795 			if (self->inuse)
2796 			{ //not disconnecting
2797 				G_SetAnim(self, NULL, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART, 0);
2798 			}
2799 
2800 			self->client->ps.pm_type = sPMType;
2801 
2802 			if (meansOfDeath == MOD_SABER || (meansOfDeath == MOD_MELEE && G_HeavyMelee( attacker )) )//saber or heavy melee (claws)
2803 			{ //update the anim on the actual skeleton (so bolt point will reflect the correct position) and then check for dismem
2804 				G_UpdateClientAnims(self, 1.0f);
2805 				G_CheckForDismemberment(self, attacker, self->pos1, damage, anim, qfalse);
2806 			}
2807 		}
2808 		else if (self->NPC && self->client && self->client->NPC_class != CLASS_MARK1 &&
2809 			self->client->NPC_class != CLASS_VEHICLE)
2810 		{ //in this case if we're an NPC it's my guess that we want to get removed straight away.
2811 			self->think = G_FreeEntity;
2812 			self->nextthink = level.time;
2813 		}
2814 
2815 		//self->client->ps.legsAnim = anim;
2816 		//self->client->ps.torsoAnim = anim;
2817 //		self->client->ps.pm_flags |= PMF_UPDATE_ANIM;		// Make sure the pmove sets up the GHOUL2 anims.
2818 
2819 		//rww - do this on respawn, not death
2820 		//CopyToBodyQue (self);
2821 
2822 		//G_AddEvent( self, EV_DEATH1 + i, killer );
2823 		if (wasJediMaster)
2824 		{
2825 			G_AddEvent( self, EV_DEATH1 + deathAnim, 1 );
2826 		}
2827 		else
2828 		{
2829 			G_AddEvent( self, EV_DEATH1 + deathAnim, 0 );
2830 		}
2831 
2832 		if (self != attacker)
2833 		{ //don't make NPCs want to murder you on respawn for killing yourself!
2834 			G_DeathAlert( self, attacker );
2835 		}
2836 
2837 		// the body can still be gibbed
2838 		if (!self->NPC)
2839 		{ //don't remove NPCs like this!
2840 			self->die = body_die;
2841 		}
2842 
2843 		//It won't gib, it will disintegrate (because this is Star Wars).
2844 		self->takedamage = qtrue;
2845 
2846 		// globally cycle through the different death animations
2847 		deathAnim = ( deathAnim + 1 ) % 3;
2848 	}
2849 
2850 	if ( self->NPC )
2851 	{//If an NPC, make sure we start running our scripts again- this gets set to infinite while we fall to our deaths
2852 		self->NPC->nextBStateThink = level.time;
2853 	}
2854 
2855 	if ( G_ActivateBehavior( self, BSET_DEATH ) )
2856 	{
2857 		//deathScript = qtrue;
2858 	}
2859 
2860 	if ( self->NPC && (self->NPC->scriptFlags&SCF_FFDEATH) )
2861 	{
2862 		if ( G_ActivateBehavior( self, BSET_FFDEATH ) )
2863 		{//FIXME: should running this preclude running the normal deathscript?
2864 			//deathScript = qtrue;
2865 		}
2866 		G_UseTargets2( self, self, self->target4 );
2867 	}
2868 
2869 	/*
2870 	if ( !deathScript && !(self->svFlags&SVF_KILLED_SELF) )
2871 	{
2872 		//Should no longer run scripts
2873 		//WARNING!!! DO NOT DO THIS WHILE RUNNING A SCRIPT, ICARUS WILL CRASH!!!
2874 		//FIXME: shouldn't ICARUS handle this internally?
2875 		ICARUS_FreeEnt(self);
2876 	}
2877 	*/
2878 	//rwwFIXMEFIXME: Do this too?
2879 
2880 	// Free up any timers we may have on us.
2881 	TIMER_Clear2( self );
2882 
2883 	trap->LinkEntity ((sharedEntity_t *)self);
2884 
2885 	if ( self->NPC )
2886 	{
2887 		self->NPC->timeOfDeath = level.time;//this will change - used for debouncing post-death events
2888 	}
2889 
2890 	// Start any necessary death fx for this entity
2891 	if ( self->NPC )
2892 		DeathFX( self );
2893 
2894 
2895 	if (level.gametype == GT_POWERDUEL && !g_noPDuelCheck)
2896 	{ //powerduel checks
2897 		if (self->client->sess.duelTeam == DUELTEAM_LONE)
2898 		{ //automatically means a win as there is only one
2899 			G_AddPowerDuelScore(DUELTEAM_DOUBLE, 1);
2900 			G_AddPowerDuelLoserScore(DUELTEAM_LONE, 1);
2901 			g_endPDuel = qtrue;
2902 		}
2903 		else if (self->client->sess.duelTeam == DUELTEAM_DOUBLE)
2904 		{
2905 			gentity_t *check;
2906 			qboolean heLives = qfalse;
2907 
2908 			for ( i=0; i<MAX_CLIENTS; i++ )
2909 			{
2910 				check = &g_entities[i];
2911 				if (check->inuse && check->client && check->s.number != self->s.number &&
2912 					check->client->pers.connected == CON_CONNECTED && !check->client->iAmALoser &&
2913 					check->client->ps.stats[STAT_HEALTH] > 0 &&
2914 					check->client->sess.sessionTeam != TEAM_SPECTATOR &&
2915 					check->client->sess.duelTeam == DUELTEAM_DOUBLE)
2916 				{ //still an active living paired duelist so it's not over yet.
2917 					heLives = qtrue;
2918 					break;
2919 				}
2920 			}
2921 
2922 			if (!heLives)
2923 			{ //they're all dead, give the lone duelist the win.
2924 				G_AddPowerDuelScore(DUELTEAM_LONE, 1);
2925 				G_AddPowerDuelLoserScore(DUELTEAM_DOUBLE, 1);
2926 				g_endPDuel = qtrue;
2927 			}
2928 		}
2929 	}
2930 }
2931 
2932 
2933 /*
2934 ================
2935 CheckArmor
2936 ================
2937 */
CheckArmor(gentity_t * ent,int damage,int dflags)2938 int CheckArmor (gentity_t *ent, int damage, int dflags)
2939 {
2940 	gclient_t	*client;
2941 	int			save;
2942 	int			count;
2943 
2944 	if (!damage)
2945 		return 0;
2946 
2947 	client = ent->client;
2948 
2949 	if (!client)
2950 		return 0;
2951 
2952 	if (dflags & DAMAGE_NO_ARMOR)
2953 		return 0;
2954 
2955 	if ( client->NPC_class == CLASS_VEHICLE
2956 		&& ent->m_pVehicle
2957 		&& ent->client->ps.electrifyTime > level.time )
2958 	{//ion-cannon has disabled this ship's shields, take damage on hull!
2959 		return 0;
2960 	}
2961 	// armor
2962 	count = client->ps.stats[STAT_ARMOR];
2963 
2964 	if (dflags & DAMAGE_HALF_ABSORB)
2965 	{	// Half the damage gets absorbed by the shields, rather than 100%
2966 		save = ceil( damage * ARMOR_PROTECTION );
2967 	}
2968 	else
2969 	{	// All the damage gets absorbed by the shields.
2970 		save = damage;
2971 	}
2972 
2973 	// save is the most damage that the armor is elibigle to protect, of course, but it's limited by the total armor.
2974 	if (save >= count)
2975 		save = count;
2976 
2977 	if (!save)
2978 		return 0;
2979 
2980 	if (dflags & DAMAGE_HALF_ARMOR_REDUCTION)		// Armor isn't whittled so easily by sniper shots.
2981 	{
2982 		client->ps.stats[STAT_ARMOR] -= (int)(save*ARMOR_REDUCTION_FACTOR);
2983 	}
2984 	else
2985 	{
2986 		client->ps.stats[STAT_ARMOR] -= save;
2987 	}
2988 
2989 	return save;
2990 }
2991 
2992 
G_ApplyKnockback(gentity_t * targ,vec3_t newDir,float knockback)2993 void G_ApplyKnockback( gentity_t *targ, vec3_t newDir, float knockback )
2994 {
2995 	vec3_t	kvel;
2996 	float	mass;
2997 
2998 	if ( targ->physicsBounce > 0 )	//overide the mass
2999 		mass = targ->physicsBounce;
3000 	else
3001 		mass = 200;
3002 
3003 	if ( g_gravity.value > 0 )
3004 	{
3005 		VectorScale( newDir, g_knockback.value * (float)knockback / mass * 0.8, kvel );
3006 		kvel[2] = newDir[2] * g_knockback.value * (float)knockback / mass * 1.5;
3007 	}
3008 	else
3009 	{
3010 		VectorScale( newDir, g_knockback.value * (float)knockback / mass, kvel );
3011 	}
3012 
3013 	if ( targ->client )
3014 	{
3015 		VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );
3016 	}
3017 	else if ( targ->s.pos.trType != TR_STATIONARY && targ->s.pos.trType != TR_LINEAR_STOP && targ->s.pos.trType != TR_NONLINEAR_STOP )
3018 	{
3019 		VectorAdd( targ->s.pos.trDelta, kvel, targ->s.pos.trDelta );
3020 		VectorCopy( targ->r.currentOrigin, targ->s.pos.trBase );
3021 		targ->s.pos.trTime = level.time;
3022 	}
3023 
3024 	// set the timer so that the other client can't cancel
3025 	// out the movement immediately
3026 	if ( targ->client && !targ->client->ps.pm_time )
3027 	{
3028 		int		t;
3029 
3030 		t = knockback * 2;
3031 		if ( t < 50 ) {
3032 			t = 50;
3033 		}
3034 		if ( t > 200 ) {
3035 			t = 200;
3036 		}
3037 		targ->client->ps.pm_time = t;
3038 		targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
3039 	}
3040 }
3041 
3042 /*
3043 ================
3044 RaySphereIntersections
3045 ================
3046 */
RaySphereIntersections(vec3_t origin,float radius,vec3_t point,vec3_t dir,vec3_t intersections[2])3047 int RaySphereIntersections( vec3_t origin, float radius, vec3_t point, vec3_t dir, vec3_t intersections[2] ) {
3048 	float b, c, d, t;
3049 
3050 	//	| origin - (point + t * dir) | = radius
3051 	//	a = dir[0]^2 + dir[1]^2 + dir[2]^2;
3052 	//	b = 2 * (dir[0] * (point[0] - origin[0]) + dir[1] * (point[1] - origin[1]) + dir[2] * (point[2] - origin[2]));
3053 	//	c = (point[0] - origin[0])^2 + (point[1] - origin[1])^2 + (point[2] - origin[2])^2 - radius^2;
3054 
3055 	// normalize dir so a = 1
3056 	VectorNormalize(dir);
3057 	b = 2 * (dir[0] * (point[0] - origin[0]) + dir[1] * (point[1] - origin[1]) + dir[2] * (point[2] - origin[2]));
3058 	c = (point[0] - origin[0]) * (point[0] - origin[0]) +
3059 		(point[1] - origin[1]) * (point[1] - origin[1]) +
3060 		(point[2] - origin[2]) * (point[2] - origin[2]) -
3061 		radius * radius;
3062 
3063 	d = b * b - 4 * c;
3064 	if (d > 0) {
3065 		t = (- b + sqrt(d)) / 2;
3066 		VectorMA(point, t, dir, intersections[0]);
3067 		t = (- b - sqrt(d)) / 2;
3068 		VectorMA(point, t, dir, intersections[1]);
3069 		return 2;
3070 	}
3071 	else if (d == 0) {
3072 		t = (- b ) / 2;
3073 		VectorMA(point, t, dir, intersections[0]);
3074 		return 1;
3075 	}
3076 	return 0;
3077 }
3078 
3079 /*
3080 ===================================
3081 rww - beginning of the majority of the dismemberment and location based damage code.
3082 ===================================
3083 */
3084 char *hitLocName[HL_MAX] =
3085 {
3086 	"none",	//HL_NONE = 0,
3087 	"right foot",	//HL_FOOT_RT,
3088 	"left foot",	//HL_FOOT_LT,
3089 	"right leg",	//HL_LEG_RT,
3090 	"left leg",	//HL_LEG_LT,
3091 	"waist",	//HL_WAIST,
3092 	"back right shoulder",	//HL_BACK_RT,
3093 	"back left shoulder",	//HL_BACK_LT,
3094 	"back",	//HL_BACK,
3095 	"front right shouler",	//HL_CHEST_RT,
3096 	"front left shoulder",	//HL_CHEST_LT,
3097 	"chest",	//HL_CHEST,
3098 	"right arm",	//HL_ARM_RT,
3099 	"left arm",	//HL_ARM_LT,
3100 	"right hand",	//HL_HAND_RT,
3101 	"left hand",	//HL_HAND_LT,
3102 	"head",	//HL_HEAD
3103 	"generic1",	//HL_GENERIC1,
3104 	"generic2",	//HL_GENERIC2,
3105 	"generic3",	//HL_GENERIC3,
3106 	"generic4",	//HL_GENERIC4,
3107 	"generic5",	//HL_GENERIC5,
3108 	"generic6"	//HL_GENERIC6
3109 };
3110 
G_GetDismemberLoc(gentity_t * self,vec3_t boltPoint,int limbType)3111 void G_GetDismemberLoc(gentity_t *self, vec3_t boltPoint, int limbType)
3112 { //Just get the general area without using server-side ghoul2
3113 	vec3_t fwd, right, up;
3114 
3115 	AngleVectors(self->r.currentAngles, fwd, right, up);
3116 
3117 	VectorCopy(self->r.currentOrigin, boltPoint);
3118 
3119 	switch (limbType)
3120 	{
3121 	case G2_MODELPART_HEAD:
3122 		boltPoint[0] += up[0]*24;
3123 		boltPoint[1] += up[1]*24;
3124 		boltPoint[2] += up[2]*24;
3125 		break;
3126 	case G2_MODELPART_WAIST:
3127 		boltPoint[0] += up[0]*4;
3128 		boltPoint[1] += up[1]*4;
3129 		boltPoint[2] += up[2]*4;
3130 		break;
3131 	case G2_MODELPART_LARM:
3132 		boltPoint[0] += up[0]*18;
3133 		boltPoint[1] += up[1]*18;
3134 		boltPoint[2] += up[2]*18;
3135 
3136 		boltPoint[0] -= right[0]*10;
3137 		boltPoint[1] -= right[1]*10;
3138 		boltPoint[2] -= right[2]*10;
3139 		break;
3140 	case G2_MODELPART_RARM:
3141 		boltPoint[0] += up[0]*18;
3142 		boltPoint[1] += up[1]*18;
3143 		boltPoint[2] += up[2]*18;
3144 
3145 		boltPoint[0] += right[0]*10;
3146 		boltPoint[1] += right[1]*10;
3147 		boltPoint[2] += right[2]*10;
3148 		break;
3149 	case G2_MODELPART_RHAND:
3150 		boltPoint[0] += up[0]*8;
3151 		boltPoint[1] += up[1]*8;
3152 		boltPoint[2] += up[2]*8;
3153 
3154 		boltPoint[0] += right[0]*10;
3155 		boltPoint[1] += right[1]*10;
3156 		boltPoint[2] += right[2]*10;
3157 		break;
3158 	case G2_MODELPART_LLEG:
3159 		boltPoint[0] -= up[0]*4;
3160 		boltPoint[1] -= up[1]*4;
3161 		boltPoint[2] -= up[2]*4;
3162 
3163 		boltPoint[0] -= right[0]*10;
3164 		boltPoint[1] -= right[1]*10;
3165 		boltPoint[2] -= right[2]*10;
3166 		break;
3167 	case G2_MODELPART_RLEG:
3168 		boltPoint[0] -= up[0]*4;
3169 		boltPoint[1] -= up[1]*4;
3170 		boltPoint[2] -= up[2]*4;
3171 
3172 		boltPoint[0] += right[0]*10;
3173 		boltPoint[1] += right[1]*10;
3174 		boltPoint[2] += right[2]*10;
3175 		break;
3176 	default:
3177 		break;
3178 	}
3179 
3180 	return;
3181 }
3182 
G_GetDismemberBolt(gentity_t * self,vec3_t boltPoint,int limbType)3183 void G_GetDismemberBolt(gentity_t *self, vec3_t boltPoint, int limbType)
3184 {
3185 	int useBolt = self->genericValue5;
3186 	vec3_t properOrigin, properAngles, addVel;
3187 	//matrix3_t legAxis;
3188 	mdxaBone_t	boltMatrix;
3189 	float fVSpeed = 0;
3190 	char *rotateBone = NULL;
3191 
3192 	switch (limbType)
3193 	{
3194 	case G2_MODELPART_HEAD:
3195 		rotateBone = "cranium";
3196 		break;
3197 	case G2_MODELPART_WAIST:
3198 		if (self->localAnimIndex <= 1)
3199 		{ //humanoid
3200 			rotateBone = "thoracic";
3201 		}
3202 		else
3203 		{
3204 			rotateBone = "pelvis";
3205 		}
3206 		break;
3207 	case G2_MODELPART_LARM:
3208 		rotateBone = "lradius";
3209 		break;
3210 	case G2_MODELPART_RARM:
3211 		rotateBone = "rradius";
3212 		break;
3213 	case G2_MODELPART_RHAND:
3214 		rotateBone = "rhand";
3215 		break;
3216 	case G2_MODELPART_LLEG:
3217 		rotateBone = "ltibia";
3218 		break;
3219 	case G2_MODELPART_RLEG:
3220 		rotateBone = "rtibia";
3221 		break;
3222 	default:
3223 		rotateBone = "rtibia";
3224 		break;
3225 	}
3226 
3227 	useBolt = trap->G2API_AddBolt(self->ghoul2, 0, rotateBone);
3228 
3229 	VectorCopy(self->client->ps.origin, properOrigin);
3230 	VectorCopy(self->client->ps.viewangles, properAngles);
3231 
3232 	//try to predict the origin based on velocity so it's more like what the client is seeing
3233 	VectorCopy(self->client->ps.velocity, addVel);
3234 	VectorNormalize(addVel);
3235 
3236 	if (self->client->ps.velocity[0] < 0)
3237 	{
3238 		fVSpeed += (-self->client->ps.velocity[0]);
3239 	}
3240 	else
3241 	{
3242 		fVSpeed += self->client->ps.velocity[0];
3243 	}
3244 	if (self->client->ps.velocity[1] < 0)
3245 	{
3246 		fVSpeed += (-self->client->ps.velocity[1]);
3247 	}
3248 	else
3249 	{
3250 		fVSpeed += self->client->ps.velocity[1];
3251 	}
3252 	if (self->client->ps.velocity[2] < 0)
3253 	{
3254 		fVSpeed += (-self->client->ps.velocity[2]);
3255 	}
3256 	else
3257 	{
3258 		fVSpeed += self->client->ps.velocity[2];
3259 	}
3260 
3261 	fVSpeed *= 0.08f;
3262 
3263 	properOrigin[0] += addVel[0]*fVSpeed;
3264 	properOrigin[1] += addVel[1]*fVSpeed;
3265 	properOrigin[2] += addVel[2]*fVSpeed;
3266 
3267 	properAngles[0] = 0;
3268 	properAngles[1] = self->client->ps.viewangles[YAW];
3269 	properAngles[2] = 0;
3270 
3271 	trap->G2API_GetBoltMatrix(self->ghoul2, 0, useBolt, &boltMatrix, properAngles, properOrigin, level.time, NULL, self->modelScale);
3272 
3273 	boltPoint[0] = boltMatrix.matrix[0][3];
3274 	boltPoint[1] = boltMatrix.matrix[1][3];
3275 	boltPoint[2] = boltMatrix.matrix[2][3];
3276 
3277 	trap->G2API_GetBoltMatrix(self->ghoul2, 1, 0, &boltMatrix, properAngles, properOrigin, level.time, NULL, self->modelScale);
3278 
3279 	if (self->client && limbType == G2_MODELPART_RHAND)
3280 	{ //Make some saber hit sparks over the severed wrist area
3281 		vec3_t boltAngles;
3282 		gentity_t *te;
3283 
3284 		boltAngles[0] = -boltMatrix.matrix[0][1];
3285 		boltAngles[1] = -boltMatrix.matrix[1][1];
3286 		boltAngles[2] = -boltMatrix.matrix[2][1];
3287 
3288 		te = G_TempEntity( boltPoint, EV_SABER_HIT );
3289 		te->s.otherEntityNum = self->s.number;
3290 		te->s.otherEntityNum2 = ENTITYNUM_NONE;
3291 		te->s.weapon = 0;//saberNum
3292 		te->s.legsAnim = 0;//bladeNum
3293 
3294 		VectorCopy(boltPoint, te->s.origin);
3295 		VectorCopy(boltAngles, te->s.angles);
3296 
3297 		if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
3298 		{ //don't let it play with no direction
3299 			te->s.angles[1] = 1;
3300 		}
3301 
3302 		te->s.eventParm = 16; //lots of sparks
3303 	}
3304 }
3305 
LimbTouch(gentity_t * self,gentity_t * other,trace_t * trace)3306 void LimbTouch( gentity_t *self, gentity_t *other, trace_t *trace )
3307 {
3308 }
3309 
LimbThink(gentity_t * ent)3310 void LimbThink( gentity_t *ent )
3311 {
3312 	float gravity = 3.0f;
3313 	float mass = 0.09f;
3314 	float bounce = 1.3f;
3315 
3316 	switch (ent->s.modelGhoul2)
3317 	{
3318 	case G2_MODELPART_HEAD:
3319 		mass = 0.08f;
3320 		bounce = 1.4f;
3321 		break;
3322 	case G2_MODELPART_WAIST:
3323 		mass = 0.1f;
3324 		bounce = 1.2f;
3325 		break;
3326 	case G2_MODELPART_LARM:
3327 	case G2_MODELPART_RARM:
3328 	case G2_MODELPART_RHAND:
3329 	case G2_MODELPART_LLEG:
3330 	case G2_MODELPART_RLEG:
3331 	default:
3332 		break;
3333 	}
3334 
3335 	if (ent->speed < level.time)
3336 	{
3337 		ent->think = G_FreeEntity;
3338 		ent->nextthink = level.time;
3339 		return;
3340 	}
3341 
3342 	if (ent->genericValue5 <= level.time)
3343 	{ //this will be every frame by standard, but we want to compensate in case sv_fps is not 20.
3344 		G_RunExPhys(ent, gravity, mass, bounce, qtrue, NULL, 0);
3345 		ent->genericValue5 = level.time + 50;
3346 	}
3347 
3348 	ent->nextthink = level.time;
3349 }
3350 
3351 extern qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize );
3352 
G_Dismember(gentity_t * ent,gentity_t * enemy,vec3_t point,int limbType,float limbRollBase,float limbPitchBase,int deathAnim,qboolean postDeath)3353 void G_Dismember( gentity_t *ent, gentity_t *enemy, vec3_t point, int limbType, float limbRollBase, float limbPitchBase, int deathAnim, qboolean postDeath )
3354 {
3355 	vec3_t	newPoint, dir, vel;
3356 	gentity_t *limb;
3357 	char	limbName[MAX_QPATH];
3358 	char	stubName[MAX_QPATH];
3359 	char	stubCapName[MAX_QPATH];
3360 
3361 	if (limbType == G2_MODELPART_HEAD)
3362 	{
3363 		Q_strncpyz( limbName , "head", sizeof( limbName  ) );
3364 		Q_strncpyz( stubCapName, "torso_cap_head", sizeof( stubCapName ) );
3365 	}
3366 	else if (limbType == G2_MODELPART_WAIST)
3367 	{
3368 		Q_strncpyz( limbName, "torso", sizeof( limbName ) );
3369 		Q_strncpyz( stubCapName, "hips_cap_torso", sizeof( stubCapName ) );
3370 	}
3371 	else if (limbType == G2_MODELPART_LARM)
3372 	{
3373 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "l_arm", limbName, sizeof(limbName) );
3374 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "torso", stubName, sizeof(stubName) );
3375 		Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_arm", stubName );
3376 	}
3377 	else if (limbType == G2_MODELPART_RARM)
3378 	{
3379 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_arm", limbName, sizeof(limbName) );
3380 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "torso", stubName, sizeof(stubName) );
3381 		Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_arm", stubName );
3382 	}
3383 	else if (limbType == G2_MODELPART_RHAND)
3384 	{
3385 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_hand", limbName, sizeof(limbName) );
3386 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_arm", stubName, sizeof(stubName) );
3387 		Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_hand", stubName );
3388 	}
3389 	else if (limbType == G2_MODELPART_LLEG)
3390 	{
3391 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "l_leg", limbName, sizeof(limbName) );
3392 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "hips", stubName, sizeof(stubName) );
3393 		Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_leg", stubName );
3394 	}
3395 	else if (limbType == G2_MODELPART_RLEG)
3396 	{
3397 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_leg", limbName, sizeof(limbName) );
3398 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "hips", stubName, sizeof(stubName) );
3399 		Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
3400 	}
3401 	else
3402 	{//umm... just default to the right leg, I guess (same as on client)
3403 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "r_leg", limbName, sizeof(limbName) );
3404 		BG_GetRootSurfNameWithVariant( ent->ghoul2, "hips", stubName, sizeof(stubName) );
3405 		Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
3406 	}
3407 
3408 	if (ent->ghoul2 && limbName[0] && trap->G2API_GetSurfaceRenderStatus(ent->ghoul2, 0, limbName))
3409 	{ //is it already off? If so there's no reason to be doing it again, so get out of here.
3410 		return;
3411 	}
3412 
3413 	VectorCopy( point, newPoint );
3414 	limb = G_Spawn();
3415 	limb->classname = "playerlimb";
3416 
3417 	/*
3418 	if (limbType == G2_MODELPART_WAIST)
3419 	{ //slight hack
3420 		newPoint[2] += 1;
3421 	}
3422 	*/
3423 
3424 	G_SetOrigin( limb, newPoint );
3425 	VectorCopy( newPoint, limb->s.pos.trBase );
3426 	limb->think = LimbThink;
3427 	limb->touch = LimbTouch;
3428 	limb->speed = level.time + Q_irand(8000, 16000);
3429 	limb->nextthink = level.time + FRAMETIME;
3430 
3431 	limb->r.svFlags = SVF_USE_CURRENT_ORIGIN;
3432 	limb->clipmask = MASK_SOLID;
3433 	limb->r.contents = CONTENTS_TRIGGER;
3434 	limb->physicsObject = qtrue;
3435 	VectorSet( limb->r.mins, -6.0f, -6.0f, -3.0f );
3436 	VectorSet( limb->r.maxs, 6.0f, 6.0f, 6.0f );
3437 
3438 	limb->s.g2radius = 200;
3439 
3440 	limb->s.eType = ET_GENERAL;
3441 	limb->s.weapon = G2_MODEL_PART;
3442 	limb->s.modelGhoul2 = limbType;
3443 	limb->s.modelindex = ent->s.number;
3444 	if (!ent->client)
3445 	{
3446 		limb->s.modelindex = -1;
3447 		limb->s.otherEntityNum2 = ent->s.number;
3448 	}
3449 
3450 	VectorClear(limb->s.apos.trDelta);
3451 
3452 	if (ent->client)
3453 	{
3454 		VectorCopy(ent->client->ps.viewangles, limb->r.currentAngles);
3455 		VectorCopy(ent->client->ps.viewangles, limb->s.apos.trBase);
3456 	}
3457 	else
3458 	{
3459 		VectorCopy(ent->r.currentAngles, limb->r.currentAngles);
3460 		VectorCopy(ent->r.currentAngles, limb->s.apos.trBase);
3461 	}
3462 
3463 	//Set up the ExPhys values for the entity.
3464 	limb->epGravFactor = 0;
3465 	VectorClear(limb->epVelocity);
3466 	VectorSubtract( point, ent->r.currentOrigin, dir );
3467 	VectorNormalize( dir );
3468 	if (ent->client)
3469 	{
3470 		VectorCopy(ent->client->ps.velocity, vel);
3471 	}
3472 	else
3473 	{
3474 		VectorCopy(ent->s.pos.trDelta, vel);
3475 	}
3476 	VectorMA( vel, 80, dir, limb->epVelocity );
3477 
3478 	//add some vertical velocity
3479 	if (limbType == G2_MODELPART_HEAD ||
3480 		limbType == G2_MODELPART_WAIST)
3481 	{
3482 		limb->epVelocity[2] += 10;
3483 	}
3484 
3485 	if (enemy && enemy->client && ent && ent != enemy && ent->s.number != enemy->s.number &&
3486 		enemy->client->ps.weapon == WP_SABER && enemy->client->olderIsValid &&
3487 		(level.time - enemy->client->lastSaberStorageTime) < 200)
3488 	{ //The enemy has valid saber positions between this and last frame. Use them to factor in direction of the limb.
3489 		vec3_t dif;
3490 		float totalDistance;
3491 		const float distScale = 1.2f;
3492 
3493 		//scale down the initial velocity first, which is based on the speed of the limb owner.
3494 		//ExPhys object velocity operates on a slightly different scale than Q3-based physics velocity.
3495 		VectorScale(limb->epVelocity, 0.4f, limb->epVelocity);
3496 
3497 		VectorSubtract(enemy->client->lastSaberBase_Always, enemy->client->olderSaberBase, dif);
3498 		totalDistance = VectorNormalize(dif);
3499 
3500 		VectorScale(dif, totalDistance*distScale, dif);
3501 		VectorAdd(limb->epVelocity, dif, limb->epVelocity);
3502 
3503 		if (ent->client && (ent->client->ps.torsoTimer > 0 || !BG_InDeathAnim(ent->client->ps.torsoAnim)))
3504 		{ //if he's done with his death anim we don't actually want the limbs going far
3505 			vec3_t preVel;
3506 
3507 			VectorCopy(limb->epVelocity, preVel);
3508 			preVel[2] = 0;
3509 			totalDistance = VectorNormalize(preVel);
3510 
3511 			if (totalDistance < 40.0f)
3512 			{
3513 				float mAmt = 40.0f;//60.0f/totalDistance;
3514 
3515 				limb->epVelocity[0] = preVel[0]*mAmt;
3516 				limb->epVelocity[1] = preVel[1]*mAmt;
3517 			}
3518 		}
3519 		else if (ent->client)
3520 		{
3521 			VectorScale(limb->epVelocity, 0.3f, limb->epVelocity);
3522 		}
3523 	}
3524 
3525 	if (ent->s.eType == ET_NPC && ent->ghoul2 && limbName[0] && stubCapName[0])
3526 	{ //if it's an npc remove these surfs on the server too. For players we don't even care cause there's no further dismemberment after death.
3527 		trap->G2API_SetSurfaceOnOff(ent->ghoul2, limbName, 0x00000100);
3528 		trap->G2API_SetSurfaceOnOff(ent->ghoul2, stubCapName, 0);
3529 	}
3530 
3531 	if ( level.gametype >= GT_TEAM && ent->s.eType != ET_NPC )
3532 	{//Team game
3533 		switch ( ent->client->sess.sessionTeam )
3534 		{
3535 		case TEAM_RED:
3536 			limb->s.customRGBA[0] = 255;
3537 			limb->s.customRGBA[1] = 0;
3538 			limb->s.customRGBA[2] = 0;
3539 			break;
3540 
3541 		case TEAM_BLUE:
3542 			limb->s.customRGBA[0] = 0;
3543 			limb->s.customRGBA[1] = 0;
3544 			limb->s.customRGBA[2] = 255;
3545 			break;
3546 
3547 		default:
3548 			limb->s.customRGBA[0] = ent->s.customRGBA[0];
3549 			limb->s.customRGBA[1] = ent->s.customRGBA[1];
3550 			limb->s.customRGBA[2] = ent->s.customRGBA[2];
3551 			limb->s.customRGBA[3] = ent->s.customRGBA[3];
3552 			break;
3553 		}
3554 	}
3555 	else
3556 	{//FFA
3557 		limb->s.customRGBA[0] = ent->s.customRGBA[0];
3558 		limb->s.customRGBA[1] = ent->s.customRGBA[1];
3559 		limb->s.customRGBA[2] = ent->s.customRGBA[2];
3560 		limb->s.customRGBA[3] = ent->s.customRGBA[3];
3561 	}
3562 
3563 	trap->LinkEntity( (sharedEntity_t *)limb );
3564 }
3565 
DismembermentTest(gentity_t * self)3566 void DismembermentTest(gentity_t *self)
3567 {
3568 	int sect = G2_MODELPART_HEAD;
3569 	vec3_t boltPoint;
3570 
3571 	while (sect <= G2_MODELPART_RLEG)
3572 	{
3573 		G_GetDismemberBolt(self, boltPoint, sect);
3574 		G_Dismember( self, self, boltPoint, sect, 90, 0, BOTH_DEATH1, qfalse );
3575 		sect++;
3576 	}
3577 }
3578 
DismembermentByNum(gentity_t * self,int num)3579 void DismembermentByNum(gentity_t *self, int num)
3580 {
3581 	int sect = G2_MODELPART_HEAD;
3582 	vec3_t boltPoint;
3583 
3584 	switch (num)
3585 	{
3586 	case 0:
3587 		sect = G2_MODELPART_HEAD;
3588 		break;
3589 	case 1:
3590 		sect = G2_MODELPART_WAIST;
3591 		break;
3592 	case 2:
3593 		sect = G2_MODELPART_LARM;
3594 		break;
3595 	case 3:
3596 		sect = G2_MODELPART_RARM;
3597 		break;
3598 	case 4:
3599 		sect = G2_MODELPART_RHAND;
3600 		break;
3601 	case 5:
3602 		sect = G2_MODELPART_LLEG;
3603 		break;
3604 	case 6:
3605 		sect = G2_MODELPART_RLEG;
3606 		break;
3607 	default:
3608 		break;
3609 	}
3610 
3611 	G_GetDismemberBolt(self, boltPoint, sect);
3612 	G_Dismember( self, self, boltPoint, sect, 90, 0, BOTH_DEATH1, qfalse );
3613 }
3614 
G_GetHitQuad(gentity_t * self,vec3_t hitloc)3615 int G_GetHitQuad( gentity_t *self, vec3_t hitloc )
3616 {
3617 	vec3_t diff, fwdangles={0,0,0}, right;
3618 	vec3_t clEye;
3619 	float rightdot;
3620 	float zdiff;
3621 	int hitLoc = gPainHitLoc;
3622 
3623 	if (self->client)
3624 	{
3625 		VectorCopy(self->client->ps.origin, clEye);
3626 		clEye[2] += self->client->ps.viewheight;
3627 	}
3628 	else
3629 	{
3630 		VectorCopy(self->s.pos.trBase, clEye);
3631 		clEye[2] += 16;
3632 	}
3633 
3634 	VectorSubtract( hitloc, clEye, diff );
3635 	diff[2] = 0;
3636 	VectorNormalize( diff );
3637 
3638 	if (self->client)
3639 	{
3640 		fwdangles[1] = self->client->ps.viewangles[1];
3641 	}
3642 	else
3643 	{
3644 		fwdangles[1] = self->s.apos.trBase[1];
3645 	}
3646 	// Ultimately we might care if the shot was ahead or behind, but for now, just quadrant is fine.
3647 	AngleVectors( fwdangles, NULL, right, NULL );
3648 
3649 	rightdot = DotProduct(right, diff);
3650 	zdiff = hitloc[2] - clEye[2];
3651 
3652 	if ( zdiff > 0 )
3653 	{
3654 		if ( rightdot > 0.3 )
3655 		{
3656 			hitLoc = G2_MODELPART_RARM;
3657 		}
3658 		else if ( rightdot < -0.3 )
3659 		{
3660 			hitLoc = G2_MODELPART_LARM;
3661 		}
3662 		else
3663 		{
3664 			hitLoc = G2_MODELPART_HEAD;
3665 		}
3666 	}
3667 	else if ( zdiff > -20 )
3668 	{
3669 		if ( rightdot > 0.1 )
3670 		{
3671 			hitLoc = G2_MODELPART_RARM;
3672 		}
3673 		else if ( rightdot < -0.1 )
3674 		{
3675 			hitLoc = G2_MODELPART_LARM;
3676 		}
3677 		else
3678 		{
3679 			hitLoc = G2_MODELPART_HEAD;
3680 		}
3681 	}
3682 	else
3683 	{
3684 		if ( rightdot >= 0 )
3685 		{
3686 			hitLoc = G2_MODELPART_RLEG;
3687 		}
3688 		else
3689 		{
3690 			hitLoc = G2_MODELPART_LLEG;
3691 		}
3692 	}
3693 
3694 	return hitLoc;
3695 }
3696 
3697 int gGAvoidDismember = 0;
3698 
3699 void UpdateClientRenderBolts(gentity_t *self, vec3_t renderOrigin, vec3_t renderAngles);
3700 
G_GetHitLocFromSurfName(gentity_t * ent,const char * surfName,int * hitLoc,vec3_t point,vec3_t dir,vec3_t bladeDir,int mod)3701 qboolean G_GetHitLocFromSurfName( gentity_t *ent, const char *surfName, int *hitLoc, vec3_t point, vec3_t dir, vec3_t bladeDir, int mod )
3702 {
3703 	qboolean dismember = qfalse;
3704 	int actualTime;
3705 	int kneeLBolt = -1;
3706 	int kneeRBolt = -1;
3707 	int handRBolt = -1;
3708 	int handLBolt = -1;
3709 	int footRBolt = -1;
3710 	int footLBolt = -1;
3711 
3712 	*hitLoc = HL_NONE;
3713 
3714 	if ( !surfName || !surfName[0] )
3715 	{
3716 		return qfalse;
3717 	}
3718 
3719 	if( !ent->client )
3720 	{
3721 		return qfalse;
3722 	}
3723 
3724 	if (!point)
3725 	{
3726 		return qfalse;
3727 	}
3728 
3729 	if ( ent->client
3730 		&& ( ent->client->NPC_class == CLASS_R2D2
3731 			|| ent->client->NPC_class == CLASS_R5D2
3732 			|| ent->client->NPC_class == CLASS_GONK
3733 			|| ent->client->NPC_class == CLASS_MOUSE
3734 			|| ent->client->NPC_class == CLASS_SENTRY
3735 			|| ent->client->NPC_class == CLASS_INTERROGATOR
3736 			|| ent->client->NPC_class == CLASS_PROBE ) )
3737 	{//we don't care about per-surface hit-locations or dismemberment for these guys
3738 		return qfalse;
3739 	}
3740 
3741 	if (ent->localAnimIndex <= 1)
3742 	{ //humanoid
3743 		handLBolt = trap->G2API_AddBolt(ent->ghoul2, 0, "*l_hand");
3744 		handRBolt = trap->G2API_AddBolt(ent->ghoul2, 0, "*r_hand");
3745 		kneeLBolt = trap->G2API_AddBolt(ent->ghoul2, 0, "*hips_l_knee");
3746 		kneeRBolt = trap->G2API_AddBolt(ent->ghoul2, 0, "*hips_r_knee");
3747 		footLBolt = trap->G2API_AddBolt(ent->ghoul2, 0, "*l_leg_foot");
3748 		footRBolt = trap->G2API_AddBolt(ent->ghoul2, 0, "*r_leg_foot");
3749 	}
3750 
3751 	if ( ent->client && (ent->client->NPC_class == CLASS_ATST) )
3752 	{
3753 		//FIXME: almost impossible to hit these... perhaps we should
3754 		//		check for splashDamage and do radius damage to these parts?
3755 		//		Or, if we ever get bbox G2 traces, that may fix it, too
3756 		if (!Q_stricmp("head_light_blaster_cann",surfName))
3757 		{
3758 			*hitLoc = HL_ARM_LT;
3759 		}
3760 		else if (!Q_stricmp("head_concussion_charger",surfName))
3761 		{
3762 			*hitLoc = HL_ARM_RT;
3763 		}
3764 		return(qfalse);
3765 	}
3766 	else if ( ent->client && (ent->client->NPC_class == CLASS_MARK1) )
3767 	{
3768 		if (!Q_stricmp("l_arm",surfName))
3769 		{
3770 			*hitLoc = HL_ARM_LT;
3771 		}
3772 		else if (!Q_stricmp("r_arm",surfName))
3773 		{
3774 			*hitLoc = HL_ARM_RT;
3775 		}
3776 		else if (!Q_stricmp("torso_front",surfName))
3777 		{
3778 			*hitLoc = HL_CHEST;
3779 		}
3780 		else if (!Q_stricmp("torso_tube1",surfName))
3781 		{
3782 			*hitLoc = HL_GENERIC1;
3783 		}
3784 		else if (!Q_stricmp("torso_tube2",surfName))
3785 		{
3786 			*hitLoc = HL_GENERIC2;
3787 		}
3788 		else if (!Q_stricmp("torso_tube3",surfName))
3789 		{
3790 			*hitLoc = HL_GENERIC3;
3791 		}
3792 		else if (!Q_stricmp("torso_tube4",surfName))
3793 		{
3794 			*hitLoc = HL_GENERIC4;
3795 		}
3796 		else if (!Q_stricmp("torso_tube5",surfName))
3797 		{
3798 			*hitLoc = HL_GENERIC5;
3799 		}
3800 		else if (!Q_stricmp("torso_tube6",surfName))
3801 		{
3802 			*hitLoc = HL_GENERIC6;
3803 		}
3804 		return(qfalse);
3805 	}
3806 	else if ( ent->client && (ent->client->NPC_class == CLASS_MARK2) )
3807 	{
3808 		if (!Q_stricmp("torso_canister1",surfName))
3809 		{
3810 			*hitLoc = HL_GENERIC1;
3811 		}
3812 		else if (!Q_stricmp("torso_canister2",surfName))
3813 		{
3814 			*hitLoc = HL_GENERIC2;
3815 		}
3816 		else if (!Q_stricmp("torso_canister3",surfName))
3817 		{
3818 			*hitLoc = HL_GENERIC3;
3819 		}
3820 		return(qfalse);
3821 	}
3822 	else if ( ent->client && (ent->client->NPC_class == CLASS_GALAKMECH) )
3823 	{
3824 		if (!Q_stricmp("torso_antenna",surfName)||!Q_stricmp("torso_antenna_base",surfName))
3825 		{
3826 			*hitLoc = HL_GENERIC1;
3827 		}
3828 		else if (!Q_stricmp("torso_shield",surfName))
3829 		{
3830 			*hitLoc = HL_GENERIC2;
3831 		}
3832 		else
3833 		{
3834 			*hitLoc = HL_CHEST;
3835 		}
3836 		return(qfalse);
3837 	}
3838 
3839 	//FIXME: check the hitLoc and hitDir against the cap tag for the place
3840 	//where the split will be- if the hit dir is roughly perpendicular to
3841 	//the direction of the cap, then the split is allowed, otherwise we
3842 	//hit it at the wrong angle and should not dismember...
3843 	actualTime = level.time;
3844 	if ( !Q_strncmp( "hips", surfName, 4 ) )
3845 	{//FIXME: test properly for legs
3846 		*hitLoc = HL_WAIST;
3847 		if ( ent->client != NULL && ent->ghoul2 )
3848 		{
3849 			mdxaBone_t	boltMatrix;
3850 			vec3_t	tagOrg, angles;
3851 
3852 			VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
3853 			if (kneeLBolt>=0)
3854 			{
3855 				trap->G2API_GetBoltMatrix( ent->ghoul2, 0, kneeLBolt,
3856 								&boltMatrix, angles, ent->r.currentOrigin,
3857 								actualTime, NULL, ent->modelScale );
3858 				BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
3859 				if ( DistanceSquared( point, tagOrg ) < 100 )
3860 				{//actually hit the knee
3861 					*hitLoc = HL_LEG_LT;
3862 				}
3863 			}
3864 			if (*hitLoc == HL_WAIST)
3865 			{
3866 				if (kneeRBolt>=0)
3867 				{
3868 					trap->G2API_GetBoltMatrix( ent->ghoul2, 0, kneeRBolt,
3869 									&boltMatrix, angles, ent->r.currentOrigin,
3870 									actualTime, NULL, ent->modelScale );
3871 					BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
3872 					if ( DistanceSquared( point, tagOrg ) < 100 )
3873 					{//actually hit the knee
3874 						*hitLoc = HL_LEG_RT;
3875 					}
3876 				}
3877 			}
3878 		}
3879 	}
3880 	else if ( !Q_strncmp( "torso", surfName, 5 ) )
3881 	{
3882 		if ( !ent->client )
3883 		{
3884 			*hitLoc = HL_CHEST;
3885 		}
3886 		else
3887 		{
3888 			vec3_t	t_fwd, t_rt, t_up, dirToImpact;
3889 			float frontSide, rightSide, upSide;
3890 			AngleVectors( ent->client->renderInfo.torsoAngles, t_fwd, t_rt, t_up );
3891 
3892 			if (ent->client->renderInfo.boltValidityTime != level.time)
3893 			{
3894 				vec3_t renderAng;
3895 
3896 				renderAng[0] = 0;
3897 				renderAng[1] = ent->client->ps.viewangles[YAW];
3898 				renderAng[2] = 0;
3899 
3900 				UpdateClientRenderBolts(ent, ent->client->ps.origin, renderAng);
3901 			}
3902 
3903 			VectorSubtract( point, ent->client->renderInfo.torsoPoint, dirToImpact );
3904 			frontSide = DotProduct( t_fwd, dirToImpact );
3905 			rightSide = DotProduct( t_rt, dirToImpact );
3906 			upSide = DotProduct( t_up, dirToImpact );
3907 			if ( upSide < -10 )
3908 			{//hit at waist
3909 				*hitLoc = HL_WAIST;
3910 			}
3911 			else
3912 			{//hit on upper torso
3913 				if ( rightSide > 4 )
3914 				{
3915 					*hitLoc = HL_ARM_RT;
3916 				}
3917 				else if ( rightSide < -4 )
3918 				{
3919 					*hitLoc = HL_ARM_LT;
3920 				}
3921 				else if ( rightSide > 2 )
3922 				{
3923 					if ( frontSide > 0 )
3924 					{
3925 						*hitLoc = HL_CHEST_RT;
3926 					}
3927 					else
3928 					{
3929 						*hitLoc = HL_BACK_RT;
3930 					}
3931 				}
3932 				else if ( rightSide < -2 )
3933 				{
3934 					if ( frontSide > 0 )
3935 					{
3936 						*hitLoc = HL_CHEST_LT;
3937 					}
3938 					else
3939 					{
3940 						*hitLoc = HL_BACK_LT;
3941 					}
3942 				}
3943 				else if ( upSide > -3 && mod == MOD_SABER )
3944 				{
3945 					*hitLoc = HL_HEAD;
3946 				}
3947 				else if ( frontSide > 0 )
3948 				{
3949 					*hitLoc = HL_CHEST;
3950 				}
3951 				else
3952 				{
3953 					*hitLoc = HL_BACK;
3954 				}
3955 			}
3956 		}
3957 	}
3958 	else if ( !Q_strncmp( "head", surfName, 4 ) )
3959 	{
3960 		*hitLoc = HL_HEAD;
3961 	}
3962 	else if ( !Q_strncmp( "r_arm", surfName, 5 ) )
3963 	{
3964 		*hitLoc = HL_ARM_RT;
3965 		if ( ent->client != NULL && ent->ghoul2 )
3966 		{
3967 			mdxaBone_t	boltMatrix;
3968 			vec3_t	tagOrg, angles;
3969 
3970 			VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
3971 			if (handRBolt>=0)
3972 			{
3973 				trap->G2API_GetBoltMatrix( ent->ghoul2, 0, handRBolt,
3974 								&boltMatrix, angles, ent->r.currentOrigin,
3975 								actualTime, NULL, ent->modelScale );
3976 				BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
3977 				if ( DistanceSquared( point, tagOrg ) < 256 )
3978 				{//actually hit the hand
3979 					*hitLoc = HL_HAND_RT;
3980 				}
3981 			}
3982 		}
3983 	}
3984 	else if ( !Q_strncmp( "l_arm", surfName, 5 ) )
3985 	{
3986 		*hitLoc = HL_ARM_LT;
3987 		if ( ent->client != NULL && ent->ghoul2 )
3988 		{
3989 			mdxaBone_t	boltMatrix;
3990 			vec3_t	tagOrg, angles;
3991 
3992 			VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
3993 			if (handLBolt>=0)
3994 			{
3995 				trap->G2API_GetBoltMatrix( ent->ghoul2, 0, handLBolt,
3996 								&boltMatrix, angles, ent->r.currentOrigin,
3997 								actualTime, NULL, ent->modelScale );
3998 				BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
3999 				if ( DistanceSquared( point, tagOrg ) < 256 )
4000 				{//actually hit the hand
4001 					*hitLoc = HL_HAND_LT;
4002 				}
4003 			}
4004 		}
4005 	}
4006 	else if ( !Q_strncmp( "r_leg", surfName, 5 ) )
4007 	{
4008 		*hitLoc = HL_LEG_RT;
4009 		if ( ent->client != NULL && ent->ghoul2 )
4010 		{
4011 			mdxaBone_t	boltMatrix;
4012 			vec3_t	tagOrg, angles;
4013 
4014 			VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
4015 			if (footRBolt>=0)
4016 			{
4017 				trap->G2API_GetBoltMatrix( ent->ghoul2, 0, footRBolt,
4018 								&boltMatrix, angles, ent->r.currentOrigin,
4019 								actualTime, NULL, ent->modelScale );
4020 				BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
4021 				if ( DistanceSquared( point, tagOrg ) < 100 )
4022 				{//actually hit the foot
4023 					*hitLoc = HL_FOOT_RT;
4024 				}
4025 			}
4026 		}
4027 	}
4028 	else if ( !Q_strncmp( "l_leg", surfName, 5 ) )
4029 	{
4030 		*hitLoc = HL_LEG_LT;
4031 		if ( ent->client != NULL && ent->ghoul2 )
4032 		{
4033 			mdxaBone_t	boltMatrix;
4034 			vec3_t	tagOrg, angles;
4035 
4036 			VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
4037 			if (footLBolt>=0)
4038 			{
4039 				trap->G2API_GetBoltMatrix( ent->ghoul2, 0, footLBolt,
4040 								&boltMatrix, angles, ent->r.currentOrigin,
4041 								actualTime, NULL, ent->modelScale );
4042 				BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
4043 				if ( DistanceSquared( point, tagOrg ) < 100 )
4044 				{//actually hit the foot
4045 					*hitLoc = HL_FOOT_LT;
4046 				}
4047 			}
4048 		}
4049 	}
4050 	else if ( !Q_strncmp( "r_hand", surfName, 6 ) || !Q_strncmp( "w_", surfName, 2 ) )
4051 	{//right hand or weapon
4052 		*hitLoc = HL_HAND_RT;
4053 	}
4054 	else if ( !Q_strncmp( "l_hand", surfName, 6 ) )
4055 	{
4056 		*hitLoc = HL_HAND_LT;
4057 	}
4058 	/*
4059 #ifdef _DEBUG
4060 	else
4061 	{
4062 		Com_Printf( "ERROR: surface %s does not belong to any hitLocation!!!\n", surfName );
4063 	}
4064 #endif //_DEBUG
4065 	*/
4066 
4067 	//if ( g_dismemberment->integer >= 11381138 || !ent->client->dismembered )
4068 	if (g_dismember.integer == 100)
4069 	{ //full probability...
4070 		if ( ent->client && ent->client->NPC_class == CLASS_PROTOCOL )
4071 		{
4072 			dismember = qtrue;
4073 		}
4074 		else if ( dir && (dir[0] || dir[1] || dir[2]) &&
4075 			bladeDir && (bladeDir[0] || bladeDir[1] || bladeDir[2]) )
4076 		{//we care about direction (presumably for dismemberment)
4077 			//if ( g_dismemberProbabilities->value<=0.0f||G_Dismemberable( ent, *hitLoc ) )
4078 			if (1) //Fix me?
4079 			{//either we don't care about probabilties or the probability let us continue
4080 				char *tagName = NULL;
4081 				float	aoa = 0.5f;
4082 				//dir must be roughly perpendicular to the hitLoc's cap bolt
4083 				switch ( *hitLoc )
4084 				{
4085 					case HL_LEG_RT:
4086 						tagName = "*hips_cap_r_leg";
4087 						break;
4088 					case HL_LEG_LT:
4089 						tagName = "*hips_cap_l_leg";
4090 						break;
4091 					case HL_WAIST:
4092 						tagName = "*hips_cap_torso";
4093 						aoa = 0.25f;
4094 						break;
4095 					case HL_CHEST_RT:
4096 					case HL_ARM_RT:
4097 					case HL_BACK_LT:
4098 						tagName = "*torso_cap_r_arm";
4099 						break;
4100 					case HL_CHEST_LT:
4101 					case HL_ARM_LT:
4102 					case HL_BACK_RT:
4103 						tagName = "*torso_cap_l_arm";
4104 						break;
4105 					case HL_HAND_RT:
4106 						tagName = "*r_arm_cap_r_hand";
4107 						break;
4108 					case HL_HAND_LT:
4109 						tagName = "*l_arm_cap_l_hand";
4110 						break;
4111 					case HL_HEAD:
4112 						tagName = "*torso_cap_head";
4113 						aoa = 0.25f;
4114 						break;
4115 					case HL_CHEST:
4116 					case HL_BACK:
4117 					case HL_FOOT_RT:
4118 					case HL_FOOT_LT:
4119 					default:
4120 						//no dismemberment possible with these, so no checks needed
4121 						break;
4122 				}
4123 				if ( tagName )
4124 				{
4125 					int tagBolt = trap->G2API_AddBolt( ent->ghoul2, 0, tagName );
4126 					if ( tagBolt != -1 )
4127 					{
4128 						mdxaBone_t	boltMatrix;
4129 						vec3_t	tagOrg, tagDir, angles;
4130 
4131 						VectorSet( angles, 0, ent->r.currentAngles[YAW], 0 );
4132 						trap->G2API_GetBoltMatrix( ent->ghoul2, 0, tagBolt,
4133 										&boltMatrix, angles, ent->r.currentOrigin,
4134 										actualTime, NULL, ent->modelScale );
4135 						BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, tagOrg );
4136 						BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, tagDir );
4137 						if ( DistanceSquared( point, tagOrg ) < 256 )
4138 						{//hit close
4139 							float dot = DotProduct( dir, tagDir );
4140 							if ( dot < aoa && dot > -aoa )
4141 							{//hit roughly perpendicular
4142 								dot = DotProduct( bladeDir, tagDir );
4143 								if ( dot < aoa && dot > -aoa )
4144 								{//blade was roughly perpendicular
4145 									dismember = qtrue;
4146 								}
4147 							}
4148 						}
4149 					}
4150 				}
4151 			}
4152 		}
4153 		else
4154 		{ //hmm, no direction supplied.
4155 			dismember = qtrue;
4156 		}
4157 	}
4158 	return dismember;
4159 }
4160 
G_CheckForDismemberment(gentity_t * ent,gentity_t * enemy,vec3_t point,int damage,int deathAnim,qboolean postDeath)4161 void G_CheckForDismemberment(gentity_t *ent, gentity_t *enemy, vec3_t point, int damage, int deathAnim, qboolean postDeath)
4162 {
4163 	int hitLoc = -1, hitLocUse = -1;
4164 	vec3_t boltPoint;
4165 	int dismember = g_dismember.integer;
4166 
4167 	if (ent->localAnimIndex > 1)
4168 	{
4169 		if (!ent->NPC)
4170 		{
4171 			return;
4172 		}
4173 
4174 		if (ent->client->NPC_class != CLASS_PROTOCOL)
4175 		{ //this is the only non-humanoid allowed to do dismemberment.
4176 			return;
4177 		}
4178 	}
4179 
4180 	if (!dismember)
4181 	{
4182 		return;
4183 	}
4184 
4185 	if (gGAvoidDismember == 1)
4186 	{
4187 		return;
4188 	}
4189 
4190 	if (gGAvoidDismember != 2)
4191 	{ //this means do the dismemberment regardless of randomness and damage
4192 		if (Q_irand(0, 100) > dismember)
4193 		{
4194 			return;
4195 		}
4196 
4197 		if (damage < 5)
4198 		{
4199 			return;
4200 		}
4201 	}
4202 
4203 	if (gGAvoidDismember == 2)
4204 	{
4205 		hitLoc = HL_HAND_RT;
4206 	}
4207 	else
4208 	{
4209 		if (d_saberGhoul2Collision.integer && ent->client && ent->client->g2LastSurfaceTime == level.time)
4210 		{
4211 			char hitSurface[MAX_QPATH];
4212 
4213 			trap->G2API_GetSurfaceName(ent->ghoul2, ent->client->g2LastSurfaceHit, 0, hitSurface);
4214 
4215 			if (hitSurface[0])
4216 			{
4217 				G_GetHitLocFromSurfName(ent, hitSurface, &hitLoc, point, vec3_origin, vec3_origin, MOD_UNKNOWN);
4218 			}
4219 		}
4220 
4221 		if (hitLoc == -1)
4222 		{
4223 			hitLoc = G_GetHitLocation( ent, point );
4224 		}
4225 	}
4226 
4227 	switch(hitLoc)
4228 	{
4229 	case HL_FOOT_RT:
4230 	case HL_LEG_RT:
4231 		hitLocUse = G2_MODELPART_RLEG;
4232 		break;
4233 	case HL_FOOT_LT:
4234 	case HL_LEG_LT:
4235 		hitLocUse = G2_MODELPART_LLEG;
4236 		break;
4237 
4238 	case HL_WAIST:
4239 		hitLocUse = G2_MODELPART_WAIST;
4240 		break;
4241 		/*
4242 	case HL_BACK_RT:
4243 	case HL_BACK_LT:
4244 	case HL_BACK:
4245 	case HL_CHEST_RT:
4246 	case HL_CHEST_LT:
4247 	case HL_CHEST:
4248 		break;
4249 		*/
4250 	case HL_ARM_RT:
4251 		hitLocUse = G2_MODELPART_RARM;
4252 		break;
4253 	case HL_HAND_RT:
4254 		hitLocUse = G2_MODELPART_RHAND;
4255 		break;
4256 	case HL_ARM_LT:
4257 	case HL_HAND_LT:
4258 		hitLocUse = G2_MODELPART_LARM;
4259 		break;
4260 	case HL_HEAD:
4261 		hitLocUse = G2_MODELPART_HEAD;
4262 		break;
4263 	default:
4264 		hitLocUse = G_GetHitQuad(ent, point);
4265 		break;
4266 	}
4267 
4268 	if (hitLocUse == -1)
4269 	{
4270 		return;
4271 	}
4272 
4273 	if (ent->client)
4274 	{
4275 		G_GetDismemberBolt(ent, boltPoint, hitLocUse);
4276 		if ( g_austrian.integer
4277 			&& (level.gametype == GT_DUEL || level.gametype == GT_POWERDUEL) )
4278 		{
4279 			G_LogPrintf( "Duel Dismemberment: %s dismembered at %s\n", ent->client->pers.netname, hitLocName[hitLoc] );
4280 		}
4281 	}
4282 	else
4283 	{
4284 		G_GetDismemberLoc(ent, boltPoint, hitLocUse);
4285 	}
4286 	G_Dismember(ent, enemy, boltPoint, hitLocUse, 90, 0, deathAnim, postDeath);
4287 }
4288 
G_LocationBasedDamageModifier(gentity_t * ent,vec3_t point,int mod,int dflags,int * damage)4289 void G_LocationBasedDamageModifier(gentity_t *ent, vec3_t point, int mod, int dflags, int *damage)
4290 {
4291 	int hitLoc = -1;
4292 
4293 	if (!g_locationBasedDamage.integer)
4294 	{ //then leave it alone
4295 		return;
4296 	}
4297 
4298 	if ( (dflags&DAMAGE_NO_HIT_LOC) )
4299 	{ //then leave it alone
4300 		return;
4301 	}
4302 
4303 	if (mod == MOD_SABER && *damage <= 1)
4304 	{ //don't bother for idle damage
4305 		return;
4306 	}
4307 
4308 	if (!point)
4309 	{
4310 		return;
4311 	}
4312 
4313 	if ( ent->client && ent->client->NPC_class == CLASS_VEHICLE )
4314 	{//no location-based damage on vehicles
4315 		return;
4316 	}
4317 
4318 	if ((d_saberGhoul2Collision.integer && ent->client && ent->client->g2LastSurfaceTime == level.time && mod == MOD_SABER) || //using ghoul2 collision? Then if the mod is a saber we should have surface data from the last hit (unless thrown).
4319 		(d_projectileGhoul2Collision.integer && ent->client && ent->client->g2LastSurfaceTime == level.time)) //It's safe to assume we died from the projectile that just set our surface index. So, go ahead and use that as the surf I guess.
4320 	{
4321 		char hitSurface[MAX_QPATH];
4322 
4323 		trap->G2API_GetSurfaceName(ent->ghoul2, ent->client->g2LastSurfaceHit, 0, hitSurface);
4324 
4325 		if (hitSurface[0])
4326 		{
4327 			G_GetHitLocFromSurfName(ent, hitSurface, &hitLoc, point, vec3_origin, vec3_origin, MOD_UNKNOWN);
4328 		}
4329 	}
4330 
4331 	if (hitLoc == -1)
4332 	{
4333 		hitLoc = G_GetHitLocation( ent, point );
4334 	}
4335 
4336 	switch (hitLoc)
4337 	{
4338 	case HL_FOOT_RT:
4339 	case HL_FOOT_LT:
4340 		*damage *= 0.5;
4341 		break;
4342 	case HL_LEG_RT:
4343 	case HL_LEG_LT:
4344 		*damage *= 0.7;
4345 		break;
4346 	case HL_WAIST:
4347 	case HL_BACK_RT:
4348 	case HL_BACK_LT:
4349 	case HL_BACK:
4350 	case HL_CHEST_RT:
4351 	case HL_CHEST_LT:
4352 	case HL_CHEST:
4353 		break; //normal damage
4354 	case HL_ARM_RT:
4355 	case HL_ARM_LT:
4356 		*damage *= 0.85;
4357 		break;
4358 	case HL_HAND_RT:
4359 	case HL_HAND_LT:
4360 		*damage *= 0.6;
4361 		break;
4362 	case HL_HEAD:
4363 		*damage *= 1.3;
4364 		break;
4365 	default:
4366 		break; //do nothing then
4367 	}
4368 }
4369 /*
4370 ===================================
4371 rww - end dismemberment/lbd
4372 ===================================
4373 */
4374 
G_ThereIsAMaster(void)4375 qboolean G_ThereIsAMaster(void)
4376 {
4377 	int i = 0;
4378 	gentity_t *ent;
4379 
4380 	while (i < MAX_CLIENTS)
4381 	{
4382 		ent = &g_entities[i];
4383 
4384 		if (ent && ent->client && ent->client->ps.isJediMaster)
4385 		{
4386 			return qtrue;
4387 		}
4388 
4389 		i++;
4390 	}
4391 
4392 	return qfalse;
4393 }
4394 
G_Knockdown(gentity_t * victim)4395 void G_Knockdown( gentity_t *victim )
4396 {
4397 	if ( victim && victim->client && BG_KnockDownable(&victim->client->ps) )
4398 	{
4399 		victim->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
4400 		victim->client->ps.forceDodgeAnim = 0;
4401 		victim->client->ps.forceHandExtendTime = level.time + 1100;
4402 		victim->client->ps.quickerGetup = qfalse;
4403 	}
4404 }
4405 
4406 /*
4407 ============
4408 G_Damage
4409 
4410 targ		entity that is being damaged
4411 inflictor	entity that is causing the damage
4412 attacker	entity that caused the inflictor to damage targ
4413 	example: targ=monster, inflictor=rocket, attacker=player
4414 
4415 dir			direction of the attack for knockback
4416 point		point at which the damage is being inflicted, used for headshots
4417 damage		amount of damage being inflicted
4418 knockback	force to be applied against targ as a result of the damage
4419 
4420 inflictor, attacker, dir, and point can be NULL for environmental effects
4421 
4422 dflags		these flags are used to control how G_Damage works
4423 	DAMAGE_RADIUS			damage was indirect (from a nearby explosion)
4424 	DAMAGE_NO_ARMOR			armor does not protect from this damage
4425 	DAMAGE_NO_KNOCKBACK		do not affect velocity, just view angles
4426 	DAMAGE_NO_PROTECTION	kills godmode, armor, everything
4427 	DAMAGE_HALF_ABSORB		half shields, half health
4428 	DAMAGE_HALF_ARMOR_REDUCTION		Any damage that shields incur is halved
4429 ============
4430 */
4431 extern qboolean gSiegeRoundBegun;
4432 
4433 int gPainMOD = 0;
4434 int gPainHitLoc = -1;
4435 vec3_t gPainPoint;
4436 
G_Damage(gentity_t * targ,gentity_t * inflictor,gentity_t * attacker,vec3_t dir,vec3_t point,int damage,int dflags,int mod)4437 void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) {
4438 	gclient_t	*client;
4439 	int			take, asave, max, subamt = 0, knockback;
4440 	float		famt = 0, hamt = 0, shieldAbsorbed = 0;
4441 
4442 	if (!targ)
4443 		return;
4444 
4445 	if (targ && targ->damageRedirect)
4446 	{
4447 		G_Damage(&g_entities[targ->damageRedirectTo], inflictor, attacker, dir, point, damage, dflags, mod);
4448 		return;
4449 	}
4450 
4451 	if (mod == MOD_DEMP2 && targ && targ->inuse && targ->client)
4452 	{
4453 		if ( targ->client->ps.electrifyTime < level.time )
4454 		{//electrocution effect
4455 			if (targ->s.eType == ET_NPC && targ->s.NPC_class == CLASS_VEHICLE &&
4456 				targ->m_pVehicle && (targ->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER || targ->m_pVehicle->m_pVehicleInfo->type == VH_WALKER))
4457 			{ //do some extra stuff to speeders/walkers
4458 				targ->client->ps.electrifyTime = level.time + Q_irand( 3000, 4000 );
4459 			}
4460 			else if ( targ->s.NPC_class != CLASS_VEHICLE
4461 				|| (targ->m_pVehicle && targ->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER) )
4462 			{//don't do this to fighters
4463 				targ->client->ps.electrifyTime = level.time + Q_irand( 300, 800 );
4464 			}
4465 		}
4466 	}
4467 
4468 	if (level.gametype == GT_SIEGE &&
4469 		!gSiegeRoundBegun)
4470 	{ //nothing can be damaged til the round starts.
4471 		return;
4472 	}
4473 
4474 	if (!targ->takedamage) {
4475 		return;
4476 	}
4477 
4478 	if ( (targ->flags&FL_SHIELDED) && mod != MOD_SABER  && !targ->client)
4479 	{//magnetically protected, this thing can only be damaged by lightsabers
4480 		return;
4481 	}
4482 
4483 	if ((targ->flags & FL_DMG_BY_SABER_ONLY) && mod != MOD_SABER)
4484 	{ //saber-only damage
4485 		return;
4486 	}
4487 
4488 	if ( targ->client )
4489 	{//don't take damage when in a walker, or fighter
4490 		//unless the walker/fighter is dead!!! -rww
4491 		if ( targ->client->ps.clientNum < MAX_CLIENTS && targ->client->ps.m_iVehicleNum )
4492 		{
4493 			gentity_t *veh = &g_entities[targ->client->ps.m_iVehicleNum];
4494 			if ( veh->m_pVehicle && veh->health > 0 )
4495 			{
4496 				if ( veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER ||
4497 					 veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
4498 				{
4499 					if (!(dflags & DAMAGE_NO_PROTECTION))
4500 					{
4501 						return;
4502 					}
4503 				}
4504 			}
4505 		}
4506 	}
4507 
4508 	if ((targ->flags & FL_DMG_BY_HEAVY_WEAP_ONLY))
4509 	{ //only take damage from explosives and such
4510 		if (mod != MOD_REPEATER_ALT &&
4511 			mod != MOD_ROCKET &&
4512 			mod != MOD_FLECHETTE_ALT_SPLASH &&
4513 			mod != MOD_ROCKET_HOMING &&
4514 			mod != MOD_THERMAL &&
4515 			mod != MOD_THERMAL_SPLASH &&
4516 			mod != MOD_TRIP_MINE_SPLASH &&
4517 			mod != MOD_TIMED_MINE_SPLASH &&
4518 			mod != MOD_DET_PACK_SPLASH &&
4519 			mod != MOD_VEHICLE &&
4520 			mod != MOD_CONC &&
4521 			mod != MOD_CONC_ALT &&
4522 			mod != MOD_SABER &&
4523 			mod != MOD_TURBLAST &&
4524 			mod != MOD_SUICIDE &&
4525 			mod != MOD_FALLING &&
4526 			mod != MOD_CRUSH &&
4527 			mod != MOD_TELEFRAG &&
4528 			mod != MOD_TRIGGER_HURT)
4529 		{
4530 			if ( mod != MOD_MELEE || !G_HeavyMelee( attacker ) )
4531 			{ //let classes with heavy melee ability damage heavy wpn dmg doors with fists
4532 				return;
4533 			}
4534 		}
4535 	}
4536 
4537 	if (targ->flags & FL_BBRUSH)
4538 	{
4539 		if (mod == MOD_DEMP2 ||
4540 			mod == MOD_DEMP2_ALT ||
4541 			mod == MOD_BRYAR_PISTOL ||
4542 			mod == MOD_BRYAR_PISTOL_ALT ||
4543 			mod == MOD_MELEE)
4544 		{ //these don't damage bbrushes.. ever
4545 			if ( mod != MOD_MELEE || !G_HeavyMelee( attacker ) )
4546 			{ //let classes with heavy melee ability damage breakable brushes with fists
4547 				return;
4548 			}
4549 		}
4550 	}
4551 
4552 	if (targ && targ->client && targ->client->ps.duelInProgress)
4553 	{
4554 		if (attacker && attacker->client && attacker->s.number != targ->client->ps.duelIndex)
4555 		{
4556 			return;
4557 		}
4558 		else if (attacker && attacker->client && mod != MOD_SABER)
4559 		{
4560 			return;
4561 		}
4562 	}
4563 	if (attacker && attacker->client && attacker->client->ps.duelInProgress)
4564 	{
4565 		if (targ && targ->client && targ->s.number != attacker->client->ps.duelIndex)
4566 		{
4567 			return;
4568 		}
4569 		else if (targ && targ->client && mod != MOD_SABER)
4570 		{
4571 			return;
4572 		}
4573 	}
4574 
4575 	if ( !(dflags & DAMAGE_NO_PROTECTION) )
4576 	{//rage overridden by no_protection
4577 		if (targ && targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_RAGE)))
4578 		{
4579 			damage *= 0.5;
4580 		}
4581 	}
4582 
4583 	// the intermission has allready been qualified for, so don't
4584 	// allow any extra scoring
4585 	if ( level.intermissionQueued ) {
4586 		return;
4587 	}
4588 	if ( !inflictor ) {
4589 		inflictor = &g_entities[ENTITYNUM_WORLD];
4590 	}
4591 	if ( !attacker ) {
4592 		attacker = &g_entities[ENTITYNUM_WORLD];
4593 	}
4594 
4595 	// shootable doors / buttons don't actually have any health
4596 
4597 	//if genericValue4 == 1 then it's glass or a breakable and those do have health
4598 	if ( targ->s.eType == ET_MOVER && targ->genericValue4 != 1 ) {
4599 		if ( targ->use && targ->moverState == MOVER_POS1 ) {
4600 			GlobalUse( targ, inflictor, attacker );
4601 		}
4602 		return;
4603 	}
4604 	// reduce damage by the attacker's handicap value
4605 	// unless they are rocket jumping
4606 	if ( attacker->client
4607 		&& attacker != targ
4608 		&& attacker->s.eType == ET_PLAYER
4609 		&& level.gametype != GT_SIEGE )
4610 	{
4611 		max = attacker->client->ps.stats[STAT_MAX_HEALTH];
4612 		damage = damage * max / 100;
4613 	}
4614 
4615 	if ( !(dflags&DAMAGE_NO_HIT_LOC) )
4616 	{//see if we should modify it by damage location
4617 		if (targ->inuse && (targ->client || targ->s.eType == ET_NPC) &&
4618 			attacker->inuse && (attacker->client || attacker->s.eType == ET_NPC))
4619 		{ //check for location based damage stuff.
4620 			G_LocationBasedDamageModifier(targ, point, mod, dflags, &damage);
4621 		}
4622 	}
4623 
4624 	if ( targ->client
4625 		&& targ->client->NPC_class == CLASS_RANCOR
4626 		&& (!attacker||!attacker->client||attacker->client->NPC_class!=CLASS_RANCOR) )
4627 	{
4628 		// I guess always do 10 points of damage...feel free to tweak as needed
4629 		if ( damage < 10 )
4630 		{//ignore piddly little damage
4631 			damage = 0;
4632 		}
4633 		else if ( damage >= 10 )
4634 		{
4635 			damage = 10;
4636 		}
4637 	}
4638 
4639 	client = targ->client;
4640 
4641 	if ( client ) {
4642 		if ( client->noclip ) {
4643 			return;
4644 		}
4645 	}
4646 
4647 	if ( !dir ) {
4648 		dflags |= DAMAGE_NO_KNOCKBACK;
4649 	} else {
4650 		VectorNormalize(dir);
4651 	}
4652 
4653 	knockback = damage;
4654 	if ( knockback > 200 ) {
4655 		knockback = 200;
4656 	}
4657 	if ( targ->flags & FL_NO_KNOCKBACK ) {
4658 		knockback = 0;
4659 	}
4660 	if ( dflags & DAMAGE_NO_KNOCKBACK ) {
4661 		knockback = 0;
4662 	}
4663 
4664 	// figure momentum add, even if the damage won't be taken
4665 	if ( knockback && targ->client ) {
4666 		vec3_t	kvel;
4667 		float	mass;
4668 
4669 		mass = 200;
4670 
4671 		if (mod == MOD_SABER)
4672 		{
4673 			float saberKnockbackScale = g_saberDmgVelocityScale.value;
4674 			if ( (dflags&DAMAGE_SABER_KNOCKBACK1)
4675 				|| (dflags&DAMAGE_SABER_KNOCKBACK2) )
4676 			{//saber does knockback, scale it by the right number
4677 				if ( !saberKnockbackScale )
4678 				{
4679 					saberKnockbackScale = 1.0f;
4680 				}
4681 				if ( attacker
4682 					&& attacker->client )
4683 				{
4684 					if ( (dflags&DAMAGE_SABER_KNOCKBACK1) )
4685 					{
4686 						if ( attacker && attacker->client )
4687 						{
4688 							saberKnockbackScale *= attacker->client->saber[0].knockbackScale;
4689 						}
4690 					}
4691 					if ( (dflags&DAMAGE_SABER_KNOCKBACK1_B2) )
4692 					{
4693 						if ( attacker && attacker->client )
4694 						{
4695 							saberKnockbackScale *= attacker->client->saber[0].knockbackScale2;
4696 						}
4697 					}
4698 					if ( (dflags&DAMAGE_SABER_KNOCKBACK2) )
4699 					{
4700 						if ( attacker && attacker->client )
4701 						{
4702 							saberKnockbackScale *= attacker->client->saber[1].knockbackScale;
4703 						}
4704 					}
4705 					if ( (dflags&DAMAGE_SABER_KNOCKBACK2_B2) )
4706 					{
4707 						if ( attacker && attacker->client )
4708 						{
4709 							saberKnockbackScale *= attacker->client->saber[1].knockbackScale2;
4710 						}
4711 					}
4712 				}
4713 			}
4714 			VectorScale (dir, (g_knockback.value * (float)knockback / mass)*saberKnockbackScale, kvel);
4715 		}
4716 		else
4717 		{
4718 			VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel);
4719 		}
4720 		VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity);
4721 
4722 		if (attacker && attacker->client && attacker != targ)
4723 		{
4724 			float dur = 5000;
4725 			float dur2 = 100;
4726 			if (targ->client && targ->s.eType == ET_NPC && targ->s.NPC_class == CLASS_VEHICLE)
4727 			{
4728 				dur = 25000;
4729 				dur2 = 25000;
4730 			}
4731 			targ->client->ps.otherKiller = attacker->s.number;
4732 			targ->client->ps.otherKillerTime = level.time + dur;
4733 			targ->client->ps.otherKillerDebounceTime = level.time + dur2;
4734 		}
4735 		// set the timer so that the other client can't cancel
4736 		// out the movement immediately
4737 		if ( !targ->client->ps.pm_time && (g_saberDmgVelocityScale.integer || mod != MOD_SABER || (dflags&DAMAGE_SABER_KNOCKBACK1) || (dflags&DAMAGE_SABER_KNOCKBACK2) || (dflags&DAMAGE_SABER_KNOCKBACK1_B2) || (dflags&DAMAGE_SABER_KNOCKBACK2_B2) ) ) {
4738 			int		t;
4739 
4740 			t = knockback * 2;
4741 			if ( t < 50 ) {
4742 				t = 50;
4743 			}
4744 			if ( t > 200 ) {
4745 				t = 200;
4746 			}
4747 			targ->client->ps.pm_time = t;
4748 			targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
4749 		}
4750 	}
4751 	else if (targ->client && targ->s.eType == ET_NPC && targ->s.NPC_class == CLASS_VEHICLE && attacker != targ)
4752 	{
4753 		targ->client->ps.otherKiller = attacker->s.number;
4754 		targ->client->ps.otherKillerTime = level.time + 25000;
4755 		targ->client->ps.otherKillerDebounceTime = level.time + 25000;
4756 	}
4757 
4758 
4759 	if ( (g_jediVmerc.integer || level.gametype == GT_SIEGE)
4760 		&& client )
4761 	{//less explosive damage for jedi, more saber damage for non-jedi
4762 		if ( client->ps.trueJedi
4763 			|| (level.gametype == GT_SIEGE&&client->ps.weapon == WP_SABER))
4764 		{//if the target is a trueJedi, reduce splash and explosive damage to 1/2
4765 			switch ( mod )
4766 			{
4767 			case MOD_REPEATER_ALT:
4768 			case MOD_REPEATER_ALT_SPLASH:
4769 			case MOD_DEMP2_ALT:
4770 			case MOD_FLECHETTE_ALT_SPLASH:
4771 			case MOD_ROCKET:
4772 			case MOD_ROCKET_SPLASH:
4773 			case MOD_ROCKET_HOMING:
4774 			case MOD_ROCKET_HOMING_SPLASH:
4775 			case MOD_THERMAL:
4776 			case MOD_THERMAL_SPLASH:
4777 			case MOD_TRIP_MINE_SPLASH:
4778 			case MOD_TIMED_MINE_SPLASH:
4779 			case MOD_DET_PACK_SPLASH:
4780 				damage *= 0.75;
4781 				break;
4782 			}
4783 		}
4784 		else if ( (client->ps.trueNonJedi || (level.gametype == GT_SIEGE&&client->ps.weapon != WP_SABER))
4785 			&& mod == MOD_SABER )
4786 		{//if the target is a trueNonJedi, take more saber damage... combined with the 1.5 in the w_saber stuff, this is 6 times damage!
4787 			if ( damage < 100 )
4788 			{
4789 				damage *= 4;
4790 				if ( damage > 100 )
4791 				{
4792 					damage = 100;
4793 				}
4794 			}
4795 		}
4796 	}
4797 
4798 	if (attacker->client && targ->client && level.gametype == GT_SIEGE &&
4799 		targ->client->siegeClass != -1 && (bgSiegeClasses[targ->client->siegeClass].classflags & (1<<CFL_STRONGAGAINSTPHYSICAL)))
4800 	{ //this class is flagged to take less damage from physical attacks.
4801 		//For now I'm just decreasing against any client-based attack, this can be changed later I guess.
4802 		damage *= 0.5;
4803 	}
4804 
4805 	// check for completely getting out of the damage
4806 	if ( !(dflags & DAMAGE_NO_PROTECTION) ) {
4807 
4808 		// if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target
4809 		// if the attacker was on the same team
4810 		if ( targ != attacker)
4811 		{
4812 			if (OnSameTeam (targ, attacker))
4813 			{
4814 				if ( !g_friendlyFire.integer )
4815 				{
4816 					return;
4817 				}
4818 			}
4819 			else if (attacker && attacker->inuse &&
4820 				!attacker->client && attacker->activator &&
4821 				targ != attacker->activator &&
4822 				attacker->activator->inuse && attacker->activator->client)
4823 			{ //emplaced guns don't hurt teammates of user
4824 				if (OnSameTeam (targ, attacker->activator))
4825 				{
4826 					if ( !g_friendlyFire.integer )
4827 					{
4828 						return;
4829 					}
4830 				}
4831 			}
4832 			else if (targ->inuse && targ->client &&
4833 				level.gametype >= GT_TEAM &&
4834 				attacker->s.number >= MAX_CLIENTS &&
4835 				attacker->alliedTeam &&
4836 				targ->client->sess.sessionTeam == attacker->alliedTeam &&
4837 				!g_friendlyFire.integer)
4838 			{ //things allied with my team should't hurt me.. I guess
4839 				return;
4840 			}
4841 		}
4842 
4843 		if (level.gametype == GT_JEDIMASTER && !g_friendlyFire.integer &&
4844 			targ && targ->client && attacker && attacker->client &&
4845 			targ != attacker && !targ->client->ps.isJediMaster && !attacker->client->ps.isJediMaster &&
4846 			G_ThereIsAMaster())
4847 		{
4848 			return;
4849 		}
4850 
4851 		if (targ->s.number >= MAX_CLIENTS && targ->client
4852 			&& targ->s.shouldtarget && targ->s.teamowner &&
4853 			attacker && attacker->inuse && attacker->client && targ->s.owner >= 0 && targ->s.owner < MAX_CLIENTS)
4854 		{
4855 			gentity_t *targown = &g_entities[targ->s.owner];
4856 
4857 			if (targown && targown->inuse && targown->client && OnSameTeam(targown, attacker))
4858 			{
4859 				if (!g_friendlyFire.integer)
4860 				{
4861 					return;
4862 				}
4863 			}
4864 		}
4865 
4866 		// check for godmode
4867 		if ( (targ->flags & FL_GODMODE) && targ->s.eType != ET_NPC ) {
4868 			return;
4869 		}
4870 
4871 		if (targ && targ->client && (targ->client->ps.eFlags & EF_INVULNERABLE) &&
4872 			attacker && attacker->client && targ != attacker)
4873 		{
4874 			if (targ->client->invulnerableTimer <= level.time)
4875 			{
4876 				targ->client->ps.eFlags &= ~EF_INVULNERABLE;
4877 			}
4878 			else
4879 			{
4880 				return;
4881 			}
4882 		}
4883 	}
4884 
4885 	//check for teamnodmg
4886 	//NOTE: non-client objects hitting clients (and clients hitting clients) purposely doesn't obey this teamnodmg (for emplaced guns)
4887 	if ( attacker && !targ->client )
4888 	{//attacker hit a non-client
4889 		if ( level.gametype == GT_SIEGE &&
4890 			!g_ff_objectives.integer )
4891 		{//in siege mode (and...?)
4892 			if ( targ->teamnodmg )
4893 			{//targ shouldn't take damage from a certain team
4894 				if ( attacker->client )
4895 				{//a client hit a non-client object
4896 					if ( targ->teamnodmg == attacker->client->sess.sessionTeam )
4897 					{
4898 						return;
4899 					}
4900 				}
4901 				else if ( attacker->teamnodmg )
4902 				{//a non-client hit a non-client object
4903 					//FIXME: maybe check alliedTeam instead?
4904 					if ( targ->teamnodmg == attacker->teamnodmg )
4905 					{
4906 						if (attacker->activator &&
4907 							attacker->activator->inuse &&
4908 							attacker->activator->s.number < MAX_CLIENTS &&
4909 							attacker->activator->client &&
4910 							attacker->activator->client->sess.sessionTeam != targ->teamnodmg)
4911 						{ //uh, let them damage it I guess.
4912 						}
4913 						else
4914 						{
4915 							return;
4916 						}
4917 					}
4918 				}
4919 			}
4920 		}
4921 	}
4922 
4923 	#ifdef BASE_COMPAT
4924 		// battlesuit protects from all radius damage (but takes knockback)
4925 		// and protects 50% against all damage
4926 		if ( client && client->ps.powerups[PW_BATTLESUIT] ) {
4927 			G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 );
4928 			if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) {
4929 				return;
4930 			}
4931 			damage *= 0.5;
4932 		}
4933 	#endif
4934 
4935 	// add to the attacker's hit counter (if the target isn't a general entity like a prox mine)
4936 	if ( attacker->client && targ != attacker && targ->health > 0
4937 			&& targ->s.eType != ET_MISSILE
4938 			&& targ->s.eType != ET_GENERAL
4939 			&& client) {
4940 		if ( OnSameTeam( targ, attacker ) ) {
4941 			attacker->client->ps.persistant[PERS_HITS]--;
4942 		} else {
4943 			attacker->client->ps.persistant[PERS_HITS]++;
4944 		}
4945 		attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]);
4946 	}
4947 
4948 	// always give half damage if hurting self... but not in siege.  Heavy weapons need a counter.
4949 	// calculated after knockback, so rocket jumping works
4950 	if ( targ == attacker && !(dflags & DAMAGE_NO_SELF_PROTECTION)) {
4951 		if ( level.gametype == GT_SIEGE )
4952 		{
4953 			damage *= 1.5;
4954 		}
4955 		else
4956 		{
4957 			damage *= 0.5;
4958 		}
4959 	}
4960 
4961 	if ( damage < 1 ) {
4962 		damage = 1;
4963 	}
4964 	take = damage;
4965 
4966 	// save some from armor
4967 	asave = CheckArmor (targ, take, dflags);
4968 
4969 	if (asave)
4970 	{
4971 		shieldAbsorbed = asave;
4972 	}
4973 
4974 	take -= asave;
4975 	if ( targ->client )
4976 	{//update vehicle shields and armor, check for explode
4977 		if ( targ->client->NPC_class == CLASS_VEHICLE &&
4978 			targ->m_pVehicle )
4979 		{//FIXME: should be in its own function in g_vehicles.c now, too big to be here
4980 			int surface = -1;
4981 			if ( attacker )
4982 			{//so we know the last guy who shot at us
4983 				targ->enemy = attacker;
4984 			}
4985 
4986 			if ( targ->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
4987 			{
4988 				//((CVehicleNPC *)targ->NPC)->m_ulFlags |= CVehicleNPC::VEH_BUCKING;
4989 			}
4990 
4991 			targ->m_pVehicle->m_iShields = targ->client->ps.stats[STAT_ARMOR];
4992 			G_VehUpdateShields( targ );
4993 			targ->m_pVehicle->m_iArmor -= take;
4994 			if ( targ->m_pVehicle->m_iArmor <= 0 )
4995 			{
4996 				targ->s.eFlags |= EF_DEAD;
4997 				targ->client->ps.eFlags |= EF_DEAD;
4998 				targ->m_pVehicle->m_iArmor = 0;
4999 			}
5000 			if ( targ->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
5001 			{//get the last surf that was hit
5002 				if ( targ->client && targ->client->g2LastSurfaceTime == level.time)
5003 				{
5004 					char hitSurface[MAX_QPATH];
5005 
5006 					trap->G2API_GetSurfaceName(targ->ghoul2, targ->client->g2LastSurfaceHit, 0, hitSurface);
5007 
5008 					if (hitSurface[0])
5009 					{
5010 						surface = G_ShipSurfaceForSurfName( &hitSurface[0] );
5011 
5012 						if ( take && surface > 0 )
5013 						{//hit a certain part of the ship
5014 							int deathPoint = 0;
5015 
5016 							targ->locationDamage[surface] += take;
5017 
5018 							switch(surface)
5019 							{
5020 							case SHIPSURF_FRONT:
5021 								deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_front;
5022 								break;
5023 							case SHIPSURF_BACK:
5024 								deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_back;
5025 								break;
5026 							case SHIPSURF_RIGHT:
5027 								deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_right;
5028 								break;
5029 							case SHIPSURF_LEFT:
5030 								deathPoint = targ->m_pVehicle->m_pVehicleInfo->health_left;
5031 								break;
5032 							default:
5033 								break;
5034 							}
5035 
5036 							//presume 0 means it wasn't set and so it should never die.
5037 							if ( deathPoint )
5038 							{
5039 								if ( targ->locationDamage[surface] >= deathPoint)
5040 								{ //this area of the ship is now dead
5041 									if ( G_FlyVehicleDestroySurface( targ, surface ) )
5042 									{//actually took off a surface
5043 										G_VehicleSetDamageLocFlags( targ, surface, deathPoint );
5044 									}
5045 								}
5046 								else
5047 								{
5048 									G_VehicleSetDamageLocFlags( targ, surface, deathPoint );
5049 								}
5050 							}
5051 						}
5052 					}
5053 				}
5054 			}
5055 			if ( targ->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
5056 			{
5057 				/*
5058 				if ( targ->m_pVehicle->m_iArmor <= 0 )
5059 				{//vehicle all out of armor
5060 					Vehicle_t *pVeh = targ->m_pVehicle;
5061 					if ( pVeh->m_iDieTime == 0 )
5062 					{//just start the flaming effect and explosion delay, if it's not going already...
5063 						pVeh->m_pVehicleInfo->StartDeathDelay( pVeh, 0 );
5064 					}
5065 				}
5066 				else*/
5067 				if ( attacker
5068 						//&& attacker->client
5069 						&& targ != attacker
5070 						&& point
5071 						&& !VectorCompare( targ->client->ps.origin, point )
5072 						&& targ->m_pVehicle->m_LandTrace.fraction >= 1.0f)
5073 				{//just took a hit, knock us around
5074 					vec3_t	vUp, impactDir;
5075 					float	impactStrength = (damage/200.0f)*10.0f;
5076 					float	dot = 0.0f;
5077 					if ( impactStrength > 10.0f )
5078 					{
5079 						impactStrength = 10.0f;
5080 					}
5081 					//pitch or roll us based on where we were hit
5082 					AngleVectors( targ->m_pVehicle->m_vOrientation, NULL, NULL, vUp );
5083 					VectorSubtract( point, targ->r.currentOrigin, impactDir );
5084 					VectorNormalize( impactDir );
5085 					if ( surface <= 0 )
5086 					{//no surf guess where we were hit, then
5087 						vec3_t	vFwd, vRight;
5088 						AngleVectors( targ->m_pVehicle->m_vOrientation, vFwd, vRight, vUp );
5089 						dot = DotProduct( vRight, impactDir );
5090 						if ( dot > 0.4f )
5091 						{
5092 							surface = SHIPSURF_RIGHT;
5093 						}
5094 						else if ( dot < -0.4f )
5095 						{
5096 							surface = SHIPSURF_LEFT;
5097 						}
5098 						else
5099 						{
5100 							dot = DotProduct( vFwd, impactDir );
5101 							if ( dot > 0.0f )
5102 							{
5103 								surface = SHIPSURF_FRONT;
5104 							}
5105 							else
5106 							{
5107 								surface = SHIPSURF_BACK;
5108 							}
5109 						}
5110 					}
5111 					switch ( surface )
5112 					{
5113 					case SHIPSURF_FRONT:
5114 						dot = DotProduct( vUp, impactDir );
5115 						if ( dot > 0 )
5116 						{
5117 							targ->m_pVehicle->m_vOrientation[PITCH] += impactStrength;
5118 						}
5119 						else
5120 						{
5121 							targ->m_pVehicle->m_vOrientation[PITCH] -= impactStrength;
5122 						}
5123 						break;
5124 					case SHIPSURF_BACK:
5125 						dot = DotProduct( vUp, impactDir );
5126 						if ( dot > 0 )
5127 						{
5128 							targ->m_pVehicle->m_vOrientation[PITCH] -= impactStrength;
5129 						}
5130 						else
5131 						{
5132 							targ->m_pVehicle->m_vOrientation[PITCH] += impactStrength;
5133 						}
5134 						break;
5135 					case SHIPSURF_RIGHT:
5136 						dot = DotProduct( vUp, impactDir );
5137 						if ( dot > 0 )
5138 						{
5139 							targ->m_pVehicle->m_vOrientation[ROLL] -= impactStrength;
5140 						}
5141 						else
5142 						{
5143 							targ->m_pVehicle->m_vOrientation[ROLL] += impactStrength;
5144 						}
5145 						break;
5146 					case SHIPSURF_LEFT:
5147 						dot = DotProduct( vUp, impactDir );
5148 						if ( dot > 0 )
5149 						{
5150 							targ->m_pVehicle->m_vOrientation[ROLL] += impactStrength;
5151 						}
5152 						else
5153 						{
5154 							targ->m_pVehicle->m_vOrientation[ROLL] -= impactStrength;
5155 						}
5156 						break;
5157 					}
5158 
5159 				}
5160 			}
5161 		}
5162 	}
5163 
5164 	if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
5165 	{//FIXME: screw with non-animal vehicles, too?
5166 		if ( client )
5167 		{
5168 			if ( client->NPC_class == CLASS_VEHICLE
5169 				&& targ->m_pVehicle
5170 				&& targ->m_pVehicle->m_pVehicleInfo
5171 				&& targ->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
5172 			{//all damage goes into the disruption of shields and systems
5173 				take = 0;
5174 			}
5175 			else
5176 			{
5177 
5178 				if (client->jetPackOn)
5179 				{ //disable jetpack temporarily
5180 					Jetpack_Off(targ);
5181 					client->jetPackToggleTime = level.time + Q_irand(3000, 10000);
5182 				}
5183 
5184 				if ( client->NPC_class == CLASS_PROTOCOL || client->NPC_class == CLASS_SEEKER ||
5185 					client->NPC_class == CLASS_R2D2 || client->NPC_class == CLASS_R5D2 ||
5186 					client->NPC_class == CLASS_MOUSE || client->NPC_class == CLASS_GONK )
5187 				{
5188 					// DEMP2 does more damage to these guys.
5189 					take *= 2;
5190 				}
5191 				else if ( client->NPC_class == CLASS_PROBE || client->NPC_class == CLASS_INTERROGATOR ||
5192 							client->NPC_class == CLASS_MARK1 || client->NPC_class == CLASS_MARK2 || client->NPC_class == CLASS_SENTRY ||
5193 							client->NPC_class == CLASS_ATST )
5194 				{
5195 					// DEMP2 does way more damage to these guys.
5196 					take *= 5;
5197 				}
5198 				else
5199 				{
5200 					if (take > 0)
5201 					{
5202 						take /= 3;
5203 						if (take < 1)
5204 						{
5205 							take = 1;
5206 						}
5207 					}
5208 				}
5209 			}
5210 		}
5211 	}
5212 
5213 	if ( g_debugDamage.integer ) {
5214 		trap->Print( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number,
5215 			targ->health, take, asave );
5216 	}
5217 
5218 	// add to the damage inflicted on a player this frame
5219 	// the total will be turned into screen blends and view angle kicks
5220 	// at the end of the frame
5221 	if ( client ) {
5222 		if ( attacker ) {
5223 			client->ps.persistant[PERS_ATTACKER] = attacker->s.number;
5224 		} else {
5225 			client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD;
5226 		}
5227 		client->damage_armor += asave;
5228 		client->damage_blood += take;
5229 		client->damage_knockback += knockback;
5230 		if ( dir ) {
5231 			VectorCopy ( dir, client->damage_from );
5232 			client->damage_fromWorld = qfalse;
5233 		} else {
5234 			VectorCopy ( targ->r.currentOrigin, client->damage_from );
5235 			client->damage_fromWorld = qtrue;
5236 		}
5237 
5238 		if (attacker && attacker->client)
5239 		{
5240 			BotDamageNotification(client, attacker);
5241 		}
5242 		else if (inflictor && inflictor->client)
5243 		{
5244 			BotDamageNotification(client, inflictor);
5245 		}
5246 	}
5247 
5248 	// See if it's the player hurting the emeny flag carrier
5249 	if( level.gametype == GT_CTF || level.gametype == GT_CTY) {
5250 		Team_CheckHurtCarrier(targ, attacker);
5251 	}
5252 
5253 	if (targ->client) {
5254 		// set the last client who damaged the target
5255 		targ->client->lasthurt_client = attacker->s.number;
5256 		targ->client->lasthurt_mod = mod;
5257 	}
5258 
5259 	if ( !(dflags & DAMAGE_NO_PROTECTION) )
5260 	{//protect overridden by no_protection
5261 		if (take && targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_PROTECT)))
5262 		{
5263 			if (targ->client->ps.fd.forcePower)
5264 			{
5265 				int maxtake = take;
5266 
5267 				//G_Sound(targ, CHAN_AUTO, protectHitSound);
5268 				if (targ->client->forcePowerSoundDebounce < level.time)
5269 				{
5270 					G_PreDefSound(targ->client->ps.origin, PDSOUND_PROTECTHIT);
5271 					targ->client->forcePowerSoundDebounce = level.time + 400;
5272 				}
5273 
5274 				if (targ->client->ps.fd.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_1)
5275 				{
5276 					famt = 1;
5277 					hamt = 0.40f;
5278 
5279 					if (maxtake > 100)
5280 					{
5281 						maxtake = 100;
5282 					}
5283 				}
5284 				else if (targ->client->ps.fd.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_2)
5285 				{
5286 					famt = 0.5f;
5287 					hamt = 0.60f;
5288 
5289 					if (maxtake > 200)
5290 					{
5291 						maxtake = 200;
5292 					}
5293 				}
5294 				else if (targ->client->ps.fd.forcePowerLevel[FP_PROTECT] == FORCE_LEVEL_3)
5295 				{
5296 					famt = 0.25f;
5297 					hamt = 0.80f;
5298 
5299 					if (maxtake > 400)
5300 					{
5301 						maxtake = 400;
5302 					}
5303 				}
5304 
5305 				if (!targ->client->ps.powerups[PW_FORCE_BOON])
5306 				{
5307 					targ->client->ps.fd.forcePower -= maxtake*famt;
5308 				}
5309 				else
5310 				{
5311 					targ->client->ps.fd.forcePower -= (maxtake*famt)/2;
5312 				}
5313 				subamt = (maxtake*hamt)+(take-maxtake);
5314 				if (targ->client->ps.fd.forcePower < 0)
5315 				{
5316 					subamt += targ->client->ps.fd.forcePower;
5317 					targ->client->ps.fd.forcePower = 0;
5318 				}
5319 				if (subamt)
5320 				{
5321 					take -= subamt;
5322 
5323 					if (take < 0)
5324 					{
5325 						take = 0;
5326 					}
5327 				}
5328 			}
5329 		}
5330 	}
5331 
5332 	if (shieldAbsorbed)
5333 	{
5334 		/*
5335 		if ( targ->client->NPC_class == CLASS_VEHICLE )
5336 		{
5337 			targ->client->ps.electrifyTime = level.time + Q_irand( 500, 1000 );
5338 		}
5339 		else
5340 		*/
5341 		{
5342 			gentity_t	*evEnt;
5343 
5344 			// Send off an event to show a shield shell on the player, pointing in the right direction.
5345 			//evEnt = G_TempEntity(vec3_origin, EV_SHIELD_HIT);
5346 			//rww - er.. what the? This isn't broadcast, why is it being set on vec3_origin?!
5347 			evEnt = G_TempEntity(targ->r.currentOrigin, EV_SHIELD_HIT);
5348 			evEnt->s.otherEntityNum = targ->s.number;
5349 			evEnt->s.eventParm = DirToByte(dir);
5350 			evEnt->s.time2=shieldAbsorbed;
5351 	/*
5352 			shieldAbsorbed *= 20;
5353 
5354 			if (shieldAbsorbed > 1500)
5355 			{
5356 				shieldAbsorbed = 1500;
5357 			}
5358 			if (shieldAbsorbed < 200)
5359 			{
5360 				shieldAbsorbed = 200;
5361 			}
5362 
5363 			if (targ->client->ps.powerups[PW_SHIELDHIT] < (level.time + shieldAbsorbed))
5364 			{
5365 				targ->client->ps.powerups[PW_SHIELDHIT] = level.time + shieldAbsorbed;
5366 			}
5367 			//flicker for as many ms as damage was absorbed (*20)
5368 			//therefore 10 damage causes 1/5 of a seond of flickering, whereas
5369 			//a full 100 causes 2 seconds (but is reduced to 1.5 seconds due to the max)
5370 
5371 	*/
5372 		}
5373 	}
5374 
5375 	// do the damage
5376 	if (take)
5377 	{
5378 		if (targ->client && targ->s.number < MAX_CLIENTS &&
5379 			(mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT))
5380 		{ //uh.. shock them or something. what the hell, I don't know.
5381             if (targ->client->ps.weaponTime <= 0)
5382 			{ //yeah, we were supposed to be beta a week ago, I don't feel like
5383 				//breaking the game so I'm gonna be safe and only do this only
5384 				//if your weapon is not busy
5385 				targ->client->ps.weaponTime = 2000;
5386 				targ->client->ps.electrifyTime = level.time + 2000;
5387 				if (targ->client->ps.weaponstate == WEAPON_CHARGING ||
5388 					targ->client->ps.weaponstate == WEAPON_CHARGING_ALT)
5389 				{
5390 					targ->client->ps.weaponstate = WEAPON_READY;
5391 				}
5392 			}
5393 		}
5394 
5395 		if ( !(dflags & DAMAGE_NO_PROTECTION) )
5396 		{//rage overridden by no_protection
5397 			if (targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_RAGE)) && (inflictor->client || attacker->client))
5398 			{
5399 				take /= (targ->client->ps.fd.forcePowerLevel[FP_RAGE]+1);
5400 			}
5401 		}
5402 		targ->health = targ->health - take;
5403 
5404 		if ( (targ->flags&FL_UNDYING) )
5405 		{//take damage down to 1, but never die
5406 			if ( targ->health < 1 )
5407 			{
5408 				targ->health = 1;
5409 			}
5410 		}
5411 
5412 		if ( targ->client ) {
5413 			targ->client->ps.stats[STAT_HEALTH] = targ->health;
5414 		}
5415 
5416 		if ( !(dflags & DAMAGE_NO_PROTECTION) )
5417 		{//rage overridden by no_protection
5418 			if (targ->client && (targ->client->ps.fd.forcePowersActive & (1 << FP_RAGE)) && (inflictor->client || attacker->client))
5419 			{
5420 				if (targ->health <= 0)
5421 				{
5422 					targ->health = 1;
5423 				}
5424 				if (targ->client->ps.stats[STAT_HEALTH] <= 0)
5425 				{
5426 					targ->client->ps.stats[STAT_HEALTH] = 1;
5427 				}
5428 			}
5429 		}
5430 
5431 		//We want to go ahead and set gPainHitLoc regardless of if we have a pain func,
5432 		//so we can adjust the location damage too.
5433 		if (targ->client && targ->ghoul2 && targ->client->g2LastSurfaceTime == level.time)
5434 		{ //We updated the hit surface this frame, so it's valid.
5435 			char hitSurface[MAX_QPATH];
5436 
5437 			trap->G2API_GetSurfaceName(targ->ghoul2, targ->client->g2LastSurfaceHit, 0, hitSurface);
5438 
5439 			if (hitSurface[0])
5440 			{
5441 				G_GetHitLocFromSurfName(targ, hitSurface, &gPainHitLoc, point, dir, vec3_origin, mod);
5442 			}
5443 			else
5444 			{
5445 				gPainHitLoc = -1;
5446 			}
5447 
5448 			if (gPainHitLoc < HL_MAX && gPainHitLoc >= 0 && targ->locationDamage[gPainHitLoc] < Q3_INFINITE &&
5449 				(targ->s.eType == ET_PLAYER || targ->s.NPC_class != CLASS_VEHICLE))
5450 			{
5451 				targ->locationDamage[gPainHitLoc] += take;
5452 
5453 				if (g_armBreakage.integer && !targ->client->ps.brokenLimbs &&
5454 					targ->client->ps.stats[STAT_HEALTH] > 0 && targ->health > 0 &&
5455 					!(targ->s.eFlags & EF_DEAD))
5456 				{ //check for breakage
5457 					if (targ->locationDamage[HL_ARM_RT]+targ->locationDamage[HL_HAND_RT] >= 80)
5458 					{
5459 						G_BreakArm(targ, BROKENLIMB_RARM);
5460 					}
5461 					else if (targ->locationDamage[HL_ARM_LT]+targ->locationDamage[HL_HAND_LT] >= 80)
5462 					{
5463 						G_BreakArm(targ, BROKENLIMB_LARM);
5464 					}
5465 				}
5466 			}
5467 		}
5468 		else
5469 		{
5470 			gPainHitLoc = -1;
5471 		}
5472 
5473 		if (targ->maxHealth)
5474 		{ //if this is non-zero this guy should be updated his s.health to send to the client
5475 			G_ScaleNetHealth(targ);
5476 		}
5477 
5478 		if ( targ->health <= 0 ) {
5479 			if ( client )
5480 			{
5481 				targ->flags |= FL_NO_KNOCKBACK;
5482 
5483 				if (point)
5484 				{
5485 					VectorCopy( point, targ->pos1 );
5486 				}
5487 				else
5488 				{
5489 					VectorCopy(targ->client->ps.origin, targ->pos1);
5490 				}
5491 			}
5492 			else if (targ->s.eType == ET_NPC)
5493 			{ //g2animent
5494 				VectorCopy(point, targ->pos1);
5495 			}
5496 
5497 			if (targ->health < -999)
5498 				targ->health = -999;
5499 
5500 			// If we are a breaking glass brush, store the damage point so we can do cool things with it.
5501 			if ( targ->r.svFlags & SVF_GLASS_BRUSH )
5502 			{
5503 				VectorCopy( point, targ->pos1 );
5504 				if (dir)
5505 				{
5506 					VectorCopy( dir, targ->pos2 );
5507 				}
5508 				else
5509 				{
5510 					VectorClear(targ->pos2);
5511 				}
5512 			}
5513 
5514 			if (targ->s.eType == ET_NPC &&
5515 				targ->client &&
5516 				(targ->s.eFlags & EF_DEAD))
5517 			{ //an NPC that's already dead. Maybe we can cut some more limbs off!
5518 				if ( (mod == MOD_SABER || (mod == MOD_MELEE && G_HeavyMelee( attacker )) )//saber or heavy melee (claws)
5519 					&& take > 2
5520 					&& !(dflags&DAMAGE_NO_DISMEMBER) )
5521 				{
5522 					G_CheckForDismemberment(targ, attacker, targ->pos1, take, targ->client->ps.torsoAnim, qtrue);
5523 				}
5524 			}
5525 
5526 			targ->enemy = attacker;
5527 			targ->die (targ, inflictor, attacker, take, mod);
5528 			G_ActivateBehavior( targ, BSET_DEATH );
5529 			return;
5530 		}
5531 		else
5532 		{
5533 			if ( g_debugMelee.integer )
5534 			{//getting hurt makes you let go of the wall
5535 				if ( targ->client && (targ->client->ps.pm_flags&PMF_STUCK_TO_WALL) )
5536 				{
5537 					G_LetGoOfWall( targ );
5538 				}
5539 			}
5540 			if ( targ->pain )
5541 			{
5542 				if (targ->s.eType != ET_NPC || mod != MOD_SABER || take > 1)
5543 				{ //don't even notify NPCs of pain if it's just idle saber damage
5544 					gPainMOD = mod;
5545 					if (point)
5546 					{
5547 						VectorCopy(point, gPainPoint);
5548 					}
5549 					else
5550 					{
5551 						VectorCopy(targ->r.currentOrigin, gPainPoint);
5552 					}
5553 					targ->pain (targ, attacker, take);
5554 				}
5555 			}
5556 		}
5557 
5558 		G_LogWeaponDamage(attacker->s.number, mod, take);
5559 	}
5560 
5561 }
5562 
5563 
5564 /*
5565 ============
5566 CanDamage
5567 
5568 Returns qtrue if the inflictor can directly damage the target.  Used for
5569 explosions and melee attacks.
5570 ============
5571 */
CanDamage(gentity_t * targ,vec3_t origin)5572 qboolean CanDamage (gentity_t *targ, vec3_t origin) {
5573 	vec3_t	dest;
5574 	trace_t	tr;
5575 	vec3_t	midpoint;
5576 
5577 	// use the midpoint of the bounds instead of the origin, because
5578 	// bmodels may have their origin is 0,0,0
5579 	VectorAdd (targ->r.absmin, targ->r.absmax, midpoint);
5580 	VectorScale (midpoint, 0.5, midpoint);
5581 
5582 	VectorCopy (midpoint, dest);
5583 	trap->Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, qfalse, 0, 0);
5584 	if (tr.fraction == 1.0 || tr.entityNum == targ->s.number)
5585 		return qtrue;
5586 
5587 	// this should probably check in the plane of projection,
5588 	// rather than in world coordinate, and also include Z
5589 	VectorCopy (midpoint, dest);
5590 	dest[0] += 15.0;
5591 	dest[1] += 15.0;
5592 	trap->Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, qfalse, 0, 0);
5593 	if (tr.fraction == 1.0)
5594 		return qtrue;
5595 
5596 	VectorCopy (midpoint, dest);
5597 	dest[0] += 15.0;
5598 	dest[1] -= 15.0;
5599 	trap->Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, qfalse, 0, 0);
5600 	if (tr.fraction == 1.0)
5601 		return qtrue;
5602 
5603 	VectorCopy (midpoint, dest);
5604 	dest[0] -= 15.0;
5605 	dest[1] += 15.0;
5606 	trap->Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, qfalse, 0, 0);
5607 	if (tr.fraction == 1.0)
5608 		return qtrue;
5609 
5610 	VectorCopy (midpoint, dest);
5611 	dest[0] -= 15.0;
5612 	dest[1] -= 15.0;
5613 	trap->Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID, qfalse, 0, 0);
5614 	if (tr.fraction == 1.0)
5615 		return qtrue;
5616 
5617 
5618 	return qfalse;
5619 }
5620 
5621 
5622 /*
5623 ============
5624 G_RadiusDamage
5625 ============
5626 */
G_RadiusDamage(vec3_t origin,gentity_t * attacker,float damage,float radius,gentity_t * ignore,gentity_t * missile,int mod)5627 qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius,
5628 					 gentity_t *ignore, gentity_t *missile, int mod) {
5629 	float		points, dist;
5630 	gentity_t	*ent;
5631 	int			entityList[MAX_GENTITIES];
5632 	int			numListedEntities;
5633 	vec3_t		mins, maxs;
5634 	vec3_t		v;
5635 	vec3_t		dir;
5636 	int			i, e;
5637 	qboolean	hitClient = qfalse;
5638 	qboolean	roastPeople = qfalse;
5639 
5640 	/*
5641 	if (missile && !missile->client && missile->s.weapon > WP_NONE &&
5642 		missile->s.weapon < WP_NUM_WEAPONS && missile->r.ownerNum >= 0 &&
5643 		(missile->r.ownerNum < MAX_CLIENTS || g_entities[missile->r.ownerNum].s.eType == ET_NPC))
5644 	{ //sounds like it's a valid weapon projectile.. is it a valid explosive to create marks from?
5645 		switch(missile->s.weapon)
5646 		{
5647 		case WP_FLECHETTE: //flechette issuing this will be alt-fire
5648 		case WP_ROCKET_LAUNCHER:
5649 		case WP_THERMAL:
5650 		case WP_TRIP_MINE:
5651 		case WP_DET_PACK:
5652 			roastPeople = qtrue; //Then create explosive marks
5653 			break;
5654 		default:
5655 			break;
5656 		}
5657 	}
5658 	*/
5659 	//oh well.. maybe sometime? I am trying to cut down on tempent use.
5660 
5661 	if ( radius < 1 ) {
5662 		radius = 1;
5663 	}
5664 
5665 	for ( i = 0 ; i < 3 ; i++ ) {
5666 		mins[i] = origin[i] - radius;
5667 		maxs[i] = origin[i] + radius;
5668 	}
5669 
5670 	numListedEntities = trap->EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
5671 
5672 	for ( e = 0 ; e < numListedEntities ; e++ ) {
5673 		ent = &g_entities[entityList[ e ]];
5674 
5675 		if (ent == ignore)
5676 			continue;
5677 		if (!ent->takedamage)
5678 			continue;
5679 
5680 		// find the distance from the edge of the bounding box
5681 		for ( i = 0 ; i < 3 ; i++ ) {
5682 			if ( origin[i] < ent->r.absmin[i] ) {
5683 				v[i] = ent->r.absmin[i] - origin[i];
5684 			} else if ( origin[i] > ent->r.absmax[i] ) {
5685 				v[i] = origin[i] - ent->r.absmax[i];
5686 			} else {
5687 				v[i] = 0;
5688 			}
5689 		}
5690 
5691 		dist = VectorLength( v );
5692 		if ( dist >= radius ) {
5693 			continue;
5694 		}
5695 
5696 	//	if ( ent->health <= 0 )
5697 	//		continue;
5698 
5699 		points = damage * ( 1.0 - dist / radius );
5700 
5701 		if( CanDamage (ent, origin) ) {
5702 			if( LogAccuracyHit( ent, attacker ) ) {
5703 				hitClient = qtrue;
5704 			}
5705 			VectorSubtract (ent->r.currentOrigin, origin, dir);
5706 			// push the center of mass higher than the origin so players
5707 			// get knocked into the air more
5708 			dir[2] += 24;
5709 			if (attacker && attacker->inuse && attacker->client &&
5710 				attacker->s.eType == ET_NPC && attacker->s.NPC_class == CLASS_VEHICLE &&
5711 				attacker->m_pVehicle && attacker->m_pVehicle->m_pPilot)
5712 			{ //say my pilot did it.
5713 				G_Damage (ent, NULL, (gentity_t *)attacker->m_pVehicle->m_pPilot, dir, origin, (int)points, DAMAGE_RADIUS, mod);
5714 			}
5715 			else
5716 			{
5717 				G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod);
5718 			}
5719 
5720 			if (ent && ent->client && roastPeople && missile &&
5721 				!VectorCompare(ent->r.currentOrigin, missile->r.currentOrigin))
5722 			{ //the thing calling this function can create burn marks on people, so create an event to do so
5723 				gentity_t *evEnt = G_TempEntity(ent->r.currentOrigin, EV_GHOUL2_MARK);
5724 
5725 				evEnt->s.otherEntityNum = ent->s.number; //the entity the mark should be placed on
5726 				evEnt->s.weapon = WP_ROCKET_LAUNCHER; //always say it's rocket so we make the right mark
5727 
5728 				//Try to place the decal by going from the missile location to the location of the person that was hit
5729 				VectorCopy(missile->r.currentOrigin, evEnt->s.origin);
5730 				VectorCopy(ent->r.currentOrigin, evEnt->s.origin2);
5731 
5732 				//it's hacky, but we want to move it up so it's more likely to hit
5733 				//the torso.
5734 				if (missile->r.currentOrigin[2] < ent->r.currentOrigin[2])
5735 				{ //move it up less so the decal is placed lower on the model then
5736 					evEnt->s.origin2[2] += 8;
5737 				}
5738 				else
5739 				{
5740 					evEnt->s.origin2[2] += 24;
5741 				}
5742 
5743 				//Special col check
5744 				evEnt->s.eventParm = 1;
5745 			}
5746 		}
5747 	}
5748 
5749 	return hitClient;
5750 }
5751