g_ai.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  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. qboolean FindTarget (edict_t *self);
  6. extern cvar_t *maxclients;
  7. qboolean ai_checkattack (edict_t *self, float dist);
  8. qboolean enemy_vis;
  9. qboolean enemy_infront;
  10. int enemy_range;
  11. float enemy_yaw;
  12. //============================================================================
  13. /*
  14. =================
  15. AI_SetSightClient
  16. Called once each frame to set level.sight_client to the
  17. player to be checked for in findtarget.
  18. If all clients are either dead or in notarget, sight_client
  19. will be null.
  20. In coop games, sight_client will cycle between the clients.
  21. =================
  22. */
  23. void AI_SetSightClient (void)
  24. {
  25. edict_t *ent;
  26. int start, check;
  27. if (level.sight_client == NULL)
  28. start = 1;
  29. else
  30. start = level.sight_client - g_edicts;
  31. check = start;
  32. while (1)
  33. {
  34. check++;
  35. if (check > game.maxclients)
  36. check = 1;
  37. ent = &g_edicts[check];
  38. if (ent->inuse
  39. && ent->health > 0
  40. && !(ent->flags & FL_NOTARGET) )
  41. {
  42. level.sight_client = ent;
  43. return; // got one
  44. }
  45. if (check == start)
  46. {
  47. level.sight_client = NULL;
  48. return; // nobody to see
  49. }
  50. }
  51. }
  52. //============================================================================
  53. /*
  54. =============
  55. ai_move
  56. Move the specified distance at current facing.
  57. This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
  58. ==============
  59. */
  60. void ai_move (edict_t *self, float dist)
  61. {
  62. M_walkmove (self, self->s.angles[YAW], dist);
  63. }
  64. /*
  65. =============
  66. ai_stand
  67. Used for standing around and looking for players
  68. Distance is for slight position adjustments needed by the animations
  69. ==============
  70. */
  71. void ai_stand (edict_t *self, float dist)
  72. {
  73. vec3_t v;
  74. if (dist)
  75. M_walkmove (self, self->s.angles[YAW], dist);
  76. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  77. {
  78. if (self->enemy)
  79. {
  80. VectorSubtract (self->enemy->s.origin, self->s.origin, v);
  81. self->ideal_yaw = vectoyaw(v);
  82. if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  83. {
  84. self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  85. self->monsterinfo.run (self);
  86. }
  87. M_ChangeYaw (self);
  88. ai_checkattack (self, 0);
  89. }
  90. else
  91. FindTarget (self);
  92. return;
  93. }
  94. if (FindTarget (self))
  95. return;
  96. if (level.time > self->monsterinfo.pausetime)
  97. {
  98. self->monsterinfo.walk (self);
  99. return;
  100. }
  101. if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
  102. {
  103. if (self->monsterinfo.idle_time)
  104. {
  105. self->monsterinfo.idle (self);
  106. self->monsterinfo.idle_time = level.time + 15 + random() * 15;
  107. }
  108. else
  109. {
  110. self->monsterinfo.idle_time = level.time + random() * 15;
  111. }
  112. }
  113. }
  114. /*
  115. =============
  116. ai_walk
  117. The monster is walking it's beat
  118. =============
  119. */
  120. void ai_walk (edict_t *self, float dist)
  121. {
  122. M_MoveToGoal (self, dist);
  123. // check for noticing a player
  124. if (FindTarget (self))
  125. return;
  126. if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
  127. {
  128. if (self->monsterinfo.idle_time)
  129. {
  130. self->monsterinfo.search (self);
  131. self->monsterinfo.idle_time = level.time + 15 + random() * 15;
  132. }
  133. else
  134. {
  135. self->monsterinfo.idle_time = level.time + random() * 15;
  136. }
  137. }
  138. }
  139. /*
  140. =============
  141. ai_charge
  142. Turns towards target and advances
  143. Use this call with a distnace of 0 to replace ai_face
  144. ==============
  145. */
  146. void ai_charge (edict_t *self, float dist)
  147. {
  148. vec3_t v;
  149. VectorSubtract (self->enemy->s.origin, self->s.origin, v);
  150. self->ideal_yaw = vectoyaw(v);
  151. M_ChangeYaw (self);
  152. if (dist)
  153. M_walkmove (self, self->s.angles[YAW], dist);
  154. }
  155. /*
  156. =============
  157. ai_turn
  158. don't move, but turn towards ideal_yaw
  159. Distance is for slight position adjustments needed by the animations
  160. =============
  161. */
  162. void ai_turn (edict_t *self, float dist)
  163. {
  164. if (dist)
  165. M_walkmove (self, self->s.angles[YAW], dist);
  166. if (FindTarget (self))
  167. return;
  168. M_ChangeYaw (self);
  169. }
  170. /*
  171. .enemy
  172. Will be world if not currently angry at anyone.
  173. .movetarget
  174. The next path spot to walk toward. If .enemy, ignore .movetarget.
  175. When an enemy is killed, the monster will try to return to it's path.
  176. .hunt_time
  177. Set to time + something when the player is in sight, but movement straight for
  178. him is blocked. This causes the monster to use wall following code for
  179. movement direction instead of sighting on the player.
  180. .ideal_yaw
  181. A yaw angle of the intended direction, which will be turned towards at up
  182. to 45 deg / state. If the enemy is in view and hunt_time is not active,
  183. this will be the exact line towards the enemy.
  184. .pausetime
  185. A monster will leave it's stand state and head towards it's .movetarget when
  186. time > .pausetime.
  187. walkmove(angle, speed) primitive is all or nothing
  188. */
  189. /*
  190. =============
  191. range
  192. returns the range catagorization of an entity reletive to self
  193. 0 melee range, will become hostile even if back is turned
  194. 1 visibility and infront, or visibility and show hostile
  195. 2 infront and show hostile
  196. 3 only triggered by damage
  197. =============
  198. */
  199. int range (edict_t *self, edict_t *other)
  200. {
  201. vec3_t v;
  202. float len;
  203. VectorSubtract (self->s.origin, other->s.origin, v);
  204. len = VectorLength (v);
  205. if (len < MELEE_DISTANCE)
  206. return RANGE_MELEE;
  207. if (len < 500)
  208. return RANGE_NEAR;
  209. if (len < 1000)
  210. return RANGE_MID;
  211. return RANGE_FAR;
  212. }
  213. /*
  214. =============
  215. visible
  216. returns 1 if the entity is visible to self, even if not infront ()
  217. =============
  218. */
  219. qboolean visible (edict_t *self, edict_t *other)
  220. {
  221. vec3_t spot1;
  222. vec3_t spot2;
  223. trace_t trace;
  224. VectorCopy (self->s.origin, spot1);
  225. spot1[2] += self->viewheight;
  226. VectorCopy (other->s.origin, spot2);
  227. spot2[2] += other->viewheight;
  228. trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
  229. if (trace.fraction == 1.0)
  230. return true;
  231. return false;
  232. }
  233. /*
  234. =============
  235. infront
  236. returns 1 if the entity is in front (in sight) of self
  237. =============
  238. */
  239. qboolean infront (edict_t *self, edict_t *other)
  240. {
  241. vec3_t vec;
  242. float dot;
  243. vec3_t forward;
  244. AngleVectors (self->s.angles, forward, NULL, NULL);
  245. VectorSubtract (other->s.origin, self->s.origin, vec);
  246. VectorNormalize (vec);
  247. dot = DotProduct (vec, forward);
  248. if (dot > 0.3)
  249. return true;
  250. return false;
  251. }
  252. //============================================================================
  253. void HuntTarget (edict_t *self)
  254. {
  255. vec3_t vec;
  256. self->goalentity = self->enemy;
  257. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  258. self->monsterinfo.stand (self);
  259. else
  260. self->monsterinfo.run (self);
  261. VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
  262. self->ideal_yaw = vectoyaw(vec);
  263. // wait a while before first attack
  264. if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
  265. AttackFinished (self, 1);
  266. }
  267. void FoundTarget (edict_t *self)
  268. {
  269. // let other monsters see this monster for a while
  270. if (self->enemy->client)
  271. {
  272. level.sight_entity = self;
  273. level.sight_entity_framenum = level.framenum;
  274. level.sight_entity->light_level = 128;
  275. }
  276. self->show_hostile = level.time + 1; // wake up other monsters
  277. VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
  278. self->monsterinfo.trail_time = level.time;
  279. if (!self->combattarget)
  280. {
  281. HuntTarget (self);
  282. return;
  283. }
  284. self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
  285. if (!self->movetarget)
  286. {
  287. self->goalentity = self->movetarget = self->enemy;
  288. HuntTarget (self);
  289. gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
  290. return;
  291. }
  292. // clear out our combattarget, these are a one shot deal
  293. self->combattarget = NULL;
  294. self->monsterinfo.aiflags |= AI_COMBAT_POINT;
  295. // clear the targetname, that point is ours!
  296. self->movetarget->targetname = NULL;
  297. self->monsterinfo.pausetime = 0;
  298. // run for it
  299. self->monsterinfo.run (self);
  300. }
  301. /*
  302. ===========
  303. FindTarget
  304. Self is currently not attacking anything, so try to find a target
  305. Returns TRUE if an enemy was sighted
  306. When a player fires a missile, the point of impact becomes a fakeplayer so
  307. that monsters that see the impact will respond as if they had seen the
  308. player.
  309. To avoid spending too much time, only a single client (or fakeclient) is
  310. checked each frame. This means multi player games will have slightly
  311. slower noticing monsters.
  312. ============
  313. */
  314. qboolean FindTarget (edict_t *self)
  315. {
  316. edict_t *client;
  317. qboolean heardit;
  318. int r;
  319. if (self->monsterinfo.aiflags & AI_GOOD_GUY)
  320. {
  321. if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
  322. {
  323. if (strcmp(self->goalentity->classname, "target_actor") == 0)
  324. return false;
  325. }
  326. //FIXME look for monsters?
  327. return false;
  328. }
  329. // if we're going to a combat point, just proceed
  330. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  331. return false;
  332. // if the first spawnflag bit is set, the monster will only wake up on
  333. // really seeing the player, not another monster getting angry or hearing
  334. // something
  335. // revised behavior so they will wake up if they "see" a player make a noise
  336. // but not weapon impact/explosion noises
  337. heardit = false;
  338. if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
  339. {
  340. client = level.sight_entity;
  341. if (client->enemy == self->enemy)
  342. {
  343. return false;
  344. }
  345. }
  346. else if (level.sound_entity_framenum >= (level.framenum - 1))
  347. {
  348. client = level.sound_entity;
  349. heardit = true;
  350. }
  351. else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
  352. {
  353. client = level.sound2_entity;
  354. heardit = true;
  355. }
  356. else
  357. {
  358. client = level.sight_client;
  359. if (!client)
  360. return false; // no clients to get mad at
  361. }
  362. // if the entity went away, forget it
  363. if (!client->inuse)
  364. return false;
  365. if (client == self->enemy)
  366. return true; // JDC false;
  367. if (client->client)
  368. {
  369. if (client->flags & FL_NOTARGET)
  370. return false;
  371. }
  372. else if (client->svflags & SVF_MONSTER)
  373. {
  374. if (!client->enemy)
  375. return false;
  376. if (client->enemy->flags & FL_NOTARGET)
  377. return false;
  378. }
  379. else if (heardit)
  380. {
  381. if (client->owner->flags & FL_NOTARGET)
  382. return false;
  383. }
  384. else
  385. return false;
  386. if (!heardit)
  387. {
  388. r = range (self, client);
  389. if (r == RANGE_FAR)
  390. return false;
  391. // this is where we would check invisibility
  392. // is client in an spot too dark to be seen?
  393. if (client->light_level <= 5)
  394. return false;
  395. if (!visible (self, client))
  396. {
  397. return false;
  398. }
  399. if (r == RANGE_NEAR)
  400. {
  401. if (client->show_hostile < level.time && !infront (self, client))
  402. {
  403. return false;
  404. }
  405. }
  406. else if (r == RANGE_MID)
  407. {
  408. if (!infront (self, client))
  409. {
  410. return false;
  411. }
  412. }
  413. self->enemy = client;
  414. if (strcmp(self->enemy->classname, "player_noise") != 0)
  415. {
  416. self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  417. if (!self->enemy->client)
  418. {
  419. self->enemy = self->enemy->enemy;
  420. if (!self->enemy->client)
  421. {
  422. self->enemy = NULL;
  423. return false;
  424. }
  425. }
  426. }
  427. }
  428. else // heardit
  429. {
  430. vec3_t temp;
  431. if (self->spawnflags & 1)
  432. {
  433. if (!visible (self, client))
  434. return false;
  435. }
  436. else
  437. {
  438. if (!gi.inPHS(self->s.origin, client->s.origin))
  439. return false;
  440. }
  441. VectorSubtract (client->s.origin, self->s.origin, temp);
  442. if (VectorLength(temp) > 1000) // too far to hear
  443. {
  444. return false;
  445. }
  446. // check area portals - if they are different and not connected then we can't hear it
  447. if (client->areanum != self->areanum)
  448. if (!gi.AreasConnected(self->areanum, client->areanum))
  449. return false;
  450. self->ideal_yaw = vectoyaw(temp);
  451. M_ChangeYaw (self);
  452. // hunt the sound for a bit; hopefully find the real player
  453. self->monsterinfo.aiflags |= AI_SOUND_TARGET;
  454. self->enemy = client;
  455. }
  456. //
  457. // got one
  458. //
  459. FoundTarget (self);
  460. if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
  461. self->monsterinfo.sight (self, self->enemy);
  462. return true;
  463. }
  464. //=============================================================================
  465. /*
  466. ============
  467. FacingIdeal
  468. ============
  469. */
  470. qboolean FacingIdeal(edict_t *self)
  471. {
  472. float delta;
  473. delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
  474. if (delta > 45 && delta < 315)
  475. return false;
  476. return true;
  477. }
  478. //=============================================================================
  479. qboolean M_CheckAttack (edict_t *self)
  480. {
  481. vec3_t spot1, spot2;
  482. float chance;
  483. trace_t tr;
  484. if (self->enemy->health > 0)
  485. {
  486. // see if any entities are in the way of the shot
  487. VectorCopy (self->s.origin, spot1);
  488. spot1[2] += self->viewheight;
  489. VectorCopy (self->enemy->s.origin, spot2);
  490. spot2[2] += self->enemy->viewheight;
  491. tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
  492. // do we have a clear shot?
  493. if (tr.ent != self->enemy)
  494. return false;
  495. }
  496. // melee attack
  497. if (enemy_range == RANGE_MELEE)
  498. {
  499. // don't always melee in easy mode
  500. if (skill->value == 0 && (rand()&3) )
  501. return false;
  502. if (self->monsterinfo.melee)
  503. self->monsterinfo.attack_state = AS_MELEE;
  504. else
  505. self->monsterinfo.attack_state = AS_MISSILE;
  506. return true;
  507. }
  508. // missile attack
  509. if (!self->monsterinfo.attack)
  510. return false;
  511. if (level.time < self->monsterinfo.attack_finished)
  512. return false;
  513. if (enemy_range == RANGE_FAR)
  514. return false;
  515. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  516. {
  517. chance = 0.4;
  518. }
  519. else if (enemy_range == RANGE_MELEE)
  520. {
  521. chance = 0.2;
  522. }
  523. else if (enemy_range == RANGE_NEAR)
  524. {
  525. chance = 0.1;
  526. }
  527. else if (enemy_range == RANGE_MID)
  528. {
  529. chance = 0.02;
  530. }
  531. else
  532. {
  533. return false;
  534. }
  535. if (skill->value == 0)
  536. chance *= 0.5;
  537. else if (skill->value >= 2)
  538. chance *= 2;
  539. if (random () < chance)
  540. {
  541. self->monsterinfo.attack_state = AS_MISSILE;
  542. self->monsterinfo.attack_finished = level.time + 2*random();
  543. return true;
  544. }
  545. if (self->flags & FL_FLY)
  546. {
  547. if (random() < 0.3)
  548. self->monsterinfo.attack_state = AS_SLIDING;
  549. else
  550. self->monsterinfo.attack_state = AS_STRAIGHT;
  551. }
  552. return false;
  553. }
  554. /*
  555. =============
  556. ai_run_melee
  557. Turn and close until within an angle to launch a melee attack
  558. =============
  559. */
  560. void ai_run_melee(edict_t *self)
  561. {
  562. self->ideal_yaw = enemy_yaw;
  563. M_ChangeYaw (self);
  564. if (FacingIdeal(self))
  565. {
  566. self->monsterinfo.melee (self);
  567. self->monsterinfo.attack_state = AS_STRAIGHT;
  568. }
  569. }
  570. /*
  571. =============
  572. ai_run_missile
  573. Turn in place until within an angle to launch a missile attack
  574. =============
  575. */
  576. void ai_run_missile(edict_t *self)
  577. {
  578. self->ideal_yaw = enemy_yaw;
  579. M_ChangeYaw (self);
  580. if (FacingIdeal(self))
  581. {
  582. self->monsterinfo.attack (self);
  583. self->monsterinfo.attack_state = AS_STRAIGHT;
  584. }
  585. };
  586. /*
  587. =============
  588. ai_run_slide
  589. Strafe sideways, but stay at aproximately the same range
  590. =============
  591. */
  592. void ai_run_slide(edict_t *self, float distance)
  593. {
  594. float ofs;
  595. self->ideal_yaw = enemy_yaw;
  596. M_ChangeYaw (self);
  597. if (self->monsterinfo.lefty)
  598. ofs = 90;
  599. else
  600. ofs = -90;
  601. if (M_walkmove (self, self->ideal_yaw + ofs, distance))
  602. return;
  603. self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
  604. M_walkmove (self, self->ideal_yaw - ofs, distance);
  605. }
  606. /*
  607. =============
  608. ai_checkattack
  609. Decides if we're going to attack or do something else
  610. used by ai_run and ai_stand
  611. =============
  612. */
  613. qboolean ai_checkattack (edict_t *self, float dist)
  614. {
  615. vec3_t temp;
  616. qboolean hesDeadJim;
  617. // this causes monsters to run blindly to the combat point w/o firing
  618. if (self->goalentity)
  619. {
  620. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  621. return false;
  622. if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  623. {
  624. if ((level.time - self->enemy->teleport_time) > 5.0)
  625. {
  626. if (self->goalentity == self->enemy)
  627. if (self->movetarget)
  628. self->goalentity = self->movetarget;
  629. else
  630. self->goalentity = NULL;
  631. self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  632. if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
  633. self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  634. }
  635. else
  636. {
  637. self->show_hostile = level.time + 1;
  638. return false;
  639. }
  640. }
  641. }
  642. enemy_vis = false;
  643. // see if the enemy is dead
  644. hesDeadJim = false;
  645. if ((!self->enemy) || (!self->enemy->inuse))
  646. {
  647. hesDeadJim = true;
  648. }
  649. else if (self->monsterinfo.aiflags & AI_MEDIC)
  650. {
  651. if (self->enemy->health > 0)
  652. {
  653. hesDeadJim = true;
  654. self->monsterinfo.aiflags &= ~AI_MEDIC;
  655. }
  656. }
  657. else
  658. {
  659. if (self->monsterinfo.aiflags & AI_BRUTAL)
  660. {
  661. if (self->enemy->health <= -80)
  662. hesDeadJim = true;
  663. }
  664. else
  665. {
  666. if (self->enemy->health <= 0)
  667. hesDeadJim = true;
  668. }
  669. }
  670. if (hesDeadJim)
  671. {
  672. self->enemy = NULL;
  673. // FIXME: look all around for other targets
  674. if (self->oldenemy && self->oldenemy->health > 0)
  675. {
  676. self->enemy = self->oldenemy;
  677. self->oldenemy = NULL;
  678. HuntTarget (self);
  679. }
  680. else
  681. {
  682. if (self->movetarget)
  683. {
  684. self->goalentity = self->movetarget;
  685. self->monsterinfo.walk (self);
  686. }
  687. else
  688. {
  689. // we need the pausetime otherwise the stand code
  690. // will just revert to walking with no target and
  691. // the monsters will wonder around aimlessly trying
  692. // to hunt the world entity
  693. self->monsterinfo.pausetime = level.time + 100000000;
  694. self->monsterinfo.stand (self);
  695. }
  696. return true;
  697. }
  698. }
  699. self->show_hostile = level.time + 1; // wake up other monsters
  700. // check knowledge of enemy
  701. enemy_vis = visible(self, self->enemy);
  702. if (enemy_vis)
  703. {
  704. self->monsterinfo.search_time = level.time + 5;
  705. VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
  706. }
  707. // look for other coop players here
  708. // if (coop && self->monsterinfo.search_time < level.time)
  709. // {
  710. // if (FindTarget (self))
  711. // return true;
  712. // }
  713. enemy_infront = infront(self, self->enemy);
  714. enemy_range = range(self, self->enemy);
  715. VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
  716. enemy_yaw = vectoyaw(temp);
  717. // JDC self->ideal_yaw = enemy_yaw;
  718. if (self->monsterinfo.attack_state == AS_MISSILE)
  719. {
  720. ai_run_missile (self);
  721. return true;
  722. }
  723. if (self->monsterinfo.attack_state == AS_MELEE)
  724. {
  725. ai_run_melee (self);
  726. return true;
  727. }
  728. // if enemy is not currently visible, we will never attack
  729. if (!enemy_vis)
  730. return false;
  731. return self->monsterinfo.checkattack (self);
  732. }
  733. /*
  734. =============
  735. ai_run
  736. The monster has an enemy it is trying to kill
  737. =============
  738. */
  739. void ai_run (edict_t *self, float dist)
  740. {
  741. vec3_t v;
  742. edict_t *tempgoal;
  743. edict_t *save;
  744. qboolean new;
  745. edict_t *marker;
  746. float d1, d2;
  747. trace_t tr;
  748. vec3_t v_forward, v_right;
  749. float left, center, right;
  750. vec3_t left_target, right_target;
  751. // if we're going to a combat point, just proceed
  752. if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
  753. {
  754. M_MoveToGoal (self, dist);
  755. return;
  756. }
  757. if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
  758. {
  759. VectorSubtract (self->s.origin, self->enemy->s.origin, v);
  760. if (VectorLength(v) < 64)
  761. {
  762. self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
  763. self->monsterinfo.stand (self);
  764. return;
  765. }
  766. M_MoveToGoal (self, dist);
  767. if (!FindTarget (self))
  768. return;
  769. }
  770. if (ai_checkattack (self, dist))
  771. return;
  772. if (self->monsterinfo.attack_state == AS_SLIDING)
  773. {
  774. ai_run_slide (self, dist);
  775. return;
  776. }
  777. if (enemy_vis)
  778. {
  779. // if (self.aiflags & AI_LOST_SIGHT)
  780. // dprint("regained sight\n");
  781. M_MoveToGoal (self, dist);
  782. self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
  783. VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
  784. self->monsterinfo.trail_time = level.time;
  785. return;
  786. }
  787. // coop will change to another enemy if visible
  788. if (coop->value)
  789. { // FIXME: insane guys get mad with this, which causes crashes!
  790. if (FindTarget (self))
  791. return;
  792. }
  793. if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20)))
  794. {
  795. M_MoveToGoal (self, dist);
  796. self->monsterinfo.search_time = 0;
  797. // dprint("search timeout\n");
  798. return;
  799. }
  800. save = self->goalentity;
  801. tempgoal = G_Spawn();
  802. self->goalentity = tempgoal;
  803. new = false;
  804. if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
  805. {
  806. // just lost sight of the player, decide where to go first
  807. // dprint("lost sight of player, last seen at "); dprint(vtos(self.last_sighting)); dprint("\n");
  808. self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
  809. self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
  810. new = true;
  811. }
  812. if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
  813. {
  814. self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
  815. // dprint("reached current goal: "); dprint(vtos(self.origin)); dprint(" "); dprint(vtos(self.last_sighting)); dprint(" "); dprint(ftos(vlen(self.origin - self.last_sighting))); dprint("\n");
  816. // give ourself more time since we got this far
  817. self->monsterinfo.search_time = level.time + 5;
  818. if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
  819. {
  820. // dprint("was temp goal; retrying original\n");
  821. self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
  822. marker = NULL;
  823. VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
  824. new = true;
  825. }
  826. else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
  827. {
  828. self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
  829. marker = PlayerTrail_PickFirst (self);
  830. }
  831. else
  832. {
  833. marker = PlayerTrail_PickNext (self);
  834. }
  835. if (marker)
  836. {
  837. VectorCopy (marker->s.origin, self->monsterinfo.last_sighting);
  838. self->monsterinfo.trail_time = marker->timestamp;
  839. self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
  840. // dprint("heading is "); dprint(ftos(self.ideal_yaw)); dprint("\n");
  841. // debug_drawline(self.origin, self.last_sighting, 52);
  842. new = true;
  843. }
  844. }
  845. VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v);
  846. d1 = VectorLength(v);
  847. if (d1 <= dist)
  848. {
  849. self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
  850. dist = d1;
  851. }
  852. VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin);
  853. if (new)
  854. {
  855. // gi.dprintf("checking for course correction\n");
  856. tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
  857. if (tr.fraction < 1)
  858. {
  859. VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  860. d1 = VectorLength(v);
  861. center = tr.fraction;
  862. d2 = d1 * ((center+1)/2);
  863. self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  864. AngleVectors(self->s.angles, v_forward, v_right, NULL);
  865. VectorSet(v, d2, -16, 0);
  866. G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
  867. tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
  868. left = tr.fraction;
  869. VectorSet(v, d2, 16, 0);
  870. G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
  871. tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
  872. right = tr.fraction;
  873. center = (d1*center)/d2;
  874. if (left >= center && left > right)
  875. {
  876. if (left < 1)
  877. {
  878. VectorSet(v, d2 * left * 0.5, -16, 0);
  879. G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
  880. // gi.dprintf("incomplete path, go part way and adjust again\n");
  881. }
  882. VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
  883. self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  884. VectorCopy (left_target, self->goalentity->s.origin);
  885. VectorCopy (left_target, self->monsterinfo.last_sighting);
  886. VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  887. self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  888. // gi.dprintf("adjusted left\n");
  889. // debug_drawline(self.origin, self.last_sighting, 152);
  890. }
  891. else if (right >= center && right > left)
  892. {
  893. if (right < 1)
  894. {
  895. VectorSet(v, d2 * right * 0.5, 16, 0);
  896. G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
  897. // gi.dprintf("incomplete path, go part way and adjust again\n");
  898. }
  899. VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
  900. self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
  901. VectorCopy (right_target, self->goalentity->s.origin);
  902. VectorCopy (right_target, self->monsterinfo.last_sighting);
  903. VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  904. self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
  905. // gi.dprintf("adjusted right\n");
  906. // debug_drawline(self.origin, self.last_sighting, 152);
  907. }
  908. }
  909. // else gi.dprintf("course was fine\n");
  910. }
  911. M_MoveToGoal (self, dist);
  912. G_FreeEdict(tempgoal);
  913. if (self)
  914. self->goalentity = save;
  915. }