g_combat.c 14 KB

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