g_combat.c 15 KB

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