1 // g_combat.c
2 
3 #include "g_local.h"
4 
5 void M_SetEffects (edict_t *self);
6 
7 /*
8 ROGUE
9 clean up heal targets for medic
10 */
cleanupHealTarget(edict_t * ent)11 void cleanupHealTarget (edict_t *ent)
12 {
13 	ent->monsterinfo.healer = NULL;
14 	ent->takedamage = DAMAGE_YES;
15 	ent->monsterinfo.aiflags &= ~AI_RESURRECTING;
16 	M_SetEffects (ent);
17 }
18 /*
19 ============
20 CanDamage
21 
22 Returns true if the inflictor can directly damage the target.  Used for
23 explosions and melee attacks.
24 ============
25 */
CanDamage(edict_t * targ,edict_t * inflictor)26 qboolean CanDamage (edict_t *targ, edict_t *inflictor)
27 {
28 	vec3_t	dest;
29 	trace_t	trace;
30 
31 // bmodels need special checking because their origin is 0,0,0
32 	if (targ->movetype == MOVETYPE_PUSH)
33 	{
34 		VectorAdd (targ->absmin, targ->absmax, dest);
35 		VectorScale (dest, 0.5, dest);
36 		trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
37 		if (trace.fraction == 1.0)
38 			return true;
39 		if (trace.ent == targ)
40 			return true;
41 		return false;
42 	}
43 
44 	trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
45 	if (trace.fraction == 1.0)
46 		return true;
47 
48 	VectorCopy (targ->s.origin, dest);
49 	dest[0] += 15.0;
50 	dest[1] += 15.0;
51 	trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
52 	if (trace.fraction == 1.0)
53 		return true;
54 
55 	VectorCopy (targ->s.origin, dest);
56 	dest[0] += 15.0;
57 	dest[1] -= 15.0;
58 	trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
59 	if (trace.fraction == 1.0)
60 		return true;
61 
62 	VectorCopy (targ->s.origin, dest);
63 	dest[0] -= 15.0;
64 	dest[1] += 15.0;
65 	trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
66 	if (trace.fraction == 1.0)
67 		return true;
68 
69 	VectorCopy (targ->s.origin, dest);
70 	dest[0] -= 15.0;
71 	dest[1] -= 15.0;
72 	trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
73 	if (trace.fraction == 1.0)
74 		return true;
75 
76 
77 	return false;
78 }
79 
80 
81 /*
82 ============
83 Killed
84 ============
85 */
Killed(edict_t * targ,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)86 void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
87 {
88 	if (targ->health < -999)
89 		targ->health = -999;
90 
91 	if (targ->monsterinfo.aiflags & AI_MEDIC)
92 	{
93 		if (targ->enemy)  // god, I hope so
94 		{
95 			cleanupHealTarget (targ->enemy);
96 		}
97 
98 		// clean up self
99 		targ->monsterinfo.aiflags &= ~AI_MEDIC;
100 		targ->enemy = attacker;
101 	}
102 	else
103 	{
104 		targ->enemy = attacker;
105 	}
106 
107 	if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
108 	{
109 //		targ->svflags |= SVF_DEADMONSTER;	// now treat as a different content type
110 		//ROGUE - free up slot for spawned monster if it's spawned
111 		if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER)
112 		{
113 			if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse &&
114 				!strcmp(targ->monsterinfo.commander->classname, "monster_carrier"))
115 			{
116 				targ->monsterinfo.commander->monsterinfo.monster_slots++;
117 //				if ((g_showlogic) && (g_showlogic->value))
118 //					gi.dprintf ("g_combat: freeing up carrier slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots);
119 			}
120 		}
121 		if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C)
122 		{
123 			if (targ->monsterinfo.commander)
124 			{
125 				if (targ->monsterinfo.commander->inuse && !strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander"))
126 				{
127 					targ->monsterinfo.commander->monsterinfo.monster_slots++;
128 //					if ((g_showlogic) && (g_showlogic->value))
129 //						gi.dprintf ("g_combat: freeing up medic slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots);
130 				}
131 //				else
132 //					if ((g_showlogic) && (g_showlogic->value))
133 //						gi.dprintf ("my commander is dead!  he's a %s\n", targ->monsterinfo.commander->classname);
134 			}
135 //			else if ((g_showlogic) && (g_showlogic->value))
136 //				gi.dprintf ("My commander is GONE\n");
137 
138 		}
139 		if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
140 		{
141 			// need to check this because we can have variable numbers of coop players
142 			if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse &&
143 				!strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13))
144 			{
145 				if (targ->monsterinfo.commander->monsterinfo.monster_used > 0)
146 					targ->monsterinfo.commander->monsterinfo.monster_used--;
147 //				if ((g_showlogic) && (g_showlogic->value))
148 //					gi.dprintf ("g_combat: freeing up black widow slot - %d used\n", targ->monsterinfo.commander->monsterinfo.monster_used);
149 			}
150 		}
151 		//rogue
152 		if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
153 		{
154 			level.killed_monsters++;
155 			if (coop->value && attacker->client)
156 				attacker->client->resp.score++;
157 			// medics won't heal monsters that they kill themselves
158 			// PMM - now they will
159 //			if (strcmp(attacker->classname, "monster_medic") == 0)
160 //				targ->owner = attacker;
161 		}
162 	}
163 
164 	if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
165 	{	// doors, triggers, etc
166 		targ->die (targ, inflictor, attacker, damage, point);
167 		return;
168 	}
169 
170 	if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
171 	{
172 		targ->touch = NULL;
173 		monster_death_use (targ);
174 	}
175 
176 	targ->die (targ, inflictor, attacker, damage, point);
177 }
178 
179 
180 /*
181 ================
182 SpawnDamage
183 ================
184 */
SpawnDamage(int type,vec3_t origin,vec3_t normal,int damage)185 void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
186 {
187 	if (damage > 255)
188 		damage = 255;
189 	gi.WriteByte (svc_temp_entity);
190 	gi.WriteByte (type);
191 //	gi.WriteByte (damage);
192 	gi.WritePosition (origin);
193 	gi.WriteDir (normal);
194 	gi.multicast (origin, MULTICAST_PVS);
195 }
196 
197 
198 /*
199 ============
200 T_Damage
201 
202 targ		entity that is being damaged
203 inflictor	entity that is causing the damage
204 attacker	entity that caused the inflictor to damage targ
205 	example: targ=monster, inflictor=rocket, attacker=player
206 
207 dir			direction of the attack
208 point		point at which the damage is being inflicted
209 normal		normal vector from that point
210 damage		amount of damage being inflicted
211 knockback	force to be applied against targ as a result of the damage
212 
213 dflags		these flags are used to control how T_Damage works
214 	DAMAGE_RADIUS			damage was indirect (from a nearby explosion)
215 	DAMAGE_NO_ARMOR			armor does not protect from this damage
216 	DAMAGE_ENERGY			damage is from an energy based weapon
217 	DAMAGE_NO_KNOCKBACK		do not affect velocity, just view angles
218 	DAMAGE_BULLET			damage is from a bullet (used for ricochets)
219 	DAMAGE_NO_PROTECTION	kills godmode, armor, everything
220 ============
221 */
CheckPowerArmor(edict_t * ent,vec3_t point,vec3_t normal,int damage,int dflags)222 static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
223 {
224 	gclient_t	*client;
225 	int			save;
226 	int			power_armor_type;
227 	int			index;
228 	int			damagePerCell;
229 	int			pa_te_type;
230 	int			power;
231 	int			power_used;
232 
233 	if (!damage)
234 		return 0;
235 
236 	client = ent->client;
237 
238 	if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR))		// PGM
239 		return 0;
240 
241 	if (client)
242 	{
243 		power_armor_type = PowerArmorType (ent);
244 		if (power_armor_type != POWER_ARMOR_NONE)
245 		{
246 			index = ITEM_INDEX(FindItem("Cells"));
247 			power = client->pers.inventory[index];
248 		}
249 	}
250 	else if (ent->svflags & SVF_MONSTER)
251 	{
252 		power_armor_type = ent->monsterinfo.power_armor_type;
253 		power = ent->monsterinfo.power_armor_power;
254 	}
255 	else
256 		return 0;
257 
258 	if (power_armor_type == POWER_ARMOR_NONE)
259 		return 0;
260 	if (!power)
261 		return 0;
262 
263 	if (power_armor_type == POWER_ARMOR_SCREEN)
264 	{
265 		vec3_t		vec;
266 		float		dot;
267 		vec3_t		forward;
268 
269 		// only works if damage point is in front
270 		AngleVectors (ent->s.angles, forward, NULL, NULL);
271 		VectorSubtract (point, ent->s.origin, vec);
272 		VectorNormalize (vec);
273 		dot = DotProduct (vec, forward);
274 		if (dot <= 0.3)
275 			return 0;
276 
277 		damagePerCell = 1;
278 		pa_te_type = TE_SCREEN_SPARKS;
279 		damage = damage / 3;
280 	}
281 	else
282 	{
283 		damagePerCell = 2;
284 		pa_te_type = TE_SHIELD_SPARKS;
285 		damage = (2 * damage) / 3;
286 	}
287 
288 	// etf rifle
289 	if (dflags & DAMAGE_NO_REG_ARMOR)
290 		save = (power * damagePerCell) / 2;
291 	else
292 		save = power * damagePerCell;
293 
294 	if (!save)
295 		return 0;
296 	if (save > damage)
297 		save = damage;
298 
299 	SpawnDamage (pa_te_type, point, normal, save);
300 	ent->powerarmor_time = level.time + 0.2;
301 
302 	if (dflags & DAMAGE_NO_REG_ARMOR)
303 		power_used = (save / damagePerCell) * 2;
304 	else
305 		power_used = save / damagePerCell;
306 
307 	if (client)
308 		client->pers.inventory[index] -= power_used;
309 	else
310 		ent->monsterinfo.power_armor_power -= power_used;
311 	return save;
312 }
313 
CheckArmor(edict_t * ent,vec3_t point,vec3_t normal,int damage,int te_sparks,int dflags)314 static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
315 {
316 	gclient_t	*client;
317 	int			save;
318 	int			index;
319 	gitem_t		*armor;
320 
321 	if (!damage)
322 		return 0;
323 
324 	client = ent->client;
325 
326 	if (!client)
327 		return 0;
328 
329 	// ROGUE - added DAMAGE_NO_REG_ARMOR for atf rifle
330 	if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR))
331 		return 0;
332 
333 	index = ArmorIndex (ent);
334 	if (!index)
335 		return 0;
336 
337 	armor = GetItemByIndex (index);
338 
339 	if (dflags & DAMAGE_ENERGY)
340 		save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
341 	else
342 		save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
343 	if (save >= client->pers.inventory[index])
344 		save = client->pers.inventory[index];
345 
346 	if (!save)
347 		return 0;
348 
349 	client->pers.inventory[index] -= save;
350 	SpawnDamage (te_sparks, point, normal, save);
351 
352 	return save;
353 }
354 
M_ReactToDamage(edict_t * targ,edict_t * attacker,edict_t * inflictor)355 void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
356 {
357 	// pmm
358 	qboolean new_tesla;
359 
360 	if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
361 		return;
362 
363 //=======
364 //ROGUE
365 	// logic for tesla - if you are hit by a tesla, and can't see who you should be mad at (attacker)
366 	// attack the tesla
367 	// also, target the tesla if it's a "new" tesla
368 	if ((inflictor) && (!strcmp(inflictor->classname, "tesla")))
369 	{
370 		new_tesla = MarkTeslaArea(targ, inflictor);
371 		if (new_tesla)
372 			TargetTesla (targ, inflictor);
373 		return;
374 		// FIXME - just ignore teslas when you're TARGET_ANGER or MEDIC
375 /*		if (!(targ->enemy && (targ->monsterinfo.aiflags & (AI_TARGET_ANGER|AI_MEDIC))))
376 		{
377 			// FIXME - coop issues?
378 			if ((!targ->enemy) || (!visible(targ, targ->enemy)))
379 			{
380 				gi.dprintf ("can't see player, switching to tesla\n");
381 				TargetTesla (targ, inflictor);
382 				return;
383 			}
384 			gi.dprintf ("can see player, ignoring tesla\n");
385 		}
386 		else if ((g_showlogic) && (g_showlogic->value))
387 			gi.dprintf ("no enemy, or I'm doing other, more important things, than worrying about a damned tesla!\n");
388 */
389 	}
390 //ROGUE
391 //=======
392 
393 	if (attacker == targ || attacker == targ->enemy)
394 		return;
395 
396 	// if we are a good guy monster and our attacker is a player
397 	// or another good guy, do not get mad at them
398 	if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
399 	{
400 		if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
401 			return;
402 	}
403 
404 //PGM
405 	// if we're currently mad at something a target_anger made us mad at, ignore
406 	// damage
407 	if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
408 	{
409 		float	percentHealth;
410 
411 		// make sure whatever we were pissed at is still around.
412 		if(targ->enemy->inuse)
413 		{
414 			percentHealth = (float)(targ->health) / (float)(targ->max_health);
415 			if( targ->enemy->inuse && percentHealth > 0.33)
416 				return;
417 		}
418 
419 		// remove the target anger flag
420 		targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
421 	}
422 //PGM
423 
424 // PMM
425 // if we're healing someone, do like above and try to stay with them
426 	if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
427 	{
428 		float	percentHealth;
429 
430 		percentHealth = (float)(targ->health) / (float)(targ->max_health);
431 		// ignore it some of the time
432 		if( targ->enemy->inuse && percentHealth > 0.25)
433 			return;
434 
435 		// remove the medic flag
436 		targ->monsterinfo.aiflags &= ~AI_MEDIC;
437 		cleanupHealTarget (targ->enemy);
438 	}
439 // PMM
440 
441 	// we now know that we are not both good guys
442 
443 	// if attacker is a client, get mad at them because he's good and we're not
444 	if (attacker->client)
445 	{
446 		targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
447 
448 		// this can only happen in coop (both new and old enemies are clients)
449 		// only switch if can't see the current enemy
450 		if (targ->enemy && targ->enemy->client)
451 		{
452 			if (visible(targ, targ->enemy))
453 			{
454 				targ->oldenemy = attacker;
455 				return;
456 			}
457 			targ->oldenemy = targ->enemy;
458 		}
459 		targ->enemy = attacker;
460 		if (!(targ->monsterinfo.aiflags & AI_DUCKED))
461 			FoundTarget (targ);
462 		return;
463 	}
464 
465 	// it's the same base (walk/swim/fly) type and a different classname and it's not a tank
466 	// (they spray too much), get mad at them
467 	// PMM
468 	// added medics to this
469 	// FIXME -
470 	// this really should be turned into an AI flag marking appropriate monsters as "don't shoot me"
471 	//   this also leads to the problem of tanks and medics being able to, at will, kill monsters with
472 	//   no chance of retaliation.  My vote is to make those monsters who are designed as "don't shoot me"
473 	//   such that they also ignore being shot by monsters as well
474 	/*
475 	if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
476 		 (strcmp (targ->classname, attacker->classname) != 0) &&
477 		 (strcmp(attacker->classname, "monster_tank") != 0) &&
478 		 (strcmp(attacker->classname, "monster_supertank") != 0) &&
479 		 (strcmp(attacker->classname, "monster_makron") != 0) &&
480 		 (strcmp(attacker->classname, "monster_jorg") != 0) &&
481 		 (strcmp(attacker->classname, "monster_carrier") != 0) &&
482  		 (strncmp(attacker->classname, "monster_medic", 12) != 0) ) // this should get medics & medic_commanders
483 	*/
484 	if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
485 		(strcmp (targ->classname, attacker->classname) != 0) &&
486 		!(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
487 		!(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS) )
488 	{
489 		if (targ->enemy && targ->enemy->client)
490 			targ->oldenemy = targ->enemy;
491 		targ->enemy = attacker;
492 		if (!(targ->monsterinfo.aiflags & AI_DUCKED))
493 			FoundTarget (targ);
494 	}
495 	// if they *meant* to shoot us, then shoot back
496 	else if (attacker->enemy == targ)
497 	{
498 		if (targ->enemy && targ->enemy->client)
499 			targ->oldenemy = targ->enemy;
500 		targ->enemy = attacker;
501 		if (!(targ->monsterinfo.aiflags & AI_DUCKED))
502 			FoundTarget (targ);
503 	}
504 	// otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
505 	else if (attacker->enemy && attacker->enemy != targ)
506 	{
507 		if (targ->enemy && targ->enemy->client)
508 			targ->oldenemy = targ->enemy;
509 		targ->enemy = attacker->enemy;
510 		if (!(targ->monsterinfo.aiflags & AI_DUCKED))
511 			FoundTarget (targ);
512 	}
513 }
514 
CheckTeamDamage(edict_t * targ,edict_t * attacker)515 qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
516 {
517 		//FIXME make the next line real and uncomment this block
518 		// if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
519 	return false;
520 }
521 
T_Damage(edict_t * targ,edict_t * inflictor,edict_t * attacker,vec3_t dir,vec3_t point,vec3_t normal,int damage,int knockback,int dflags,int mod)522 void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
523 {
524 	gclient_t	*client;
525 	int			take;
526 	int			save;
527 	int			asave;
528 	int			psave;
529 	int			te_sparks;
530 	int			sphere_notified;	// PGM
531 
532 	if (!targ->takedamage)
533 		return;
534 
535 	sphere_notified = false;		// PGM
536 
537 	// friendly fire avoidance
538 	// if enabled you can't hurt teammates (but you can hurt yourself)
539 	// knockback still occurs
540 	if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
541 	{
542 		if (OnSameTeam (targ, attacker))
543 		{
544 			// PMM - nukes kill everyone
545 			if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && (mod != MOD_NUKE))
546 				damage = 0;
547 			else
548 				mod |= MOD_FRIENDLY_FIRE;
549 		}
550 	}
551 	meansOfDeath = mod;
552 
553 //ROGUE
554 	// allow the deathmatch game to change values
555 	if (deathmatch->value && gamerules && gamerules->value)
556 	{
557 		if(DMGame.ChangeDamage)
558 			damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
559 		if(DMGame.ChangeKnockback)
560 			knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
561 
562 		if(!damage)
563 			return;
564 	}
565 //ROGUE
566 
567 	// easy mode takes half damage
568 	if (skill->value == 0 && deathmatch->value == 0 && targ->client)
569 	{
570 		damage *= 0.5;
571 		if (!damage)
572 			damage = 1;
573 	}
574 
575 	client = targ->client;
576 
577 	// PMM - defender sphere takes half damage
578 	if ((client) && (client->owned_sphere) && (client->owned_sphere->spawnflags == 1))
579 	{
580 		damage *= 0.5;
581 		if (!damage)
582 			damage = 1;
583 	}
584 
585 	if (dflags & DAMAGE_BULLET)
586 		te_sparks = TE_BULLET_SPARKS;
587 	else
588 		te_sparks = TE_SPARKS;
589 
590 	VectorNormalize(dir);
591 
592 // bonus damage for suprising a monster
593 	if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
594 		damage *= 2;
595 
596 	if (targ->flags & FL_NO_KNOCKBACK)
597 		knockback = 0;
598 
599 // figure momentum add
600 	if (!(dflags & DAMAGE_NO_KNOCKBACK))
601 	{
602 		if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
603 		{
604 			vec3_t	kvel;
605 			float	mass;
606 
607 			if (targ->mass < 50)
608 				mass = 50;
609 			else
610 				mass = targ->mass;
611 
612 			if (targ->client  && attacker == targ)
613 				VectorScale (dir, 1600.0 * (float)knockback / mass, kvel);	// the rocket jump hack...
614 			else
615 				VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
616 
617 			VectorAdd (targ->velocity, kvel, targ->velocity);
618 		}
619 	}
620 
621 	take = damage;
622 	save = 0;
623 
624 	// check for godmode
625 	if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
626 	{
627 		take = 0;
628 		save = damage;
629 		SpawnDamage (te_sparks, point, normal, save);
630 	}
631 
632 	// check for invincibility
633 	if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
634 	{
635 		if (targ->pain_debounce_time < level.time)
636 		{
637 			gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
638 			targ->pain_debounce_time = level.time + 2;
639 		}
640 		take = 0;
641 		save = damage;
642 	}
643 	// ROGUE
644 	// check for monster invincibility
645 	if (((targ->svflags & SVF_MONSTER) && targ->monsterinfo.invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
646 	{
647 		if (targ->pain_debounce_time < level.time)
648 		{
649 			gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
650 			targ->pain_debounce_time = level.time + 2;
651 		}
652 		take = 0;
653 		save = damage;
654 	}
655 	// ROGUE
656 
657 	psave = CheckPowerArmor (targ, point, normal, take, dflags);
658 	take -= psave;
659 
660 	asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
661 	take -= asave;
662 
663 	//treat cheat/powerup savings the same as armor
664 	asave += save;
665 
666 	// team damage avoidance
667 	if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
668 		return;
669 
670 // ROGUE - this option will do damage both to the armor and person. originally for DPU rounds
671 	if (dflags & DAMAGE_DESTROY_ARMOR)
672 	{
673 		if(!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
674 		   !(client && client->invincible_framenum > level.framenum))
675 		{
676 			take = damage;
677 		}
678 	}
679 // ROGUE
680 
681 // do the damage
682 	if (take)
683 	{
684 //PGM		need more blood for chainfist.
685 		if(targ->flags & FL_MECHANICAL)
686 		{
687 			SpawnDamage ( TE_ELECTRIC_SPARKS, point, normal, take);
688 		}
689 		else if ((targ->svflags & SVF_MONSTER) || (client))
690 		{
691 			if(mod == MOD_CHAINFIST)
692 				SpawnDamage (TE_MOREBLOOD, point, normal, 255);
693 			else
694 				SpawnDamage (TE_BLOOD, point, normal, take);
695 		}
696 		else
697 			SpawnDamage (te_sparks, point, normal, take);
698 //PGM
699 
700 		targ->health = targ->health - take;
701 
702 //PGM - spheres need to know who to shoot at
703 		if(client && client->owned_sphere)
704 		{
705 			sphere_notified = true;
706 			if(client->owned_sphere->pain)
707 				client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
708 		}
709 //PGM
710 
711 		if (targ->health <= 0)
712 		{
713 			if ((targ->svflags & SVF_MONSTER) || (client))
714 				targ->flags |= FL_NO_KNOCKBACK;
715 			Killed (targ, inflictor, attacker, take, point);
716 			return;
717 		}
718 	}
719 
720 //PGM - spheres need to know who to shoot at
721 	if (!sphere_notified)
722 	{
723 		if(client && client->owned_sphere)
724 		{
725 			sphere_notified = true;
726 			if(client->owned_sphere->pain)
727 				client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
728 		}
729 	}
730 //PGM
731 
732 	if (targ->svflags & SVF_MONSTER)
733 	{
734 		M_ReactToDamage (targ, attacker, inflictor);
735 		// PMM - fixme - if anyone else but the medic ever uses AI_MEDIC, check for it here instead
736 		// of in the medic's pain function
737 		if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
738 		{
739 			targ->pain (targ, attacker, knockback, take);
740 			// nightmare mode monsters don't go into pain frames often
741 			if (skill->value == 3)
742 				targ->pain_debounce_time = level.time + 5;
743 		}
744 	}
745 	else if (client)
746 	{
747 		if (!(targ->flags & FL_GODMODE) && (take))
748 			targ->pain (targ, attacker, knockback, take);
749 	}
750 	else if (take)
751 	{
752 		if (targ->pain)
753 			targ->pain (targ, attacker, knockback, take);
754 	}
755 
756 	// add to the damage inflicted on a player this frame
757 	// the total will be turned into screen blends and view angle kicks
758 	// at the end of the frame
759 	if (client)
760 	{
761 		client->damage_parmor += psave;
762 		client->damage_armor += asave;
763 		client->damage_blood += take;
764 		client->damage_knockback += knockback;
765 		VectorCopy (point, client->damage_from);
766 	}
767 }
768 
769 
770 /*
771 ============
772 T_RadiusDamage
773 ============
774 */
T_RadiusDamage(edict_t * inflictor,edict_t * attacker,float damage,edict_t * ignore,float radius,int mod)775 void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
776 {
777 	float	points;
778 	edict_t	*ent = NULL;
779 	vec3_t	v;
780 	vec3_t	dir;
781 
782 	while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
783 	{
784 		if (ent == ignore)
785 			continue;
786 		if (!ent->takedamage)
787 			continue;
788 
789 		VectorAdd (ent->mins, ent->maxs, v);
790 		VectorMA (ent->s.origin, 0.5, v, v);
791 		VectorSubtract (inflictor->s.origin, v, v);
792 		points = damage - 0.5 * VectorLength (v);
793 		if (ent == attacker)
794 			points = points * 0.5;
795 		if (points > 0)
796 		{
797 			if (CanDamage (ent, inflictor))
798 			{
799 				VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
800 				T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
801 			}
802 		}
803 	}
804 }
805 
806 // **********************
807 // ROGUE
808 
809 /*
810 ============
811 T_RadiusNukeDamage
812 
813 Like T_RadiusDamage, but ignores walls (skips CanDamage check, among others)
814 // up to KILLZONE radius, do 10,000 points
815 // after that, do damage linearly out to KILLZONE2 radius
816 ============
817 */
818 
T_RadiusNukeDamage(edict_t * inflictor,edict_t * attacker,float damage,edict_t * ignore,float radius,int mod)819 void T_RadiusNukeDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
820 {
821 	float	points;
822 	edict_t	*ent = NULL;
823 	vec3_t	v;
824 	vec3_t	dir;
825 	float	len;
826 	float	killzone, killzone2;
827 	trace_t	tr;
828 	float	dist;
829 
830 	killzone = radius;
831 	killzone2 = radius*2.0;
832 
833 	while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL)
834 	{
835 // ignore nobody
836 		if (ent == ignore)
837 			continue;
838 		if (!ent->takedamage)
839 			continue;
840 		if (!ent->inuse)
841 			continue;
842 		if (!(ent->client || (ent->svflags & SVF_MONSTER) || (ent->svflags & SVF_DAMAGEABLE)))
843 			continue;
844 
845 		VectorAdd (ent->mins, ent->maxs, v);
846 		VectorMA (ent->s.origin, 0.5, v, v);
847 		VectorSubtract (inflictor->s.origin, v, v);
848 		len = VectorLength(v);
849 		if (len <= killzone)
850 		{
851 			if (ent->client)
852 				ent->flags |= FL_NOGIB;
853 			points = 10000;
854 		}
855 		else if (len <= killzone2)
856 			points = (damage/killzone)*(killzone2 - len);
857 		else
858 			points = 0;
859 //		points = damage - 0.005 * len*len;
860 //		if (ent == attacker)
861 //			points = points * 0.5;
862 //		if ((g_showlogic) && (g_showlogic->value))
863 //		{
864 //			if (!(strcmp(ent->classname, "player")))
865 //				gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, inflictor->teammaster->client->pers.netname);
866 //			else
867 //				gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, ent->classname);
868 //		}
869 		if (points > 0)
870 		{
871 //			if (CanDamage (ent, inflictor))
872 //			{
873 				if (ent->client)
874 					ent->client->nuke_framenum = level.framenum + 20;
875 				VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
876 				T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
877 //			}
878 		}
879 	}
880 	ent = g_edicts+1; // skip the worldspawn
881 	// cycle through players
882 	while (ent)
883 	{
884 		if ((ent->client) && (ent->client->nuke_framenum != level.framenum+20) && (ent->inuse))
885 		{
886 			tr = gi.trace (inflictor->s.origin, NULL, NULL, ent->s.origin, inflictor, MASK_SOLID);
887 			if (tr.fraction == 1.0)
888 			{
889 //				if ((g_showlogic) && (g_showlogic->value))
890 //					gi.dprintf ("Undamaged player in LOS with nuke, flashing!\n");
891 				ent->client->nuke_framenum = level.framenum + 20;
892 			}
893 			else
894 			{
895 				dist = realrange (ent, inflictor);
896 				if (dist < 2048)
897 					ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 15);
898 				else
899 					ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 10);
900 			}
901 			ent++;
902 		}
903 		else
904 			ent = NULL;
905 	}
906 }
907 
908 /*
909 ============
910 T_RadiusClassDamage
911 
912 Like T_RadiusDamage, but ignores anything with classname=ignoreClass
913 ============
914 */
T_RadiusClassDamage(edict_t * inflictor,edict_t * attacker,float damage,char * ignoreClass,float radius,int mod)915 void T_RadiusClassDamage (edict_t *inflictor, edict_t *attacker, float damage, char *ignoreClass, float radius, int mod)
916 {
917 	float	points;
918 	edict_t	*ent = NULL;
919 	vec3_t	v;
920 	vec3_t	dir;
921 
922 	while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
923 	{
924 		if (ent->classname && !strcmp(ent->classname, ignoreClass))
925 			continue;
926 		if (!ent->takedamage)
927 			continue;
928 
929 		VectorAdd (ent->mins, ent->maxs, v);
930 		VectorMA (ent->s.origin, 0.5, v, v);
931 		VectorSubtract (inflictor->s.origin, v, v);
932 		points = damage - 0.5 * VectorLength (v);
933 		if (ent == attacker)
934 			points = points * 0.5;
935 		if (points > 0)
936 		{
937 			if (CanDamage (ent, inflictor))
938 			{
939 				VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
940 				T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
941 			}
942 		}
943 	}
944 }
945 
946 // ROGUE
947 // ********************
948