|
|
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: // this can only happen in coop (both new and old enemies are clients)
298: // only switch if can't see the current enemy
299: if (targ->enemy && targ->enemy->client)
300: {
301: if (visible(targ, targ->enemy))
302: {
303: targ->oldenemy = attacker;
304: return;
305: }
306: targ->oldenemy = targ->enemy;
307: }
308: targ->enemy = attacker;
309: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
310: FoundTarget (targ);
311: return;
312: }
313:
314: // it's the same base (walk/swim/fly) type and a different classname and it's not a tank
315: // (they spray too much), get mad at them
316: if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
317: (strcmp (targ->classname, attacker->classname) != 0) &&
318: (strcmp(attacker->classname, "monster_tank") != 0) &&
319: (strcmp(attacker->classname, "monster_supertank") != 0) &&
320: (strcmp(attacker->classname, "monster_makron") != 0) &&
321: (strcmp(attacker->classname, "monster_jorg") != 0) )
322: {
323: if (targ->enemy)
324: if (targ->enemy->client)
325: targ->oldenemy = targ->enemy;
326: targ->enemy = attacker;
327: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
328: FoundTarget (targ);
329: }
330: else
331: // otherwise get mad at whoever they are mad at (help our buddy)
332: {
333: if (targ->enemy)
334: if (targ->enemy->client)
335: targ->oldenemy = targ->enemy;
336: targ->enemy = attacker->enemy;
337: if (!(targ->monsterinfo.aiflags & AI_DUCKED))
338: FoundTarget (targ);
339: }
340: }
341:
342: qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
343: {
344: //FIXME make the next line real and uncomment this block
345: // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
346: return false;
347: }
348:
349: 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)
350: {
351: gclient_t *client;
352: int take;
353: int save;
354: int asave;
355: int psave;
356: int te_sparks;
357:
358: if (!targ->takedamage)
359: return;
360:
361: // friendly fire avoidance
362: // if enabled you can't hurt teammates (but you can hurt yourself)
363: // knockback still occurs
364: if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
365: {
366: if (OnSameTeam (targ, attacker))
367: {
368: if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
369: damage = 0;
370: else
371: mod |= MOD_FRIENDLY_FIRE;
372: }
373: }
374: meansOfDeath = mod;
375:
376: // easy mode takes half damage
377: if (skill->value == 0 && deathmatch->value == 0 && targ->client)
378: {
379: damage *= 0.5;
380: if (!damage)
381: damage = 1;
382: }
383:
384: client = targ->client;
385:
386: if (dflags & DAMAGE_BULLET)
387: te_sparks = TE_BULLET_SPARKS;
388: else
389: te_sparks = TE_SPARKS;
390:
391: VectorNormalize(dir);
392:
393: // bonus damage for suprising a monster
394: if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
395: damage *= 2;
396:
397: if (targ->flags & FL_NO_KNOCKBACK)
398: knockback = 0;
399:
400: // figure momentum add
401: if (!(dflags & DAMAGE_NO_KNOCKBACK))
402: {
403: if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
404: {
405: vec3_t kvel;
406: float mass;
407:
408: if (targ->mass < 50)
409: mass = 50;
410: else
411: mass = targ->mass;
412:
413: if (targ->client && attacker == targ)
414: VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
415: else
416: VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
417:
418: VectorAdd (targ->velocity, kvel, targ->velocity);
419: }
420: }
421:
422: take = damage;
423: save = 0;
424:
425: // check for godmode
426: if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
427: {
428: take = 0;
429: save = damage;
430: SpawnDamage (te_sparks, point, normal, save);
431: }
432:
433: // check for invincibility
434: if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
435: {
436: if (targ->pain_debounce_time < level.time)
437: {
438: gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
439: targ->pain_debounce_time = level.time + 2;
440: }
441: take = 0;
442: save = damage;
443: }
444:
445: psave = CheckPowerArmor (targ, point, normal, take, dflags);
446: take -= psave;
447:
448: asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
449: take -= asave;
450:
451: //treat cheat/powerup savings the same as armor
452: asave += save;
453:
454: // team damage avoidance
455: if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
456: return;
457:
458: // do the damage
459: if (take)
460: {
461: if ((targ->svflags & SVF_MONSTER) || (client))
462: SpawnDamage (TE_BLOOD, point, normal, take);
463: else
464: SpawnDamage (te_sparks, point, normal, take);
465:
466:
467: targ->health = targ->health - take;
468:
469: if (targ->health <= 0)
470: {
471: if ((targ->svflags & SVF_MONSTER) || (client))
472: targ->flags |= FL_NO_KNOCKBACK;
473: Killed (targ, inflictor, attacker, take, point);
474: return;
475: }
476: }
477:
478: if (targ->svflags & SVF_MONSTER)
479: {
480: M_ReactToDamage (targ, attacker);
481: if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
482: {
483: targ->pain (targ, attacker, knockback, take);
484: // nightmare mode monsters don't go into pain frames often
485: if (skill->value == 3)
486: targ->pain_debounce_time = level.time + 5;
487: }
488: }
489: else if (client)
490: {
491: if (!(targ->flags & FL_GODMODE) && (take))
492: targ->pain (targ, attacker, knockback, take);
493: }
494: else if (take)
495: {
496: if (targ->pain)
497: targ->pain (targ, attacker, knockback, take);
498: }
499:
500: // add to the damage inflicted on a player this frame
501: // the total will be turned into screen blends and view angle kicks
502: // at the end of the frame
503: if (client)
504: {
505: client->damage_parmor += psave;
506: client->damage_armor += asave;
507: client->damage_blood += take;
508: client->damage_knockback += knockback;
509: VectorCopy (point, client->damage_from);
510: }
511: }
512:
513:
514: /*
515: ============
516: T_RadiusDamage
517: ============
518: */
519: void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
520: {
521: float points;
522: edict_t *ent = NULL;
523: vec3_t v;
524: vec3_t dir;
525:
526: while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
527: {
528: if (ent == ignore)
529: continue;
530: if (!ent->takedamage)
531: continue;
532:
533: VectorAdd (ent->mins, ent->maxs, v);
534: VectorMA (ent->s.origin, 0.5, v, v);
535: VectorSubtract (inflictor->s.origin, v, v);
536: points = damage - 0.5 * VectorLength (v);
537: if (ent == attacker)
538: points = points * 0.5;
539: if (points > 0)
540: {
541: if (CanDamage (ent, inflictor))
542: {
543: VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
544: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
545: }
546: }
547: }
548: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.