fight.qc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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_visible, 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. if (trace_ent != targ)
  63. return FALSE; // don't have a clear shot
  64. if (trace_inopen && trace_inwater)
  65. return FALSE; // sight line crossed contents
  66. if (enemy_range == RANGE_MELEE)
  67. { // melee attack
  68. if (self.th_melee)
  69. {
  70. if (self.classname == "monster_knight")
  71. knight_attack ();
  72. else
  73. self.th_melee ();
  74. return TRUE;
  75. }
  76. }
  77. // missile attack
  78. if (!self.th_missile)
  79. return FALSE;
  80. if (time < self.attack_finished)
  81. return FALSE;
  82. if (enemy_range == RANGE_FAR)
  83. return FALSE;
  84. if (enemy_range == RANGE_MELEE)
  85. {
  86. chance = 0.9;
  87. self.attack_finished = 0;
  88. }
  89. else if (enemy_range == RANGE_NEAR)
  90. {
  91. if (self.th_melee)
  92. chance = 0.2;
  93. else
  94. chance = 0.4;
  95. }
  96. else if (enemy_range == RANGE_MID)
  97. {
  98. if (self.th_melee)
  99. chance = 0.05;
  100. else
  101. chance = 0.1;
  102. }
  103. else
  104. chance = 0;
  105. if (random () < chance)
  106. {
  107. self.th_missile ();
  108. SUB_AttackFinished (2*random());
  109. return TRUE;
  110. }
  111. return FALSE;
  112. };
  113. /*
  114. =============
  115. ai_face
  116. Stay facing the enemy
  117. =============
  118. */
  119. void() ai_face =
  120. {
  121. self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
  122. ChangeYaw ();
  123. };
  124. /*
  125. =============
  126. ai_charge
  127. The monster is in a melee attack, so get as close as possible to .enemy
  128. =============
  129. */
  130. float (entity targ) visible;
  131. float(entity targ) infront;
  132. float(entity targ) range;
  133. void(float d) ai_charge =
  134. {
  135. ai_face ();
  136. movetogoal (d); // done in C code...
  137. };
  138. void() ai_charge_side =
  139. {
  140. local vector dtemp;
  141. local float heading;
  142. // aim to the left of the enemy for a flyby
  143. self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
  144. ChangeYaw ();
  145. makevectorsfixed(self.angles);
  146. dtemp = self.enemy.origin - 30*v_right;
  147. heading = vectoyaw(dtemp - self.origin);
  148. walkmove(heading, 20);
  149. };
  150. /*
  151. =============
  152. ai_melee
  153. =============
  154. */
  155. void() ai_melee =
  156. {
  157. local vector delta;
  158. local float ldmg;
  159. if (!self.enemy)
  160. return; // removed before stroke
  161. delta = self.enemy.origin - self.origin;
  162. if (vlen(delta) > 60)
  163. return;
  164. ldmg = (random() + random() + random()) * 3;
  165. T_Damage (self.enemy, self, self, ldmg);
  166. };
  167. void() ai_melee_side =
  168. {
  169. local vector delta;
  170. local float ldmg;
  171. if (!self.enemy)
  172. return; // removed before stroke
  173. ai_charge_side();
  174. delta = self.enemy.origin - self.origin;
  175. if (vlen(delta) > 60)
  176. return;
  177. if (!CanDamage (self.enemy, self))
  178. return;
  179. ldmg = (random() + random() + random()) * 3;
  180. T_Damage (self.enemy, self, self, ldmg);
  181. };
  182. //=============================================================================
  183. /*
  184. ===========
  185. SoldierCheckAttack
  186. The player is in view, so decide to move or launch an attack
  187. Returns FALSE if movement should continue
  188. ============
  189. */
  190. float() SoldierCheckAttack =
  191. {
  192. local vector spot1, spot2;
  193. local entity targ;
  194. local float chance;
  195. targ = self.enemy;
  196. // see if any entities are in the way of the shot
  197. spot1 = self.origin + self.view_ofs;
  198. spot2 = targ.origin + targ.view_ofs;
  199. traceline (spot1, spot2, FALSE, self);
  200. if (trace_inopen && trace_inwater)
  201. return FALSE; // sight line crossed contents
  202. if (trace_ent != targ)
  203. return FALSE; // don't have a clear shot
  204. // missile attack
  205. if (time < self.attack_finished)
  206. return FALSE;
  207. if (enemy_range == RANGE_FAR)
  208. return FALSE;
  209. if (enemy_range == RANGE_MELEE)
  210. chance = 0.9;
  211. else if (enemy_range == RANGE_NEAR)
  212. chance = 0.4;
  213. else if (enemy_range == RANGE_MID)
  214. chance = 0.05;
  215. else
  216. chance = 0;
  217. if (random () < chance)
  218. {
  219. self.th_missile ();
  220. SUB_AttackFinished (1 + random());
  221. if (random() < 0.3)
  222. self.lefty = !self.lefty;
  223. return TRUE;
  224. }
  225. return FALSE;
  226. };
  227. //=============================================================================
  228. /*
  229. ===========
  230. ShamCheckAttack
  231. The player is in view, so decide to move or launch an attack
  232. Returns FALSE if movement should continue
  233. ============
  234. */
  235. float() ShamCheckAttack =
  236. {
  237. local vector spot1, spot2;
  238. local entity targ;
  239. if (enemy_range == RANGE_MELEE)
  240. {
  241. if (CanDamage (self.enemy, self))
  242. {
  243. self.attack_state = AS_MELEE;
  244. return TRUE;
  245. }
  246. }
  247. if (time < self.attack_finished)
  248. return FALSE;
  249. if (!enemy_visible)
  250. return FALSE;
  251. targ = self.enemy;
  252. // see if any entities are in the way of the shot
  253. spot1 = self.origin + self.view_ofs;
  254. spot2 = targ.origin + targ.view_ofs;
  255. if (vlen(spot1 - spot2) > 600)
  256. return FALSE;
  257. traceline (spot1, spot2, FALSE, self);
  258. if (trace_inopen && trace_inwater)
  259. return FALSE; // sight line crossed contents
  260. if (trace_ent != targ)
  261. {
  262. return FALSE; // don't have a clear shot
  263. }
  264. // missile attack
  265. if (enemy_range == RANGE_FAR)
  266. return FALSE;
  267. self.attack_state = AS_MISSILE;
  268. SUB_AttackFinished (2 + 2*random());
  269. return TRUE;
  270. };
  271. //============================================================================
  272. /*
  273. ===========
  274. OgreCheckAttack
  275. The player is in view, so decide to move or launch an attack
  276. Returns FALSE if movement should continue
  277. ============
  278. */
  279. float() OgreCheckAttack =
  280. {
  281. local vector spot1, spot2;
  282. local entity targ;
  283. local float chance;
  284. if (enemy_range == RANGE_MELEE)
  285. {
  286. if (CanDamage (self.enemy, self))
  287. {
  288. self.attack_state = AS_MELEE;
  289. return TRUE;
  290. }
  291. }
  292. if (time < self.attack_finished)
  293. return FALSE;
  294. if (!enemy_visible)
  295. return FALSE;
  296. targ = self.enemy;
  297. // see if any entities are in the way of the shot
  298. spot1 = self.origin + self.view_ofs;
  299. spot2 = targ.origin + targ.view_ofs;
  300. traceline (spot1, spot2, FALSE, self);
  301. if (trace_inopen && trace_inwater)
  302. return FALSE; // sight line crossed contents
  303. if (trace_ent != targ)
  304. {
  305. return FALSE; // don't have a clear shot
  306. }
  307. // missile attack
  308. if (time < self.attack_finished)
  309. return FALSE;
  310. if (enemy_range == RANGE_FAR)
  311. return FALSE;
  312. else if (enemy_range == RANGE_NEAR)
  313. chance = 0.10;
  314. else if (enemy_range == RANGE_MID)
  315. chance = 0.05;
  316. else
  317. chance = 0;
  318. self.attack_state = AS_MISSILE;
  319. SUB_AttackFinished (1 + 2*random());
  320. return TRUE;
  321. };