m_arachnid.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. TANK
  6. ==============================================================================
  7. */
  8. #include "g_local.h"
  9. #include "m_arachnid.h"
  10. #include "m_flash.h"
  11. static cached_soundindex sound_pain;
  12. static cached_soundindex sound_death;
  13. static cached_soundindex sound_sight;
  14. MONSTERINFO_SIGHT(arachnid_sight) (edict_t *self, edict_t *other) -> void
  15. {
  16. gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
  17. }
  18. //
  19. // stand
  20. //
  21. mframe_t arachnid_frames_stand[] = {
  22. { ai_stand },
  23. { ai_stand },
  24. { ai_stand },
  25. { ai_stand },
  26. { ai_stand },
  27. { ai_stand },
  28. { ai_stand },
  29. { ai_stand },
  30. { ai_stand },
  31. { ai_stand },
  32. { ai_stand },
  33. { ai_stand },
  34. { ai_stand }
  35. };
  36. MMOVE_T(arachnid_move_stand) = { FRAME_idle1, FRAME_idle13, arachnid_frames_stand, nullptr };
  37. MONSTERINFO_STAND(arachnid_stand) (edict_t *self) -> void
  38. {
  39. M_SetAnimation(self, &arachnid_move_stand);
  40. }
  41. //
  42. // walk
  43. //
  44. static cached_soundindex sound_step;
  45. void arachnid_footstep(edict_t *self)
  46. {
  47. gi.sound(self, CHAN_BODY, sound_step, 0.5f, ATTN_IDLE, 0.0f);
  48. }
  49. mframe_t arachnid_frames_walk[] = {
  50. { ai_walk, 8, arachnid_footstep },
  51. { ai_walk, 8 },
  52. { ai_walk, 8 },
  53. { ai_walk, 8 },
  54. { ai_walk, 8 },
  55. { ai_walk, 8, arachnid_footstep },
  56. { ai_walk, 8 },
  57. { ai_walk, 8 },
  58. { ai_walk, 8 },
  59. { ai_walk, 8 }
  60. };
  61. MMOVE_T(arachnid_move_walk) = { FRAME_walk1, FRAME_walk10, arachnid_frames_walk, nullptr };
  62. MONSTERINFO_WALK(arachnid_walk) (edict_t *self) -> void
  63. {
  64. M_SetAnimation(self, &arachnid_move_walk);
  65. }
  66. //
  67. // run
  68. //
  69. mframe_t arachnid_frames_run[] = {
  70. { ai_run, 8, arachnid_footstep },
  71. { ai_run, 8 },
  72. { ai_run, 8 },
  73. { ai_run, 8 },
  74. { ai_run, 8 },
  75. { ai_run, 8, arachnid_footstep },
  76. { ai_run, 8 },
  77. { ai_run, 8 },
  78. { ai_run, 8 },
  79. { ai_run, 8 }
  80. };
  81. MMOVE_T(arachnid_move_run) = { FRAME_walk1, FRAME_walk10, arachnid_frames_run, nullptr };
  82. MONSTERINFO_RUN(arachnid_run) (edict_t *self) -> void
  83. {
  84. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  85. {
  86. M_SetAnimation(self, &arachnid_move_stand);
  87. return;
  88. }
  89. M_SetAnimation(self, &arachnid_move_run);
  90. }
  91. //
  92. // pain
  93. //
  94. mframe_t arachnid_frames_pain1[] = {
  95. { ai_move },
  96. { ai_move },
  97. { ai_move },
  98. { ai_move },
  99. { ai_move }
  100. };
  101. MMOVE_T(arachnid_move_pain1) = { FRAME_pain11, FRAME_pain15, arachnid_frames_pain1, arachnid_run };
  102. mframe_t arachnid_frames_pain2[] = {
  103. { ai_move },
  104. { ai_move },
  105. { ai_move },
  106. { ai_move },
  107. { ai_move },
  108. { ai_move }
  109. };
  110. MMOVE_T(arachnid_move_pain2) = { FRAME_pain21, FRAME_pain26, arachnid_frames_pain2, arachnid_run };
  111. PAIN(arachnid_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
  112. {
  113. if (level.time < self->pain_debounce_time)
  114. return;
  115. self->pain_debounce_time = level.time + 3_sec;
  116. gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
  117. if (!M_ShouldReactToPain(self, mod))
  118. return; // no pain anims in nightmare
  119. float r = frandom();
  120. if (r < 0.5f)
  121. M_SetAnimation(self, &arachnid_move_pain1);
  122. else
  123. M_SetAnimation(self, &arachnid_move_pain2);
  124. }
  125. static cached_soundindex sound_charge;
  126. void arachnid_charge_rail(edict_t *self)
  127. {
  128. if (!self->enemy || !self->enemy->inuse)
  129. return;
  130. gi.sound(self, CHAN_WEAPON, sound_charge, 1.f, ATTN_NORM, 0.f);
  131. self->pos1 = self->enemy->s.origin;
  132. self->pos1[2] += self->enemy->viewheight;
  133. }
  134. void arachnid_rail(edict_t *self)
  135. {
  136. vec3_t start;
  137. vec3_t dir;
  138. vec3_t forward, right;
  139. monster_muzzleflash_id_t id;
  140. switch (self->s.frame)
  141. {
  142. case FRAME_rails4:
  143. default:
  144. id = MZ2_ARACHNID_RAIL1;
  145. break;
  146. case FRAME_rails8:
  147. id = MZ2_ARACHNID_RAIL2;
  148. break;
  149. case FRAME_rails_up7:
  150. id = MZ2_ARACHNID_RAIL_UP1;
  151. break;
  152. case FRAME_rails_up11:
  153. id = MZ2_ARACHNID_RAIL_UP2;
  154. break;
  155. }
  156. AngleVectors(self->s.angles, forward, right, nullptr);
  157. start = M_ProjectFlashSource(self, monster_flash_offset[id], forward, right);
  158. // calc direction to where we targeted
  159. dir = self->pos1 - start;
  160. dir.normalize();
  161. monster_fire_railgun(self, start, dir, 35, 100, id);
  162. }
  163. mframe_t arachnid_frames_attack1[] = {
  164. { ai_charge, 0, arachnid_charge_rail },
  165. { ai_charge },
  166. { ai_charge },
  167. { ai_charge, 0, arachnid_rail },
  168. { ai_charge, 0, arachnid_charge_rail },
  169. { ai_charge },
  170. { ai_charge },
  171. { ai_charge, 0, arachnid_rail },
  172. { ai_charge },
  173. { ai_charge },
  174. { ai_charge }
  175. };
  176. MMOVE_T(arachnid_attack1) = { FRAME_rails1, FRAME_rails11, arachnid_frames_attack1, arachnid_run };
  177. mframe_t arachnid_frames_attack_up1[] = {
  178. { ai_charge },
  179. { ai_charge },
  180. { ai_charge },
  181. { ai_charge, 0, arachnid_charge_rail },
  182. { ai_charge },
  183. { ai_charge },
  184. { ai_charge, 0, arachnid_rail },
  185. { ai_charge, 0, arachnid_charge_rail },
  186. { ai_charge },
  187. { ai_charge },
  188. { ai_charge, 0, arachnid_rail },
  189. { ai_charge },
  190. { ai_charge },
  191. { ai_charge },
  192. { ai_charge },
  193. { ai_charge },
  194. };
  195. MMOVE_T(arachnid_attack_up1) = { FRAME_rails_up1, FRAME_rails_up16, arachnid_frames_attack_up1, arachnid_run };
  196. static cached_soundindex sound_melee, sound_melee_hit;
  197. void arachnid_melee_charge(edict_t *self)
  198. {
  199. gi.sound(self, CHAN_WEAPON, sound_melee, 1.f, ATTN_NORM, 0.f);
  200. }
  201. void arachnid_melee_hit(edict_t *self)
  202. {
  203. if (!fire_hit(self, { MELEE_DISTANCE, 0, 0 }, 15, 50))
  204. self->monsterinfo.melee_debounce_time = level.time + 1000_ms;
  205. }
  206. mframe_t arachnid_frames_melee[] = {
  207. { ai_charge },
  208. { ai_charge },
  209. { ai_charge },
  210. { ai_charge, 0, arachnid_melee_charge },
  211. { ai_charge },
  212. { ai_charge, 0, arachnid_melee_hit },
  213. { ai_charge },
  214. { ai_charge },
  215. { ai_charge, 0, arachnid_melee_charge },
  216. { ai_charge },
  217. { ai_charge, 0, arachnid_melee_hit },
  218. { ai_charge }
  219. };
  220. MMOVE_T(arachnid_melee) = { FRAME_melee_atk1, FRAME_melee_atk12, arachnid_frames_melee, arachnid_run };
  221. MONSTERINFO_ATTACK(arachnid_attack) (edict_t *self) -> void
  222. {
  223. if (!self->enemy || !self->enemy->inuse)
  224. return;
  225. if (self->monsterinfo.melee_debounce_time < level.time && range_to(self, self->enemy) < MELEE_DISTANCE)
  226. M_SetAnimation(self, &arachnid_melee);
  227. else if ((self->enemy->s.origin[2] - self->s.origin[2]) > 150.f)
  228. M_SetAnimation(self, &arachnid_attack_up1);
  229. else
  230. M_SetAnimation(self, &arachnid_attack1);
  231. }
  232. //
  233. // death
  234. //
  235. void arachnid_dead(edict_t *self)
  236. {
  237. self->mins = { -16, -16, -24 };
  238. self->maxs = { 16, 16, -8 };
  239. self->movetype = MOVETYPE_TOSS;
  240. self->svflags |= SVF_DEADMONSTER;
  241. self->nextthink = 0_ms;
  242. gi.linkentity(self);
  243. }
  244. mframe_t arachnid_frames_death1[] = {
  245. { ai_move, 0 },
  246. { ai_move, -1.23f },
  247. { ai_move, -1.23f },
  248. { ai_move, -1.23f },
  249. { ai_move, -1.23f },
  250. { ai_move, -1.64f },
  251. { ai_move, -1.64f },
  252. { ai_move, -2.45f },
  253. { ai_move, -8.63f },
  254. { ai_move, -4.0f },
  255. { ai_move, -4.5f },
  256. { ai_move, -6.8f },
  257. { ai_move, -8.0f },
  258. { ai_move, -5.4f },
  259. { ai_move, -3.4f },
  260. { ai_move, -1.9f },
  261. { ai_move },
  262. { ai_move },
  263. { ai_move },
  264. { ai_move }
  265. };
  266. MMOVE_T(arachnid_move_death) = { FRAME_death1, FRAME_death20, arachnid_frames_death1, arachnid_dead };
  267. DIE(arachnid_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
  268. {
  269. // check for gib
  270. if (M_CheckGib(self, mod))
  271. {
  272. gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
  273. ThrowGibs(self, damage, {
  274. { 2, "models/objects/gibs/bone/tris.md2" },
  275. { 4, "models/objects/gibs/sm_meat/tris.md2" },
  276. { "models/objects/gibs/head2/tris.md2", GIB_HEAD }
  277. });
  278. self->deadflag = true;
  279. return;
  280. }
  281. if (self->deadflag)
  282. return;
  283. // regular death
  284. gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
  285. self->deadflag = true;
  286. self->takedamage = true;
  287. M_SetAnimation(self, &arachnid_move_death);
  288. }
  289. //
  290. // monster_arachnid
  291. //
  292. /*QUAKED monster_arachnid (1 .5 0) (-48 -48 -20) (48 48 48) Ambush Trigger_Spawn Sight
  293. */
  294. void SP_monster_arachnid(edict_t *self)
  295. {
  296. if ( !M_AllowSpawn( self ) ) {
  297. G_FreeEdict( self );
  298. return;
  299. }
  300. sound_step.assign("insane/insane11.wav");
  301. sound_charge.assign("gladiator/railgun.wav");
  302. sound_melee.assign("gladiator/melee3.wav");
  303. sound_melee_hit.assign("gladiator/melee2.wav");
  304. sound_pain.assign("arachnid/pain.wav");
  305. sound_death.assign("arachnid/death.wav");
  306. sound_sight.assign("arachnid/sight.wav");
  307. self->s.modelindex = gi.modelindex("models/monsters/arachnid/tris.md2");
  308. self->mins = { -48, -48, -20 };
  309. self->maxs = { 48, 48, 48 };
  310. self->movetype = MOVETYPE_STEP;
  311. self->solid = SOLID_BBOX;
  312. self->health = 1000 * st.health_multiplier;
  313. self->gib_health = -200;
  314. self->monsterinfo.scale = MODEL_SCALE;
  315. self->mass = 450;
  316. self->pain = arachnid_pain;
  317. self->die = arachnid_die;
  318. self->monsterinfo.stand = arachnid_stand;
  319. self->monsterinfo.walk = arachnid_walk;
  320. self->monsterinfo.run = arachnid_run;
  321. self->monsterinfo.attack = arachnid_attack;
  322. self->monsterinfo.sight = arachnid_sight;
  323. gi.linkentity(self);
  324. M_SetAnimation(self, &arachnid_move_stand);
  325. walkmonster_start(self);
  326. }