combat.qc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. combat.qc
  3. damage, obit, etc related functions
  4. Copyright (C) 1996-1997 Id Software, Inc.
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. See the GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to:
  15. Free Software Foundation, Inc.
  16. 59 Temple Place - Suite 330
  17. Boston, MA 02111-1307, USA
  18. */
  19. void() T_MissileTouch;
  20. void() info_player_start;
  21. void(entity targ, entity attacker) ClientObituary;
  22. void(entity inflictor, entity attacker, float damage, entity ignore, string dtype) T_RadiusDamage;
  23. /*SERVER
  24. void() monster_death_use;
  25. */
  26. //============================================================================
  27. /*
  28. ============
  29. CanDamage
  30. Returns true if the inflictor can directly damage the target. Used for
  31. explosions and melee attacks.
  32. ============
  33. */
  34. float(entity targ, entity inflictor) CanDamage =
  35. {
  36. // bmodels need special checking because their origin is 0,0,0
  37. if (targ.movetype == MOVETYPE_PUSH)
  38. {
  39. traceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self);
  40. if (trace_fraction == 1)
  41. return TRUE;
  42. if (trace_ent == targ)
  43. return TRUE;
  44. return FALSE;
  45. }
  46. traceline(inflictor.origin, targ.origin, TRUE, self);
  47. if (trace_fraction == 1)
  48. return TRUE;
  49. traceline(inflictor.origin, targ.origin + '15 15 0', TRUE, self);
  50. if (trace_fraction == 1)
  51. return TRUE;
  52. traceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, self);
  53. if (trace_fraction == 1)
  54. return TRUE;
  55. traceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, self);
  56. if (trace_fraction == 1)
  57. return TRUE;
  58. traceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, self);
  59. if (trace_fraction == 1)
  60. return TRUE;
  61. return FALSE;
  62. };
  63. /*
  64. ============
  65. Killed
  66. ============
  67. */
  68. void(entity targ, entity attacker) Killed =
  69. {
  70. local entity oself;
  71. oself = self;
  72. self = targ;
  73. if (self.health < -99)
  74. self.health = -99; // don't let sbar look bad if a player
  75. if (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE)
  76. { // doors, triggers, etc
  77. self.th_die ();
  78. self = oself;
  79. return;
  80. }
  81. self.enemy = attacker;
  82. // bump the monster counter
  83. if (self.flags & FL_MONSTER)
  84. {
  85. killed_monsters = killed_monsters + 1;
  86. WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
  87. }
  88. ClientObituary(self, attacker);
  89. self.takedamage = DAMAGE_NO;
  90. self.touch = SUB_Null;
  91. self.effects = 0;
  92. /*SERVER
  93. monster_death_use();
  94. */
  95. self.th_die ();
  96. self = oself;
  97. };
  98. /*
  99. ============
  100. T_Damage
  101. The damage is coming from inflictor, but get mad at attacker
  102. This should be the only function that ever reduces health.
  103. ============
  104. */
  105. void(entity targ, entity inflictor, entity attacker, float damage) T_Damage=
  106. {
  107. local vector dir;
  108. local entity oldself;
  109. local float save;
  110. local float take;
  111. local string s;
  112. local string attackerteam, targteam;
  113. if (!targ.takedamage)
  114. return;
  115. // used by buttons and triggers to set activator for target firing
  116. damage_attacker = attacker;
  117. // check for quad damage powerup on the attacker
  118. if (attacker.super_damage_finished > time && inflictor.classname != "door")
  119. if (deathmatch == 4)
  120. damage = damage * 8;
  121. else
  122. damage = damage * 4;
  123. // save damage based on the target's armor level
  124. save = ceil(targ.armortype*damage);
  125. if (save >= targ.armorvalue)
  126. {
  127. save = targ.armorvalue;
  128. targ.armortype = 0; // lost all armor
  129. targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3));
  130. }
  131. targ.armorvalue = targ.armorvalue - save;
  132. take = ceil(damage-save);
  133. // add to the damage total for clients, which will be sent as a single
  134. // message at the end of the frame
  135. // FIXME: remove after combining shotgun blasts?
  136. if (targ.flags & FL_CLIENT)
  137. {
  138. targ.dmg_take = targ.dmg_take + take;
  139. targ.dmg_save = targ.dmg_save + save;
  140. targ.dmg_inflictor = inflictor;
  141. }
  142. damage_inflictor = inflictor;
  143. // figure momentum add
  144. if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )
  145. {
  146. dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
  147. dir = normalize(dir);
  148. // Set kickback for smaller weapons
  149. //Zoid -- use normal NQ kickback
  150. // // Read: only if it's not yourself doing the damage
  151. // if ( (damage < 60) & ((attacker.classname == "player") & (targ.classname == "player")) & ( attacker.netname != targ.netname))
  152. // targ.velocity = targ.velocity + dir * damage * 11;
  153. // else
  154. // Otherwise, these rules apply to rockets and grenades
  155. // for blast velocity
  156. targ.velocity = targ.velocity + dir * damage * 8;
  157. // Rocket Jump modifiers
  158. if ( (rj > 1) & ((attacker.classname == "player") & (targ.classname == "player")) & ( attacker.netname == targ.netname))
  159. targ.velocity = targ.velocity + dir * damage * rj;
  160. }
  161. // check for godmode or invincibility
  162. if (targ.flags & FL_GODMODE)
  163. return;
  164. if (targ.invincible_finished >= time)
  165. {
  166. if (self.invincible_sound < time)
  167. {
  168. sound (targ, CHAN_ITEM, "items/protect3.wav", 1, ATTN_NORM);
  169. self.invincible_sound = time + 2;
  170. }
  171. return;
  172. }
  173. // team play damage avoidance
  174. //ZOID 12-13-96: self.team doesn't work in QW. Use keys
  175. attackerteam = infokey(attacker, "team");
  176. targteam = infokey(targ, "team");
  177. if ((teamplay == 1) && (targteam == attackerteam) &&
  178. (attacker.classname == "player") && (attackerteam != "") &&
  179. inflictor.classname !="door")
  180. return;
  181. if ((teamplay == 3) && (targteam == attackerteam) &&
  182. (attacker.classname == "player") && (attackerteam != "") &&
  183. (targ != attacker)&& inflictor.classname !="door")
  184. return;
  185. // do the damage
  186. targ.health = targ.health - take;
  187. if (targ.health <= 0)
  188. {
  189. Killed (targ, attacker);
  190. return;
  191. }
  192. // react to the damage
  193. oldself = self;
  194. self = targ;
  195. /*SERVER
  196. if ( (self.flags & FL_MONSTER) && attacker != world)
  197. {
  198. // get mad unless of the same class (except for soldiers)
  199. if (self != attacker && attacker != self.enemy)
  200. {
  201. if ( (self.classname != attacker.classname)
  202. || (self.classname == "monster_army" ) )
  203. {
  204. if (self.enemy.classname == "player")
  205. self.oldenemy = self.enemy;
  206. self.enemy = attacker;
  207. FoundTarget ();
  208. }
  209. }
  210. }
  211. */
  212. if (self.th_pain)
  213. {
  214. self.th_pain (attacker, take);
  215. }
  216. self = oldself;
  217. };
  218. /*
  219. ============
  220. T_RadiusDamage
  221. ============
  222. */
  223. void(entity inflictor, entity attacker, float damage, entity ignore, string dtype) T_RadiusDamage =
  224. {
  225. local float points;
  226. local entity head;
  227. local vector org;
  228. head = findradius(inflictor.origin, damage+40);
  229. while (head)
  230. {
  231. //bprint (PRINT_HIGH, head.classname);
  232. //bprint (PRINT_HIGH, " | ");
  233. //bprint (PRINT_HIGH, head.netname);
  234. //bprint (PRINT_HIGH, "\n");
  235. if (head != ignore)
  236. {
  237. if (head.takedamage)
  238. {
  239. org = head.origin + (head.mins + head.maxs)*0.5;
  240. points = 0.5*vlen (inflictor.origin - org);
  241. if (points < 0)
  242. points = 0;
  243. points = damage - points;
  244. if (head == attacker)
  245. points = points * 0.5;
  246. if (points > 0)
  247. {
  248. if (CanDamage (head, inflictor))
  249. {
  250. head.deathtype = dtype;
  251. T_Damage (head, inflictor, attacker, points);
  252. }
  253. }
  254. }
  255. }
  256. head = head.chain;
  257. }
  258. };
  259. /*
  260. ============
  261. T_BeamDamage
  262. ============
  263. */
  264. void(entity attacker, float damage) T_BeamDamage =
  265. {
  266. local float points;
  267. local entity head;
  268. head = findradius(attacker.origin, damage+40);
  269. while (head)
  270. {
  271. if (head.takedamage)
  272. {
  273. points = 0.5*vlen (attacker.origin - head.origin);
  274. if (points < 0)
  275. points = 0;
  276. points = damage - points;
  277. if (head == attacker)
  278. points = points * 0.5;
  279. if (points > 0)
  280. {
  281. if (CanDamage (head, attacker))
  282. T_Damage (head, attacker, attacker, points);
  283. }
  284. }
  285. head = head.chain;
  286. }
  287. };