m_flipper.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. FLIPPER
  6. ==============================================================================
  7. */
  8. #include "g_local.h"
  9. #include "m_flipper.h"
  10. static cached_soundindex sound_chomp;
  11. static cached_soundindex sound_attack;
  12. static cached_soundindex sound_pain1;
  13. static cached_soundindex sound_pain2;
  14. static cached_soundindex sound_death;
  15. static cached_soundindex sound_idle;
  16. static cached_soundindex sound_search;
  17. static cached_soundindex sound_sight;
  18. mframe_t flipper_frames_stand[] = {
  19. { ai_stand }
  20. };
  21. MMOVE_T(flipper_move_stand) = { FRAME_flphor01, FRAME_flphor01, flipper_frames_stand, nullptr };
  22. MONSTERINFO_STAND(flipper_stand) (edict_t *self) -> void
  23. {
  24. M_SetAnimation(self, &flipper_move_stand);
  25. }
  26. constexpr float FLIPPER_RUN_SPEED = 24;
  27. mframe_t flipper_frames_run[] = {
  28. { ai_run, FLIPPER_RUN_SPEED }, // 6
  29. { ai_run, FLIPPER_RUN_SPEED },
  30. { ai_run, FLIPPER_RUN_SPEED },
  31. { ai_run, FLIPPER_RUN_SPEED },
  32. { ai_run, FLIPPER_RUN_SPEED }, // 10
  33. { ai_run, FLIPPER_RUN_SPEED },
  34. { ai_run, FLIPPER_RUN_SPEED },
  35. { ai_run, FLIPPER_RUN_SPEED },
  36. { ai_run, FLIPPER_RUN_SPEED },
  37. { ai_run, FLIPPER_RUN_SPEED },
  38. { ai_run, FLIPPER_RUN_SPEED },
  39. { ai_run, FLIPPER_RUN_SPEED },
  40. { ai_run, FLIPPER_RUN_SPEED },
  41. { ai_run, FLIPPER_RUN_SPEED },
  42. { ai_run, FLIPPER_RUN_SPEED }, // 20
  43. { ai_run, FLIPPER_RUN_SPEED },
  44. { ai_run, FLIPPER_RUN_SPEED },
  45. { ai_run, FLIPPER_RUN_SPEED },
  46. { ai_run, FLIPPER_RUN_SPEED },
  47. { ai_run, FLIPPER_RUN_SPEED },
  48. { ai_run, FLIPPER_RUN_SPEED },
  49. { ai_run, FLIPPER_RUN_SPEED },
  50. { ai_run, FLIPPER_RUN_SPEED },
  51. { ai_run, FLIPPER_RUN_SPEED } // 29
  52. };
  53. MMOVE_T(flipper_move_run_loop) = { FRAME_flpver06, FRAME_flpver29, flipper_frames_run, nullptr };
  54. void flipper_run_loop(edict_t *self)
  55. {
  56. M_SetAnimation(self, &flipper_move_run_loop);
  57. }
  58. mframe_t flipper_frames_run_start[] = {
  59. { ai_run, 8 },
  60. { ai_run, 8 },
  61. { ai_run, 8 },
  62. { ai_run, 8 },
  63. { ai_run, 8 },
  64. { ai_run, 8 }
  65. };
  66. MMOVE_T(flipper_move_run_start) = { FRAME_flpver01, FRAME_flpver06, flipper_frames_run_start, flipper_run_loop };
  67. void flipper_run(edict_t *self)
  68. {
  69. M_SetAnimation(self, &flipper_move_run_start);
  70. }
  71. /* Standard Swimming */
  72. mframe_t flipper_frames_walk[] = {
  73. { ai_walk, 4 },
  74. { ai_walk, 4 },
  75. { ai_walk, 4 },
  76. { ai_walk, 4 },
  77. { ai_walk, 4 },
  78. { ai_walk, 4 },
  79. { ai_walk, 4 },
  80. { ai_walk, 4 },
  81. { ai_walk, 4 },
  82. { ai_walk, 4 },
  83. { ai_walk, 4 },
  84. { ai_walk, 4 },
  85. { ai_walk, 4 },
  86. { ai_walk, 4 },
  87. { ai_walk, 4 },
  88. { ai_walk, 4 },
  89. { ai_walk, 4 },
  90. { ai_walk, 4 },
  91. { ai_walk, 4 },
  92. { ai_walk, 4 },
  93. { ai_walk, 4 },
  94. { ai_walk, 4 },
  95. { ai_walk, 4 },
  96. { ai_walk, 4 }
  97. };
  98. MMOVE_T(flipper_move_walk) = { FRAME_flphor01, FRAME_flphor24, flipper_frames_walk, nullptr };
  99. MONSTERINFO_WALK(flipper_walk) (edict_t *self) -> void
  100. {
  101. M_SetAnimation(self, &flipper_move_walk);
  102. }
  103. mframe_t flipper_frames_start_run[] = {
  104. { ai_run },
  105. { ai_run },
  106. { ai_run },
  107. { ai_run },
  108. { ai_run, 8, flipper_run }
  109. };
  110. MMOVE_T(flipper_move_start_run) = { FRAME_flphor01, FRAME_flphor05, flipper_frames_start_run, nullptr };
  111. MONSTERINFO_RUN(flipper_start_run) (edict_t *self) -> void
  112. {
  113. M_SetAnimation(self, &flipper_move_start_run);
  114. }
  115. mframe_t flipper_frames_pain2[] = {
  116. { ai_move },
  117. { ai_move },
  118. { ai_move },
  119. { ai_move },
  120. { ai_move }
  121. };
  122. MMOVE_T(flipper_move_pain2) = { FRAME_flppn101, FRAME_flppn105, flipper_frames_pain2, flipper_run };
  123. mframe_t flipper_frames_pain1[] = {
  124. { ai_move },
  125. { ai_move },
  126. { ai_move },
  127. { ai_move },
  128. { ai_move }
  129. };
  130. MMOVE_T(flipper_move_pain1) = { FRAME_flppn201, FRAME_flppn205, flipper_frames_pain1, flipper_run };
  131. void flipper_bite(edict_t *self)
  132. {
  133. vec3_t aim = { MELEE_DISTANCE, 0, 0 };
  134. fire_hit(self, aim, 5, 0);
  135. }
  136. void flipper_preattack(edict_t *self)
  137. {
  138. gi.sound(self, CHAN_WEAPON, sound_chomp, 1, ATTN_NORM, 0);
  139. }
  140. mframe_t flipper_frames_attack[] = {
  141. { ai_charge, 0, flipper_preattack },
  142. { ai_charge },
  143. { ai_charge },
  144. { ai_charge },
  145. { ai_charge },
  146. { ai_charge },
  147. { ai_charge },
  148. { ai_charge },
  149. { ai_charge },
  150. { ai_charge },
  151. { ai_charge },
  152. { ai_charge },
  153. { ai_charge },
  154. { ai_charge, 0, flipper_bite },
  155. { ai_charge },
  156. { ai_charge },
  157. { ai_charge },
  158. { ai_charge },
  159. { ai_charge, 0, flipper_bite },
  160. { ai_charge }
  161. };
  162. MMOVE_T(flipper_move_attack) = { FRAME_flpbit01, FRAME_flpbit20, flipper_frames_attack, flipper_run };
  163. MONSTERINFO_MELEE(flipper_melee) (edict_t *self) -> void
  164. {
  165. M_SetAnimation(self, &flipper_move_attack);
  166. }
  167. PAIN(flipper_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
  168. {
  169. int n;
  170. if (level.time < self->pain_debounce_time)
  171. return;
  172. self->pain_debounce_time = level.time + 3_sec;
  173. n = brandom();
  174. if (n == 0)
  175. gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
  176. else
  177. gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
  178. if (!M_ShouldReactToPain(self, mod))
  179. return; // no pain anims in nightmare
  180. if (n == 0)
  181. M_SetAnimation(self, &flipper_move_pain1);
  182. else
  183. M_SetAnimation(self, &flipper_move_pain2);
  184. }
  185. MONSTERINFO_SETSKIN(flipper_setskin) (edict_t *self) -> void
  186. {
  187. if (self->health < (self->max_health / 2))
  188. self->s.skinnum = 1;
  189. else
  190. self->s.skinnum = 0;
  191. }
  192. void flipper_dead(edict_t *self)
  193. {
  194. self->mins = { -16, -16, -8 };
  195. self->maxs = { 16, 16, 8 };
  196. monster_dead(self);
  197. }
  198. mframe_t flipper_frames_death[] = {
  199. { ai_move },
  200. { ai_move },
  201. { ai_move },
  202. { ai_move },
  203. { ai_move },
  204. { ai_move },
  205. { ai_move },
  206. { ai_move },
  207. { ai_move },
  208. { ai_move },
  209. { ai_move },
  210. { ai_move },
  211. { ai_move },
  212. { ai_move },
  213. { ai_move },
  214. { ai_move },
  215. { ai_move },
  216. { ai_move },
  217. { ai_move },
  218. { ai_move },
  219. { ai_move },
  220. { ai_move },
  221. { ai_move },
  222. { ai_move },
  223. { ai_move },
  224. { ai_move },
  225. { ai_move },
  226. { ai_move },
  227. { ai_move },
  228. { ai_move },
  229. { ai_move },
  230. { ai_move },
  231. { ai_move },
  232. { ai_move },
  233. { ai_move },
  234. { ai_move },
  235. { ai_move },
  236. { ai_move },
  237. { ai_move },
  238. { ai_move },
  239. { ai_move },
  240. { ai_move },
  241. { ai_move },
  242. { ai_move },
  243. { ai_move },
  244. { ai_move },
  245. { ai_move },
  246. { ai_move },
  247. { ai_move },
  248. { ai_move },
  249. { ai_move },
  250. { ai_move },
  251. { ai_move },
  252. { ai_move },
  253. { ai_move },
  254. { ai_move }
  255. };
  256. MMOVE_T(flipper_move_death) = { FRAME_flpdth01, FRAME_flpdth56, flipper_frames_death, flipper_dead };
  257. MONSTERINFO_SIGHT(flipper_sight) (edict_t *self, edict_t *other) -> void
  258. {
  259. gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
  260. }
  261. DIE(flipper_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
  262. {
  263. // check for gib
  264. if (M_CheckGib(self, mod))
  265. {
  266. gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
  267. ThrowGibs(self, damage, {
  268. { 2, "models/objects/gibs/bone/tris.md2" },
  269. { 2, "models/objects/gibs/sm_meat/tris.md2" },
  270. { "models/objects/gibs/head2/tris.md2", GIB_HEAD }
  271. });
  272. self->deadflag = true;
  273. return;
  274. }
  275. if (self->deadflag)
  276. return;
  277. // regular death
  278. gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
  279. self->deadflag = true;
  280. self->takedamage = true;
  281. self->svflags |= SVF_DEADMONSTER;
  282. M_SetAnimation(self, &flipper_move_death);
  283. }
  284. static void flipper_set_fly_parameters(edict_t *self)
  285. {
  286. self->monsterinfo.fly_thrusters = false;
  287. self->monsterinfo.fly_acceleration = 30.f;
  288. self->monsterinfo.fly_speed = 110.f;
  289. // only melee, so get in close
  290. self->monsterinfo.fly_min_distance = 10.f;
  291. self->monsterinfo.fly_max_distance = 10.f;
  292. }
  293. /*QUAKED monster_flipper (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
  294. */
  295. void SP_monster_flipper(edict_t *self)
  296. {
  297. if ( !M_AllowSpawn( self ) ) {
  298. G_FreeEdict( self );
  299. return;
  300. }
  301. sound_pain1.assign("flipper/flppain1.wav");
  302. sound_pain2.assign("flipper/flppain2.wav");
  303. sound_death.assign("flipper/flpdeth1.wav");
  304. sound_chomp.assign("flipper/flpatck1.wav");
  305. sound_attack.assign("flipper/flpatck2.wav");
  306. sound_idle.assign("flipper/flpidle1.wav");
  307. sound_search.assign("flipper/flpsrch1.wav");
  308. sound_sight.assign("flipper/flpsght1.wav");
  309. self->movetype = MOVETYPE_STEP;
  310. self->solid = SOLID_BBOX;
  311. self->s.modelindex = gi.modelindex("models/monsters/flipper/tris.md2");
  312. self->mins = { -16, -16, -8 };
  313. self->maxs = { 16, 16, 20 };
  314. self->health = 50 * st.health_multiplier;
  315. self->gib_health = -30;
  316. self->mass = 100;
  317. self->pain = flipper_pain;
  318. self->die = flipper_die;
  319. self->monsterinfo.stand = flipper_stand;
  320. self->monsterinfo.walk = flipper_walk;
  321. self->monsterinfo.run = flipper_start_run;
  322. self->monsterinfo.melee = flipper_melee;
  323. self->monsterinfo.sight = flipper_sight;
  324. self->monsterinfo.setskin = flipper_setskin;
  325. gi.linkentity(self);
  326. M_SetAnimation(self, &flipper_move_stand);
  327. self->monsterinfo.scale = MODEL_SCALE;
  328. self->monsterinfo.aiflags |= AI_ALTERNATE_FLY;
  329. flipper_set_fly_parameters(self);
  330. swimmonster_start(self);
  331. }