m_gladiator.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. GLADIATOR
  6. ==============================================================================
  7. */
  8. #include "g_local.h"
  9. #include "m_gladiator.h"
  10. #include "m_flash.h"
  11. static cached_soundindex sound_pain1;
  12. static cached_soundindex sound_pain2;
  13. static cached_soundindex sound_die;
  14. static cached_soundindex sound_die2;
  15. static cached_soundindex sound_gun;
  16. static cached_soundindex sound_gunb;
  17. static cached_soundindex sound_cleaver_swing;
  18. static cached_soundindex sound_cleaver_hit;
  19. static cached_soundindex sound_cleaver_miss;
  20. static cached_soundindex sound_idle;
  21. static cached_soundindex sound_search;
  22. static cached_soundindex sound_sight;
  23. MONSTERINFO_IDLE(gladiator_idle) (edict_t *self) -> void
  24. {
  25. gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
  26. }
  27. MONSTERINFO_SIGHT(gladiator_sight) (edict_t *self, edict_t *other) -> void
  28. {
  29. gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
  30. }
  31. MONSTERINFO_SEARCH(gladiator_search) (edict_t *self) -> void
  32. {
  33. gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
  34. }
  35. void gladiator_cleaver_swing(edict_t *self)
  36. {
  37. gi.sound(self, CHAN_WEAPON, sound_cleaver_swing, 1, ATTN_NORM, 0);
  38. }
  39. mframe_t gladiator_frames_stand[] = {
  40. { ai_stand },
  41. { ai_stand },
  42. { ai_stand },
  43. { ai_stand },
  44. { ai_stand },
  45. { ai_stand },
  46. { ai_stand }
  47. };
  48. MMOVE_T(gladiator_move_stand) = { FRAME_stand1, FRAME_stand7, gladiator_frames_stand, nullptr };
  49. MONSTERINFO_STAND(gladiator_stand) (edict_t *self) -> void
  50. {
  51. M_SetAnimation(self, &gladiator_move_stand);
  52. }
  53. mframe_t gladiator_frames_walk[] = {
  54. { ai_walk, 15 },
  55. { ai_walk, 7 },
  56. { ai_walk, 6 },
  57. { ai_walk, 5 },
  58. { ai_walk, 2, monster_footstep },
  59. { ai_walk },
  60. { ai_walk, 2 },
  61. { ai_walk, 8 },
  62. { ai_walk, 12 },
  63. { ai_walk, 8 },
  64. { ai_walk, 5 },
  65. { ai_walk, 5 },
  66. { ai_walk, 2, monster_footstep },
  67. { ai_walk, 2 },
  68. { ai_walk, 1 },
  69. { ai_walk, 8 }
  70. };
  71. MMOVE_T(gladiator_move_walk) = { FRAME_walk1, FRAME_walk16, gladiator_frames_walk, nullptr };
  72. MONSTERINFO_WALK(gladiator_walk) (edict_t *self) -> void
  73. {
  74. M_SetAnimation(self, &gladiator_move_walk);
  75. }
  76. mframe_t gladiator_frames_run[] = {
  77. { ai_run, 23 },
  78. { ai_run, 14 },
  79. { ai_run, 14, monster_footstep },
  80. { ai_run, 21 },
  81. { ai_run, 12 },
  82. { ai_run, 13, monster_footstep }
  83. };
  84. MMOVE_T(gladiator_move_run) = { FRAME_run1, FRAME_run6, gladiator_frames_run, nullptr };
  85. MONSTERINFO_RUN(gladiator_run) (edict_t *self) -> void
  86. {
  87. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  88. M_SetAnimation(self, &gladiator_move_stand);
  89. else
  90. M_SetAnimation(self, &gladiator_move_run);
  91. }
  92. void GladiatorMelee(edict_t *self)
  93. {
  94. vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 };
  95. if (fire_hit(self, aim, irandom(20, 25), 300))
  96. gi.sound(self, CHAN_AUTO, sound_cleaver_hit, 1, ATTN_NORM, 0);
  97. else
  98. {
  99. gi.sound(self, CHAN_AUTO, sound_cleaver_miss, 1, ATTN_NORM, 0);
  100. self->monsterinfo.melee_debounce_time = level.time + 1.5_sec;
  101. }
  102. }
  103. mframe_t gladiator_frames_attack_melee[] = {
  104. { ai_charge },
  105. { ai_charge },
  106. { ai_charge, 0, gladiator_cleaver_swing },
  107. { ai_charge },
  108. { ai_charge, 0, GladiatorMelee },
  109. { ai_charge },
  110. { ai_charge },
  111. { ai_charge },
  112. { ai_charge, 0, gladiator_cleaver_swing },
  113. { ai_charge },
  114. { ai_charge },
  115. { ai_charge, 0, GladiatorMelee },
  116. { ai_charge },
  117. { ai_charge }
  118. };
  119. MMOVE_T(gladiator_move_attack_melee) = { FRAME_melee3, FRAME_melee16, gladiator_frames_attack_melee, gladiator_run };
  120. MONSTERINFO_MELEE(gladiator_melee) (edict_t *self) -> void
  121. {
  122. M_SetAnimation(self, &gladiator_move_attack_melee);
  123. }
  124. void GladiatorGun(edict_t *self)
  125. {
  126. vec3_t start;
  127. vec3_t dir;
  128. vec3_t forward, right;
  129. AngleVectors(self->s.angles, forward, right, nullptr);
  130. start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1], forward, right);
  131. // calc direction to where we targted
  132. dir = self->pos1 - start;
  133. dir.normalize();
  134. monster_fire_railgun(self, start, dir, 50, 100, MZ2_GLADIATOR_RAILGUN_1);
  135. }
  136. mframe_t gladiator_frames_attack_gun[] = {
  137. { ai_charge },
  138. { ai_charge },
  139. { ai_charge },
  140. { ai_charge, 0, GladiatorGun },
  141. { ai_charge },
  142. { ai_charge },
  143. { ai_charge },
  144. { ai_charge, 0, monster_footstep },
  145. { ai_charge }
  146. };
  147. MMOVE_T(gladiator_move_attack_gun) = { FRAME_attack1, FRAME_attack9, gladiator_frames_attack_gun, gladiator_run };
  148. // RAFAEL
  149. void gladbGun(edict_t *self)
  150. {
  151. vec3_t start;
  152. vec3_t dir;
  153. vec3_t forward, right;
  154. AngleVectors(self->s.angles, forward, right, nullptr);
  155. start = M_ProjectFlashSource(self, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1], forward, right);
  156. // calc direction to where we targeted
  157. dir = self->pos1 - start;
  158. dir.normalize();
  159. int damage = 35;
  160. int radius_damage = 45;
  161. if (self->s.frame > FRAME_attack3)
  162. {
  163. damage /= 2;
  164. radius_damage /= 2;
  165. }
  166. fire_plasma(self, start, dir, damage, 725, radius_damage, radius_damage);
  167. // save for aiming the shot
  168. self->pos1 = self->enemy->s.origin;
  169. self->pos1[2] += self->enemy->viewheight;
  170. }
  171. void gladbGun_check(edict_t *self)
  172. {
  173. if (skill->integer == 3)
  174. gladbGun(self);
  175. }
  176. mframe_t gladb_frames_attack_gun[] = {
  177. { ai_charge },
  178. { ai_charge },
  179. { ai_charge, 0, gladbGun },
  180. { ai_charge },
  181. { ai_charge },
  182. { ai_charge, 0, gladbGun },
  183. { ai_charge },
  184. { ai_charge },
  185. { ai_charge, 0, gladbGun_check }
  186. };
  187. MMOVE_T(gladb_move_attack_gun) = { FRAME_attack1, FRAME_attack9, gladb_frames_attack_gun, gladiator_run };
  188. // RAFAEL
  189. MONSTERINFO_ATTACK(gladiator_attack) (edict_t *self) -> void
  190. {
  191. float range;
  192. vec3_t v;
  193. // a small safe zone
  194. v = self->s.origin - self->enemy->s.origin;
  195. range = v.length();
  196. if (range <= (MELEE_DISTANCE + 32) && self->monsterinfo.melee_debounce_time <= level.time)
  197. return;
  198. else if (!M_CheckClearShot(self, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1]))
  199. return;
  200. // charge up the railgun
  201. self->pos1 = self->enemy->s.origin; // save for aiming the shot
  202. self->pos1[2] += self->enemy->viewheight;
  203. // RAFAEL
  204. if (self->style == 1)
  205. {
  206. gi.sound(self, CHAN_WEAPON, sound_gunb, 1, ATTN_NORM, 0);
  207. M_SetAnimation(self, &gladb_move_attack_gun);
  208. }
  209. else
  210. {
  211. // RAFAEL
  212. gi.sound(self, CHAN_WEAPON, sound_gun, 1, ATTN_NORM, 0);
  213. M_SetAnimation(self, &gladiator_move_attack_gun);
  214. // RAFAEL
  215. }
  216. // RAFAEL
  217. }
  218. mframe_t gladiator_frames_pain[] = {
  219. { ai_move },
  220. { ai_move },
  221. { ai_move },
  222. { ai_move }
  223. };
  224. MMOVE_T(gladiator_move_pain) = { FRAME_pain2, FRAME_pain5, gladiator_frames_pain, gladiator_run };
  225. mframe_t gladiator_frames_pain_air[] = {
  226. { ai_move },
  227. { ai_move },
  228. { ai_move },
  229. { ai_move },
  230. { ai_move }
  231. };
  232. MMOVE_T(gladiator_move_pain_air) = { FRAME_painup2, FRAME_painup6, gladiator_frames_pain_air, gladiator_run };
  233. PAIN(gladiator_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
  234. {
  235. if (level.time < self->pain_debounce_time)
  236. {
  237. if ((self->velocity[2] > 100) && (self->monsterinfo.active_move == &gladiator_move_pain))
  238. M_SetAnimation(self, &gladiator_move_pain_air);
  239. return;
  240. }
  241. self->pain_debounce_time = level.time + 3_sec;
  242. if (frandom() < 0.5f)
  243. gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
  244. else
  245. gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
  246. if (!M_ShouldReactToPain(self, mod))
  247. return; // no pain anims in nightmare
  248. if (self->velocity[2] > 100)
  249. M_SetAnimation(self, &gladiator_move_pain_air);
  250. else
  251. M_SetAnimation(self, &gladiator_move_pain);
  252. }
  253. MONSTERINFO_SETSKIN(gladiator_setskin) (edict_t *self) -> void
  254. {
  255. if (self->health < (self->max_health / 2))
  256. self->s.skinnum |= 1;
  257. else
  258. self->s.skinnum &= ~1;
  259. }
  260. void gladiator_dead(edict_t *self)
  261. {
  262. self->mins = { -16, -16, -24 };
  263. self->maxs = { 16, 16, -8 };
  264. monster_dead(self);
  265. }
  266. static void gladiator_shrink(edict_t *self)
  267. {
  268. self->maxs[2] = 0;
  269. self->svflags |= SVF_DEADMONSTER;
  270. gi.linkentity(self);
  271. }
  272. mframe_t gladiator_frames_death[] = {
  273. { ai_move },
  274. { ai_move },
  275. { ai_move, 0, gladiator_shrink },
  276. { ai_move, 0, monster_footstep },
  277. { ai_move },
  278. { ai_move },
  279. { ai_move },
  280. { ai_move },
  281. { ai_move },
  282. { ai_move },
  283. { ai_move },
  284. { ai_move },
  285. { ai_move },
  286. { ai_move },
  287. { ai_move },
  288. { ai_move },
  289. { ai_move },
  290. { ai_move },
  291. { ai_move },
  292. { ai_move },
  293. { ai_move }
  294. };
  295. MMOVE_T(gladiator_move_death) = { FRAME_death2, FRAME_death22, gladiator_frames_death, gladiator_dead };
  296. DIE(gladiator_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
  297. {
  298. // check for gib
  299. if (M_CheckGib(self, mod))
  300. {
  301. gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
  302. self->s.skinnum /= 2;
  303. ThrowGibs(self, damage, {
  304. { 2, "models/objects/gibs/bone/tris.md2" },
  305. { 2, "models/objects/gibs/sm_meat/tris.md2" },
  306. { 2, "models/monsters/gladiatr/gibs/thigh.md2", GIB_SKINNED },
  307. { "models/monsters/gladiatr/gibs/larm.md2", GIB_SKINNED | GIB_UPRIGHT },
  308. { "models/monsters/gladiatr/gibs/rarm.md2", GIB_SKINNED | GIB_UPRIGHT },
  309. { "models/monsters/gladiatr/gibs/chest.md2", GIB_SKINNED },
  310. { "models/monsters/gladiatr/gibs/head.md2", GIB_SKINNED | GIB_HEAD }
  311. });
  312. self->deadflag = true;
  313. return;
  314. }
  315. if (self->deadflag)
  316. return;
  317. // regular death
  318. gi.sound(self, CHAN_BODY, sound_die, 1, ATTN_NORM, 0);
  319. if (brandom())
  320. gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
  321. self->deadflag = true;
  322. self->takedamage = true;
  323. M_SetAnimation(self, &gladiator_move_death);
  324. }
  325. //===========
  326. // PGM
  327. MONSTERINFO_BLOCKED(gladiator_blocked) (edict_t *self, float dist) -> bool
  328. {
  329. if (blocked_checkplat(self, dist))
  330. return true;
  331. return false;
  332. }
  333. // PGM
  334. //===========
  335. /*QUAKED monster_gladiator (1 .5 0) (-32 -32 -24) (32 32 64) Ambush Trigger_Spawn Sight
  336. */
  337. void SP_monster_gladiator(edict_t *self)
  338. {
  339. if ( !M_AllowSpawn( self ) ) {
  340. G_FreeEdict( self );
  341. return;
  342. }
  343. sound_pain1.assign("gladiator/pain.wav");
  344. sound_pain2.assign("gladiator/gldpain2.wav");
  345. sound_die.assign("gladiator/glddeth2.wav");
  346. sound_die2.assign("gladiator/death.wav");
  347. sound_cleaver_swing.assign("gladiator/melee1.wav");
  348. sound_cleaver_hit.assign("gladiator/melee2.wav");
  349. sound_cleaver_miss.assign("gladiator/melee3.wav");
  350. sound_idle.assign("gladiator/gldidle1.wav");
  351. sound_search.assign("gladiator/gldsrch1.wav");
  352. sound_sight.assign("gladiator/sight.wav");
  353. self->movetype = MOVETYPE_STEP;
  354. self->solid = SOLID_BBOX;
  355. self->s.modelindex = gi.modelindex("models/monsters/gladiatr/tris.md2");
  356. gi.modelindex("models/monsters/gladiatr/gibs/chest.md2");
  357. gi.modelindex("models/monsters/gladiatr/gibs/head.md2");
  358. gi.modelindex("models/monsters/gladiatr/gibs/larm.md2");
  359. gi.modelindex("models/monsters/gladiatr/gibs/rarm.md2");
  360. gi.modelindex("models/monsters/gladiatr/gibs/thigh.md2");
  361. // RAFAEL
  362. if (strcmp(self->classname, "monster_gladb") == 0)
  363. {
  364. sound_gunb.assign("weapons/plasshot.wav");
  365. self->health = 250 * st.health_multiplier;
  366. self->mass = 350;
  367. if (!st.was_key_specified("power_armor_type"))
  368. self->monsterinfo.power_armor_type = IT_ITEM_POWER_SHIELD;
  369. if (!st.was_key_specified("power_armor_power"))
  370. self->monsterinfo.power_armor_power = 250;
  371. self->s.skinnum = 2;
  372. self->style = 1;
  373. self->monsterinfo.weapon_sound = gi.soundindex("weapons/phaloop.wav");
  374. }
  375. else
  376. {
  377. // RAFAEL
  378. sound_gun.assign("gladiator/railgun.wav");
  379. self->health = 400 * st.health_multiplier;
  380. self->mass = 400;
  381. // RAFAEL
  382. self->monsterinfo.weapon_sound = gi.soundindex("weapons/rg_hum.wav");
  383. }
  384. // RAFAEL
  385. self->gib_health = -175;
  386. self->mins = { -32, -32, -24 };
  387. self->maxs = { 32, 32, 42 };
  388. self->pain = gladiator_pain;
  389. self->die = gladiator_die;
  390. self->monsterinfo.stand = gladiator_stand;
  391. self->monsterinfo.walk = gladiator_walk;
  392. self->monsterinfo.run = gladiator_run;
  393. self->monsterinfo.dodge = nullptr;
  394. self->monsterinfo.attack = gladiator_attack;
  395. self->monsterinfo.melee = gladiator_melee;
  396. self->monsterinfo.sight = gladiator_sight;
  397. self->monsterinfo.idle = gladiator_idle;
  398. self->monsterinfo.search = gladiator_search;
  399. self->monsterinfo.blocked = gladiator_blocked; // PGM
  400. self->monsterinfo.setskin = gladiator_setskin;
  401. gi.linkentity(self);
  402. M_SetAnimation(self, &gladiator_move_stand);
  403. self->monsterinfo.scale = MODEL_SCALE;
  404. walkmonster_start(self);
  405. }
  406. //
  407. // monster_gladb
  408. // RAFAEL
  409. //
  410. /*QUAKED monster_gladb (1 .5 0) (-32 -32 -24) (32 32 64) Ambush Trigger_Spawn Sight
  411. */
  412. void SP_monster_gladb(edict_t *self)
  413. {
  414. SP_monster_gladiator(self);
  415. }