g_combat.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  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. void M_SetEffects (edict_t *self);
  6. /*
  7. ROGUE
  8. clean up heal targets for medic
  9. */
  10. void cleanupHealTarget (edict_t *ent)
  11. {
  12. ent->monsterinfo.healer = NULL;
  13. ent->takedamage = DAMAGE_YES;
  14. ent->monsterinfo.aiflags &= ~AI_RESURRECTING;
  15. M_SetEffects (ent);
  16. }
  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. if (targ->monsterinfo.aiflags & AI_MEDIC)
  79. {
  80. if (targ->enemy) // god, I hope so
  81. {
  82. cleanupHealTarget (targ->enemy);
  83. }
  84. // clean up self
  85. targ->monsterinfo.aiflags &= ~AI_MEDIC;
  86. targ->enemy = attacker;
  87. }
  88. else
  89. {
  90. targ->enemy = attacker;
  91. }
  92. if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
  93. {
  94. // targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
  95. //ROGUE - free up slot for spawned monster if it's spawned
  96. if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER)
  97. {
  98. if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse &&
  99. !strcmp(targ->monsterinfo.commander->classname, "monster_carrier"))
  100. {
  101. targ->monsterinfo.commander->monsterinfo.monster_slots++;
  102. // if ((g_showlogic) && (g_showlogic->value))
  103. // gi.dprintf ("g_combat: freeing up carrier slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots);
  104. }
  105. }
  106. if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C)
  107. {
  108. if (targ->monsterinfo.commander)
  109. {
  110. if (targ->monsterinfo.commander->inuse && !strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander"))
  111. {
  112. targ->monsterinfo.commander->monsterinfo.monster_slots++;
  113. // if ((g_showlogic) && (g_showlogic->value))
  114. // gi.dprintf ("g_combat: freeing up medic slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots);
  115. }
  116. // else
  117. // if ((g_showlogic) && (g_showlogic->value))
  118. // gi.dprintf ("my commander is dead! he's a %s\n", targ->monsterinfo.commander->classname);
  119. }
  120. // else if ((g_showlogic) && (g_showlogic->value))
  121. // gi.dprintf ("My commander is GONE\n");
  122. }
  123. if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
  124. {
  125. // need to check this because we can have variable numbers of coop players
  126. if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse &&
  127. !strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13))
  128. {
  129. if (targ->monsterinfo.commander->monsterinfo.monster_used > 0)
  130. targ->monsterinfo.commander->monsterinfo.monster_used--;
  131. // if ((g_showlogic) && (g_showlogic->value))
  132. // gi.dprintf ("g_combat: freeing up black widow slot - %d used\n", targ->monsterinfo.commander->monsterinfo.monster_used);
  133. }
  134. }
  135. //rogue
  136. if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
  137. {
  138. level.killed_monsters++;
  139. if (coop->value && attacker->client)
  140. attacker->client->resp.score++;
  141. // medics won't heal monsters that they kill themselves
  142. // PMM - now they will
  143. // if (strcmp(attacker->classname, "monster_medic") == 0)
  144. // targ->owner = attacker;
  145. }
  146. }
  147. if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
  148. { // doors, triggers, etc
  149. targ->die (targ, inflictor, attacker, damage, point);
  150. return;
  151. }
  152. if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
  153. {
  154. targ->touch = NULL;
  155. monster_death_use (targ);
  156. }
  157. targ->die (targ, inflictor, attacker, damage, point);
  158. }
  159. /*
  160. ================
  161. SpawnDamage
  162. ================
  163. */
  164. void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
  165. {
  166. if (damage > 255)
  167. damage = 255;
  168. gi.WriteByte (svc_temp_entity);
  169. gi.WriteByte (type);
  170. // gi.WriteByte (damage);
  171. gi.WritePosition (origin);
  172. gi.WriteDir (normal);
  173. gi.multicast (origin, MULTICAST_PVS);
  174. }
  175. /*
  176. ============
  177. T_Damage
  178. targ entity that is being damaged
  179. inflictor entity that is causing the damage
  180. attacker entity that caused the inflictor to damage targ
  181. example: targ=monster, inflictor=rocket, attacker=player
  182. dir direction of the attack
  183. point point at which the damage is being inflicted
  184. normal normal vector from that point
  185. damage amount of damage being inflicted
  186. knockback force to be applied against targ as a result of the damage
  187. dflags these flags are used to control how T_Damage works
  188. DAMAGE_RADIUS damage was indirect (from a nearby explosion)
  189. DAMAGE_NO_ARMOR armor does not protect from this damage
  190. DAMAGE_ENERGY damage is from an energy based weapon
  191. DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
  192. DAMAGE_BULLET damage is from a bullet (used for ricochets)
  193. DAMAGE_NO_PROTECTION kills godmode, armor, everything
  194. ============
  195. */
  196. static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
  197. {
  198. gclient_t *client;
  199. int save;
  200. int power_armor_type;
  201. int index;
  202. int damagePerCell;
  203. int pa_te_type;
  204. int power;
  205. int power_used;
  206. if (!damage)
  207. return 0;
  208. client = ent->client;
  209. if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR)) // PGM
  210. return 0;
  211. if (client)
  212. {
  213. power_armor_type = PowerArmorType (ent);
  214. if (power_armor_type != POWER_ARMOR_NONE)
  215. {
  216. index = ITEM_INDEX(FindItem("Cells"));
  217. power = client->pers.inventory[index];
  218. }
  219. }
  220. else if (ent->svflags & SVF_MONSTER)
  221. {
  222. power_armor_type = ent->monsterinfo.power_armor_type;
  223. power = ent->monsterinfo.power_armor_power;
  224. }
  225. else
  226. return 0;
  227. if (power_armor_type == POWER_ARMOR_NONE)
  228. return 0;
  229. if (!power)
  230. return 0;
  231. if (power_armor_type == POWER_ARMOR_SCREEN)
  232. {
  233. vec3_t vec;
  234. float dot;
  235. vec3_t forward;
  236. // only works if damage point is in front
  237. AngleVectors (ent->s.angles, forward, NULL, NULL);
  238. VectorSubtract (point, ent->s.origin, vec);
  239. VectorNormalize (vec);
  240. dot = DotProduct (vec, forward);
  241. if (dot <= 0.3)
  242. return 0;
  243. damagePerCell = 1;
  244. pa_te_type = TE_SCREEN_SPARKS;
  245. damage = damage / 3;
  246. }
  247. else
  248. {
  249. damagePerCell = 2;
  250. pa_te_type = TE_SHIELD_SPARKS;
  251. damage = (2 * damage) / 3;
  252. }
  253. // etf rifle
  254. if (dflags & DAMAGE_NO_REG_ARMOR)
  255. save = (power * damagePerCell) / 2;
  256. else
  257. save = power * damagePerCell;
  258. if (!save)
  259. return 0;
  260. if (save > damage)
  261. save = damage;
  262. SpawnDamage (pa_te_type, point, normal, save);
  263. ent->powerarmor_time = level.time + 0.2;
  264. if (dflags & DAMAGE_NO_REG_ARMOR)
  265. power_used = (save / damagePerCell) * 2;
  266. else
  267. power_used = save / damagePerCell;
  268. if (client)
  269. client->pers.inventory[index] -= power_used;
  270. else
  271. ent->monsterinfo.power_armor_power -= power_used;
  272. return save;
  273. }
  274. static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
  275. {
  276. gclient_t *client;
  277. int save;
  278. int index;
  279. gitem_t *armor;
  280. if (!damage)
  281. return 0;
  282. client = ent->client;
  283. if (!client)
  284. return 0;
  285. // ROGUE - added DAMAGE_NO_REG_ARMOR for atf rifle
  286. if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR))
  287. return 0;
  288. index = ArmorIndex (ent);
  289. if (!index)
  290. return 0;
  291. armor = GetItemByIndex (index);
  292. if (dflags & DAMAGE_ENERGY)
  293. save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
  294. else
  295. save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
  296. if (save >= client->pers.inventory[index])
  297. save = client->pers.inventory[index];
  298. if (!save)
  299. return 0;
  300. client->pers.inventory[index] -= save;
  301. SpawnDamage (te_sparks, point, normal, save);
  302. return save;
  303. }
  304. void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
  305. {
  306. // pmm
  307. qboolean new_tesla;
  308. if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
  309. return;
  310. //=======
  311. //ROGUE
  312. // logic for tesla - if you are hit by a tesla, and can't see who you should be mad at (attacker)
  313. // attack the tesla
  314. // also, target the tesla if it's a "new" tesla
  315. if ((inflictor) && (!strcmp(inflictor->classname, "tesla")))
  316. {
  317. new_tesla = MarkTeslaArea(targ, inflictor);
  318. if (new_tesla)
  319. TargetTesla (targ, inflictor);
  320. return;
  321. // FIXME - just ignore teslas when you're TARGET_ANGER or MEDIC
  322. /* if (!(targ->enemy && (targ->monsterinfo.aiflags & (AI_TARGET_ANGER|AI_MEDIC))))
  323. {
  324. // FIXME - coop issues?
  325. if ((!targ->enemy) || (!visible(targ, targ->enemy)))
  326. {
  327. gi.dprintf ("can't see player, switching to tesla\n");
  328. TargetTesla (targ, inflictor);
  329. return;
  330. }
  331. gi.dprintf ("can see player, ignoring tesla\n");
  332. }
  333. else if ((g_showlogic) && (g_showlogic->value))
  334. gi.dprintf ("no enemy, or I'm doing other, more important things, than worrying about a damned tesla!\n");
  335. */
  336. }
  337. //ROGUE
  338. //=======
  339. if (attacker == targ || attacker == targ->enemy)
  340. return;
  341. // if we are a good guy monster and our attacker is a player
  342. // or another good guy, do not get mad at them
  343. if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
  344. {
  345. if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
  346. return;
  347. }
  348. //PGM
  349. // if we're currently mad at something a target_anger made us mad at, ignore
  350. // damage
  351. if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
  352. {
  353. float percentHealth;
  354. // make sure whatever we were pissed at is still around.
  355. if(targ->enemy->inuse)
  356. {
  357. percentHealth = (float)(targ->health) / (float)(targ->max_health);
  358. if( targ->enemy->inuse && percentHealth > 0.33)
  359. return;
  360. }
  361. // remove the target anger flag
  362. targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
  363. }
  364. //PGM
  365. // PMM
  366. // if we're healing someone, do like above and try to stay with them
  367. if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
  368. {
  369. float percentHealth;
  370. percentHealth = (float)(targ->health) / (float)(targ->max_health);
  371. // ignore it some of the time
  372. if( targ->enemy->inuse && percentHealth > 0.25)
  373. return;
  374. // remove the medic flag
  375. targ->monsterinfo.aiflags &= ~AI_MEDIC;
  376. cleanupHealTarget (targ->enemy);
  377. }
  378. // PMM
  379. // we now know that we are not both good guys
  380. // if attacker is a client, get mad at them because he's good and we're not
  381. if (attacker->client)
  382. {
  383. targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  384. // this can only happen in coop (both new and old enemies are clients)
  385. // only switch if can't see the current enemy
  386. if (targ->enemy && targ->enemy->client)
  387. {
  388. if (visible(targ, targ->enemy))
  389. {
  390. targ->oldenemy = attacker;
  391. return;
  392. }
  393. targ->oldenemy = targ->enemy;
  394. }
  395. targ->enemy = attacker;
  396. if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  397. FoundTarget (targ);
  398. return;
  399. }
  400. // it's the same base (walk/swim/fly) type and a different classname and it's not a tank
  401. // (they spray too much), get mad at them
  402. // PMM
  403. // added medics to this
  404. // FIXME -
  405. // this really should be turned into an AI flag marking appropriate monsters as "don't shoot me"
  406. // this also leads to the problem of tanks and medics being able to, at will, kill monsters with
  407. // no chance of retaliation. My vote is to make those monsters who are designed as "don't shoot me"
  408. // such that they also ignore being shot by monsters as well
  409. /*
  410. if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
  411. (strcmp (targ->classname, attacker->classname) != 0) &&
  412. (strcmp(attacker->classname, "monster_tank") != 0) &&
  413. (strcmp(attacker->classname, "monster_supertank") != 0) &&
  414. (strcmp(attacker->classname, "monster_makron") != 0) &&
  415. (strcmp(attacker->classname, "monster_jorg") != 0) &&
  416. (strcmp(attacker->classname, "monster_carrier") != 0) &&
  417. (strncmp(attacker->classname, "monster_medic", 12) != 0) ) // this should get medics & medic_commanders
  418. */
  419. if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
  420. (strcmp (targ->classname, attacker->classname) != 0) &&
  421. !(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
  422. !(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS) )
  423. {
  424. if (targ->enemy && targ->enemy->client)
  425. targ->oldenemy = targ->enemy;
  426. targ->enemy = attacker;
  427. if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  428. FoundTarget (targ);
  429. }
  430. // if they *meant* to shoot us, then shoot back
  431. else if (attacker->enemy == targ)
  432. {
  433. if (targ->enemy && targ->enemy->client)
  434. targ->oldenemy = targ->enemy;
  435. targ->enemy = attacker;
  436. if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  437. FoundTarget (targ);
  438. }
  439. // otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
  440. else if (attacker->enemy && attacker->enemy != targ)
  441. {
  442. if (targ->enemy && targ->enemy->client)
  443. targ->oldenemy = targ->enemy;
  444. targ->enemy = attacker->enemy;
  445. if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  446. FoundTarget (targ);
  447. }
  448. }
  449. qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
  450. {
  451. //FIXME make the next line real and uncomment this block
  452. // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
  453. return false;
  454. }
  455. 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)
  456. {
  457. gclient_t *client;
  458. int take;
  459. int save;
  460. int asave;
  461. int psave;
  462. int te_sparks;
  463. int sphere_notified; // PGM
  464. if (!targ->takedamage)
  465. return;
  466. sphere_notified = false; // PGM
  467. // friendly fire avoidance
  468. // if enabled you can't hurt teammates (but you can hurt yourself)
  469. // knockback still occurs
  470. if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
  471. {
  472. if (OnSameTeam (targ, attacker))
  473. {
  474. // PMM - nukes kill everyone
  475. if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && (mod != MOD_NUKE))
  476. damage = 0;
  477. else
  478. mod |= MOD_FRIENDLY_FIRE;
  479. }
  480. }
  481. meansOfDeath = mod;
  482. //ROGUE
  483. // allow the deathmatch game to change values
  484. if (deathmatch->value && gamerules && gamerules->value)
  485. {
  486. if(DMGame.ChangeDamage)
  487. damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
  488. if(DMGame.ChangeKnockback)
  489. knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
  490. if(!damage)
  491. return;
  492. }
  493. //ROGUE
  494. // easy mode takes half damage
  495. if (skill->value == 0 && deathmatch->value == 0 && targ->client)
  496. {
  497. damage *= 0.5;
  498. if (!damage)
  499. damage = 1;
  500. }
  501. client = targ->client;
  502. // PMM - defender sphere takes half damage
  503. if ((client) && (client->owned_sphere) && (client->owned_sphere->spawnflags == 1))
  504. {
  505. damage *= 0.5;
  506. if (!damage)
  507. damage = 1;
  508. }
  509. if (dflags & DAMAGE_BULLET)
  510. te_sparks = TE_BULLET_SPARKS;
  511. else
  512. te_sparks = TE_SPARKS;
  513. VectorNormalize(dir);
  514. // bonus damage for suprising a monster
  515. if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
  516. damage *= 2;
  517. if (targ->flags & FL_NO_KNOCKBACK)
  518. knockback = 0;
  519. // figure momentum add
  520. if (!(dflags & DAMAGE_NO_KNOCKBACK))
  521. {
  522. if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
  523. {
  524. vec3_t kvel;
  525. float mass;
  526. if (targ->mass < 50)
  527. mass = 50;
  528. else
  529. mass = targ->mass;
  530. if (targ->client && attacker == targ)
  531. VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
  532. else
  533. VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
  534. VectorAdd (targ->velocity, kvel, targ->velocity);
  535. }
  536. }
  537. take = damage;
  538. save = 0;
  539. // check for godmode
  540. if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
  541. {
  542. take = 0;
  543. save = damage;
  544. SpawnDamage (te_sparks, point, normal, save);
  545. }
  546. // check for invincibility
  547. if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
  548. {
  549. if (targ->pain_debounce_time < level.time)
  550. {
  551. gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
  552. targ->pain_debounce_time = level.time + 2;
  553. }
  554. take = 0;
  555. save = damage;
  556. }
  557. // ROGUE
  558. // check for monster invincibility
  559. if (((targ->svflags & SVF_MONSTER) && targ->monsterinfo.invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
  560. {
  561. if (targ->pain_debounce_time < level.time)
  562. {
  563. gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
  564. targ->pain_debounce_time = level.time + 2;
  565. }
  566. take = 0;
  567. save = damage;
  568. }
  569. // ROGUE
  570. psave = CheckPowerArmor (targ, point, normal, take, dflags);
  571. take -= psave;
  572. asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
  573. take -= asave;
  574. //treat cheat/powerup savings the same as armor
  575. asave += save;
  576. // team damage avoidance
  577. if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
  578. return;
  579. // ROGUE - this option will do damage both to the armor and person. originally for DPU rounds
  580. if (dflags & DAMAGE_DESTROY_ARMOR)
  581. {
  582. if(!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
  583. !(client && client->invincible_framenum > level.framenum))
  584. {
  585. take = damage;
  586. }
  587. }
  588. // ROGUE
  589. // do the damage
  590. if (take)
  591. {
  592. //PGM need more blood for chainfist.
  593. if(targ->flags & FL_MECHANICAL)
  594. {
  595. SpawnDamage ( TE_ELECTRIC_SPARKS, point, normal, take);
  596. }
  597. else if ((targ->svflags & SVF_MONSTER) || (client))
  598. {
  599. if(mod == MOD_CHAINFIST)
  600. SpawnDamage (TE_MOREBLOOD, point, normal, 255);
  601. else
  602. SpawnDamage (TE_BLOOD, point, normal, take);
  603. }
  604. else
  605. SpawnDamage (te_sparks, point, normal, take);
  606. //PGM
  607. targ->health = targ->health - take;
  608. //PGM - spheres need to know who to shoot at
  609. if(client && client->owned_sphere)
  610. {
  611. sphere_notified = true;
  612. if(client->owned_sphere->pain)
  613. client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
  614. }
  615. //PGM
  616. if (targ->health <= 0)
  617. {
  618. if ((targ->svflags & SVF_MONSTER) || (client))
  619. targ->flags |= FL_NO_KNOCKBACK;
  620. Killed (targ, inflictor, attacker, take, point);
  621. return;
  622. }
  623. }
  624. //PGM - spheres need to know who to shoot at
  625. if (!sphere_notified)
  626. {
  627. if(client && client->owned_sphere)
  628. {
  629. sphere_notified = true;
  630. if(client->owned_sphere->pain)
  631. client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
  632. }
  633. }
  634. //PGM
  635. if (targ->svflags & SVF_MONSTER)
  636. {
  637. M_ReactToDamage (targ, attacker, inflictor);
  638. // PMM - fixme - if anyone else but the medic ever uses AI_MEDIC, check for it here instead
  639. // of in the medic's pain function
  640. if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
  641. {
  642. targ->pain (targ, attacker, knockback, take);
  643. // nightmare mode monsters don't go into pain frames often
  644. if (skill->value == 3)
  645. targ->pain_debounce_time = level.time + 5;
  646. }
  647. }
  648. else if (client)
  649. {
  650. if (!(targ->flags & FL_GODMODE) && (take))
  651. targ->pain (targ, attacker, knockback, take);
  652. }
  653. else if (take)
  654. {
  655. if (targ->pain)
  656. targ->pain (targ, attacker, knockback, take);
  657. }
  658. // add to the damage inflicted on a player this frame
  659. // the total will be turned into screen blends and view angle kicks
  660. // at the end of the frame
  661. if (client)
  662. {
  663. client->damage_parmor += psave;
  664. client->damage_armor += asave;
  665. client->damage_blood += take;
  666. client->damage_knockback += knockback;
  667. VectorCopy (point, client->damage_from);
  668. }
  669. }
  670. /*
  671. ============
  672. T_RadiusDamage
  673. ============
  674. */
  675. void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
  676. {
  677. float points;
  678. edict_t *ent = NULL;
  679. vec3_t v;
  680. vec3_t dir;
  681. while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
  682. {
  683. if (ent == ignore)
  684. continue;
  685. if (!ent->takedamage)
  686. continue;
  687. VectorAdd (ent->mins, ent->maxs, v);
  688. VectorMA (ent->s.origin, 0.5, v, v);
  689. VectorSubtract (inflictor->s.origin, v, v);
  690. points = damage - 0.5 * VectorLength (v);
  691. if (ent == attacker)
  692. points = points * 0.5;
  693. if (points > 0)
  694. {
  695. if (CanDamage (ent, inflictor))
  696. {
  697. VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
  698. T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  699. }
  700. }
  701. }
  702. }
  703. // **********************
  704. // ROGUE
  705. /*
  706. ============
  707. T_RadiusNukeDamage
  708. Like T_RadiusDamage, but ignores walls (skips CanDamage check, among others)
  709. // up to KILLZONE radius, do 10,000 points
  710. // after that, do damage linearly out to KILLZONE2 radius
  711. ============
  712. */
  713. void T_RadiusNukeDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
  714. {
  715. float points;
  716. edict_t *ent = NULL;
  717. vec3_t v;
  718. vec3_t dir;
  719. float len;
  720. float killzone, killzone2;
  721. trace_t tr;
  722. float dist;
  723. killzone = radius;
  724. killzone2 = radius*2.0;
  725. while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL)
  726. {
  727. // ignore nobody
  728. if (ent == ignore)
  729. continue;
  730. if (!ent->takedamage)
  731. continue;
  732. if (!ent->inuse)
  733. continue;
  734. if (!(ent->client || (ent->svflags & SVF_MONSTER) || (ent->svflags & SVF_DAMAGEABLE)))
  735. continue;
  736. VectorAdd (ent->mins, ent->maxs, v);
  737. VectorMA (ent->s.origin, 0.5, v, v);
  738. VectorSubtract (inflictor->s.origin, v, v);
  739. len = VectorLength(v);
  740. if (len <= killzone)
  741. {
  742. if (ent->client)
  743. ent->flags |= FL_NOGIB;
  744. points = 10000;
  745. }
  746. else if (len <= killzone2)
  747. points = (damage/killzone)*(killzone2 - len);
  748. else
  749. points = 0;
  750. // points = damage - 0.005 * len*len;
  751. // if (ent == attacker)
  752. // points = points * 0.5;
  753. // if ((g_showlogic) && (g_showlogic->value))
  754. // {
  755. // if (!(strcmp(ent->classname, "player")))
  756. // gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, inflictor->teammaster->client->pers.netname);
  757. // else
  758. // gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, ent->classname);
  759. // }
  760. if (points > 0)
  761. {
  762. // if (CanDamage (ent, inflictor))
  763. // {
  764. if (ent->client)
  765. ent->client->nuke_framenum = level.framenum + 20;
  766. VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
  767. T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  768. // }
  769. }
  770. }
  771. ent = g_edicts+1; // skip the worldspawn
  772. // cycle through players
  773. while (ent)
  774. {
  775. if ((ent->client) && (ent->client->nuke_framenum != level.framenum+20) && (ent->inuse))
  776. {
  777. tr = gi.trace (inflictor->s.origin, NULL, NULL, ent->s.origin, inflictor, MASK_SOLID);
  778. if (tr.fraction == 1.0)
  779. {
  780. // if ((g_showlogic) && (g_showlogic->value))
  781. // gi.dprintf ("Undamaged player in LOS with nuke, flashing!\n");
  782. ent->client->nuke_framenum = level.framenum + 20;
  783. }
  784. else
  785. {
  786. dist = realrange (ent, inflictor);
  787. if (dist < 2048)
  788. ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 15);
  789. else
  790. ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 10);
  791. }
  792. ent++;
  793. }
  794. else
  795. ent = NULL;
  796. }
  797. }
  798. /*
  799. ============
  800. T_RadiusClassDamage
  801. Like T_RadiusDamage, but ignores anything with classname=ignoreClass
  802. ============
  803. */
  804. void T_RadiusClassDamage (edict_t *inflictor, edict_t *attacker, float damage, char *ignoreClass, float radius, int mod)
  805. {
  806. float points;
  807. edict_t *ent = NULL;
  808. vec3_t v;
  809. vec3_t dir;
  810. while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
  811. {
  812. if (ent->classname && !strcmp(ent->classname, ignoreClass))
  813. continue;
  814. if (!ent->takedamage)
  815. continue;
  816. VectorAdd (ent->mins, ent->maxs, v);
  817. VectorMA (ent->s.origin, 0.5, v, v);
  818. VectorSubtract (inflictor->s.origin, v, v);
  819. points = damage - 0.5 * VectorLength (v);
  820. if (ent == attacker)
  821. points = points * 0.5;
  822. if (points > 0)
  823. {
  824. if (CanDamage (ent, inflictor))
  825. {
  826. VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
  827. T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  828. }
  829. }
  830. }
  831. }
  832. // ROGUE
  833. // ********************