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