g_ai.cpp 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. // g_ai.c
  4. #include "g_local.h"
  5. bool FindTarget(edict_t *self);
  6. bool ai_checkattack(edict_t *self, float dist);
  7. bool enemy_vis;
  8. bool enemy_infront;
  9. float enemy_yaw;
  10. // ROGUE
  11. constexpr float MAX_SIDESTEP = 8.0f;
  12. // ROGUE
  13. //============================================================================
  14. /*
  15. =================
  16. AI_GetSightClient
  17. For a given monster, check active players to see
  18. who we can see. We don't care who we see, as long
  19. as it's something we can shoot.
  20. =================
  21. */
  22. edict_t *AI_GetSightClient(edict_t *self)
  23. {
  24. if (level.intermissiontime)
  25. return nullptr;
  26. edict_t **visible_players = (edict_t **) alloca(sizeof(edict_t *) * game.maxclients);
  27. size_t num_visible = 0;
  28. for (auto player : active_players())
  29. {
  30. if (player->health <= 0 || player->deadflag || !player->solid)
  31. continue;
  32. else if (player->flags & (FL_NOTARGET | FL_DISGUISED))
  33. continue;
  34. // if we're touching them, allow to pass through
  35. if (!boxes_intersect(self->absmin, self->absmax, player->absmin, player->absmax))
  36. {
  37. if ((!(self->monsterinfo.aiflags & AI_THIRD_EYE) && !infront(self, player)) || !visible(self, player))
  38. continue;
  39. }
  40. visible_players[num_visible++] = player; // got one
  41. }
  42. if (!num_visible)
  43. return nullptr;
  44. return visible_players[irandom(num_visible)];
  45. }
  46. //============================================================================
  47. /*
  48. =============
  49. ai_move
  50. Move the specified distance at current facing.
  51. This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
  52. ==============
  53. */
  54. void ai_move(edict_t *self, float dist)
  55. {
  56. M_walkmove(self, self->s.angles[YAW], dist);
  57. }
  58. /*
  59. =============
  60. ai_stand
  61. Used for standing around and looking for players
  62. Distance is for slight position adjustments needed by the animations
  63. ==============
  64. */
  65. void ai_stand(edict_t *self, float dist)
  66. {
  67. vec3_t v;
  68. // ROGUE
  69. bool retval;
  70. // ROGUE
  71. if (dist || (self->monsterinfo.aiflags & AI_ALTERNATE_FLY))
  72. M_walkmove(self, self->s.angles[YAW], dist);
  73. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  74. {
  75. // [Paril-KEX] check if we've been pushed out of our point_combat
  76. if (!(self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) &&
  77. self->movetarget && self->movetarget->classname && !strcmp(self->movetarget->classname, "point_combat"))
  78. {
  79. if (!boxes_intersect(self->absmin, self->absmax, self->movetarget->absmin, self->movetarget->absmax))
  80. {
  81. self->monsterinfo.aiflags &= ~AI_STAND_GROUND;
  82. self->monsterinfo.aiflags |= AI_COMBAT_POINT;
  83. self->goalentity = self->movetarget;
  84. self->monsterinfo.run(self);
  85. return;
  86. }
  87. }
  88. if (self->enemy && !(self->enemy->classname && !strcmp(self->enemy->classname, "player_noise")))
  89. {
  90. v = self->enemy->s.origin - self->s.origin;
  91. self->ideal_yaw = vectoyaw(v);
  92. if (!FacingIdeal(self) && (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND))
  93. {
  94. self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  95. self->monsterinfo.run(self);
  96. }
  97. // ROGUE
  98. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  99. // ROGUE
  100. M_ChangeYaw(self);
  101. // find out if we're going to be shooting
  102. retval = ai_checkattack(self, 0);
  103. // record sightings of player
  104. if ((self->enemy) && (self->enemy->inuse))
  105. {
  106. if (visible(self, self->enemy))
  107. {
  108. self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  109. self->monsterinfo.last_sighting = self->monsterinfo.saved_goal = self->enemy->s.origin;
  110. self->monsterinfo.blind_fire_target = self->monsterinfo.last_sighting + (self->enemy->velocity * -0.1f);
  111. self->monsterinfo.trail_time = level.time;
  112. self->monsterinfo.blind_fire_delay = 0_ms;
  113. }
  114. else
  115. {
  116. if (FindTarget(self))
  117. return;
  118. self->monsterinfo.aiflags |= AI_LOST_SIGHT;
  119. }
  120. // Paril: fixes rare cases of a stand ground monster being stuck
  121. // aiming at a sound target that they can still see
  122. if ((self->monsterinfo.aiflags & AI_SOUND_TARGET) && !retval)
  123. {
  124. if (FindTarget(self))
  125. return;
  126. }
  127. }
  128. // check retval to make sure we're not blindfiring
  129. else if (!retval)
  130. {
  131. FindTarget(self);
  132. return;
  133. }
  134. // ROGUE
  135. }
  136. else
  137. FindTarget(self);
  138. return;
  139. }
  140. // Paril: this fixes a bug somewhere else that sometimes causes
  141. // a monster to be given an enemy without ever calling HuntTarget.
  142. if (self->enemy && !(self->monsterinfo.aiflags & AI_SOUND_TARGET))
  143. {
  144. HuntTarget(self);
  145. return;
  146. }
  147. if (FindTarget(self))
  148. return;
  149. if (level.time > self->monsterinfo.pausetime)
  150. {
  151. self->monsterinfo.walk(self);
  152. return;
  153. }
  154. if (!(self->spawnflags & SPAWNFLAG_MONSTER_AMBUSH) && (self->monsterinfo.idle) &&
  155. (level.time > self->monsterinfo.idle_time))
  156. {
  157. if (self->monsterinfo.idle_time)
  158. {
  159. self->monsterinfo.idle(self);
  160. self->monsterinfo.idle_time = level.time + random_time(15_sec, 30_sec);
  161. }
  162. else
  163. {
  164. self->monsterinfo.idle_time = level.time + random_time(15_sec);
  165. }
  166. }
  167. }
  168. /*
  169. =============
  170. ai_walk
  171. The monster is walking it's beat
  172. =============
  173. */
  174. void ai_walk(edict_t *self, float dist)
  175. {
  176. edict_t *temp_goal = nullptr;
  177. if (!self->goalentity && (self->monsterinfo.aiflags & AI_GOOD_GUY))
  178. {
  179. vec3_t fwd;
  180. AngleVectors(self->s.angles, fwd, nullptr, nullptr);
  181. temp_goal = G_Spawn();
  182. temp_goal->s.origin = self->s.origin + fwd * 64;
  183. self->goalentity = temp_goal;
  184. }
  185. M_MoveToGoal(self, dist);
  186. if (temp_goal)
  187. {
  188. G_FreeEdict(temp_goal);
  189. self->goalentity = nullptr;
  190. }
  191. // check for noticing a player
  192. if (FindTarget(self))
  193. return;
  194. if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
  195. {
  196. if (self->monsterinfo.idle_time)
  197. {
  198. self->monsterinfo.search(self);
  199. self->monsterinfo.idle_time = level.time + random_time(15_sec, 30_sec);
  200. }
  201. else
  202. {
  203. self->monsterinfo.idle_time = level.time + random_time(15_sec);
  204. }
  205. }
  206. }
  207. /*
  208. =============
  209. ai_charge
  210. Turns towards target and advances
  211. Use this call with a distance of 0 to replace ai_face
  212. ==============
  213. */
  214. void ai_charge(edict_t *self, float dist)
  215. {
  216. vec3_t v;
  217. // ROGUE
  218. float ofs;
  219. // PMM - made AI_MANUAL_STEERING affect things differently here .. they turn, but
  220. // don't set the ideal_yaw
  221. // This is put in there so monsters won't move towards the origin after killing
  222. // a tesla. This could be problematic, so keep an eye on it.
  223. if (!self->enemy || !self->enemy->inuse) // PGM
  224. return; // PGM
  225. // PMM - save blindfire target
  226. if (visible(self, self->enemy))
  227. self->monsterinfo.blind_fire_target = self->enemy->s.origin + (self->enemy->velocity * -0.1f);
  228. // pmm
  229. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  230. {
  231. // ROGUE
  232. v = self->enemy->s.origin - self->s.origin;
  233. self->ideal_yaw = vectoyaw(v);
  234. // ROGUE
  235. }
  236. // ROGUE
  237. M_ChangeYaw(self);
  238. if (dist || (self->monsterinfo.aiflags & AI_ALTERNATE_FLY))
  239. // ROGUE
  240. {
  241. if (self->monsterinfo.aiflags & AI_CHARGING)
  242. {
  243. M_MoveToGoal(self, dist);
  244. return;
  245. }
  246. // circle strafe support
  247. if (self->monsterinfo.attack_state == AS_SLIDING)
  248. {
  249. // if we're fighting a tesla, NEVER circle strafe
  250. if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla_mine")))
  251. ofs = 0;
  252. else if (self->monsterinfo.lefty)
  253. ofs = 90;
  254. else
  255. ofs = -90;
  256. dist *= self->monsterinfo.active_move->sidestep_scale;
  257. if (M_walkmove(self, self->ideal_yaw + ofs, dist))
  258. return;
  259. self->monsterinfo.lefty = !self->monsterinfo.lefty;
  260. M_walkmove(self, self->ideal_yaw - ofs, dist);
  261. }
  262. else
  263. // ROGUE
  264. M_walkmove(self, self->s.angles[YAW], dist);
  265. // ROGUE
  266. }
  267. // ROGUE
  268. // [Paril-KEX] if our enemy is literally right next to us, give
  269. // us more rotational speed so we don't get circled
  270. if (range_to(self, self->enemy) <= RANGE_MELEE * 2.5f)
  271. M_ChangeYaw(self);
  272. }
  273. /*
  274. =============
  275. ai_turn
  276. don't move, but turn towards ideal_yaw
  277. Distance is for slight position adjustments needed by the animations
  278. =============
  279. */
  280. void ai_turn(edict_t *self, float dist)
  281. {
  282. if (dist || (self->monsterinfo.aiflags & AI_ALTERNATE_FLY))
  283. M_walkmove(self, self->s.angles[YAW], dist);
  284. if (FindTarget(self))
  285. return;
  286. // ROGUE
  287. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  288. // ROGUE
  289. M_ChangeYaw(self);
  290. }
  291. /*
  292. .enemy
  293. Will be world if not currently angry at anyone.
  294. .movetarget
  295. The next path spot to walk toward. If .enemy, ignore .movetarget.
  296. When an enemy is killed, the monster will try to return to it's path.
  297. .hunt_time
  298. Set to time + something when the player is in sight, but movement straight for
  299. him is blocked. This causes the monster to use wall following code for
  300. movement direction instead of sighting on the player.
  301. .ideal_yaw
  302. A yaw angle of the intended direction, which will be turned towards at up
  303. to 45 deg / state. If the enemy is in view and hunt_time is not active,
  304. this will be the exact line towards the enemy.
  305. .pausetime
  306. A monster will leave it's stand state and head towards it's .movetarget when
  307. time > .pausetime.
  308. walkmove(angle, speed) primitive is all or nothing
  309. */
  310. /*
  311. =============
  312. range_to
  313. returns the distance of an entity relative to self.
  314. in general, the results determine how an AI reacts:
  315. melee melee range, will become hostile even if back is turned
  316. near visibility and infront, or visibility and show hostile
  317. mid infront and show hostile
  318. > mid only triggered by damage
  319. =============
  320. */
  321. float range_to(edict_t *self, edict_t *other)
  322. {
  323. return distance_between_boxes(self->absmin, self->absmax, other->absmin, other->absmax);
  324. }
  325. /*
  326. =============
  327. visible
  328. returns 1 if the entity is visible to self, even if not infront ()
  329. =============
  330. */
  331. bool visible(edict_t *self, edict_t *other, bool through_glass)
  332. {
  333. // never visible
  334. if (other->flags & FL_NOVISIBLE)
  335. return false;
  336. // [Paril-KEX] bit of a hack, but we'll tweak monster-player visibility
  337. // if they have the invisibility powerup.
  338. if (other->client)
  339. {
  340. // always visible in rtest
  341. if (self->hackflags & HACKFLAG_ATTACK_PLAYER)
  342. return self->inuse;
  343. // fix intermission
  344. if (!other->solid)
  345. return false;
  346. if (other->client->invisible_time > level.time)
  347. {
  348. // can't see us at all after this time
  349. if (other->client->invisibility_fade_time <= level.time)
  350. return false;
  351. // otherwise, throw in some randomness
  352. if (frandom() > other->s.alpha)
  353. return false;
  354. }
  355. }
  356. vec3_t spot1;
  357. vec3_t spot2;
  358. trace_t trace;
  359. spot1 = self->s.origin;
  360. spot1[2] += self->viewheight;
  361. spot2 = other->s.origin;
  362. spot2[2] += other->viewheight;
  363. contents_t mask = MASK_OPAQUE;
  364. if (!through_glass)
  365. mask |= CONTENTS_WINDOW;
  366. trace = gi.traceline(spot1, spot2, self, mask);
  367. return trace.fraction == 1.0f || trace.ent == other; // PGM
  368. }
  369. /*
  370. =============
  371. infront
  372. returns 1 if the entity is in front (in sight) of self
  373. =============
  374. */
  375. bool infront(edict_t *self, edict_t *other)
  376. {
  377. vec3_t vec;
  378. float dot;
  379. vec3_t forward;
  380. AngleVectors(self->s.angles, forward, nullptr, nullptr);
  381. vec = other->s.origin - self->s.origin;
  382. vec.normalize();
  383. dot = vec.dot(forward);
  384. // [Paril-KEX] if we're an ambush monster, reduce our cone of
  385. // vision to not ruin surprises, unless we already had an enemy.
  386. if (self->spawnflags.has(SPAWNFLAG_MONSTER_AMBUSH) && !self->monsterinfo.trail_time && !self->enemy)
  387. return dot > 0.15f;
  388. return dot > -0.30f;
  389. }
  390. //============================================================================
  391. void HuntTarget(edict_t *self, bool animate_state)
  392. {
  393. vec3_t vec;
  394. self->goalentity = self->enemy;
  395. if (animate_state)
  396. {
  397. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  398. self->monsterinfo.stand(self);
  399. else
  400. self->monsterinfo.run(self);
  401. }
  402. vec = self->enemy->s.origin - self->s.origin;
  403. self->ideal_yaw = vectoyaw(vec);
  404. }
  405. void FoundTarget(edict_t *self)
  406. {
  407. // let other monsters see this monster for a while
  408. if (self->enemy->client)
  409. {
  410. // ROGUE
  411. if (self->enemy->flags & FL_DISGUISED)
  412. self->enemy->flags &= ~FL_DISGUISED;
  413. // ROGUE
  414. self->enemy->client->sight_entity = self;
  415. self->enemy->client->sight_entity_time = level.time;
  416. self->enemy->show_hostile = level.time + 1_sec; // wake up other monsters
  417. }
  418. // [Paril-KEX] the first time we spot something, give us a bit of a grace
  419. // period on firing
  420. if (!self->monsterinfo.trail_time)
  421. self->monsterinfo.attack_finished = level.time + 600_ms;
  422. // give easy/medium a little more reaction time
  423. self->monsterinfo.attack_finished += skill->integer == 0 ? 400_ms : skill->integer == 1 ? 200_ms : 0_ms;
  424. self->monsterinfo.last_sighting = self->monsterinfo.saved_goal = self->enemy->s.origin;
  425. self->monsterinfo.trail_time = level.time;
  426. // ROGUE
  427. self->monsterinfo.blind_fire_target = self->monsterinfo.last_sighting + (self->enemy->velocity * -0.1f);
  428. self->monsterinfo.blind_fire_delay = 0_ms;
  429. // ROGUE
  430. // [Paril-KEX] for alternate fly, pick a new position immediately
  431. self->monsterinfo.fly_position_time = 0_ms;
  432. self->monsterinfo.aiflags &= ~AI_THIRD_EYE;
  433. // Paril: if we're heading to a combat point/path corner, don't
  434. // hunt the new target yet.
  435. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  436. return;
  437. if (!self->combattarget)
  438. {
  439. HuntTarget(self);
  440. return;
  441. }
  442. self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
  443. if (!self->movetarget)
  444. {
  445. self->goalentity = self->movetarget = self->enemy;
  446. HuntTarget(self);
  447. gi.Com_PrintFmt("{}: combattarget {} not found\n", *self, self->combattarget);
  448. return;
  449. }
  450. // clear out our combattarget, these are a one shot deal
  451. self->combattarget = nullptr;
  452. self->monsterinfo.aiflags |= AI_COMBAT_POINT;
  453. // clear the targetname, that point is ours!
  454. // [Paril-KEX] not any more, we can re-use them
  455. //self->movetarget->targetname = nullptr;
  456. self->monsterinfo.pausetime = 0_ms;
  457. // run for it
  458. self->monsterinfo.run(self);
  459. }
  460. // [Paril-KEX] monsters that were alerted by players will
  461. // be temporarily stored on player entities, so we can
  462. // check them & get mad at them even around corners
  463. static edict_t *AI_GetMonsterAlertedByPlayers(edict_t *self)
  464. {
  465. for (auto player : active_players())
  466. {
  467. // dead
  468. if (player->health <= 0 || player->deadflag || !player->solid)
  469. continue;
  470. // we didn't alert any other monster, or it wasn't recently
  471. if (!player->client->sight_entity || !(player->client->sight_entity_time >= (level.time - FRAME_TIME_S)))
  472. continue;
  473. // if we can't see the monster, don't bother
  474. if (!visible(self, player->client->sight_entity))
  475. continue;
  476. // probably good
  477. return player->client->sight_entity;
  478. }
  479. return nullptr;
  480. }
  481. // [Paril-KEX] per-player sounds
  482. static edict_t *AI_GetSoundClient(edict_t *self, bool direct)
  483. {
  484. edict_t *best_sound = nullptr;
  485. float best_distance = std::numeric_limits<float>::max();
  486. for (auto player : active_players())
  487. {
  488. // dead
  489. if (player->health <= 0 || player->deadflag || !player->solid)
  490. continue;
  491. edict_t *sound = direct ? player->client->sound_entity : player->client->sound2_entity;
  492. if (!sound)
  493. continue;
  494. // too late
  495. gtime_t &time = direct ? player->client->sound_entity_time : player->client->sound2_entity_time;
  496. if (!(time >= (level.time - FRAME_TIME_S)))
  497. continue;
  498. // prefer the closest one we heard
  499. float dist = (self->s.origin - sound->s.origin).length();
  500. if (!best_sound || dist < best_distance)
  501. {
  502. best_distance = dist;
  503. best_sound = sound;
  504. }
  505. }
  506. return best_sound;
  507. }
  508. bool G_MonsterSourceVisible(edict_t *self, edict_t *client)
  509. {
  510. // this is where we would check invisibility
  511. float r = range_to(self, client);
  512. if (r > RANGE_MID)
  513. return false;
  514. // Paril: revised so that monsters can be woken up
  515. // by players 'seen' and attacked at by other monsters
  516. // if they are close enough. they don't have to be visible.
  517. bool is_visible =
  518. ((r <= RANGE_NEAR && client->show_hostile >= level.time && !(self->spawnflags & SPAWNFLAG_MONSTER_AMBUSH)) ||
  519. (visible(self, client) && (r <= RANGE_MELEE || (self->monsterinfo.aiflags & AI_THIRD_EYE) || infront(self, client))));
  520. return is_visible;
  521. }
  522. /*
  523. ===========
  524. FindTarget
  525. Self is currently not attacking anything, so try to find a target
  526. Returns TRUE if an enemy was sighted
  527. When a player fires a missile, the point of impact becomes a fakeplayer so
  528. that monsters that see the impact will respond as if they had seen the
  529. player.
  530. To avoid spending too much time, only a single client (or fakeclient) is
  531. checked each frame. This means multi player games will have slightly
  532. slower noticing monsters.
  533. ============
  534. */
  535. bool FindTarget(edict_t *self)
  536. {
  537. edict_t *client = nullptr;
  538. bool heardit;
  539. bool ignore_sight_sound = false;
  540. // [Paril-KEX] if we're in a level transition, don't worry about enemies
  541. if (globals.server_flags & SERVER_FLAG_LOADING)
  542. return false;
  543. // N64 cutscene behavior
  544. if (self->hackflags & HACKFLAG_END_CUTSCENE)
  545. return false;
  546. if (self->monsterinfo.aiflags & AI_GOOD_GUY)
  547. {
  548. if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
  549. {
  550. if (strcmp(self->goalentity->classname, "target_actor") == 0)
  551. return false;
  552. }
  553. // FIXME look for monsters?
  554. return false;
  555. }
  556. // if we're going to a combat point, just proceed
  557. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  558. return false;
  559. // if the first spawnflag bit is set, the monster will only wake up on
  560. // really seeing the player, not another monster getting angry or hearing
  561. // something
  562. // revised behavior so they will wake up if they "see" a player make a noise
  563. // but not weapon impact/explosion noises
  564. heardit = false;
  565. // Paril: revised so that monsters will first try to consider
  566. // the current sight client immediately if they can see it.
  567. // this fixes them dancing in front of you if you fire every frame.
  568. if ((client = AI_GetSightClient(self)))
  569. {
  570. if (client == self->enemy)
  571. {
  572. return false;
  573. }
  574. }
  575. // check indirect sources
  576. if (!client)
  577. {
  578. // check monsters that were alerted by players; we can only be alerted if we
  579. // can see them
  580. if (!(self->spawnflags & SPAWNFLAG_MONSTER_AMBUSH) && (client = AI_GetMonsterAlertedByPlayers(self)))
  581. {
  582. // KEX_FIXME: when does this happen?
  583. // [Paril-KEX] adjusted to clear the client
  584. // so we can try other things
  585. if (client->enemy == self->enemy ||
  586. !G_MonsterSourceVisible(self, client))
  587. client = nullptr;
  588. }
  589. // ROGUE
  590. if (client == nullptr)
  591. {
  592. if (level.disguise_violation_time > level.time)
  593. {
  594. client = level.disguise_violator;
  595. }
  596. // ROGUE
  597. else if ((client = AI_GetSoundClient(self, true)))
  598. {
  599. heardit = true;
  600. }
  601. else if (!(self->enemy) && !(self->spawnflags & SPAWNFLAG_MONSTER_AMBUSH) &&
  602. (client = AI_GetSoundClient(self, false)))
  603. {
  604. heardit = true;
  605. }
  606. }
  607. }
  608. if (!client)
  609. return false; // no clients to get mad at
  610. // if the entity went away, forget it
  611. if (!client->inuse)
  612. return false;
  613. if (client == self->enemy)
  614. {
  615. bool skip_found = true;
  616. // [Paril-KEX] slight special behavior if we are currently going to a sound
  617. // and we hear a new one; because player noises are re-used, this can leave
  618. // us with the "same" enemy even though it's a different noise.
  619. if (heardit && (self->monsterinfo.aiflags & AI_SOUND_TARGET))
  620. {
  621. vec3_t temp = client->s.origin - self->s.origin;
  622. self->ideal_yaw = vectoyaw(temp);
  623. if (!FacingIdeal(self))
  624. skip_found = false;
  625. else if (!SV_CloseEnough(self, client, 8.f))
  626. skip_found = false;
  627. if (!skip_found && (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND))
  628. {
  629. self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  630. }
  631. }
  632. if (skip_found)
  633. return true; // JDC false;
  634. }
  635. // ROGUE - hintpath coop fix
  636. if ((self->monsterinfo.aiflags & AI_HINT_PATH) && coop->integer)
  637. heardit = false;
  638. // ROGUE
  639. if (client->svflags & SVF_MONSTER)
  640. {
  641. if (!client->enemy)
  642. return false;
  643. if (client->enemy->flags & FL_NOTARGET)
  644. return false;
  645. }
  646. else if (heardit)
  647. {
  648. // pgm - a little more paranoia won't hurt....
  649. if ((client->owner) && (client->owner->flags & FL_NOTARGET))
  650. return false;
  651. }
  652. else if (!client->client)
  653. return false;
  654. if (!heardit)
  655. {
  656. // this is where we would check invisibility
  657. float r = range_to(self, client);
  658. if (r > RANGE_MID)
  659. return false;
  660. // Paril: revised so that monsters can be woken up
  661. // by players 'seen' and attacked at by other monsters
  662. // if they are close enough. they don't have to be visible.
  663. bool is_visible =
  664. ((r <= RANGE_NEAR && client->show_hostile >= level.time && !(self->spawnflags & SPAWNFLAG_MONSTER_AMBUSH)) ||
  665. (visible(self, client) && (r <= RANGE_MELEE || (self->monsterinfo.aiflags & AI_THIRD_EYE) || infront(self, client))));
  666. if (!is_visible)
  667. return false;
  668. self->enemy = client;
  669. if (strcmp(self->enemy->classname, "player_noise") != 0)
  670. {
  671. self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  672. if (!self->enemy->client)
  673. {
  674. self->enemy = self->enemy->enemy;
  675. if (!self->enemy->client)
  676. {
  677. self->enemy = nullptr;
  678. return false;
  679. }
  680. }
  681. }
  682. if (self->enemy->client && self->enemy->client->invisible_time > level.time && self->enemy->client->invisibility_fade_time <= level.time)
  683. {
  684. self->enemy = nullptr;
  685. return false;
  686. }
  687. if (self->monsterinfo.close_sight_tripped)
  688. ignore_sight_sound = true;
  689. else
  690. self->monsterinfo.close_sight_tripped = true;
  691. }
  692. else // heardit
  693. {
  694. vec3_t temp;
  695. if (self->spawnflags.has(SPAWNFLAG_MONSTER_AMBUSH))
  696. {
  697. if (!visible(self, client))
  698. return false;
  699. }
  700. else
  701. {
  702. if (!gi.inPHS(self->s.origin, client->s.origin, true))
  703. return false;
  704. }
  705. temp = client->s.origin - self->s.origin;
  706. if (temp.length() > 1000) // too far to hear
  707. return false;
  708. // check area portals - if they are different and not connected then we can't hear it
  709. if (client->areanum != self->areanum)
  710. if (!gi.AreasConnected(self->areanum, client->areanum))
  711. return false;
  712. self->ideal_yaw = vectoyaw(temp);
  713. // ROGUE
  714. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  715. // ROGUE
  716. M_ChangeYaw(self);
  717. // hunt the sound for a bit; hopefully find the real player
  718. self->monsterinfo.aiflags |= AI_SOUND_TARGET;
  719. self->enemy = client;
  720. }
  721. //
  722. // got one
  723. //
  724. // ROGUE - if we got an enemy, we need to bail out of hint paths, so take over here
  725. if (self->monsterinfo.aiflags & AI_HINT_PATH)
  726. hintpath_stop(self); // this calls foundtarget for us
  727. else
  728. FoundTarget(self);
  729. // ROGUE
  730. if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight) &&
  731. // Paril: adjust to prevent monsters getting stuck in sight loops
  732. !ignore_sight_sound)
  733. self->monsterinfo.sight(self, self->enemy);
  734. return true;
  735. }
  736. //=============================================================================
  737. /*
  738. ============
  739. FacingIdeal
  740. ============
  741. */
  742. bool FacingIdeal(edict_t *self)
  743. {
  744. float delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
  745. if (self->monsterinfo.aiflags & AI_PATHING)
  746. return !(delta > 5 && delta < 355);
  747. return !(delta > 45 && delta < 315);
  748. }
  749. //=============================================================================
  750. // [Paril-KEX] split this out so we can use it for the other bosses
  751. bool M_CheckAttack_Base(edict_t *self, float stand_ground_chance, float melee_chance, float near_chance, float mid_chance, float far_chance, float strafe_scalar)
  752. {
  753. vec3_t spot1, spot2;
  754. float chance;
  755. trace_t tr;
  756. if (self->enemy->flags & FL_NOVISIBLE)
  757. return false;
  758. if (self->enemy->health > 0)
  759. {
  760. if (self->enemy->client)
  761. {
  762. if (self->enemy->client->invisible_time > level.time)
  763. {
  764. // can't see us at all after this time
  765. if (self->enemy->client->invisibility_fade_time <= level.time)
  766. return false;
  767. }
  768. }
  769. spot1 = self->s.origin;
  770. spot1[2] += self->viewheight;
  771. // see if any entities are in the way of the shot
  772. if (!self->enemy->client || self->enemy->solid)
  773. {
  774. spot2 = self->enemy->s.origin;
  775. spot2[2] += self->enemy->viewheight;
  776. tr = gi.traceline(spot1, spot2, self,
  777. MASK_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_SLIME | CONTENTS_LAVA);
  778. }
  779. else
  780. {
  781. tr.ent = world;
  782. tr.fraction = 0;
  783. }
  784. // do we have a clear shot?
  785. if (!(self->hackflags & HACKFLAG_ATTACK_PLAYER) && tr.ent != self->enemy && !(tr.ent->svflags & SVF_PLAYER))
  786. {
  787. // ROGUE - we want them to go ahead and shoot at info_notnulls if they can.
  788. if (self->enemy->solid != SOLID_NOT || tr.fraction < 1.0f) // PGM
  789. {
  790. // PMM - if we can't see our target, and we're not blocked by a monster, go into blind fire if available
  791. // Paril - *and* we have at least seen them once
  792. if (!(tr.ent->svflags & SVF_MONSTER) && !visible(self, self->enemy) && self->monsterinfo.had_visibility)
  793. {
  794. if (self->monsterinfo.blindfire && (self->monsterinfo.blind_fire_delay <= 20_sec))
  795. {
  796. if (level.time < self->monsterinfo.attack_finished)
  797. {
  798. // ROGUE
  799. return false;
  800. }
  801. // ROGUE
  802. if (level.time < (self->monsterinfo.trail_time + self->monsterinfo.blind_fire_delay))
  803. {
  804. // wait for our time
  805. return false;
  806. }
  807. else
  808. {
  809. // make sure we're not going to shoot a monster
  810. tr = gi.traceline(spot1, self->monsterinfo.blind_fire_target, self,
  811. CONTENTS_MONSTER);
  812. if (tr.allsolid || tr.startsolid || ((tr.fraction < 1.0f) && (tr.ent != self->enemy)))
  813. return false;
  814. self->monsterinfo.attack_state = AS_BLIND;
  815. return true;
  816. }
  817. }
  818. }
  819. // pmm
  820. return false;
  821. }
  822. }
  823. }
  824. // ROGUE
  825. float enemy_range = range_to(self, self->enemy);
  826. // melee attack
  827. if (enemy_range <= RANGE_MELEE)
  828. {
  829. if (self->monsterinfo.melee && self->monsterinfo.melee_debounce_time <= level.time)
  830. self->monsterinfo.attack_state = AS_MELEE;
  831. else
  832. self->monsterinfo.attack_state = AS_MISSILE;
  833. return true;
  834. }
  835. // if we were in melee just before this but we're too far away, get out of melee state now
  836. if (self->monsterinfo.attack_state == AS_MELEE && self->monsterinfo.melee_debounce_time > level.time)
  837. self->monsterinfo.attack_state = AS_MISSILE;
  838. // missile attack
  839. if (!self->monsterinfo.attack)
  840. {
  841. // ROGUE - fix for melee only monsters & strafing
  842. self->monsterinfo.attack_state = AS_STRAIGHT;
  843. // ROGUE
  844. return false;
  845. }
  846. if (level.time < self->monsterinfo.attack_finished)
  847. return false;
  848. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  849. {
  850. chance = stand_ground_chance;
  851. }
  852. else if (enemy_range <= RANGE_MELEE)
  853. {
  854. chance = melee_chance;
  855. }
  856. else if (enemy_range <= RANGE_NEAR)
  857. {
  858. chance = near_chance;
  859. }
  860. else if (enemy_range <= RANGE_MID)
  861. {
  862. chance = mid_chance;
  863. }
  864. else
  865. {
  866. chance = far_chance;
  867. }
  868. // PGM - go ahead and shoot every time if it's a info_notnull
  869. if ((!self->enemy->client && self->enemy->solid == SOLID_NOT) || (frandom() < chance))
  870. {
  871. self->monsterinfo.attack_state = AS_MISSILE;
  872. self->monsterinfo.attack_finished = level.time;
  873. return true;
  874. }
  875. // ROGUE -daedalus should strafe more .. this can be done here or in a customized
  876. // check_attack code for the hover.
  877. if (self->flags & FL_FLY)
  878. {
  879. if (self->monsterinfo.strafe_check_time <= level.time)
  880. {
  881. // originally, just 0.3
  882. float strafe_chance;
  883. if (!(strcmp(self->classname, "monster_daedalus")))
  884. strafe_chance = 0.8f;
  885. else
  886. strafe_chance = 0.6f;
  887. // if enemy is tesla, never strafe
  888. if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla_mine")))
  889. strafe_chance = 0;
  890. else
  891. strafe_chance *= strafe_scalar;
  892. if (strafe_chance)
  893. {
  894. monster_attack_state_t new_state = AS_STRAIGHT;
  895. if (frandom() < strafe_chance)
  896. new_state = AS_SLIDING;
  897. if (new_state != self->monsterinfo.attack_state)
  898. {
  899. self->monsterinfo.strafe_check_time = level.time + random_time(1_sec, 3_sec);
  900. self->monsterinfo.attack_state = new_state;
  901. }
  902. }
  903. }
  904. }
  905. // do we want the monsters strafing?
  906. // [Paril-KEX] no, we don't
  907. // [Paril-KEX] if we're pathing, don't immediately reset us to
  908. // straight; this allows us to turn to fire and not jerk back and
  909. // forth.
  910. else if (!(self->monsterinfo.aiflags & AI_PATHING))
  911. self->monsterinfo.attack_state = AS_STRAIGHT;
  912. // ROGUE
  913. return false;
  914. }
  915. MONSTERINFO_CHECKATTACK(M_CheckAttack) (edict_t *self) -> bool
  916. {
  917. return M_CheckAttack_Base(self, 0.7f, 0.4f, 0.25f, 0.06f, 0.f, 1.0f);
  918. }
  919. /*
  920. =============
  921. ai_run_melee
  922. Turn and close until within an angle to launch a melee attack
  923. =============
  924. */
  925. void ai_run_melee(edict_t *self)
  926. {
  927. self->ideal_yaw = enemy_yaw;
  928. // ROGUE
  929. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  930. // ROGUE
  931. M_ChangeYaw(self);
  932. if (FacingIdeal(self))
  933. {
  934. self->monsterinfo.melee(self);
  935. self->monsterinfo.attack_state = AS_STRAIGHT;
  936. }
  937. }
  938. /*
  939. =============
  940. ai_run_missile
  941. Turn in place until within an angle to launch a missile attack
  942. =============
  943. */
  944. void ai_run_missile(edict_t *self)
  945. {
  946. self->ideal_yaw = enemy_yaw;
  947. // ROGUE
  948. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  949. // ROGUE
  950. M_ChangeYaw(self);
  951. if (FacingIdeal(self))
  952. {
  953. if (self->monsterinfo.attack)
  954. {
  955. self->monsterinfo.attack(self);
  956. self->monsterinfo.attack_finished = level.time + random_time(1.0_sec, 2.0_sec);
  957. }
  958. // ROGUE
  959. if ((self->monsterinfo.attack_state == AS_MISSILE) || (self->monsterinfo.attack_state == AS_BLIND))
  960. // ROGUE
  961. self->monsterinfo.attack_state = AS_STRAIGHT;
  962. }
  963. };
  964. /*
  965. =============
  966. ai_run_slide
  967. Strafe sideways, but stay at aproximately the same range
  968. =============
  969. */
  970. // ROGUE
  971. void ai_run_slide(edict_t *self, float distance)
  972. {
  973. float ofs;
  974. float angle;
  975. self->ideal_yaw = enemy_yaw;
  976. angle = 90;
  977. if (self->monsterinfo.lefty)
  978. ofs = angle;
  979. else
  980. ofs = -angle;
  981. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  982. M_ChangeYaw(self);
  983. // PMM - clamp maximum sideways move for non flyers to make them look less jerky
  984. if (!(self->flags & FL_FLY))
  985. distance = min(distance, MAX_SIDESTEP / (gi.frame_time_ms / 10));
  986. if (M_walkmove(self, self->ideal_yaw + ofs, distance))
  987. return;
  988. // PMM - if we're dodging, give up on it and go straight
  989. if (self->monsterinfo.aiflags & AI_DODGING)
  990. {
  991. monster_done_dodge(self);
  992. // by setting as_straight, caller will know to try straight move
  993. self->monsterinfo.attack_state = AS_STRAIGHT;
  994. return;
  995. }
  996. self->monsterinfo.lefty = !self->monsterinfo.lefty;
  997. if (M_walkmove(self, self->ideal_yaw - ofs, distance))
  998. return;
  999. // PMM - if we're dodging, give up on it and go straight
  1000. if (self->monsterinfo.aiflags & AI_DODGING)
  1001. monster_done_dodge(self);
  1002. // PMM - the move failed, so signal the caller (ai_run) to try going straight
  1003. self->monsterinfo.attack_state = AS_STRAIGHT;
  1004. }
  1005. // ROGUE
  1006. /*
  1007. =============
  1008. ai_checkattack
  1009. Decides if we're going to attack or do something else
  1010. used by ai_run and ai_stand
  1011. =============
  1012. */
  1013. bool ai_checkattack(edict_t *self, float dist)
  1014. {
  1015. vec3_t temp;
  1016. bool hesDeadJim;
  1017. // ROGUE
  1018. bool retval;
  1019. // ROGUE
  1020. if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  1021. self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  1022. // this causes monsters to run blindly to the combat point w/o firing
  1023. if (self->goalentity)
  1024. {
  1025. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  1026. {
  1027. if (self->enemy && range_to(self, self->enemy) > 100.f)
  1028. return false;
  1029. }
  1030. if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  1031. {
  1032. if ((level.time - self->enemy->teleport_time) > 5_sec)
  1033. {
  1034. if (self->goalentity == self->enemy)
  1035. {
  1036. if (self->movetarget)
  1037. self->goalentity = self->movetarget;
  1038. else
  1039. self->goalentity = nullptr;
  1040. }
  1041. self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  1042. }
  1043. else
  1044. {
  1045. self->enemy->show_hostile = level.time + 1_sec;
  1046. return false;
  1047. }
  1048. }
  1049. }
  1050. enemy_vis = false;
  1051. // see if the enemy is dead
  1052. hesDeadJim = false;
  1053. if ((!self->enemy) || (!self->enemy->inuse))
  1054. {
  1055. hesDeadJim = true;
  1056. } else if ( self->monsterinfo.aiflags & AI_FORGET_ENEMY )
  1057. {
  1058. self->monsterinfo.aiflags &= ~AI_FORGET_ENEMY;
  1059. hesDeadJim = true;
  1060. }
  1061. else if (self->monsterinfo.aiflags & AI_MEDIC)
  1062. {
  1063. if (!(self->enemy->inuse) || (self->enemy->health > 0))
  1064. hesDeadJim = true;
  1065. }
  1066. else
  1067. {
  1068. if (!(self->monsterinfo.aiflags & AI_BRUTAL))
  1069. {
  1070. if (self->enemy->health <= 0)
  1071. hesDeadJim = true;
  1072. }
  1073. // [Paril-KEX] if our enemy was invisible, lose sight now
  1074. if (self->enemy->client && self->enemy->client->invisible_time > level.time && self->enemy->client->invisibility_fade_time <= level.time &&
  1075. (self->monsterinfo.aiflags & AI_PURSUE_NEXT))
  1076. {
  1077. hesDeadJim = true;
  1078. }
  1079. }
  1080. if (hesDeadJim && !(self->hackflags & HACKFLAG_ATTACK_PLAYER))
  1081. {
  1082. // ROGUE
  1083. self->monsterinfo.aiflags &= ~AI_MEDIC;
  1084. // ROGUE
  1085. self->enemy = self->goalentity = nullptr;
  1086. self->monsterinfo.close_sight_tripped = false;
  1087. // FIXME: look all around for other targets
  1088. if (self->oldenemy && self->oldenemy->health > 0)
  1089. {
  1090. self->enemy = self->oldenemy;
  1091. self->oldenemy = nullptr;
  1092. HuntTarget(self);
  1093. }
  1094. // ROGUE - multiple teslas make monsters lose track of the player.
  1095. else if (self->monsterinfo.last_player_enemy && self->monsterinfo.last_player_enemy->health > 0)
  1096. {
  1097. self->enemy = self->monsterinfo.last_player_enemy;
  1098. self->oldenemy = nullptr;
  1099. self->monsterinfo.last_player_enemy = nullptr;
  1100. HuntTarget(self);
  1101. }
  1102. // ROGUE
  1103. else
  1104. {
  1105. if (self->movetarget && !(self->monsterinfo.aiflags & AI_STAND_GROUND))
  1106. {
  1107. self->goalentity = self->movetarget;
  1108. self->monsterinfo.walk(self);
  1109. }
  1110. else
  1111. {
  1112. // we need the pausetime otherwise the stand code
  1113. // will just revert to walking with no target and
  1114. // the monsters will wonder around aimlessly trying
  1115. // to hunt the world entity
  1116. self->monsterinfo.pausetime = HOLD_FOREVER;
  1117. self->monsterinfo.stand(self);
  1118. if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  1119. self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  1120. }
  1121. return true;
  1122. }
  1123. }
  1124. // check knowledge of enemy
  1125. enemy_vis = visible(self, self->enemy);
  1126. if (enemy_vis)
  1127. {
  1128. self->monsterinfo.had_visibility = true;
  1129. self->enemy->show_hostile = level.time + 1_sec; // wake up other monsters
  1130. self->monsterinfo.search_time = level.time + 5_sec;
  1131. self->monsterinfo.last_sighting = self->monsterinfo.saved_goal = self->enemy->s.origin;
  1132. // ROGUE
  1133. if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
  1134. {
  1135. self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  1136. if (self->monsterinfo.move_block_change_time < level.time)
  1137. self->monsterinfo.aiflags &= ~AI_TEMP_MELEE_COMBAT;
  1138. }
  1139. self->monsterinfo.trail_time = level.time;
  1140. self->monsterinfo.blind_fire_target = self->monsterinfo.last_sighting + (self->enemy->velocity * -0.1f);
  1141. self->monsterinfo.blind_fire_delay = 0_ms;
  1142. // ROGUE
  1143. }
  1144. enemy_infront = infront(self, self->enemy);
  1145. temp = self->enemy->s.origin - self->s.origin;
  1146. enemy_yaw = vectoyaw(temp);
  1147. // PMM -- reordered so the monster specific checkattack is called before the run_missle/melee/checkvis
  1148. // stuff .. this allows for, among other things, circle strafing and attacking while in ai_run
  1149. retval = false;
  1150. if (self->monsterinfo.checkattack_time <= level.time)
  1151. {
  1152. self->monsterinfo.checkattack_time = level.time + 0.1_sec;
  1153. retval = self->monsterinfo.checkattack(self);
  1154. }
  1155. if (retval || self->monsterinfo.attack_state >= AS_MISSILE)
  1156. {
  1157. // PMM
  1158. if (self->monsterinfo.attack_state == AS_MISSILE)
  1159. {
  1160. ai_run_missile(self);
  1161. return true;
  1162. }
  1163. if (self->monsterinfo.attack_state == AS_MELEE)
  1164. {
  1165. ai_run_melee(self);
  1166. return true;
  1167. }
  1168. // PMM -- added so monsters can shoot blind
  1169. if (self->monsterinfo.attack_state == AS_BLIND)
  1170. {
  1171. ai_run_missile(self);
  1172. return true;
  1173. }
  1174. // pmm
  1175. // if enemy is not currently visible, we will never attack
  1176. if (!enemy_vis)
  1177. return false;
  1178. // PMM
  1179. }
  1180. return retval;
  1181. // PMM
  1182. }
  1183. /*
  1184. =============
  1185. ai_run
  1186. The monster has an enemy it is trying to kill
  1187. =============
  1188. */
  1189. void ai_run(edict_t *self, float dist)
  1190. {
  1191. vec3_t v;
  1192. edict_t *tempgoal;
  1193. edict_t *save;
  1194. bool newEnemy;
  1195. edict_t *marker;
  1196. float d1, d2;
  1197. trace_t tr;
  1198. vec3_t v_forward, v_right;
  1199. float left, center, right;
  1200. vec3_t left_target, right_target;
  1201. // ROGUE
  1202. bool retval;
  1203. bool alreadyMoved = false;
  1204. bool gotcha = false;
  1205. edict_t *realEnemy;
  1206. // ROGUE
  1207. // if we're going to a combat point, just proceed
  1208. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  1209. {
  1210. ai_checkattack(self, dist);
  1211. M_MoveToGoal(self, dist);
  1212. if (self->movetarget)
  1213. {
  1214. // nb: this is done from the centroid and not viewheight on purpose;
  1215. trace_t tr = gi.trace((self->absmax + self->absmin) * 0.5f, { -2.f, -2.f, -2.f }, { 2.f, 2.f, 2.f }, self->movetarget->s.origin, self, CONTENTS_SOLID);
  1216. // [Paril-KEX] special case: if we're stand ground & knocked way too far away
  1217. // from our path_corner, or we can't see it any more, assume all
  1218. // is lost.
  1219. if ((self->monsterinfo.aiflags & AI_REACHED_HOLD_COMBAT) && (((closest_point_to_box(self->movetarget->s.origin, self->absmin, self->absmax) - self->movetarget->s.origin).length() > 160.f)
  1220. || (tr.fraction < 1.0f && tr.plane.normal.z <= 0.7f))) // if we hit a climbable, ignore this result
  1221. {
  1222. self->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
  1223. self->movetarget = nullptr;
  1224. self->target = nullptr;
  1225. self->goalentity = self->enemy;
  1226. }
  1227. else
  1228. return;
  1229. }
  1230. else
  1231. return;
  1232. }
  1233. // PMM
  1234. if ((self->monsterinfo.aiflags & AI_DUCKED) && self->monsterinfo.unduck)
  1235. self->monsterinfo.unduck(self);
  1236. //==========
  1237. // PGM
  1238. // if we're currently looking for a hint path
  1239. if (self->monsterinfo.aiflags & AI_HINT_PATH)
  1240. {
  1241. // determine direction to our destination hintpath.
  1242. M_MoveToGoal(self, dist);
  1243. if (!self->inuse)
  1244. return;
  1245. // first off, make sure we're looking for the player, not a noise he made
  1246. if (self->enemy)
  1247. {
  1248. if (self->enemy->inuse)
  1249. {
  1250. if (strcmp(self->enemy->classname, "player_noise") != 0)
  1251. realEnemy = self->enemy;
  1252. else if (self->enemy->owner)
  1253. realEnemy = self->enemy->owner;
  1254. else // uh oh, can't figure out enemy, bail
  1255. {
  1256. self->enemy = nullptr;
  1257. hintpath_stop(self);
  1258. return;
  1259. }
  1260. }
  1261. else
  1262. {
  1263. self->enemy = nullptr;
  1264. hintpath_stop(self);
  1265. return;
  1266. }
  1267. }
  1268. else
  1269. {
  1270. hintpath_stop(self);
  1271. return;
  1272. }
  1273. if (coop->integer)
  1274. {
  1275. // if we're in coop, check my real enemy first .. if I SEE him, set gotcha to true
  1276. if (self->enemy && visible(self, realEnemy))
  1277. gotcha = true;
  1278. else // otherwise, let FindTarget bump us out of hint paths, if appropriate
  1279. FindTarget(self);
  1280. }
  1281. else
  1282. {
  1283. if (self->enemy && visible(self, realEnemy))
  1284. gotcha = true;
  1285. }
  1286. // if we see the player, stop following hintpaths.
  1287. if (gotcha)
  1288. // disconnect from hintpaths and start looking normally for players.
  1289. hintpath_stop(self);
  1290. return;
  1291. }
  1292. // PGM
  1293. //==========
  1294. if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  1295. {
  1296. // PMM - paranoia checking
  1297. if (self->enemy)
  1298. v = self->s.origin - self->enemy->s.origin;
  1299. bool touching_noise = SV_CloseEnough(self, self->enemy, dist * (gi.tick_rate / 10));
  1300. if ((!self->enemy) || (touching_noise && FacingIdeal(self)))
  1301. // pmm
  1302. {
  1303. self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  1304. self->s.angles[YAW] = self->ideal_yaw;
  1305. self->monsterinfo.stand(self);
  1306. self->monsterinfo.close_sight_tripped = false;
  1307. return;
  1308. }
  1309. // if we're close to the goal, just turn
  1310. if (touching_noise)
  1311. M_ChangeYaw(self);
  1312. else
  1313. M_MoveToGoal(self, dist);
  1314. // ROGUE - prevent double moves for sound_targets
  1315. alreadyMoved = true;
  1316. if (!self->inuse)
  1317. return; // PGM - g_touchtrigger free problem
  1318. // ROGUE
  1319. if (!FindTarget(self))
  1320. return;
  1321. }
  1322. // PMM -- moved ai_checkattack up here so the monsters can attack while strafing or charging
  1323. // PMM -- if we're dodging, make sure to keep the attack_state AS_SLIDING
  1324. retval = ai_checkattack(self, dist);
  1325. // PMM - don't strafe if we can't see our enemy
  1326. if ((!enemy_vis) && (self->monsterinfo.attack_state == AS_SLIDING))
  1327. self->monsterinfo.attack_state = AS_STRAIGHT;
  1328. // unless we're dodging (dodging out of view looks smart)
  1329. if (self->monsterinfo.aiflags & AI_DODGING)
  1330. self->monsterinfo.attack_state = AS_SLIDING;
  1331. // pmm
  1332. if (self->monsterinfo.attack_state == AS_SLIDING)
  1333. {
  1334. // PMM - protect against double moves
  1335. if (!alreadyMoved)
  1336. ai_run_slide(self, dist);
  1337. // PMM
  1338. // we're using attack_state as the return value out of ai_run_slide to indicate whether or not the
  1339. // move succeeded. If the move succeeded, and we're still sliding, we're done in here (since we've
  1340. // had our chance to shoot in ai_checkattack, and have moved).
  1341. // if the move failed, our state is as_straight, and it will be taken care of below
  1342. if ((!retval) && (self->monsterinfo.attack_state == AS_SLIDING))
  1343. return;
  1344. }
  1345. else if (self->monsterinfo.aiflags & AI_CHARGING)
  1346. {
  1347. self->ideal_yaw = enemy_yaw;
  1348. if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
  1349. M_ChangeYaw(self);
  1350. }
  1351. if (retval)
  1352. {
  1353. // PMM - is this useful? Monsters attacking usually call the ai_charge routine..
  1354. // the only monster this affects should be the soldier
  1355. if ((dist || (self->monsterinfo.aiflags & AI_ALTERNATE_FLY)) && (!alreadyMoved) && (self->monsterinfo.attack_state == AS_STRAIGHT) &&
  1356. (!(self->monsterinfo.aiflags & AI_STAND_GROUND)))
  1357. {
  1358. M_MoveToGoal(self, dist);
  1359. }
  1360. if ((self->enemy) && (self->enemy->inuse) && (enemy_vis))
  1361. {
  1362. if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
  1363. {
  1364. self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  1365. if (self->monsterinfo.move_block_change_time < level.time)
  1366. self->monsterinfo.aiflags &= ~AI_TEMP_MELEE_COMBAT;
  1367. }
  1368. self->monsterinfo.last_sighting = self->monsterinfo.saved_goal = self->enemy->s.origin;
  1369. self->monsterinfo.trail_time = level.time;
  1370. // PMM
  1371. self->monsterinfo.blind_fire_target = self->monsterinfo.last_sighting + (self->enemy->velocity * -0.1f);
  1372. self->monsterinfo.blind_fire_delay = 0_ms;
  1373. // pmm
  1374. }
  1375. return;
  1376. }
  1377. // PMM
  1378. // PGM - added a little paranoia checking here... 9/22/98
  1379. if ((self->enemy) && (self->enemy->inuse) && (enemy_vis))
  1380. {
  1381. // PMM - check for alreadyMoved
  1382. if (!alreadyMoved)
  1383. M_MoveToGoal(self, dist);
  1384. if (!self->inuse)
  1385. return; // PGM - g_touchtrigger free problem
  1386. if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
  1387. {
  1388. self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  1389. if (self->monsterinfo.move_block_change_time < level.time)
  1390. self->monsterinfo.aiflags &= ~AI_TEMP_MELEE_COMBAT;
  1391. }
  1392. self->monsterinfo.last_sighting = self->monsterinfo.saved_goal = self->enemy->s.origin;
  1393. self->monsterinfo.trail_time = level.time;
  1394. // PMM
  1395. self->monsterinfo.blind_fire_target = self->monsterinfo.last_sighting + (self->enemy->velocity * -0.1f);
  1396. self->monsterinfo.blind_fire_delay = 0_ms;
  1397. // pmm
  1398. // [Paril-KEX] if our enemy is literally right next to us, give
  1399. // us more rotational speed so we don't get circled
  1400. if (range_to(self, self->enemy) <= RANGE_MELEE * 2.5f)
  1401. M_ChangeYaw(self);
  1402. return;
  1403. }
  1404. //=======
  1405. // PGM
  1406. // if we've been looking (unsuccessfully) for the player for 10 seconds
  1407. // PMM - reduced to 5, makes them much nastier
  1408. if ((self->monsterinfo.trail_time + 5_sec) <= level.time)
  1409. {
  1410. // and we haven't checked for valid hint paths in the last 10 seconds
  1411. if ((self->monsterinfo.last_hint_time + 10_sec) <= level.time)
  1412. {
  1413. // check for hint_paths.
  1414. self->monsterinfo.last_hint_time = level.time;
  1415. if (monsterlost_checkhint(self))
  1416. return;
  1417. }
  1418. }
  1419. // PGM
  1420. //=======
  1421. // PMM - moved down here to allow monsters to get on hint paths
  1422. // coop will change to another enemy if visible
  1423. if (coop->integer)
  1424. FindTarget(self);
  1425. // pmm
  1426. if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20_sec)))
  1427. {
  1428. // PMM - double move protection
  1429. if (!alreadyMoved)
  1430. M_MoveToGoal(self, dist);
  1431. self->monsterinfo.search_time = 0_ms;
  1432. return;
  1433. }
  1434. save = self->goalentity;
  1435. tempgoal = G_Spawn();
  1436. self->goalentity = tempgoal;
  1437. newEnemy = false;
  1438. if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
  1439. {
  1440. // just lost sight of the player, decide where to go first
  1441. self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
  1442. self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
  1443. newEnemy = true;
  1444. // immediately try paths
  1445. self->monsterinfo.path_blocked_counter = 0_ms;
  1446. self->monsterinfo.path_wait_time = 0_ms;
  1447. }
  1448. if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
  1449. {
  1450. self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
  1451. // give ourself more time since we got this far
  1452. self->monsterinfo.search_time = level.time + 5_sec;
  1453. if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
  1454. {
  1455. self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
  1456. marker = nullptr;
  1457. self->monsterinfo.last_sighting = self->monsterinfo.saved_goal;
  1458. newEnemy = true;
  1459. }
  1460. else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
  1461. {
  1462. self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
  1463. marker = PlayerTrail_Pick(self, false);
  1464. }
  1465. else
  1466. {
  1467. marker = PlayerTrail_Pick(self, true);
  1468. }
  1469. if (marker)
  1470. {
  1471. self->monsterinfo.last_sighting = marker->s.origin;
  1472. self->monsterinfo.trail_time = marker->timestamp;
  1473. self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
  1474. newEnemy = true;
  1475. }
  1476. }
  1477. if (!(self->monsterinfo.aiflags & AI_PATHING) &&
  1478. boxes_intersect(self->monsterinfo.last_sighting, self->monsterinfo.last_sighting, self->s.origin + self->mins, self->s.origin + self->maxs))
  1479. {
  1480. self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
  1481. dist = min(dist, (self->s.origin - self->monsterinfo.last_sighting).length());
  1482. // [Paril-KEX] this helps them navigate corners when two next pursuits
  1483. // are really close together
  1484. self->monsterinfo.random_change_time = level.time + 10_hz;
  1485. }
  1486. self->goalentity->s.origin = self->monsterinfo.last_sighting;
  1487. if (newEnemy)
  1488. {
  1489. tr =
  1490. gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
  1491. if (tr.fraction < 1)
  1492. {
  1493. v = self->goalentity->s.origin - self->s.origin;
  1494. d1 = v.length();
  1495. center = tr.fraction;
  1496. d2 = d1 * ((center + 1) / 2);
  1497. float backup_yaw = self->s.angles.y;
  1498. self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  1499. AngleVectors(self->s.angles, v_forward, v_right, nullptr);
  1500. v = { d2, -16, 0 };
  1501. left_target = G_ProjectSource(self->s.origin, v, v_forward, v_right);
  1502. tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
  1503. left = tr.fraction;
  1504. v = { d2, 16, 0 };
  1505. right_target = G_ProjectSource(self->s.origin, v, v_forward, v_right);
  1506. tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
  1507. right = tr.fraction;
  1508. center = (d1 * center) / d2;
  1509. if (left >= center && left > right)
  1510. {
  1511. if (left < 1)
  1512. {
  1513. v = { d2 * left * 0.5f, -16, 0 };
  1514. left_target = G_ProjectSource(self->s.origin, v, v_forward, v_right);
  1515. }
  1516. self->monsterinfo.saved_goal = self->monsterinfo.last_sighting;
  1517. self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  1518. self->goalentity->s.origin = left_target;
  1519. self->monsterinfo.last_sighting = left_target;
  1520. v = self->goalentity->s.origin - self->s.origin;
  1521. self->ideal_yaw = vectoyaw(v);
  1522. }
  1523. else if (right >= center && right > left)
  1524. {
  1525. if (right < 1)
  1526. {
  1527. v = { d2 * right * 0.5f, 16, 0 };
  1528. right_target = G_ProjectSource(self->s.origin, v, v_forward, v_right);
  1529. }
  1530. self->monsterinfo.saved_goal = self->monsterinfo.last_sighting;
  1531. self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  1532. self->goalentity->s.origin = right_target;
  1533. self->monsterinfo.last_sighting = right_target;
  1534. v = self->goalentity->s.origin - self->s.origin;
  1535. self->ideal_yaw = vectoyaw(v);
  1536. }
  1537. self->s.angles[YAW] = backup_yaw;
  1538. }
  1539. }
  1540. M_MoveToGoal(self, dist);
  1541. G_FreeEdict(tempgoal);
  1542. if (!self->inuse)
  1543. return; // PGM - g_touchtrigger free problem
  1544. if (self)
  1545. self->goalentity = save;
  1546. }