|
|
1.1 root 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: */
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: */
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: */
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: */
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: */
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:
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:
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:
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:
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: */
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:
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: */
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: // ********************
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.