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