m_gunner.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. GUNNER
  6. ==============================================================================
  7. */
  8. #include "g_local.h"
  9. #include "m_gunner.h"
  10. static int sound_pain;
  11. static int sound_pain2;
  12. static int sound_death;
  13. static int sound_idle;
  14. static int sound_open;
  15. static int sound_search;
  16. static int sound_sight;
  17. void gunner_idlesound (edict_t *self)
  18. {
  19. gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
  20. }
  21. void gunner_sight (edict_t *self, edict_t *other)
  22. {
  23. gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
  24. }
  25. void gunner_search (edict_t *self)
  26. {
  27. gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
  28. }
  29. qboolean visible (edict_t *self, edict_t *other);
  30. void GunnerGrenade (edict_t *self);
  31. void GunnerFire (edict_t *self);
  32. void gunner_fire_chain(edict_t *self);
  33. void gunner_refire_chain(edict_t *self);
  34. void gunner_stand (edict_t *self);
  35. mframe_t gunner_frames_fidget [] =
  36. {
  37. ai_stand, 0, NULL,
  38. ai_stand, 0, NULL,
  39. ai_stand, 0, NULL,
  40. ai_stand, 0, NULL,
  41. ai_stand, 0, NULL,
  42. ai_stand, 0, NULL,
  43. ai_stand, 0, NULL,
  44. ai_stand, 0, gunner_idlesound,
  45. ai_stand, 0, NULL,
  46. ai_stand, 0, NULL,
  47. ai_stand, 0, NULL,
  48. ai_stand, 0, NULL,
  49. ai_stand, 0, NULL,
  50. ai_stand, 0, NULL,
  51. ai_stand, 0, NULL,
  52. ai_stand, 0, NULL,
  53. ai_stand, 0, NULL,
  54. ai_stand, 0, NULL,
  55. ai_stand, 0, NULL,
  56. ai_stand, 0, NULL,
  57. ai_stand, 0, NULL,
  58. ai_stand, 0, NULL,
  59. ai_stand, 0, NULL,
  60. ai_stand, 0, NULL,
  61. ai_stand, 0, NULL,
  62. ai_stand, 0, NULL,
  63. ai_stand, 0, NULL,
  64. ai_stand, 0, NULL,
  65. ai_stand, 0, NULL,
  66. ai_stand, 0, NULL,
  67. ai_stand, 0, NULL,
  68. ai_stand, 0, NULL,
  69. ai_stand, 0, NULL,
  70. ai_stand, 0, NULL,
  71. ai_stand, 0, NULL,
  72. ai_stand, 0, NULL,
  73. ai_stand, 0, NULL,
  74. ai_stand, 0, NULL,
  75. ai_stand, 0, NULL,
  76. ai_stand, 0, NULL,
  77. ai_stand, 0, NULL,
  78. ai_stand, 0, NULL,
  79. ai_stand, 0, NULL,
  80. ai_stand, 0, NULL,
  81. ai_stand, 0, NULL,
  82. ai_stand, 0, NULL,
  83. ai_stand, 0, NULL,
  84. ai_stand, 0, NULL,
  85. ai_stand, 0, NULL
  86. };
  87. mmove_t gunner_move_fidget = {FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand};
  88. void gunner_fidget (edict_t *self)
  89. {
  90. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  91. return;
  92. if (random() <= 0.05)
  93. self->monsterinfo.currentmove = &gunner_move_fidget;
  94. }
  95. mframe_t gunner_frames_stand [] =
  96. {
  97. ai_stand, 0, NULL,
  98. ai_stand, 0, NULL,
  99. ai_stand, 0, NULL,
  100. ai_stand, 0, NULL,
  101. ai_stand, 0, NULL,
  102. ai_stand, 0, NULL,
  103. ai_stand, 0, NULL,
  104. ai_stand, 0, NULL,
  105. ai_stand, 0, NULL,
  106. ai_stand, 0, gunner_fidget,
  107. ai_stand, 0, NULL,
  108. ai_stand, 0, NULL,
  109. ai_stand, 0, NULL,
  110. ai_stand, 0, NULL,
  111. ai_stand, 0, NULL,
  112. ai_stand, 0, NULL,
  113. ai_stand, 0, NULL,
  114. ai_stand, 0, NULL,
  115. ai_stand, 0, NULL,
  116. ai_stand, 0, gunner_fidget,
  117. ai_stand, 0, NULL,
  118. ai_stand, 0, NULL,
  119. ai_stand, 0, NULL,
  120. ai_stand, 0, NULL,
  121. ai_stand, 0, NULL,
  122. ai_stand, 0, NULL,
  123. ai_stand, 0, NULL,
  124. ai_stand, 0, NULL,
  125. ai_stand, 0, NULL,
  126. ai_stand, 0, gunner_fidget
  127. };
  128. mmove_t gunner_move_stand = {FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL};
  129. void gunner_stand (edict_t *self)
  130. {
  131. self->monsterinfo.currentmove = &gunner_move_stand;
  132. }
  133. mframe_t gunner_frames_walk [] =
  134. {
  135. ai_walk, 0, NULL,
  136. ai_walk, 3, NULL,
  137. ai_walk, 4, NULL,
  138. ai_walk, 5, NULL,
  139. ai_walk, 7, NULL,
  140. ai_walk, 2, NULL,
  141. ai_walk, 6, NULL,
  142. ai_walk, 4, NULL,
  143. ai_walk, 2, NULL,
  144. ai_walk, 7, NULL,
  145. ai_walk, 5, NULL,
  146. ai_walk, 7, NULL,
  147. ai_walk, 4, NULL
  148. };
  149. mmove_t gunner_move_walk = {FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL};
  150. void gunner_walk (edict_t *self)
  151. {
  152. self->monsterinfo.currentmove = &gunner_move_walk;
  153. }
  154. mframe_t gunner_frames_run [] =
  155. {
  156. ai_run, 26, NULL,
  157. ai_run, 9, NULL,
  158. ai_run, 9, NULL,
  159. ai_run, 9, monster_done_dodge,
  160. ai_run, 15, NULL,
  161. ai_run, 10, NULL,
  162. ai_run, 13, NULL,
  163. ai_run, 6, NULL
  164. };
  165. mmove_t gunner_move_run = {FRAME_run01, FRAME_run08, gunner_frames_run, NULL};
  166. void gunner_run (edict_t *self)
  167. {
  168. monster_done_dodge(self);
  169. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  170. self->monsterinfo.currentmove = &gunner_move_stand;
  171. else
  172. self->monsterinfo.currentmove = &gunner_move_run;
  173. }
  174. mframe_t gunner_frames_runandshoot [] =
  175. {
  176. ai_run, 32, NULL,
  177. ai_run, 15, NULL,
  178. ai_run, 10, NULL,
  179. ai_run, 18, NULL,
  180. ai_run, 8, NULL,
  181. ai_run, 20, NULL
  182. };
  183. mmove_t gunner_move_runandshoot = {FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, NULL};
  184. void gunner_runandshoot (edict_t *self)
  185. {
  186. self->monsterinfo.currentmove = &gunner_move_runandshoot;
  187. }
  188. mframe_t gunner_frames_pain3 [] =
  189. {
  190. ai_move, -3, NULL,
  191. ai_move, 1, NULL,
  192. ai_move, 1, NULL,
  193. ai_move, 0, NULL,
  194. ai_move, 1, NULL
  195. };
  196. mmove_t gunner_move_pain3 = {FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run};
  197. mframe_t gunner_frames_pain2 [] =
  198. {
  199. ai_move, -2, NULL,
  200. ai_move, 11, NULL,
  201. ai_move, 6, NULL,
  202. ai_move, 2, NULL,
  203. ai_move, -1, NULL,
  204. ai_move, -7, NULL,
  205. ai_move, -2, NULL,
  206. ai_move, -7, NULL
  207. };
  208. mmove_t gunner_move_pain2 = {FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run};
  209. mframe_t gunner_frames_pain1 [] =
  210. {
  211. ai_move, 2, NULL,
  212. ai_move, 0, NULL,
  213. ai_move, -5, NULL,
  214. ai_move, 3, NULL,
  215. ai_move, -1, NULL,
  216. ai_move, 0, NULL,
  217. ai_move, 0, NULL,
  218. ai_move, 0, NULL,
  219. ai_move, 0, NULL,
  220. ai_move, 1, NULL,
  221. ai_move, 1, NULL,
  222. ai_move, 2, NULL,
  223. ai_move, 1, NULL,
  224. ai_move, 0, NULL,
  225. ai_move, -2, NULL,
  226. ai_move, -2, NULL,
  227. ai_move, 0, NULL,
  228. ai_move, 0, NULL
  229. };
  230. mmove_t gunner_move_pain1 = {FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run};
  231. void gunner_pain (edict_t *self, edict_t *other, float kick, int damage)
  232. {
  233. if (self->health < (self->max_health / 2))
  234. self->s.skinnum = 1;
  235. monster_done_dodge (self);
  236. if (!self->groundentity)
  237. {
  238. // if ((g_showlogic) && (g_showlogic->value))
  239. // gi.dprintf ("gunner: pain avoided due to no ground\n");
  240. return;
  241. }
  242. if (level.time < self->pain_debounce_time)
  243. return;
  244. self->pain_debounce_time = level.time + 3;
  245. if (rand()&1)
  246. gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
  247. else
  248. gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
  249. if (skill->value == 3)
  250. return; // no pain anims in nightmare
  251. if (damage <= 10)
  252. self->monsterinfo.currentmove = &gunner_move_pain3;
  253. else if (damage <= 25)
  254. self->monsterinfo.currentmove = &gunner_move_pain2;
  255. else
  256. self->monsterinfo.currentmove = &gunner_move_pain1;
  257. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  258. // PMM - clear duck flag
  259. if (self->monsterinfo.aiflags & AI_DUCKED)
  260. monster_duck_up(self);
  261. }
  262. void gunner_dead (edict_t *self)
  263. {
  264. VectorSet (self->mins, -16, -16, -24);
  265. VectorSet (self->maxs, 16, 16, -8);
  266. self->movetype = MOVETYPE_TOSS;
  267. self->svflags |= SVF_DEADMONSTER;
  268. self->nextthink = 0;
  269. gi.linkentity (self);
  270. }
  271. mframe_t gunner_frames_death [] =
  272. {
  273. ai_move, 0, NULL,
  274. ai_move, 0, NULL,
  275. ai_move, 0, NULL,
  276. ai_move, -7, NULL,
  277. ai_move, -3, NULL,
  278. ai_move, -5, NULL,
  279. ai_move, 8, NULL,
  280. ai_move, 6, NULL,
  281. ai_move, 0, NULL,
  282. ai_move, 0, NULL,
  283. ai_move, 0, NULL
  284. };
  285. mmove_t gunner_move_death = {FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead};
  286. void gunner_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  287. {
  288. int n;
  289. // check for gib
  290. if (self->health <= self->gib_health)
  291. {
  292. gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  293. for (n= 0; n < 2; n++)
  294. ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
  295. for (n= 0; n < 4; n++)
  296. ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  297. ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
  298. self->deadflag = DEAD_DEAD;
  299. return;
  300. }
  301. if (self->deadflag == DEAD_DEAD)
  302. return;
  303. // regular death
  304. gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
  305. self->deadflag = DEAD_DEAD;
  306. self->takedamage = DAMAGE_YES;
  307. self->monsterinfo.currentmove = &gunner_move_death;
  308. }
  309. // PMM - changed to duck code for new dodge
  310. //
  311. // this is specific to the gunner, leave it be
  312. //
  313. void gunner_duck_down (edict_t *self)
  314. {
  315. // if (self->monsterinfo.aiflags & AI_DUCKED)
  316. // return;
  317. self->monsterinfo.aiflags |= AI_DUCKED;
  318. if (skill->value >= 2)
  319. {
  320. if (random() > 0.5)
  321. GunnerGrenade (self);
  322. }
  323. // self->maxs[2] -= 32;
  324. self->maxs[2] = self->monsterinfo.base_height - 32;
  325. self->takedamage = DAMAGE_YES;
  326. if (self->monsterinfo.duck_wait_time < level.time)
  327. self->monsterinfo.duck_wait_time = level.time + 1;
  328. gi.linkentity (self);
  329. }
  330. mframe_t gunner_frames_duck [] =
  331. {
  332. ai_move, 1, gunner_duck_down,
  333. ai_move, 1, NULL,
  334. ai_move, 1, monster_duck_hold,
  335. ai_move, 0, NULL,
  336. ai_move, -1, NULL,
  337. ai_move, -1, NULL,
  338. ai_move, 0, monster_duck_up,
  339. ai_move, -1, NULL
  340. };
  341. mmove_t gunner_move_duck = {FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run};
  342. // PMM - gunner dodge moved below so I know about attack sequences
  343. void gunner_opengun (edict_t *self)
  344. {
  345. gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
  346. }
  347. void GunnerFire (edict_t *self)
  348. {
  349. vec3_t start;
  350. vec3_t forward, right;
  351. vec3_t target;
  352. vec3_t aim;
  353. int flash_number;
  354. if(!self->enemy || !self->enemy->inuse) //PGM
  355. return; //PGM
  356. flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216);
  357. AngleVectors (self->s.angles, forward, right, NULL);
  358. G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
  359. // project enemy back a bit and target there
  360. VectorCopy (self->enemy->s.origin, target);
  361. VectorMA (target, -0.2, self->enemy->velocity, target);
  362. target[2] += self->enemy->viewheight;
  363. VectorSubtract (target, start, aim);
  364. VectorNormalize (aim);
  365. monster_fire_bullet (self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
  366. }
  367. qboolean gunner_grenade_check(edict_t *self)
  368. {
  369. vec3_t start;
  370. vec3_t forward, right;
  371. trace_t tr;
  372. vec3_t target, dir;
  373. if(!self->enemy)
  374. return false;
  375. // if the player is above my head, use machinegun.
  376. // check for flag telling us that we're blindfiring
  377. if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
  378. {
  379. if (self->s.origin[2]+self->viewheight < self->monsterinfo.blind_fire_target[2])
  380. {
  381. // if(g_showlogic && g_showlogic->value)
  382. // gi.dprintf("blind_fire_target is above my head, using machinegun\n");
  383. return false;
  384. }
  385. }
  386. else if(self->absmax[2] <= self->enemy->absmin[2])
  387. {
  388. // if(g_showlogic && g_showlogic->value)
  389. // gi.dprintf("player is above my head, using machinegun\n");
  390. return false;
  391. }
  392. // check to see that we can trace to the player before we start
  393. // tossing grenades around.
  394. AngleVectors (self->s.angles, forward, right, NULL);
  395. G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_GUNNER_GRENADE_1], forward, right, start);
  396. // pmm - check for blindfire flag
  397. if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
  398. VectorCopy (self->monsterinfo.blind_fire_target, target);
  399. else
  400. VectorCopy (self->enemy->s.origin, target);
  401. // see if we're too close
  402. VectorSubtract (self->s.origin, target, dir);
  403. if (VectorLength(dir) < 100)
  404. return false;
  405. tr = gi.trace(start, vec3_origin, vec3_origin, target, self, MASK_SHOT);
  406. if(tr.ent == self->enemy || tr.fraction == 1)
  407. return true;
  408. // if(g_showlogic && g_showlogic->value)
  409. // gi.dprintf("can't trace to target, using machinegun\n");
  410. return false;
  411. }
  412. void GunnerGrenade (edict_t *self)
  413. {
  414. vec3_t start;
  415. vec3_t forward, right, up;
  416. vec3_t aim;
  417. int flash_number;
  418. float spread;
  419. float pitch;
  420. // PMM
  421. vec3_t target;
  422. qboolean blindfire;
  423. if(!self->enemy || !self->enemy->inuse) //PGM
  424. return; //PGM
  425. // pmm
  426. if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
  427. blindfire = true;
  428. if (self->s.frame == FRAME_attak105)
  429. {
  430. spread = .02;
  431. flash_number = MZ2_GUNNER_GRENADE_1;
  432. }
  433. else if (self->s.frame == FRAME_attak108)
  434. {
  435. spread = .05;
  436. flash_number = MZ2_GUNNER_GRENADE_2;
  437. }
  438. else if (self->s.frame == FRAME_attak111)
  439. {
  440. spread = .08;
  441. flash_number = MZ2_GUNNER_GRENADE_3;
  442. }
  443. else // (self->s.frame == FRAME_attak114)
  444. {
  445. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  446. spread = .11;
  447. flash_number = MZ2_GUNNER_GRENADE_4;
  448. }
  449. // pmm
  450. // if we're shooting blind and we still can't see our enemy
  451. if ((blindfire) && (!visible(self, self->enemy)))
  452. {
  453. // and we have a valid blind_fire_target
  454. if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
  455. return;
  456. // gi.dprintf ("blind_fire_target = %s\n", vtos (self->monsterinfo.blind_fire_target));
  457. // gi.dprintf ("GunnerGrenade: ideal yaw is %f\n", self->ideal_yaw);
  458. VectorCopy (self->monsterinfo.blind_fire_target, target);
  459. }
  460. else
  461. VectorCopy (self->s.origin, target);
  462. // pmm
  463. AngleVectors (self->s.angles, forward, right, up); //PGM
  464. G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
  465. //PGM
  466. if(self->enemy)
  467. {
  468. float dist;
  469. // VectorSubtract(self->enemy->s.origin, self->s.origin, aim);
  470. VectorSubtract(target, self->s.origin, aim);
  471. dist = VectorLength(aim);
  472. // aim up if they're on the same level as me and far away.
  473. if((dist > 512) && (aim[2] < 64) && (aim[2] > -64))
  474. {
  475. aim[2] += (dist - 512);
  476. }
  477. VectorNormalize (aim);
  478. pitch = aim[2];
  479. if(pitch > 0.4)
  480. {
  481. pitch = 0.4;
  482. }
  483. else if(pitch < -0.5)
  484. pitch = -0.5;
  485. }
  486. //PGM
  487. //FIXME : do a spread -225 -75 75 225 degrees around forward
  488. // VectorCopy (forward, aim);
  489. VectorMA (forward, spread, right, aim);
  490. VectorMA (aim, pitch, up, aim);
  491. monster_fire_grenade (self, start, aim, 50, 600, flash_number);
  492. }
  493. mframe_t gunner_frames_attack_chain [] =
  494. {
  495. /*
  496. ai_charge, 0, NULL,
  497. ai_charge, 0, NULL,
  498. ai_charge, 0, NULL,
  499. ai_charge, 0, NULL,
  500. ai_charge, 0, NULL,
  501. ai_charge, 0, NULL,
  502. ai_charge, 0, NULL,
  503. ai_charge, 0, NULL,
  504. */
  505. ai_charge, 0, gunner_opengun,
  506. ai_charge, 0, NULL,
  507. ai_charge, 0, NULL,
  508. ai_charge, 0, NULL,
  509. ai_charge, 0, NULL,
  510. ai_charge, 0, NULL,
  511. ai_charge, 0, NULL
  512. };
  513. mmove_t gunner_move_attack_chain = {FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain};
  514. mframe_t gunner_frames_fire_chain [] =
  515. {
  516. ai_charge, 0, GunnerFire,
  517. ai_charge, 0, GunnerFire,
  518. ai_charge, 0, GunnerFire,
  519. ai_charge, 0, GunnerFire,
  520. ai_charge, 0, GunnerFire,
  521. ai_charge, 0, GunnerFire,
  522. ai_charge, 0, GunnerFire,
  523. ai_charge, 0, GunnerFire
  524. };
  525. mmove_t gunner_move_fire_chain = {FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain};
  526. mframe_t gunner_frames_endfire_chain [] =
  527. {
  528. ai_charge, 0, NULL,
  529. ai_charge, 0, NULL,
  530. ai_charge, 0, NULL,
  531. ai_charge, 0, NULL,
  532. ai_charge, 0, NULL,
  533. ai_charge, 0, NULL,
  534. ai_charge, 0, NULL
  535. };
  536. mmove_t gunner_move_endfire_chain = {FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run};
  537. void gunner_blind_check (edict_t *self)
  538. {
  539. vec3_t aim;
  540. if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
  541. {
  542. VectorSubtract(self->monsterinfo.blind_fire_target, self->s.origin, aim);
  543. self->ideal_yaw = vectoyaw(aim);
  544. // gi.dprintf ("blind_fire_target = %s\n", vtos (self->monsterinfo.blind_fire_target));
  545. // gi.dprintf ("gunner_attack: ideal yaw is %f\n", self->ideal_yaw);
  546. }
  547. }
  548. mframe_t gunner_frames_attack_grenade [] =
  549. {
  550. ai_charge, 0, gunner_blind_check,
  551. ai_charge, 0, NULL,
  552. ai_charge, 0, NULL,
  553. ai_charge, 0, NULL,
  554. ai_charge, 0, GunnerGrenade,
  555. ai_charge, 0, NULL,
  556. ai_charge, 0, NULL,
  557. ai_charge, 0, GunnerGrenade,
  558. ai_charge, 0, NULL,
  559. ai_charge, 0, NULL,
  560. ai_charge, 0, GunnerGrenade,
  561. ai_charge, 0, NULL,
  562. ai_charge, 0, NULL,
  563. ai_charge, 0, GunnerGrenade,
  564. ai_charge, 0, NULL,
  565. ai_charge, 0, NULL,
  566. ai_charge, 0, NULL,
  567. ai_charge, 0, NULL,
  568. ai_charge, 0, NULL,
  569. ai_charge, 0, NULL,
  570. ai_charge, 0, NULL
  571. };
  572. mmove_t gunner_move_attack_grenade = {FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run};
  573. void gunner_attack(edict_t *self)
  574. {
  575. float chance, r;
  576. monster_done_dodge(self);
  577. // PMM
  578. if (self->monsterinfo.attack_state == AS_BLIND)
  579. {
  580. // setup shot probabilities
  581. if (self->monsterinfo.blind_fire_delay < 1.0)
  582. chance = 1.0;
  583. else if (self->monsterinfo.blind_fire_delay < 7.5)
  584. chance = 0.4;
  585. else
  586. chance = 0.1;
  587. r = random();
  588. // minimum of 2 seconds, plus 0-3, after the shots are done
  589. self->monsterinfo.blind_fire_delay += 2.1 + 2.0 + random()*3.0;
  590. // don't shoot at the origin
  591. if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin))
  592. return;
  593. // don't shoot if the dice say not to
  594. if (r > chance)
  595. {
  596. // if ((g_showlogic) && (g_showlogic->value))
  597. // gi.dprintf ("blindfire - NO SHOT\n");
  598. return;
  599. }
  600. // turn on manual steering to signal both manual steering and blindfire
  601. self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  602. if (gunner_grenade_check(self))
  603. {
  604. // if the check passes, go for the attack
  605. self->monsterinfo.currentmove = &gunner_move_attack_grenade;
  606. self->monsterinfo.attack_finished = level.time + 2*random();
  607. }
  608. // pmm - should this be active?
  609. // else
  610. // self->monsterinfo.currentmove = &gunner_move_attack_chain;
  611. // if ((g_showlogic) && (g_showlogic->value))
  612. // gi.dprintf ("blind grenade check failed, doing nothing\n");
  613. // turn off blindfire flag
  614. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  615. return;
  616. }
  617. // pmm
  618. // PGM - gunner needs to use his chaingun if he's being attacked by a tesla.
  619. if ((range (self, self->enemy) == RANGE_MELEE) || self->bad_area)
  620. {
  621. self->monsterinfo.currentmove = &gunner_move_attack_chain;
  622. }
  623. else
  624. {
  625. if (random() <= 0.5 && gunner_grenade_check(self))
  626. self->monsterinfo.currentmove = &gunner_move_attack_grenade;
  627. else
  628. self->monsterinfo.currentmove = &gunner_move_attack_chain;
  629. }
  630. }
  631. void gunner_fire_chain(edict_t *self)
  632. {
  633. self->monsterinfo.currentmove = &gunner_move_fire_chain;
  634. }
  635. void gunner_refire_chain(edict_t *self)
  636. {
  637. if (self->enemy->health > 0)
  638. if ( visible (self, self->enemy) )
  639. if (random() <= 0.5)
  640. {
  641. self->monsterinfo.currentmove = &gunner_move_fire_chain;
  642. return;
  643. }
  644. self->monsterinfo.currentmove = &gunner_move_endfire_chain;
  645. }
  646. /*
  647. void gunner_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
  648. {
  649. // original quake2 dodge code
  650. if (random() > 0.25)
  651. return;
  652. if (!self->enemy)
  653. self->enemy = attacker;
  654. self->monsterinfo.currentmove = &gunner_move_duck;
  655. //===========
  656. //PMM - rogue rewrite of gunner dodge code.
  657. float r;
  658. float height;
  659. int shooting = 0;
  660. if (!self->enemy)
  661. {
  662. self->enemy = attacker;
  663. FoundTarget (self);
  664. }
  665. // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
  666. // seeing numbers like 13 and 14)
  667. if ((eta < 0.1) || (eta > 5))
  668. return;
  669. r = random();
  670. if (r > (0.25*((skill->value)+1)))
  671. return;
  672. if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
  673. (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
  674. (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
  675. )
  676. {
  677. shooting = 1;
  678. }
  679. if (self->monsterinfo.aiflags & AI_DODGING)
  680. {
  681. height = self->absmax[2];
  682. }
  683. else
  684. {
  685. height = self->absmax[2]-32-1; // the -1 is because the absmax is s.origin + maxs + 1
  686. }
  687. // check to see if it makes sense to duck
  688. if (tr->endpos[2] <= height)
  689. {
  690. vec3_t right, diff;
  691. if (shooting)
  692. {
  693. self->monsterinfo.attack_state = AS_SLIDING;
  694. return;
  695. }
  696. AngleVectors (self->s.angles, NULL, right, NULL);
  697. VectorSubtract (tr->endpos, self->s.origin, diff);
  698. if (DotProduct (right, diff) < 0)
  699. {
  700. self->monsterinfo.lefty = 1;
  701. }
  702. // if it doesn't sense to duck, try to strafe away
  703. monster_done_dodge (self);
  704. self->monsterinfo.currentmove = &gunner_move_run;
  705. self->monsterinfo.attack_state = AS_SLIDING;
  706. return;
  707. }
  708. if (skill->value == 0)
  709. {
  710. self->monsterinfo.currentmove = &gunner_move_duck;
  711. // PMM - stupid dodge
  712. self->monsterinfo.duck_wait_time = level.time + eta + 1;
  713. self->monsterinfo.aiflags |= AI_DODGING;
  714. return;
  715. }
  716. if (!shooting)
  717. {
  718. self->monsterinfo.currentmove = &gunner_move_duck;
  719. self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
  720. self->monsterinfo.aiflags |= AI_DODGING;
  721. }
  722. return;
  723. //PMM
  724. //===========
  725. }
  726. */
  727. //===========
  728. //PGM
  729. void gunner_jump_now (edict_t *self)
  730. {
  731. vec3_t forward,up;
  732. monster_jump_start (self);
  733. AngleVectors (self->s.angles, forward, NULL, up);
  734. VectorMA(self->velocity, 100, forward, self->velocity);
  735. VectorMA(self->velocity, 300, up, self->velocity);
  736. }
  737. void gunner_jump2_now (edict_t *self)
  738. {
  739. vec3_t forward,up;
  740. monster_jump_start (self);
  741. AngleVectors (self->s.angles, forward, NULL, up);
  742. VectorMA(self->velocity, 150, forward, self->velocity);
  743. VectorMA(self->velocity, 400, up, self->velocity);
  744. }
  745. void gunner_jump_wait_land (edict_t *self)
  746. {
  747. if(self->groundentity == NULL)
  748. {
  749. self->monsterinfo.nextframe = self->s.frame;
  750. if(monster_jump_finished (self))
  751. self->monsterinfo.nextframe = self->s.frame + 1;
  752. }
  753. else
  754. self->monsterinfo.nextframe = self->s.frame + 1;
  755. }
  756. mframe_t gunner_frames_jump [] =
  757. {
  758. ai_move, 0, NULL,
  759. ai_move, 0, NULL,
  760. ai_move, 0, NULL,
  761. ai_move, 0, gunner_jump_now,
  762. ai_move, 0, NULL,
  763. ai_move, 0, NULL,
  764. ai_move, 0, gunner_jump_wait_land,
  765. ai_move, 0, NULL,
  766. ai_move, 0, NULL,
  767. ai_move, 0, NULL
  768. };
  769. mmove_t gunner_move_jump = { FRAME_jump01, FRAME_jump10, gunner_frames_jump, gunner_run };
  770. mframe_t gunner_frames_jump2 [] =
  771. {
  772. ai_move, -8, NULL,
  773. ai_move, -4, NULL,
  774. ai_move, -4, NULL,
  775. ai_move, 0, gunner_jump_now,
  776. ai_move, 0, NULL,
  777. ai_move, 0, NULL,
  778. ai_move, 0, gunner_jump_wait_land,
  779. ai_move, 0, NULL,
  780. ai_move, 0, NULL,
  781. ai_move, 0, NULL
  782. };
  783. mmove_t gunner_move_jump2 = { FRAME_jump01, FRAME_jump10, gunner_frames_jump2, gunner_run };
  784. void gunner_jump (edict_t *self)
  785. {
  786. if(!self->enemy)
  787. return;
  788. monster_done_dodge (self);
  789. if(self->enemy->s.origin[2] > self->s.origin[2])
  790. self->monsterinfo.currentmove = &gunner_move_jump2;
  791. else
  792. self->monsterinfo.currentmove = &gunner_move_jump;
  793. }
  794. //===========
  795. //PGM
  796. qboolean gunner_blocked (edict_t *self, float dist)
  797. {
  798. if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
  799. return true;
  800. if(blocked_checkplat (self, dist))
  801. return true;
  802. if(blocked_checkjump (self, dist, 192, 40))
  803. {
  804. gunner_jump(self);
  805. return true;
  806. }
  807. return false;
  808. }
  809. //PGM
  810. //===========
  811. // PMM - new duck code
  812. void gunner_duck (edict_t *self, float eta)
  813. {
  814. if ((self->monsterinfo.currentmove == &gunner_move_jump2) ||
  815. (self->monsterinfo.currentmove == &gunner_move_jump))
  816. {
  817. return;
  818. }
  819. if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
  820. (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
  821. (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
  822. )
  823. {
  824. // if we're shooting, and not on easy, don't dodge
  825. if (skill->value)
  826. {
  827. self->monsterinfo.aiflags &= ~AI_DUCKED;
  828. return;
  829. }
  830. }
  831. if (skill->value == 0)
  832. // PMM - stupid dodge
  833. self->monsterinfo.duck_wait_time = level.time + eta + 1;
  834. else
  835. self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
  836. // has to be done immediately otherwise he can get stuck
  837. gunner_duck_down(self);
  838. self->monsterinfo.nextframe = FRAME_duck01;
  839. self->monsterinfo.currentmove = &gunner_move_duck;
  840. return;
  841. }
  842. void gunner_sidestep (edict_t *self)
  843. {
  844. if ((self->monsterinfo.currentmove == &gunner_move_jump2) ||
  845. (self->monsterinfo.currentmove == &gunner_move_jump))
  846. {
  847. return;
  848. }
  849. if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) ||
  850. (self->monsterinfo.currentmove == &gunner_move_fire_chain) ||
  851. (self->monsterinfo.currentmove == &gunner_move_attack_grenade)
  852. )
  853. {
  854. // if we're shooting, and not on easy, don't dodge
  855. if (skill->value)
  856. {
  857. self->monsterinfo.aiflags &= ~AI_DODGING;
  858. return;
  859. }
  860. }
  861. if (self->monsterinfo.currentmove != &gunner_move_run)
  862. self->monsterinfo.currentmove = &gunner_move_run;
  863. }
  864. /*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
  865. */
  866. void SP_monster_gunner (edict_t *self)
  867. {
  868. if (deathmatch->value)
  869. {
  870. G_FreeEdict (self);
  871. return;
  872. }
  873. sound_death = gi.soundindex ("gunner/death1.wav");
  874. sound_pain = gi.soundindex ("gunner/gunpain2.wav");
  875. sound_pain2 = gi.soundindex ("gunner/gunpain1.wav");
  876. sound_idle = gi.soundindex ("gunner/gunidle1.wav");
  877. sound_open = gi.soundindex ("gunner/gunatck1.wav");
  878. sound_search = gi.soundindex ("gunner/gunsrch1.wav");
  879. sound_sight = gi.soundindex ("gunner/sight1.wav");
  880. gi.soundindex ("gunner/gunatck2.wav");
  881. gi.soundindex ("gunner/gunatck3.wav");
  882. self->movetype = MOVETYPE_STEP;
  883. self->solid = SOLID_BBOX;
  884. self->s.modelindex = gi.modelindex ("models/monsters/gunner/tris.md2");
  885. VectorSet (self->mins, -16, -16, -24);
  886. VectorSet (self->maxs, 16, 16, 32);
  887. self->health = 175;
  888. self->gib_health = -70;
  889. self->mass = 200;
  890. self->pain = gunner_pain;
  891. self->die = gunner_die;
  892. self->monsterinfo.stand = gunner_stand;
  893. self->monsterinfo.walk = gunner_walk;
  894. self->monsterinfo.run = gunner_run;
  895. // pmm
  896. self->monsterinfo.dodge = M_MonsterDodge;
  897. self->monsterinfo.duck = gunner_duck;
  898. self->monsterinfo.unduck = monster_duck_up;
  899. self->monsterinfo.sidestep = gunner_sidestep;
  900. // self->monsterinfo.dodge = gunner_dodge;
  901. // pmm
  902. self->monsterinfo.attack = gunner_attack;
  903. self->monsterinfo.melee = NULL;
  904. self->monsterinfo.sight = gunner_sight;
  905. self->monsterinfo.search = gunner_search;
  906. self->monsterinfo.blocked = gunner_blocked; //PGM
  907. gi.linkentity (self);
  908. self->monsterinfo.currentmove = &gunner_move_stand;
  909. self->monsterinfo.scale = MODEL_SCALE;
  910. // PMM
  911. self->monsterinfo.blindfire = true;
  912. walkmonster_start (self);
  913. }