m_actor.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. // g_actor.c
  4. #include "g_local.h"
  5. #include "m_actor.h"
  6. #include "m_flash.h"
  7. constexpr const char *actor_names[] = {
  8. "Hellrot",
  9. "Tokay",
  10. "Killme",
  11. "Disruptor",
  12. "Adrianator",
  13. "Rambear",
  14. "Titus",
  15. "Bitterman"
  16. };
  17. mframe_t actor_frames_stand[] = {
  18. { ai_stand },
  19. { ai_stand },
  20. { ai_stand },
  21. { ai_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. { ai_stand },
  36. { ai_stand },
  37. { ai_stand },
  38. { ai_stand },
  39. { ai_stand },
  40. { ai_stand },
  41. { ai_stand },
  42. { ai_stand },
  43. { ai_stand },
  44. { ai_stand },
  45. { ai_stand },
  46. { ai_stand },
  47. { ai_stand },
  48. { ai_stand },
  49. { ai_stand },
  50. { ai_stand },
  51. { ai_stand },
  52. { ai_stand },
  53. { ai_stand },
  54. { ai_stand },
  55. { ai_stand },
  56. { ai_stand },
  57. { ai_stand }
  58. };
  59. MMOVE_T(actor_move_stand) = { FRAME_stand101, FRAME_stand140, actor_frames_stand, nullptr };
  60. MONSTERINFO_STAND(actor_stand) (edict_t *self) -> void
  61. {
  62. M_SetAnimation(self, &actor_move_stand);
  63. // randomize on startup
  64. if (level.time < 1_sec)
  65. self->s.frame = irandom(self->monsterinfo.active_move->firstframe, self->monsterinfo.active_move->lastframe + 1);
  66. }
  67. mframe_t actor_frames_walk[] = {
  68. { ai_walk },
  69. { ai_walk, 6 },
  70. { ai_walk, 10 },
  71. { ai_walk, 3 },
  72. { ai_walk, 2 },
  73. { ai_walk, 7 },
  74. { ai_walk, 10 },
  75. { ai_walk, 1 }
  76. };
  77. MMOVE_T(actor_move_walk) = { FRAME_walk01, FRAME_walk08, actor_frames_walk, nullptr };
  78. MONSTERINFO_WALK(actor_walk) (edict_t *self) -> void
  79. {
  80. M_SetAnimation(self, &actor_move_walk);
  81. }
  82. mframe_t actor_frames_run[] = {
  83. { ai_run, 4 },
  84. { ai_run, 15 },
  85. { ai_run, 15 },
  86. { ai_run, 8 },
  87. { ai_run, 20 },
  88. { ai_run, 15 }
  89. };
  90. MMOVE_T(actor_move_run) = { FRAME_run02, FRAME_run07, actor_frames_run, nullptr };
  91. MONSTERINFO_RUN(actor_run) (edict_t *self) -> void
  92. {
  93. if ((level.time < self->pain_debounce_time) && (!self->enemy))
  94. {
  95. if (self->movetarget)
  96. actor_walk(self);
  97. else
  98. actor_stand(self);
  99. return;
  100. }
  101. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  102. {
  103. actor_stand(self);
  104. return;
  105. }
  106. M_SetAnimation(self, &actor_move_run);
  107. }
  108. mframe_t actor_frames_pain1[] = {
  109. { ai_move, -5 },
  110. { ai_move, 4 },
  111. { ai_move, 1 }
  112. };
  113. MMOVE_T(actor_move_pain1) = { FRAME_pain101, FRAME_pain103, actor_frames_pain1, actor_run };
  114. mframe_t actor_frames_pain2[] = {
  115. { ai_move, -4 },
  116. { ai_move, 4 },
  117. { ai_move }
  118. };
  119. MMOVE_T(actor_move_pain2) = { FRAME_pain201, FRAME_pain203, actor_frames_pain2, actor_run };
  120. mframe_t actor_frames_pain3[] = {
  121. { ai_move, -1 },
  122. { ai_move, 1 },
  123. { ai_move, 0 }
  124. };
  125. MMOVE_T(actor_move_pain3) = { FRAME_pain301, FRAME_pain303, actor_frames_pain3, actor_run };
  126. mframe_t actor_frames_flipoff[] = {
  127. { ai_turn },
  128. { ai_turn },
  129. { ai_turn },
  130. { ai_turn },
  131. { ai_turn },
  132. { ai_turn },
  133. { ai_turn },
  134. { ai_turn },
  135. { ai_turn },
  136. { ai_turn },
  137. { ai_turn },
  138. { ai_turn },
  139. { ai_turn },
  140. { ai_turn }
  141. };
  142. MMOVE_T(actor_move_flipoff) = { FRAME_flip01, FRAME_flip14, actor_frames_flipoff, actor_run };
  143. mframe_t actor_frames_taunt[] = {
  144. { ai_turn },
  145. { ai_turn },
  146. { ai_turn },
  147. { ai_turn },
  148. { ai_turn },
  149. { ai_turn },
  150. { ai_turn },
  151. { ai_turn },
  152. { ai_turn },
  153. { ai_turn },
  154. { ai_turn },
  155. { ai_turn },
  156. { ai_turn },
  157. { ai_turn },
  158. { ai_turn },
  159. { ai_turn },
  160. { ai_turn }
  161. };
  162. MMOVE_T(actor_move_taunt) = { FRAME_taunt01, FRAME_taunt17, actor_frames_taunt, actor_run };
  163. const char *messages[] = {
  164. "Watch it",
  165. "#$@*&",
  166. "Idiot",
  167. "Check your targets"
  168. };
  169. PAIN(actor_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
  170. {
  171. int n;
  172. if (level.time < self->pain_debounce_time)
  173. return;
  174. self->pain_debounce_time = level.time + 3_sec;
  175. // gi.sound (self, CHAN_VOICE, actor.sound_pain, 1, ATTN_NORM, 0);
  176. if ((other->client) && (frandom() < 0.4f))
  177. {
  178. vec3_t v;
  179. const char *name;
  180. v = other->s.origin - self->s.origin;
  181. self->ideal_yaw = vectoyaw(v);
  182. if (frandom() < 0.5f)
  183. M_SetAnimation(self, &actor_move_flipoff);
  184. else
  185. M_SetAnimation(self, &actor_move_taunt);
  186. name = actor_names[(self - g_edicts) % q_countof(actor_names)];
  187. gi.LocClient_Print(other, PRINT_CHAT, "{}: {}!\n", name, random_element(messages));
  188. return;
  189. }
  190. n = irandom(3);
  191. if (n == 0)
  192. M_SetAnimation(self, &actor_move_pain1);
  193. else if (n == 1)
  194. M_SetAnimation(self, &actor_move_pain2);
  195. else
  196. M_SetAnimation(self, &actor_move_pain3);
  197. }
  198. MONSTERINFO_SETSKIN(actor_setskin) (edict_t *self) -> void
  199. {
  200. if (self->health < (self->max_health / 2))
  201. self->s.skinnum = 1;
  202. else
  203. self->s.skinnum = 0;
  204. }
  205. void actorMachineGun(edict_t *self)
  206. {
  207. vec3_t start, target;
  208. vec3_t forward, right;
  209. AngleVectors(self->s.angles, forward, right, nullptr);
  210. start = G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_ACTOR_MACHINEGUN_1], forward, right);
  211. if (self->enemy)
  212. {
  213. if (self->enemy->health > 0)
  214. {
  215. target = self->enemy->s.origin + (self->enemy->velocity * -0.2f);
  216. target[2] += self->enemy->viewheight;
  217. }
  218. else
  219. {
  220. target = self->enemy->absmin;
  221. target[2] += (self->enemy->size[2] / 2) + 1;
  222. }
  223. forward = target - start;
  224. forward.normalize();
  225. }
  226. else
  227. {
  228. AngleVectors(self->s.angles, forward, nullptr, nullptr);
  229. }
  230. monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1);
  231. }
  232. void actor_dead(edict_t *self)
  233. {
  234. self->mins = { -16, -16, -24 };
  235. self->maxs = { 16, 16, -8 };
  236. self->movetype = MOVETYPE_TOSS;
  237. self->svflags |= SVF_DEADMONSTER;
  238. self->nextthink = 0_ms;
  239. gi.linkentity(self);
  240. }
  241. mframe_t actor_frames_death1[] = {
  242. { ai_move },
  243. { ai_move },
  244. { ai_move, -13 },
  245. { ai_move, 14 },
  246. { ai_move, 3 },
  247. { ai_move, -2 },
  248. { ai_move, 1 }
  249. };
  250. MMOVE_T(actor_move_death1) = { FRAME_death101, FRAME_death107, actor_frames_death1, actor_dead };
  251. mframe_t actor_frames_death2[] = {
  252. { ai_move },
  253. { ai_move, 7 },
  254. { ai_move, -6 },
  255. { ai_move, -5 },
  256. { ai_move, 1 },
  257. { ai_move },
  258. { ai_move, -1 },
  259. { ai_move, -2 },
  260. { ai_move, -1 },
  261. { ai_move, -9 },
  262. { ai_move, -13 },
  263. { ai_move, -13 },
  264. { ai_move }
  265. };
  266. MMOVE_T(actor_move_death2) = { FRAME_death201, FRAME_death213, actor_frames_death2, actor_dead };
  267. DIE(actor_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 (self->health <= -80)
  271. {
  272. // gi.sound (self, CHAN_VOICE, actor.sound_gib, 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, actor.sound_die, 1, ATTN_NORM, 0);
  285. self->deadflag = true;
  286. self->takedamage = true;
  287. if (brandom())
  288. M_SetAnimation(self, &actor_move_death1);
  289. else
  290. M_SetAnimation(self, &actor_move_death2);
  291. }
  292. void actor_fire(edict_t *self)
  293. {
  294. actorMachineGun(self);
  295. if (level.time >= self->monsterinfo.fire_wait)
  296. self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
  297. else
  298. self->monsterinfo.aiflags |= AI_HOLD_FRAME;
  299. }
  300. mframe_t actor_frames_attack[] = {
  301. { ai_charge, -2, actor_fire },
  302. { ai_charge, -2 },
  303. { ai_charge, 3 },
  304. { ai_charge, 2 }
  305. };
  306. MMOVE_T(actor_move_attack) = { FRAME_attak01, FRAME_attak04, actor_frames_attack, actor_run };
  307. MONSTERINFO_ATTACK(actor_attack) (edict_t *self) -> void
  308. {
  309. M_SetAnimation(self, &actor_move_attack);
  310. self->monsterinfo.fire_wait = level.time + random_time(1_sec, 2.6_sec);
  311. }
  312. USE(actor_use) (edict_t *self, edict_t *other, edict_t *activator) -> void
  313. {
  314. vec3_t v;
  315. self->goalentity = self->movetarget = G_PickTarget(self->target);
  316. if ((!self->movetarget) || (strcmp(self->movetarget->classname, "target_actor") != 0))
  317. {
  318. gi.Com_PrintFmt("{}: bad target {}\n", *self, self->target);
  319. self->target = nullptr;
  320. self->monsterinfo.pausetime = HOLD_FOREVER;
  321. self->monsterinfo.stand(self);
  322. return;
  323. }
  324. v = self->goalentity->s.origin - self->s.origin;
  325. self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
  326. self->monsterinfo.walk(self);
  327. self->target = nullptr;
  328. }
  329. /*QUAKED misc_actor (1 .5 0) (-16 -16 -24) (16 16 32)
  330. */
  331. void SP_misc_actor(edict_t *self)
  332. {
  333. if ( !M_AllowSpawn( self ) ) {
  334. G_FreeEdict(self);
  335. return;
  336. }
  337. if (!self->targetname)
  338. {
  339. gi.Com_PrintFmt("{}: no targetname\n", *self);
  340. G_FreeEdict(self);
  341. return;
  342. }
  343. if (!self->target)
  344. {
  345. gi.Com_PrintFmt("{}: no target\n", *self);
  346. G_FreeEdict(self);
  347. return;
  348. }
  349. self->movetype = MOVETYPE_STEP;
  350. self->solid = SOLID_BBOX;
  351. self->s.modelindex = gi.modelindex("players/male/tris.md2");
  352. self->mins = { -16, -16, -24 };
  353. self->maxs = { 16, 16, 32 };
  354. if (!self->health)
  355. self->health = 100;
  356. self->mass = 200;
  357. self->pain = actor_pain;
  358. self->die = actor_die;
  359. self->monsterinfo.stand = actor_stand;
  360. self->monsterinfo.walk = actor_walk;
  361. self->monsterinfo.run = actor_run;
  362. self->monsterinfo.attack = actor_attack;
  363. self->monsterinfo.melee = nullptr;
  364. self->monsterinfo.sight = nullptr;
  365. self->monsterinfo.setskin = actor_setskin;
  366. self->monsterinfo.aiflags |= AI_GOOD_GUY;
  367. gi.linkentity(self);
  368. M_SetAnimation(self, &actor_move_stand);
  369. self->monsterinfo.scale = MODEL_SCALE;
  370. walkmonster_start(self);
  371. // actors always start in a dormant state, they *must* be used to get going
  372. self->use = actor_use;
  373. }
  374. /*QUAKED target_actor (.5 .3 0) (-8 -8 -8) (8 8 8) JUMP SHOOT ATTACK x HOLD BRUTAL
  375. JUMP jump in set direction upon reaching this target
  376. SHOOT take a single shot at the pathtarget
  377. ATTACK attack pathtarget until it or actor is dead
  378. "target" next target_actor
  379. "pathtarget" target of any action to be taken at this point
  380. "wait" amount of time actor should pause at this point
  381. "message" actor will "say" this to the player
  382. for JUMP only:
  383. "speed" speed thrown forward (default 200)
  384. "height" speed thrown upwards (default 200)
  385. */
  386. constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_JUMP = 1_spawnflag;
  387. constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_SHOOT = 2_spawnflag;
  388. constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_ATTACK = 4_spawnflag;
  389. constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_HOLD = 16_spawnflag;
  390. constexpr spawnflags_t SPAWNFLAG_TARGET_ACTOR_BRUTAL = 32_spawnflag;
  391. TOUCH(target_actor_touch) (edict_t *self, edict_t *other, const trace_t &tr, bool other_touching_self) -> void
  392. {
  393. vec3_t v;
  394. if (other->movetarget != self)
  395. return;
  396. if (other->enemy)
  397. return;
  398. other->goalentity = other->movetarget = nullptr;
  399. if (self->message)
  400. {
  401. edict_t *ent;
  402. for (uint32_t n = 1; n <= game.maxclients; n++)
  403. {
  404. ent = &g_edicts[n];
  405. if (!ent->inuse)
  406. continue;
  407. gi.LocClient_Print(ent, PRINT_CHAT, "{}: {}\n", actor_names[(other - g_edicts) % q_countof(actor_names)], self->message);
  408. }
  409. }
  410. if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_JUMP)) // jump
  411. {
  412. other->velocity[0] = self->movedir[0] * self->speed;
  413. other->velocity[1] = self->movedir[1] * self->speed;
  414. if (other->groundentity)
  415. {
  416. other->groundentity = nullptr;
  417. other->velocity[2] = self->movedir[2];
  418. gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0);
  419. }
  420. }
  421. if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_SHOOT)) // shoot
  422. {
  423. }
  424. else if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_ATTACK)) // attack
  425. {
  426. other->enemy = G_PickTarget(self->pathtarget);
  427. if (other->enemy)
  428. {
  429. other->goalentity = other->enemy;
  430. if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_BRUTAL))
  431. other->monsterinfo.aiflags |= AI_BRUTAL;
  432. if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_HOLD))
  433. {
  434. other->monsterinfo.aiflags |= AI_STAND_GROUND;
  435. actor_stand(other);
  436. }
  437. else
  438. {
  439. actor_run(other);
  440. }
  441. }
  442. }
  443. if (!self->spawnflags.has((SPAWNFLAG_TARGET_ACTOR_ATTACK | SPAWNFLAG_TARGET_ACTOR_SHOOT)) && (self->pathtarget))
  444. {
  445. const char *savetarget;
  446. savetarget = self->target;
  447. self->target = self->pathtarget;
  448. G_UseTargets(self, other);
  449. self->target = savetarget;
  450. }
  451. other->movetarget = G_PickTarget(self->target);
  452. if (!other->goalentity)
  453. other->goalentity = other->movetarget;
  454. if (!other->movetarget && !other->enemy)
  455. {
  456. other->monsterinfo.pausetime = HOLD_FOREVER;
  457. other->monsterinfo.stand(other);
  458. }
  459. else if (other->movetarget == other->goalentity)
  460. {
  461. v = other->movetarget->s.origin - other->s.origin;
  462. other->ideal_yaw = vectoyaw(v);
  463. }
  464. }
  465. void SP_target_actor(edict_t *self)
  466. {
  467. if (!self->targetname)
  468. gi.Com_PrintFmt("{}: no targetname\n", *self);
  469. self->solid = SOLID_TRIGGER;
  470. self->touch = target_actor_touch;
  471. self->mins = { -8, -8, -8 };
  472. self->maxs = { 8, 8, 8 };
  473. self->svflags = SVF_NOCLIENT;
  474. if (self->spawnflags.has(SPAWNFLAG_TARGET_ACTOR_JUMP))
  475. {
  476. if (!self->speed)
  477. self->speed = 200;
  478. if (!st.height)
  479. st.height = 200;
  480. if (self->s.angles[YAW] == 0)
  481. self->s.angles[YAW] = 360;
  482. G_SetMovedir(self->s.angles, self->movedir);
  483. self->movedir[2] = (float) st.height;
  484. }
  485. gi.linkentity(self);
  486. }