|
|
1.1 root 1: // g_combat.c
2:
3: #include "g_local.h"
4:
5: /*
6: ============
7: CanDamage
8:
9: Returns true if the inflictor can directly damage the target. Used for
10: explosions and melee attacks.
11: ============
12: */
13: qboolean CanDamage (edict_t *targ, edict_t *inflictor)
14: {
15: vec3_t dest;
16: trace_t trace;
17:
18: // bmodels need special checking because their origin is 0,0,0
19: if (targ->movetype == MOVETYPE_PUSH)
20: {
21: VectorAdd (targ->absmin, targ->absmax, dest);
22: VectorScale (dest, 0.5, dest);
23: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
24: if (trace.fraction == 1.0)
25: return true;
26: if (trace.ent == targ)
27: return true;
28: return false;
29: }
30:
31: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
32: if (trace.fraction == 1.0)
33: return true;
34:
35: VectorCopy (targ->s.origin, dest);
36: dest[0] += 15.0;
37: dest[1] += 15.0;
38: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
39: if (trace.fraction == 1.0)
40: return true;
41:
42: VectorCopy (targ->s.origin, dest);
43: dest[0] += 15.0;
44: dest[1] -= 15.0;
45: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
46: if (trace.fraction == 1.0)
47: return true;
48:
49: VectorCopy (targ->s.origin, dest);
50: dest[0] -= 15.0;
51: dest[1] += 15.0;
52: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
53: if (trace.fraction == 1.0)
54: return true;
55:
56: VectorCopy (targ->s.origin, dest);
57: dest[0] -= 15.0;
58: dest[1] -= 15.0;
59: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
60: if (trace.fraction == 1.0)
61: return true;
62:
63:
64: return false;
65: }
66:
67:
68: /*
69: ============
70: Killed
71: ============
72: */
73: void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
74: {
75: if (targ->health < -999)
76: targ->health = -999;
77:
78: targ->enemy = attacker;
79:
80: if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
81: {
82: // targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
83: if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY))
84: {
85: level.killed_monsters++;
86: if (coop->value && attacker->client)
87: attacker->client->resp.score++;
88: // medics won't heal monsters that they kill themselves
89: if (strcmp(attacker->classname, "monster_medic") == 0)
90: targ->owner = attacker;
91: }
92: }
93:
94: if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
95: { // doors, triggers, etc
96: targ->die (targ, inflictor, attacker, damage, point);
97: return;
98: }
99:
100: if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
101: {
102: targ->touch = NULL;
103: monster_death_use (targ);
104: }
105:
106: targ->die (targ, inflictor, attacker, damage, point);
107: }
108:
109:
110: /*
111: ================
112: SpawnDamage
113: ================
114: */
115: void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
116: {
117: if (damage > 255)
118: damage = 255;
119: gi.WriteByte (svc_temp_entity);
120: gi.WriteByte (type);
121: // gi.WriteByte (damage);
122: gi.WritePosition (origin);
123: gi.WriteDir (normal);
124: gi.multicast (origin, MULTICAST_PVS);
125: }
126:
127:
128: /*
129: ============
130: T_Damage
131:
132: targ entity that is being damaged
133: inflictor entity that is causing the damage
134: attacker entity that caused the inflictor to damage targ
135: example: targ=monster, inflictor=rocket, attacker=player
136:
137: dir direction of the attack
138: point point at which the damage is being inflicted
139: normal normal vector from that point
140: damage amount of damage being inflicted
141: knockback force to be applied against targ as a result of the damage
142:
143: dflags these flags are used to control how T_Damage works
144: DAMAGE_RADIUS damage was indirect (from a nearby explosion)
145: DAMAGE_NO_ARMOR armor does not protect from this damage
146: DAMAGE_ENERGY damage is from an energy based weapon
147: DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
148: DAMAGE_BULLET damage is from a bullet (used for ricochets)
149: DAMAGE_NO_PROTECTION kills godmode, armor, everything
150: ============
151: */
152: static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
153: {
154: gclient_t *client;
155: int save;
156: int power_armor_type;
157: int index;
158: int damagePerCell;
159: int pa_te_type;
160: int power;
161: int power_used;
162:
163: if (!damage)
164: return 0;
165:
166: client = ent->client;
167:
168: if (dflags & DAMAGE_NO_ARMOR)
169: return 0;
170:
171: if (client)
172: {
173: power_armor_type = PowerArmorType (ent);
174: if (power_armor_type != POWER_ARMOR_NONE)
175: {
176: index = ITEM_INDEX(FindItem("Cells"));
177: power = client->pers.inventory[index];
178: }
179: }
180: else if (ent->svflags & SVF_MONSTER)
181: {
182: power_armor_type = ent->monsterinfo.power_armor_type;
183: power = ent->monsterinfo.power_armor_power;
184: }
185: else
186: return 0;
187:
188: if (power_armor_type == POWER_ARMOR_NONE)
189: return 0;
190: if (!power)
191: return 0;
192:
193: if (power_armor_type == POWER_ARMOR_SCREEN)
194: {
195: vec3_t vec;
196: float dot;
197: vec3_t forward;
198:
199: // only works if damage point is in front
200: AngleVectors (ent->s.angles, forward, NULL, NULL);
201: VectorSubtract (point, ent->s.origin, vec);
202: VectorNormalize (vec);
203: dot = DotProduct (vec, forward);
204: if (dot <= 0.3)
205: return 0;
206:
207: damagePerCell = 1;
208: pa_te_type = TE_SCREEN_SPARKS;
209: damage = damage / 3;
210: }
211: else
212: {
213: damagePerCell = 2;
214: pa_te_type = TE_SHIELD_SPARKS;
215: damage = (2 * damage) / 3;
216: }
217:
218: save = power * damagePerCell;
219: if (!save)
220: return 0;
221: if (save > damage)
222: save = damage;
223:
224: SpawnDamage (pa_te_type, point, normal, save);
225: ent->powerarmor_time = level.time + 0.2;
226:
227: power_used = save / damagePerCell;
228:
229: if (client)
230: client->pers.inventory[index] -= power_used;
231: else
232: ent->monsterinfo.power_armor_power -= power_used;
233: return save;
234: }
235:
236: static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
237: {
238: gclient_t *client;
239: int save;
240: int index;
241: gitem_t *armor;
242:
243: if (!damage)
244: return 0;
245:
246: client = ent->client;
247:
248: if (!client)
249: return 0;
250:
251: if (dflags & DAMAGE_NO_ARMOR)
252: return 0;
253:
254: index = ArmorIndex (ent);
255: if (!index)
256: return 0;
257:
258: armor = GetItemByIndex (index);
259:
260: if (dflags & DAMAGE_ENERGY)
261: save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
262: else
263: save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
264: if (save >= client->pers.inventory[index])
265: save = client->pers.inventory[index];
266:
267: if (!save)
268: return 0;
269:
270: client->pers.inventory[index] -= save;
271: SpawnDamage (te_sparks, point, normal, save);
272:
273: return save;
274: }
275:
276: void M_ReactToDamage (edict_t *targ, edict_t *attacker)
277: {
278: if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
279: return;
280:
281: if (attacker == targ || attacker == targ->enemy)
282: return;
283:
284: // if we are a good guy monster and our attacker is a player
285: // or another good guy, do not get mad at them
286: if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
287: {
288: if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
289: return;
290: }
291:
292: // we now know that we are not both good guys
293:
294: // if attacker is a client, get mad at them because he's good and we're not
295: if (attacker->client)
296: {
297: targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
298:
299: // this can only happen in coop (both new and old enemies are clients)
300: // only switch if can't see the current enemy
301: if (targ->enemy && targ->enemy->client)
302: {
303: if (visible(targ, targ->enemy))
304: {
305: targ->oldenemy = attacker;
306: return;
307: }
308: targ->oldenemy = targ->enemy;
309: }
310: targ->enemy = attacker;
311: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
312: FoundTarget (targ);
313: return;
314: }
315:
316: // it's the same base (walk/swim/fly) type and a different classname and it's not a tank
317: // (they spray too much), get mad at them
318: if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
319: (strcmp (targ->classname, attacker->classname) != 0) &&
320: (strcmp(attacker->classname, "monster_tank") != 0) &&
321: (strcmp(attacker->classname, "monster_supertank") != 0) &&
322: (strcmp(attacker->classname, "monster_makron") != 0) &&
323: (strcmp(attacker->classname, "monster_jorg") != 0) )
324: {
325: if (targ->enemy && targ->enemy->client)
326: targ->oldenemy = targ->enemy;
327: targ->enemy = attacker;
328: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
329: FoundTarget (targ);
330: }
331: // if they *meant* to shoot us, then shoot back
332: else if (attacker->enemy == targ)
333: {
334: if (targ->enemy && targ->enemy->client)
335: targ->oldenemy = targ->enemy;
336: targ->enemy = attacker;
337: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
338: FoundTarget (targ);
339: }
340: // otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
341: else if (attacker->enemy && attacker->enemy != targ)
342: {
343: if (targ->enemy && targ->enemy->client)
344: targ->oldenemy = targ->enemy;
345: targ->enemy = attacker->enemy;
346: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
347: FoundTarget (targ);
348: }
349: }
350:
351: qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
352: {
353: //FIXME make the next line real and uncomment this block
354: // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
355: return false;
356: }
357:
358: 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)
359: {
360: gclient_t *client;
361: int take;
362: int save;
363: int asave;
364: int psave;
365: int te_sparks;
366:
367: if (!targ->takedamage)
368: return;
369:
370: // friendly fire avoidance
371: // if enabled you can't hurt teammates (but you can hurt yourself)
372: // knockback still occurs
373: if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
374: {
375: if (OnSameTeam (targ, attacker))
376: {
377: if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
378: damage = 0;
379: else
380: mod |= MOD_FRIENDLY_FIRE;
381: }
382: }
383: meansOfDeath = mod;
384:
385: // easy mode takes half damage
386: if (skill->value == 0 && deathmatch->value == 0 && targ->client)
387: {
388: damage *= 0.5;
389: if (!damage)
390: damage = 1;
391: }
392:
393: client = targ->client;
394:
395: if (dflags & DAMAGE_BULLET)
396: te_sparks = TE_BULLET_SPARKS;
397: else
398: te_sparks = TE_SPARKS;
399:
400: VectorNormalize(dir);
401:
402: // bonus damage for suprising a monster
403: if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
404: damage *= 2;
405:
406: if (targ->flags & FL_NO_KNOCKBACK)
407: knockback = 0;
408:
409: // figure momentum add
410: if (!(dflags & DAMAGE_NO_KNOCKBACK))
411: {
412: if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
413: {
414: vec3_t kvel;
415: float mass;
416:
417: if (targ->mass < 50)
418: mass = 50;
419: else
420: mass = targ->mass;
421:
422: if (targ->client && attacker == targ)
423: VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
424: else
425: VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
426:
427: VectorAdd (targ->velocity, kvel, targ->velocity);
428: }
429: }
430:
431: take = damage;
432: save = 0;
433:
434: // check for godmode
435: if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
436: {
437: take = 0;
438: save = damage;
439: SpawnDamage (te_sparks, point, normal, save);
440: }
441:
442: // check for invincibility
443: if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION) && mod != MOD_TRAP)
444: {
445: if (targ->pain_debounce_time < level.time)
446: {
447: gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
448: targ->pain_debounce_time = level.time + 2;
449: }
450: take = 0;
451: save = damage;
452: }
453:
454: psave = CheckPowerArmor (targ, point, normal, take, dflags);
455: take -= psave;
456:
457: asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
458: take -= asave;
459:
460: //treat cheat/powerup savings the same as armor
461: asave += save;
462:
463: // team damage avoidance
464: if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
465: return;
466:
467: // do the damage
468: if (take)
469: {
470: if ((targ->svflags & SVF_MONSTER) || (client))
471: if (strcmp (targ->classname, "monster_gekk") == 0)
472: SpawnDamage (TE_GREENBLOOD, point, normal, take);
473: else
474: SpawnDamage (TE_BLOOD, point, normal, take);
475: else
476: SpawnDamage (te_sparks, point, normal, take);
477:
478:
479: targ->health = targ->health - take;
480:
481: if (targ->health <= 0)
482: {
483: if ((targ->svflags & SVF_MONSTER) || (client))
484: targ->flags |= FL_NO_KNOCKBACK;
485: Killed (targ, inflictor, attacker, take, point);
486: return;
487: }
488: }
489:
490: if (targ->svflags & SVF_MONSTER)
491: {
492: M_ReactToDamage (targ, attacker);
493: if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
494: {
495: targ->pain (targ, attacker, knockback, take);
496: // nightmare mode monsters don't go into pain frames often
497: if (skill->value == 3)
498: targ->pain_debounce_time = level.time + 5;
499: }
500: }
501: else if (client)
502: {
503: if (!(targ->flags & FL_GODMODE) && (take))
504: targ->pain (targ, attacker, knockback, take);
505: }
506: else if (take)
507: {
508: if (targ->pain)
509: targ->pain (targ, attacker, knockback, take);
510: }
511:
512: // add to the damage inflicted on a player this frame
513: // the total will be turned into screen blends and view angle kicks
514: // at the end of the frame
515: if (client)
516: {
517: client->damage_parmor += psave;
518: client->damage_armor += asave;
519: client->damage_blood += take;
520: client->damage_knockback += knockback;
521: VectorCopy (point, client->damage_from);
522: }
523: }
524:
525:
526: /*
527: ============
528: T_RadiusDamage
529: ============
530: */
531: void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
532: {
533: float points;
534: edict_t *ent = NULL;
535: vec3_t v;
536: vec3_t dir;
537:
538: while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
539: {
540: if (ent == ignore)
541: continue;
542: if (!ent->takedamage)
543: continue;
544:
545: VectorAdd (ent->mins, ent->maxs, v);
546: VectorMA (ent->s.origin, 0.5, v, v);
547: VectorSubtract (inflictor->s.origin, v, v);
548: points = damage - 0.5 * VectorLength (v);
549: if (ent == attacker)
550: points = points * 0.5;
551: if (points > 0)
552: {
553: if (CanDamage (ent, inflictor))
554: {
555: VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
556: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
557: }
558: }
559: }
560: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.