fight.qc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /* Copyright (C) 1996-2022 id Software LLC
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  13. See file, 'COPYING', for details.
  14. */
  15. /*
  16. A monster is in fight mode if it thinks it can effectively attack its
  17. enemy.
  18. When it decides it can't attack, it goes into hunt mode.
  19. */
  20. float(float v) anglemod;
  21. void() knight_atk1;
  22. void() knight_runatk1;
  23. void() ogre_smash1;
  24. void() ogre_swing1;
  25. void() sham_smash1;
  26. void() sham_swingr1;
  27. void() sham_swingl1;
  28. float() DemonCheckAttack;
  29. void(float side) Demon_Melee;
  30. void(vector dest) ChooseTurn;
  31. void() ai_face;
  32. float enemy_vis, enemy_infront, enemy_range;
  33. float enemy_yaw;
  34. void() knight_attack =
  35. {
  36. local float len;
  37. // decide if now is a good swing time
  38. len = vlen(self.enemy.origin+self.enemy.view_ofs - (self.origin+self.view_ofs));
  39. if (len<80)
  40. knight_atk1 ();
  41. else
  42. knight_runatk1 ();
  43. };
  44. //=============================================================================
  45. /*
  46. ===========
  47. CheckAttack
  48. The player is in view, so decide to move or launch an attack
  49. Returns FALSE if movement should continue
  50. ============
  51. */
  52. float() CheckAttack =
  53. {
  54. local vector spot1, spot2;
  55. local entity targ;
  56. local float chance;
  57. targ = self.enemy;
  58. // see if any entities are in the way of the shot
  59. spot1 = self.origin + self.view_ofs;
  60. spot2 = targ.origin + targ.view_ofs;
  61. traceline (spot1, spot2, FALSE, self);
  62. //MED 10/17/96 added charmed stuff
  63. if ((trace_ent != targ) && (!self.charmed))
  64. return FALSE; // don't have a clear shot
  65. if (trace_inopen && trace_inwater)
  66. return FALSE; // sight line crossed contents
  67. if (enemy_range == RANGE_MELEE)
  68. { // melee attack
  69. if (self.th_melee)
  70. {
  71. if (self.classname == "monster_knight")
  72. knight_attack ();
  73. else
  74. self.th_melee ();
  75. return TRUE;
  76. }
  77. }
  78. // missile attack
  79. if (!self.th_missile)
  80. return FALSE;
  81. if (time < self.attack_finished)
  82. return FALSE;
  83. if (enemy_range == RANGE_FAR)
  84. return FALSE;
  85. if (enemy_range == RANGE_MELEE)
  86. {
  87. chance = 0.9;
  88. self.attack_finished = 0;
  89. }
  90. else if (enemy_range == RANGE_NEAR)
  91. {
  92. if (self.th_melee)
  93. chance = 0.2;
  94. else
  95. chance = 0.4;
  96. }
  97. else if (enemy_range == RANGE_MID)
  98. {
  99. if (self.th_melee)
  100. chance = 0.05;
  101. else
  102. chance = 0.1;
  103. }
  104. else
  105. chance = 0;
  106. if (random () < chance)
  107. {
  108. self.th_missile ();
  109. SUB_AttackFinished (2*random());
  110. return TRUE;
  111. }
  112. return FALSE;
  113. };
  114. /*
  115. =============
  116. ai_face
  117. Stay facing the enemy
  118. =============
  119. */
  120. void() ai_face =
  121. {
  122. self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
  123. ChangeYaw ();
  124. };
  125. /*
  126. =============
  127. ai_charge
  128. The monster is in a melee attack, so get as close as possible to .enemy
  129. =============
  130. */
  131. float (entity targ) visible;
  132. float(entity targ) infront;
  133. float(entity targ) range;
  134. void(float d) ai_charge =
  135. {
  136. ai_face ();
  137. movetogoal (d); // done in C code...
  138. };
  139. void() ai_charge_side =
  140. {
  141. local vector dtemp;
  142. local float heading;
  143. // aim to the left of the enemy for a flyby
  144. self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
  145. ChangeYaw ();
  146. makevectors (self.angles);
  147. dtemp = self.enemy.origin - 30*v_right;
  148. heading = vectoyaw(dtemp - self.origin);
  149. walkmove(heading, 20);
  150. };
  151. /*
  152. =============
  153. ai_melee
  154. =============
  155. */
  156. void() ai_melee =
  157. {
  158. local vector delta;
  159. local float ldmg;
  160. if (!self.enemy)
  161. return; // removed before stroke
  162. delta = self.enemy.origin - self.origin;
  163. if (vlen(delta) > 60)
  164. return;
  165. ldmg = (random() + random() + random()) * 3;
  166. T_Damage (self.enemy, self, self, ldmg);
  167. };
  168. void() ai_melee_side =
  169. {
  170. local vector delta;
  171. local float ldmg;
  172. if (!self.enemy)
  173. return; // removed before stroke
  174. ai_charge_side();
  175. delta = self.enemy.origin - self.origin;
  176. if (vlen(delta) > 60)
  177. return;
  178. if (!CanDamage (self.enemy, self))
  179. return;
  180. ldmg = (random() + random() + random()) * 3;
  181. T_Damage (self.enemy, self, self, ldmg);
  182. };
  183. //=============================================================================
  184. /*
  185. ===========
  186. SoldierCheckAttack
  187. The player is in view, so decide to move or launch an attack
  188. Returns FALSE if movement should continue
  189. ============
  190. */
  191. float() SoldierCheckAttack =
  192. {
  193. local vector spot1, spot2;
  194. local entity targ;
  195. local float chance;
  196. targ = self.enemy;
  197. // see if any entities are in the way of the shot
  198. spot1 = self.origin + self.view_ofs;
  199. spot2 = targ.origin + targ.view_ofs;
  200. traceline (spot1, spot2, FALSE, self);
  201. if (trace_inopen && trace_inwater)
  202. return FALSE; // sight line crossed contents
  203. if (trace_ent != targ)
  204. return FALSE; // don't have a clear shot
  205. // missile attack
  206. if (time < self.attack_finished)
  207. return FALSE;
  208. if (enemy_range == RANGE_FAR)
  209. return FALSE;
  210. if (enemy_range == RANGE_MELEE)
  211. chance = 0.9;
  212. else if (enemy_range == RANGE_NEAR)
  213. chance = 0.4;
  214. else if (enemy_range == RANGE_MID)
  215. chance = 0.05;
  216. else
  217. chance = 0;
  218. if (random () < chance)
  219. {
  220. self.th_missile ();
  221. SUB_AttackFinished (1 + random());
  222. if (random() < 0.3)
  223. self.lefty = !self.lefty;
  224. return TRUE;
  225. }
  226. return FALSE;
  227. };
  228. //=============================================================================
  229. /*
  230. ===========
  231. ShamCheckAttack
  232. The player is in view, so decide to move or launch an attack
  233. Returns FALSE if movement should continue
  234. ============
  235. */
  236. float() ShamCheckAttack =
  237. {
  238. local vector spot1, spot2;
  239. local entity targ;
  240. local float chance;
  241. local float enemy_yaw;
  242. if (enemy_range == RANGE_MELEE)
  243. {
  244. if (CanDamage (self.enemy, self))
  245. {
  246. self.attack_state = AS_MELEE;
  247. return TRUE;
  248. }
  249. }
  250. if (time < self.attack_finished)
  251. return FALSE;
  252. if (!enemy_vis)
  253. return FALSE;
  254. targ = self.enemy;
  255. // see if any entities are in the way of the shot
  256. spot1 = self.origin + self.view_ofs;
  257. spot2 = targ.origin + targ.view_ofs;
  258. if (vlen(spot1 - spot2) > 600)
  259. return FALSE;
  260. traceline (spot1, spot2, FALSE, self);
  261. if (trace_inopen && trace_inwater)
  262. return FALSE; // sight line crossed contents
  263. if (trace_ent != targ)
  264. {
  265. return FALSE; // don't have a clear shot
  266. }
  267. // missile attack
  268. if (enemy_range == RANGE_FAR)
  269. return FALSE;
  270. self.attack_state = AS_MISSILE;
  271. SUB_AttackFinished (2 + 2*random());
  272. return TRUE;
  273. };
  274. //============================================================================
  275. /*
  276. ===========
  277. OgreCheckAttack
  278. The player is in view, so decide to move or launch an attack
  279. Returns FALSE if movement should continue
  280. ============
  281. */
  282. float() OgreCheckAttack =
  283. {
  284. local vector spot1, spot2;
  285. local entity targ;
  286. local float chance;
  287. if (enemy_range == RANGE_MELEE)
  288. {
  289. if (CanDamage (self.enemy, self))
  290. {
  291. self.attack_state = AS_MELEE;
  292. return TRUE;
  293. }
  294. }
  295. if (time < self.attack_finished)
  296. return FALSE;
  297. if (!enemy_vis)
  298. return FALSE;
  299. targ = self.enemy;
  300. // see if any entities are in the way of the shot
  301. spot1 = self.origin + self.view_ofs;
  302. spot2 = targ.origin + targ.view_ofs;
  303. traceline (spot1, spot2, FALSE, self);
  304. if (trace_inopen && trace_inwater)
  305. return FALSE; // sight line crossed contents
  306. if (trace_ent != targ)
  307. {
  308. return FALSE; // don't have a clear shot
  309. }
  310. // missile attack
  311. if (time < self.attack_finished)
  312. return FALSE;
  313. if (enemy_range == RANGE_FAR)
  314. return FALSE;
  315. else if (enemy_range == RANGE_NEAR)
  316. chance = 0.10;
  317. else if (enemy_range == RANGE_MID)
  318. chance = 0.05;
  319. else
  320. chance = 0;
  321. self.attack_state = AS_MISSILE;
  322. SUB_AttackFinished (1 + 2*random());
  323. return TRUE;
  324. };