home *** CD-ROM | disk | FTP | other *** search
/ Gambler 19 / GAMBLERCD19.BIN / UTILS / 3D / BRONIE / DUAL_LAU.ZIP / src / g_ai.c next >
C/C++ Source or Header  |  1998-01-13  |  25KB  |  1,096 lines

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