1 // g_combat.c
2
3 #include "g_local.h"
4 #include "bot.h"
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 */
CanDamage(edict_t * targ,edict_t * inflictor)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 */
Killed(edict_t * targ,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)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 */
SpawnDamage(int type,vec3_t origin,vec3_t normal,int damage)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 */
CheckPowerArmor(edict_t * ent,vec3_t point,vec3_t normal,int damage,int dflags)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(Fdi_CELLS/*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
CheckArmor(edict_t * ent,vec3_t point,vec3_t normal,int damage,int te_sparks,int dflags)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
CheckTeamDamage(edict_t * targ,edict_t * attacker)276 qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
277 {
278 //ZOID
279 if (ctf->value && targ->client && attacker->client)
280 if (targ->client->resp.ctf_team == attacker->client->resp.ctf_team &&
281 targ != attacker)
282 return true;
283 //ZOID
284
285 //FIXME make the next line real and uncomment this block
286 // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
287 return false;
288 }
289
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)290 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)
291 {
292 gclient_t *client;
293 int take;
294 int save;
295 int asave;
296 int psave;
297 int te_sparks;
298
299 if (!targ->takedamage)
300 return;
301
302 if(mod == MOD_CRUSH)
303 {
304 //bot's state change
305 if((targ->svflags & SVF_MONSTER) && targ->client)
306 {
307 if((targ->client->zc.waitin_obj == inflictor && targ->client->zc.zcstate)
308 || targ->groundentity == inflictor)
309 {
310 // gi.bprintf(PRINT_HIGH,"MOOOOOOOOOOOOOOOOOOOO\n");
311 targ->client->zc.zcstate |= STS_W_DONT;
312 }
313 }
314 }
315
316 // friendly fire avoidance
317 // if enabled you can't hurt teammates (but you can hurt yourself)
318 // knockback still occurs
319 if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
320 {
321 if (OnSameTeam (targ, attacker))
322 {
323 if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
324 damage = 0;
325 else
326 mod |= MOD_FRIENDLY_FIRE;
327 }
328 else if(targ->client && !(targ->svflags & SVF_MONSTER))
329 {
330 if(attacker->client) targ->client->zc.first_target = attacker;
331 }
332 }
333 meansOfDeath = mod;
334
335 // easy mode takes half damage
336 if (skill->value == 0 && deathmatch->value == 0 && targ->client)
337 {
338 damage *= 0.5;
339 if (!damage)
340 damage = 1;
341 }
342
343 client = targ->client;
344
345 if (dflags & DAMAGE_BULLET)
346 te_sparks = TE_BULLET_SPARKS;
347 else
348 te_sparks = TE_SPARKS;
349
350 VectorNormalize(dir);
351
352 // bonus damage for suprising a monster
353 if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
354 damage *= 2;
355
356 //ZOID
357 //strength tech
358 damage = CTFApplyStrength(attacker, damage);
359 //ZOID
360
361 if (targ->flags & FL_NO_KNOCKBACK)
362 knockback = 0;
363
364 // figure momentum add
365 if (!(dflags & DAMAGE_NO_KNOCKBACK))
366 {
367 if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
368 {
369 vec3_t kvel;
370 float mass;
371
372 if (targ->mass < 50)
373 mass = 50;
374 else
375 mass = targ->mass;
376
377 if (targ->client && attacker == targ)
378 VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
379 else
380 VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
381
382 VectorAdd (targ->velocity, kvel, targ->velocity);
383 }
384 }
385
386 take = damage;
387 save = 0;
388
389 // check for godmode
390 if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
391 {
392 take = 0;
393 save = damage;
394 SpawnDamage (te_sparks, point, normal, save);
395 }
396
397 // check for invincibility
398 if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
399 {
400 if (targ->pain_debounce_time < level.time)
401 {
402 gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect3.wav"), 1, ATTN_NORM, 0);
403 // gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
404 targ->pain_debounce_time = level.time + 2;
405 }
406 take = 0;
407 save = damage;
408 }
409
410 //ZOID
411 //team armor protect
412 if (ctf->value && targ->client && attacker->client &&
413 targ->client->resp.ctf_team == attacker->client->resp.ctf_team &&
414 targ != attacker && ((int)dmflags->value & DF_ARMOR_PROTECT)) {
415 psave = asave = 0;
416 } else {
417 //ZOID
418
419 psave = CheckPowerArmor (targ, point, normal, take, dflags);
420 take -= psave;
421
422 asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
423 take -= asave;
424 }
425 //treat cheat/powerup savings the same as armor
426 asave += save;
427
428 //ZOID
429 //resistance tech
430 take = CTFApplyResistance(targ, take);
431 //ZOID
432
433 // team damage avoidance
434 if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
435 return;
436
437 //ZOID
438 CTFCheckHurtCarrier(targ, attacker);
439 //ZOID
440
441 // do the damage
442 if (take)
443 {
444 if ((targ->svflags & SVF_MONSTER) || (client))
445 {
446 SpawnDamage (TE_BLOOD, point, normal, take);
447 if(client && (targ->svflags & SVF_MONSTER) && attacker)
448 {
449 if(client->zc.battlemode & FIRE_CHIKEN) client->zc.battlemode &= ~FIRE_CHIKEN;
450
451 if(mod == MOD_RAILGUN
452 || mod == MOD_BFG_LASER
453 || mod == MOD_ROCKET
454 || mod == MOD_BLASTER
455 || mod == MOD_RIPPER
456 || mod == MOD_HYPERBLASTER
457 || mod == MOD_PHALANX)
458 {
459 if(attacker->client
460 && (9 * random() < Bot[client->zc.botindex].param[BOP_REACTION])
461 && !client->zc.first_target)
462 {
463 if(!OnSameTeam (targ, attacker)) client->zc.first_target = attacker;
464 }
465 }
466 }
467 }
468 else
469 SpawnDamage (te_sparks, point, normal, take);
470
471
472 targ->health = targ->health - take;
473
474 if (targ->health <= 0)
475 {
476 if ((targ->svflags & SVF_MONSTER) || (client))
477 targ->flags |= FL_NO_KNOCKBACK;
478 Killed (targ, inflictor, attacker, take, point);
479 return;
480 }
481 }
482
483 if (client)
484 {
485 if (!(targ->flags & FL_GODMODE) && (take))
486 targ->pain (targ, attacker, knockback, take);
487 }
488 else if (take)
489 {
490 if (targ->pain)
491 targ->pain (targ, attacker, knockback, take);
492 }
493
494 // add to the damage inflicted on a player this frame
495 // the total will be turned into screen blends and view angle kicks
496 // at the end of the frame
497 if (client)
498 {
499 client->damage_parmor += psave;
500 client->damage_armor += asave;
501 client->damage_blood += take;
502 client->damage_knockback += knockback;
503 VectorCopy (point, client->damage_from);
504 }
505 }
506
507
508 /*
509 ============
510 T_RadiusDamage
511 ============
512 */
T_RadiusDamage(edict_t * inflictor,edict_t * attacker,float damage,edict_t * ignore,float radius,int mod)513 void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
514 {
515 float points;
516 edict_t *ent = NULL;
517 vec3_t v;
518 vec3_t dir;
519
520 while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
521 {
522 if (ent == ignore)
523 continue;
524 if (!ent->takedamage)
525 continue;
526
527 VectorAdd (ent->mins, ent->maxs, v);
528 VectorMA (ent->s.origin, 0.5, v, v);
529 VectorSubtract (inflictor->s.origin, v, v);
530 points = damage - 0.5 * VectorLength (v);
531 if (ent == attacker)
532 points = points * 0.5;
533 if (points > 0)
534 {
535 if (CanDamage (ent, inflictor))
536 {
537 VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
538 T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
539 }
540 }
541 }
542 }
543