Quake II RTX doxygen  1.0 dev
g_ai.c File Reference
#include "g_local.h"

Go to the source code of this file.

Functions

qboolean FindTarget (edict_t *self)
 
qboolean ai_checkattack (edict_t *self, float dist)
 
void AI_SetSightClient (void)
 
void ai_move (edict_t *self, float dist)
 
void ai_stand (edict_t *self, float dist)
 
void ai_walk (edict_t *self, float dist)
 
void ai_charge (edict_t *self, float dist)
 
void ai_turn (edict_t *self, float dist)
 
int range (edict_t *self, edict_t *other)
 
qboolean visible (edict_t *self, edict_t *other)
 
qboolean infront (edict_t *self, edict_t *other)
 
void HuntTarget (edict_t *self)
 
void FoundTarget (edict_t *self)
 
qboolean FacingIdeal (edict_t *self)
 
qboolean M_CheckAttack (edict_t *self)
 
void ai_run_melee (edict_t *self)
 
void ai_run_missile (edict_t *self)
 
void ai_run_slide (edict_t *self, float distance)
 
void ai_run (edict_t *self, float dist)
 

Variables

cvar_t * maxclients
 
qboolean enemy_vis
 
int enemy_range
 
float enemy_yaw
 

Function Documentation

◆ ai_charge()

void ai_charge ( edict_t *  self,
float  dist 
)

Definition at line 175 of file g_ai.c.

176 {
177  vec3_t v;
178 
179  VectorSubtract(self->enemy->s.origin, self->s.origin, v);
180  self->ideal_yaw = vectoyaw(v);
181  M_ChangeYaw(self);
182 
183  if (dist)
184  M_walkmove(self, self->s.angles[YAW], dist);
185 }

◆ ai_checkattack()

qboolean ai_checkattack ( edict_t *  self,
float  dist 
)

Definition at line 702 of file g_ai.c.

703 {
704  vec3_t temp;
705  qboolean hesDeadJim;
706 
707 // this causes monsters to run blindly to the combat point w/o firing
708  if (self->goalentity) {
709  if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
710  return qfalse;
711 
712  if (self->monsterinfo.aiflags & AI_SOUND_TARGET) {
713  if ((level.time - self->enemy->teleport_time) > 5.0) {
714  if (self->goalentity == self->enemy) {
715  if (self->movetarget)
716  self->goalentity = self->movetarget;
717  else
718  self->goalentity = NULL;
719  }
720  self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
721  if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
722  self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
723  } else {
724  self->show_hostile = level.time + 1;
725  return qfalse;
726  }
727  }
728  }
729 
730  enemy_vis = qfalse;
731 
732 // see if the enemy is dead
733  hesDeadJim = qfalse;
734  if ((!self->enemy) || (!self->enemy->inuse)) {
735  hesDeadJim = qtrue;
736  } else if (self->monsterinfo.aiflags & AI_MEDIC) {
737  if (self->enemy->health > 0) {
738  hesDeadJim = qtrue;
739  self->monsterinfo.aiflags &= ~AI_MEDIC;
740  }
741  } else {
742  if (self->monsterinfo.aiflags & AI_BRUTAL) {
743  if (self->enemy->health <= -80)
744  hesDeadJim = qtrue;
745  } else {
746  if (self->enemy->health <= 0)
747  hesDeadJim = qtrue;
748  }
749  }
750 
751  if (hesDeadJim) {
752  self->enemy = NULL;
753  // FIXME: look all around for other targets
754  if (self->oldenemy && self->oldenemy->health > 0) {
755  self->enemy = self->oldenemy;
756  self->oldenemy = NULL;
757  HuntTarget(self);
758  } else {
759  if (self->movetarget) {
760  self->goalentity = self->movetarget;
761  self->monsterinfo.walk(self);
762  } else {
763  // we need the pausetime otherwise the stand code
764  // will just revert to walking with no target and
765  // the monsters will wonder around aimlessly trying
766  // to hunt the world entity
767  self->monsterinfo.pausetime = level.time + 100000000;
768  self->monsterinfo.stand(self);
769  }
770  return qtrue;
771  }
772  }
773 
774  self->show_hostile = level.time + 1; // wake up other monsters
775 
776 // check knowledge of enemy
777  enemy_vis = visible(self, self->enemy);
778  if (enemy_vis) {
779  self->monsterinfo.search_time = level.time + 5;
780  VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
781  }
782 
783 // look for other coop players here
784 // if (coop && self->monsterinfo.search_time < level.time)
785 // {
786 // if (FindTarget (self))
787 // return qtrue;
788 // }
789 
790  enemy_range = range(self, self->enemy);
791  VectorSubtract(self->enemy->s.origin, self->s.origin, temp);
792  enemy_yaw = vectoyaw(temp);
793 
794 
795  // JDC self->ideal_yaw = enemy_yaw;
796 
797  if (self->monsterinfo.attack_state == AS_MISSILE) {
798  ai_run_missile(self);
799  return qtrue;
800  }
801  if (self->monsterinfo.attack_state == AS_MELEE) {
802  ai_run_melee(self);
803  return qtrue;
804  }
805 
806  // if enemy is not currently visible, we will never attack
807  if (!enemy_vis)
808  return qfalse;
809 
810  return self->monsterinfo.checkattack(self);
811 }

Referenced by ai_run(), and ai_stand().

◆ ai_move()

void ai_move ( edict_t *  self,
float  dist 
)

Definition at line 86 of file g_ai.c.

87 {
88  M_walkmove(self, self->s.angles[YAW], dist);
89 }

◆ ai_run()

void ai_run ( edict_t *  self,
float  dist 
)

Definition at line 821 of file g_ai.c.

822 {
823  vec3_t v;
824  edict_t *tempgoal;
825  edict_t *save;
826  qboolean new;
827  edict_t *marker;
828  float d1, d2;
829  trace_t tr;
830  vec3_t v_forward, v_right;
831  float left, center, right;
832  vec3_t left_target, right_target;
833 
834  // if we're going to a combat point, just proceed
835  if (self->monsterinfo.aiflags & AI_COMBAT_POINT) {
836  M_MoveToGoal(self, dist);
837  return;
838  }
839 
840  if (self->monsterinfo.aiflags & AI_SOUND_TARGET) {
841  VectorSubtract(self->s.origin, self->enemy->s.origin, v);
842  if (VectorLength(v) < 64) {
843  self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
844  self->monsterinfo.stand(self);
845  return;
846  }
847 
848  M_MoveToGoal(self, dist);
849 
850  if (!FindTarget(self))
851  return;
852  }
853 
854  if (ai_checkattack(self, dist))
855  return;
856 
857  if (self->monsterinfo.attack_state == AS_SLIDING) {
858  ai_run_slide(self, dist);
859  return;
860  }
861 
862  if (enemy_vis) {
863 // if (self.aiflags & AI_LOST_SIGHT)
864 // dprint("regained sight\n");
865  M_MoveToGoal(self, dist);
866  self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
867  VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
868  self->monsterinfo.trail_time = level.time;
869  return;
870  }
871 
872  // coop will change to another enemy if visible
873  if (coop->value) {
874  // FIXME: insane guys get mad with this, which causes crashes!
875  if (FindTarget(self))
876  return;
877  }
878 
879  if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20))) {
880  M_MoveToGoal(self, dist);
881  self->monsterinfo.search_time = 0;
882 // dprint("search timeout\n");
883  return;
884  }
885 
886  save = self->goalentity;
887  tempgoal = G_Spawn();
888  self->goalentity = tempgoal;
889 
890  new = qfalse;
891 
892  if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT)) {
893  // just lost sight of the player, decide where to go first
894 // dprint("lost sight of player, last seen at "); dprint(vtos(self.last_sighting)); dprint("\n");
895  self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
896  self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
897  new = qtrue;
898  }
899 
900  if (self->monsterinfo.aiflags & AI_PURSUE_NEXT) {
901  self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
902 // 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");
903 
904  // give ourself more time since we got this far
905  self->monsterinfo.search_time = level.time + 5;
906 
907  if (self->monsterinfo.aiflags & AI_PURSUE_TEMP) {
908 // dprint("was temp goal; retrying original\n");
909  self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
910  marker = NULL;
911  VectorCopy(self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
912  new = qtrue;
913  } else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN) {
914  self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
915  marker = PlayerTrail_PickFirst(self);
916  } else {
917  marker = PlayerTrail_PickNext(self);
918  }
919 
920  if (marker) {
921  VectorCopy(marker->s.origin, self->monsterinfo.last_sighting);
922  self->monsterinfo.trail_time = marker->timestamp;
923  self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
924 // dprint("heading is "); dprint(ftos(self.ideal_yaw)); dprint("\n");
925 
926 // debug_drawline(self.origin, self.last_sighting, 52);
927  new = qtrue;
928  }
929  }
930 
931  VectorSubtract(self->s.origin, self->monsterinfo.last_sighting, v);
932  d1 = VectorLength(v);
933  if (d1 <= dist) {
934  self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
935  dist = d1;
936  }
937 
938  VectorCopy(self->monsterinfo.last_sighting, self->goalentity->s.origin);
939 
940  if (new) {
941 // gi.dprintf("checking for course correction\n");
942 
943  tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
944  if (tr.fraction < 1) {
945  VectorSubtract(self->goalentity->s.origin, self->s.origin, v);
946  d1 = VectorLength(v);
947  center = tr.fraction;
948  d2 = d1 * ((center + 1) / 2);
949  self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
950  AngleVectors(self->s.angles, v_forward, v_right, NULL);
951 
952  VectorSet(v, d2, -16, 0);
953  G_ProjectSource(self->s.origin, v, v_forward, v_right, left_target);
954  tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
955  left = tr.fraction;
956 
957  VectorSet(v, d2, 16, 0);
958  G_ProjectSource(self->s.origin, v, v_forward, v_right, right_target);
959  tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
960  right = tr.fraction;
961 
962  center = (d1 * center) / d2;
963  if (left >= center && left > right) {
964  if (left < 1) {
965  VectorSet(v, d2 * left * 0.5, -16, 0);
966  G_ProjectSource(self->s.origin, v, v_forward, v_right, left_target);
967 // gi.dprintf("incomplete path, go part way and adjust again\n");
968  }
969  VectorCopy(self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
970  self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
971  VectorCopy(left_target, self->goalentity->s.origin);
972  VectorCopy(left_target, self->monsterinfo.last_sighting);
973  VectorSubtract(self->goalentity->s.origin, self->s.origin, v);
974  self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
975 // gi.dprintf("adjusted left\n");
976 // debug_drawline(self.origin, self.last_sighting, 152);
977  } else if (right >= center && right > left) {
978  if (right < 1) {
979  VectorSet(v, d2 * right * 0.5, 16, 0);
980  G_ProjectSource(self->s.origin, v, v_forward, v_right, right_target);
981 // gi.dprintf("incomplete path, go part way and adjust again\n");
982  }
983  VectorCopy(self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
984  self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
985  VectorCopy(right_target, self->goalentity->s.origin);
986  VectorCopy(right_target, self->monsterinfo.last_sighting);
987  VectorSubtract(self->goalentity->s.origin, self->s.origin, v);
988  self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
989 // gi.dprintf("adjusted right\n");
990 // debug_drawline(self.origin, self.last_sighting, 152);
991  }
992  }
993 // else gi.dprintf("course was fine\n");
994  }
995 
996  M_MoveToGoal(self, dist);
997 
998  G_FreeEdict(tempgoal);
999 
1000  if (self)
1001  self->goalentity = save;
1002 }

◆ ai_run_melee()

void ai_run_melee ( edict_t *  self)

Definition at line 636 of file g_ai.c.

637 {
638  self->ideal_yaw = enemy_yaw;
639  M_ChangeYaw(self);
640 
641  if (FacingIdeal(self)) {
642  self->monsterinfo.melee(self);
643  self->monsterinfo.attack_state = AS_STRAIGHT;
644  }
645 }

Referenced by ai_checkattack().

◆ ai_run_missile()

void ai_run_missile ( edict_t *  self)

Definition at line 655 of file g_ai.c.

656 {
657  self->ideal_yaw = enemy_yaw;
658  M_ChangeYaw(self);
659 
660  if (FacingIdeal(self)) {
661  self->monsterinfo.attack(self);
662  self->monsterinfo.attack_state = AS_STRAIGHT;
663  }
664 }

Referenced by ai_checkattack().

◆ ai_run_slide()

void ai_run_slide ( edict_t *  self,
float  distance 
)

Definition at line 674 of file g_ai.c.

675 {
676  float ofs;
677 
678  self->ideal_yaw = enemy_yaw;
679  M_ChangeYaw(self);
680 
681  if (self->monsterinfo.lefty)
682  ofs = 90;
683  else
684  ofs = -90;
685 
686  if (M_walkmove(self, self->ideal_yaw + ofs, distance))
687  return;
688 
689  self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
690  M_walkmove(self, self->ideal_yaw - ofs, distance);
691 }

Referenced by ai_run().

◆ AI_SetSightClient()

void AI_SetSightClient ( void  )

Definition at line 47 of file g_ai.c.

48 {
49  edict_t *ent;
50  int start, check;
51 
52  if (level.sight_client == NULL)
53  start = 1;
54  else
55  start = level.sight_client - g_edicts;
56 
57  check = start;
58  while (1) {
59  check++;
60  if (check > game.maxclients)
61  check = 1;
62  ent = &g_edicts[check];
63  if (ent->inuse
64  && ent->health > 0
65  && !(ent->flags & FL_NOTARGET)) {
66  level.sight_client = ent;
67  return; // got one
68  }
69  if (check == start) {
70  level.sight_client = NULL;
71  return; // nobody to see
72  }
73  }
74 }

Referenced by G_RunFrame().

◆ ai_stand()

void ai_stand ( edict_t *  self,
float  dist 
)

Definition at line 100 of file g_ai.c.

101 {
102  vec3_t v;
103 
104  if (dist)
105  M_walkmove(self, self->s.angles[YAW], dist);
106 
107  if (self->monsterinfo.aiflags & AI_STAND_GROUND) {
108  if (self->enemy) {
109  VectorSubtract(self->enemy->s.origin, self->s.origin, v);
110  self->ideal_yaw = vectoyaw(v);
111  if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) {
112  self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
113  self->monsterinfo.run(self);
114  }
115  M_ChangeYaw(self);
116  ai_checkattack(self, 0);
117  } else
118  FindTarget(self);
119  return;
120  }
121 
122  if (FindTarget(self))
123  return;
124 
125  if (level.time > self->monsterinfo.pausetime) {
126  self->monsterinfo.walk(self);
127  return;
128  }
129 
130  if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time)) {
131  if (self->monsterinfo.idle_time) {
132  self->monsterinfo.idle(self);
133  self->monsterinfo.idle_time = level.time + 15 + random() * 15;
134  } else {
135  self->monsterinfo.idle_time = level.time + random() * 15;
136  }
137  }
138 }

◆ ai_turn()

void ai_turn ( edict_t *  self,
float  dist 
)

Definition at line 196 of file g_ai.c.

197 {
198  if (dist)
199  M_walkmove(self, self->s.angles[YAW], dist);
200 
201  if (FindTarget(self))
202  return;
203 
204  M_ChangeYaw(self);
205 }

◆ ai_walk()

void ai_walk ( edict_t *  self,
float  dist 
)

Definition at line 148 of file g_ai.c.

149 {
150  M_MoveToGoal(self, dist);
151 
152  // check for noticing a player
153  if (FindTarget(self))
154  return;
155 
156  if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time)) {
157  if (self->monsterinfo.idle_time) {
158  self->monsterinfo.search(self);
159  self->monsterinfo.idle_time = level.time + 15 + random() * 15;
160  } else {
161  self->monsterinfo.idle_time = level.time + random() * 15;
162  }
163  }
164 }

◆ FacingIdeal()

qboolean FacingIdeal ( edict_t *  self)

Definition at line 540 of file g_ai.c.

541 {
542  float delta;
543 
544  delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
545  if (delta > 45 && delta < 315)
546  return qfalse;
547  return qtrue;
548 }

Referenced by ai_run_melee(), and ai_run_missile().

◆ FindTarget()

qboolean FindTarget ( edict_t *  self)

Definition at line 385 of file g_ai.c.

386 {
387  edict_t *client;
388  qboolean heardit;
389  int r;
390 
391  if (self->monsterinfo.aiflags & AI_GOOD_GUY) {
392  if (self->goalentity && self->goalentity->inuse && self->goalentity->classname) {
393  if (strcmp(self->goalentity->classname, "target_actor") == 0)
394  return qfalse;
395  }
396 
397  //FIXME look for monsters?
398  return qfalse;
399  }
400 
401  // if we're going to a combat point, just proceed
402  if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
403  return qfalse;
404 
405 // if the first spawnflag bit is set, the monster will only wake up on
406 // really seeing the player, not another monster getting angry or hearing
407 // something
408 
409 // revised behavior so they will wake up if they "see" a player make a noise
410 // but not weapon impact/explosion noises
411 
412  heardit = qfalse;
413  if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1)) {
414  client = level.sight_entity;
415  if (client->enemy == self->enemy) {
416  return qfalse;
417  }
418  } else if (level.sound_entity_framenum >= (level.framenum - 1)) {
419  client = level.sound_entity;
420  heardit = qtrue;
421  } else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1)) {
422  client = level.sound2_entity;
423  heardit = qtrue;
424  } else {
425  client = level.sight_client;
426  if (!client)
427  return qfalse; // no clients to get mad at
428  }
429 
430  // if the entity went away, forget it
431  if (!client->inuse)
432  return qfalse;
433 
434  if (client == self->enemy)
435  return qtrue; // JDC qfalse;
436 
437  if (client->client) {
438  if (client->flags & FL_NOTARGET)
439  return qfalse;
440  } else if (client->svflags & SVF_MONSTER) {
441  if (!client->enemy)
442  return qfalse;
443  if (client->enemy->flags & FL_NOTARGET)
444  return qfalse;
445  } else if (heardit) {
446  if (client->owner->flags & FL_NOTARGET)
447  return qfalse;
448  } else
449  return qfalse;
450 
451  if (!heardit) {
452  r = range(self, client);
453 
454  if (r == RANGE_FAR)
455  return qfalse;
456 
457 // this is where we would check invisibility
458 
459  // is client in an spot too dark to be seen?
460  if (client->light_level <= 5)
461  return qfalse;
462 
463  if (!visible(self, client)) {
464  return qfalse;
465  }
466 
467  if (r == RANGE_NEAR) {
468  if (client->show_hostile < level.time && !infront(self, client)) {
469  return qfalse;
470  }
471  } else if (r == RANGE_MID) {
472  if (!infront(self, client)) {
473  return qfalse;
474  }
475  }
476 
477  self->enemy = client;
478 
479  if (strcmp(self->enemy->classname, "player_noise") != 0) {
480  self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
481 
482  if (!self->enemy->client) {
483  self->enemy = self->enemy->enemy;
484  if (!self->enemy->client) {
485  self->enemy = NULL;
486  return qfalse;
487  }
488  }
489  }
490  } else { // heardit
491  vec3_t temp;
492 
493  if (self->spawnflags & 1) {
494  if (!visible(self, client))
495  return qfalse;
496  } else {
497  if (!gi.inPHS(self->s.origin, client->s.origin))
498  return qfalse;
499  }
500 
501  VectorSubtract(client->s.origin, self->s.origin, temp);
502 
503  if (VectorLength(temp) > 1000) { // too far to hear
504  return qfalse;
505  }
506 
507  // check area portals - if they are different and not connected then we can't hear it
508  if (client->areanum != self->areanum)
509  if (!gi.AreasConnected(self->areanum, client->areanum))
510  return qfalse;
511 
512  self->ideal_yaw = vectoyaw(temp);
513  M_ChangeYaw(self);
514 
515  // hunt the sound for a bit; hopefully find the real player
516  self->monsterinfo.aiflags |= AI_SOUND_TARGET;
517  self->enemy = client;
518  }
519 
520 //
521 // got one
522 //
523  FoundTarget(self);
524 
525  if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
526  self->monsterinfo.sight(self, self->enemy);
527 
528  return qtrue;
529 }

Referenced by ai_run(), ai_stand(), ai_turn(), ai_walk(), and turret_driver_think().

◆ FoundTarget()

void FoundTarget ( edict_t *  self)

Definition at line 328 of file g_ai.c.

329 {
330  // let other monsters see this monster for a while
331  if (self->enemy->client) {
332  level.sight_entity = self;
334  level.sight_entity->light_level = 128;
335  }
336 
337  self->show_hostile = level.time + 1; // wake up other monsters
338 
339  VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
340  self->monsterinfo.trail_time = level.time;
341 
342  if (!self->combattarget) {
343  HuntTarget(self);
344  return;
345  }
346 
347  self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
348  if (!self->movetarget) {
349  self->goalentity = self->movetarget = self->enemy;
350  HuntTarget(self);
351  gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
352  return;
353  }
354 
355  // clear out our combattarget, these are a one shot deal
356  self->combattarget = NULL;
357  self->monsterinfo.aiflags |= AI_COMBAT_POINT;
358 
359  // clear the targetname, that point is ours!
360  self->movetarget->targetname = NULL;
361  self->monsterinfo.pausetime = 0;
362 
363  // run for it
364  self->monsterinfo.run(self);
365 }

Referenced by FindTarget(), M_ReactToDamage(), medic_cable_attack(), medic_idle(), medic_run(), medic_search(), monster_triggered_spawn(), and monster_use().

◆ HuntTarget()

void HuntTarget ( edict_t *  self)

Definition at line 312 of file g_ai.c.

313 {
314  vec3_t vec;
315 
316  self->goalentity = self->enemy;
317  if (self->monsterinfo.aiflags & AI_STAND_GROUND)
318  self->monsterinfo.stand(self);
319  else
320  self->monsterinfo.run(self);
321  VectorSubtract(self->enemy->s.origin, self->s.origin, vec);
322  self->ideal_yaw = vectoyaw(vec);
323  // wait a while before first attack
324  if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
325  AttackFinished(self, 1);
326 }

Referenced by ai_checkattack(), and FoundTarget().

◆ infront()

qboolean infront ( edict_t *  self,
edict_t *  other 
)

Definition at line 293 of file g_ai.c.

294 {
295  vec3_t vec;
296  float dot;
297  vec3_t forward;
298 
299  AngleVectors(self->s.angles, forward, NULL, NULL);
300  VectorSubtract(other->s.origin, self->s.origin, vec);
301  VectorNormalize(vec);
302  dot = DotProduct(vec, forward);
303 
304  if (dot > 0.3)
305  return qtrue;
306  return qfalse;
307 }

Referenced by boss2_reattack_mg(), check_dodge(), FindTarget(), and GL_CullLocalBox().

◆ M_CheckAttack()

qboolean M_CheckAttack ( edict_t *  self)

Definition at line 553 of file g_ai.c.

554 {
555  vec3_t spot1, spot2;
556  float chance;
557  trace_t tr;
558 
559  if (self->enemy->health > 0) {
560  // see if any entities are in the way of the shot
561  VectorCopy(self->s.origin, spot1);
562  spot1[2] += self->viewheight;
563  VectorCopy(self->enemy->s.origin, spot2);
564  spot2[2] += self->enemy->viewheight;
565 
566  tr = gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA | CONTENTS_WINDOW);
567 
568  // do we have a clear shot?
569  if (tr.ent != self->enemy)
570  return qfalse;
571  }
572 
573  // melee attack
574  if (enemy_range == RANGE_MELEE) {
575  // don't always melee in easy mode
576  if (skill->value == 0 && (rand() & 3))
577  return qfalse;
578  if (self->monsterinfo.melee)
579  self->monsterinfo.attack_state = AS_MELEE;
580  else
581  self->monsterinfo.attack_state = AS_MISSILE;
582  return qtrue;
583  }
584 
585 // missile attack
586  if (!self->monsterinfo.attack)
587  return qfalse;
588 
589  if (level.time < self->monsterinfo.attack_finished)
590  return qfalse;
591 
592  if (enemy_range == RANGE_FAR)
593  return qfalse;
594 
595  if (self->monsterinfo.aiflags & AI_STAND_GROUND) {
596  chance = 0.4;
597  } else if (enemy_range == RANGE_MELEE) {
598  chance = 0.2;
599  } else if (enemy_range == RANGE_NEAR) {
600  chance = 0.1;
601  } else if (enemy_range == RANGE_MID) {
602  chance = 0.02;
603  } else {
604  return qfalse;
605  }
606 
607  if (skill->value == 0)
608  chance *= 0.5;
609  else if (skill->value >= 2)
610  chance *= 2;
611 
612  if (random() < chance) {
613  self->monsterinfo.attack_state = AS_MISSILE;
614  self->monsterinfo.attack_finished = level.time + 2 * random();
615  return qtrue;
616  }
617 
618  if (self->flags & FL_FLY) {
619  if (random() < 0.3)
620  self->monsterinfo.attack_state = AS_SLIDING;
621  else
622  self->monsterinfo.attack_state = AS_STRAIGHT;
623  }
624 
625  return qfalse;
626 }

Referenced by medic_checkattack(), and monster_start().

◆ range()

int range ( edict_t *  self,
edict_t *  other 
)

◆ visible()

qboolean visible ( edict_t *  self,
edict_t *  other 
)

Definition at line 268 of file g_ai.c.

269 {
270  vec3_t spot1;
271  vec3_t spot2;
272  trace_t trace;
273 
274  VectorCopy(self->s.origin, spot1);
275  spot1[2] += self->viewheight;
276  VectorCopy(other->s.origin, spot2);
277  spot2[2] += other->viewheight;
278  trace = gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
279 
280  if (trace.fraction == 1.0)
281  return qtrue;
282  return qfalse;
283 }

Referenced by ai_checkattack(), chick_rerocket(), ClientBeginServerFrame(), FindTarget(), get_sunlight(), gunner_refire_chain(), hover_reattack(), jorg_reattack1(), M_ReactToDamage(), medic_continue(), medic_FindDeadMonster(), PlayerTrail_PickFirst(), supertank_reattack1(), tank_reattack_blaster(), tank_refire_rocket(), and turret_driver_think().

Variable Documentation

◆ enemy_range

int enemy_range

◆ enemy_vis

qboolean enemy_vis

Definition at line 27 of file g_ai.c.

Referenced by ai_checkattack(), and ai_run().

◆ enemy_yaw

◆ maxclients

gi
game_import_t gi
Definition: g_main.c:23
RANGE_NEAR
#define RANGE_NEAR
Definition: g_local.h:117
G_ProjectSource
void G_ProjectSource(const vec3_t point, const vec3_t distance, const vec3_t forward, const vec3_t right, vec3_t result)
Definition: g_utils.c:23
AI_MEDIC
#define AI_MEDIC
Definition: g_local.h:139
AI_COMBAT_POINT
#define AI_COMBAT_POINT
Definition: g_local.h:138
AI_BRUTAL
#define AI_BRUTAL
Definition: g_local.h:135
G_Spawn
edict_t * G_Spawn(void)
Definition: g_utils.c:391
MELEE_DISTANCE
#define MELEE_DISTANCE
Definition: g_local.h:82
AI_TEMP_STAND_GROUND
#define AI_TEMP_STAND_GROUND
Definition: g_local.h:127
AI_GOOD_GUY
#define AI_GOOD_GUY
Definition: g_local.h:134
PlayerTrail_PickNext
edict_t * PlayerTrail_PickNext(edict_t *self)
Definition: p_trail.c:118
level_locals_t::sight_client
edict_t * sight_client
Definition: g_local.h:312
AttackFinished
void AttackFinished(edict_t *self, float time)
Definition: g_monster.c:133
M_walkmove
qboolean M_walkmove(edict_t *ent, float yaw, float dist)
Definition: m_move.c:503
ai_checkattack
qboolean ai_checkattack(edict_t *self, float dist)
Definition: g_ai.c:702
AI_LOST_SIGHT
#define AI_LOST_SIGHT
Definition: g_local.h:129
AI_STAND_GROUND
#define AI_STAND_GROUND
Definition: g_local.h:126
ai_run_missile
void ai_run_missile(edict_t *self)
Definition: g_ai.c:655
vectoyaw
float vectoyaw(vec3_t vec)
Definition: g_utils.c:310
AI_PURSUE_TEMP
#define AI_PURSUE_TEMP
Definition: g_local.h:132
g_edicts
edict_t * g_edicts
Definition: g_main.c:31
other
@ other
Definition: ogg.c:63
infront
qboolean infront(edict_t *self, edict_t *other)
Definition: g_ai.c:293
HuntTarget
void HuntTarget(edict_t *self)
Definition: g_ai.c:312
level_locals_t::sight_entity_framenum
int sight_entity_framenum
Definition: g_local.h:315
vec3_origin
vec3_t vec3_origin
Definition: shared.c:21
G_FreeEdict
void G_FreeEdict(edict_t *e)
Definition: g_utils.c:421
ai_run_melee
void ai_run_melee(edict_t *self)
Definition: g_ai.c:636
AS_SLIDING
#define AS_SLIDING
Definition: g_local.h:144
M_MoveToGoal
void M_MoveToGoal(edict_t *ent, float dist)
Definition: m_move.c:477
forward
static vec3_t forward
Definition: p_view.c:27
FindTarget
qboolean FindTarget(edict_t *self)
Definition: g_ai.c:385
AS_MISSILE
#define AS_MISSILE
Definition: g_local.h:146
vtos
char * vtos(vec3_t v)
Definition: g_utils.c:275
AI_PURSUIT_LAST_SEEN
#define AI_PURSUIT_LAST_SEEN
Definition: g_local.h:130
game
game_locals_t game
Definition: g_main.c:21
random
#define random()
Definition: g_local.h:504
AngleVectors
void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Definition: shared.c:23
FL_NOTARGET
#define FL_NOTARGET
Definition: g_local.h:64
FoundTarget
void FoundTarget(edict_t *self)
Definition: g_ai.c:328
level_locals_t::sound2_entity
edict_t * sound2_entity
Definition: g_local.h:318
level_locals_t::sight_entity
edict_t * sight_entity
Definition: g_local.h:314
skill
cvar_t * skill
Definition: g_main.c:36
level_locals_t::framenum
int framenum
Definition: g_local.h:298
level_locals_t::time
float time
Definition: g_local.h:299
PlayerTrail_PickFirst
edict_t * PlayerTrail_PickFirst(edict_t *self)
Definition: p_trail.c:92
coop
cvar_t * coop
Definition: g_main.c:34
AI_SOUND_TARGET
#define AI_SOUND_TARGET
Definition: g_local.h:128
visible
qboolean visible(edict_t *self, edict_t *other)
Definition: g_ai.c:268
level_locals_t::sound_entity
edict_t * sound_entity
Definition: g_local.h:316
RANGE_MELEE
#define RANGE_MELEE
Definition: g_local.h:116
level_locals_t::sound_entity_framenum
int sound_entity_framenum
Definition: g_local.h:317
right
static vec3_t right
Definition: p_view.c:27
enemy_range
int enemy_range
Definition: g_ai.c:28
enemy_vis
qboolean enemy_vis
Definition: g_ai.c:27
level
level_locals_t level
Definition: g_main.c:22
RANGE_MID
#define RANGE_MID
Definition: g_local.h:118
FL_FLY
#define FL_FLY
Definition: g_local.h:59
ai_run_slide
void ai_run_slide(edict_t *self, float distance)
Definition: g_ai.c:674
AI_PURSUE_NEXT
#define AI_PURSUE_NEXT
Definition: g_local.h:131
range
int range(edict_t *self, edict_t *other)
Definition: g_ai.c:245
M_ChangeYaw
void M_ChangeYaw(edict_t *ent)
Definition: m_move.c:279
FacingIdeal
qboolean FacingIdeal(edict_t *self)
Definition: g_ai.c:540
G_PickTarget
edict_t * G_PickTarget(char *targetname)
Definition: g_utils.c:114
level_locals_t::sound2_entity_framenum
int sound2_entity_framenum
Definition: g_local.h:319
AS_MELEE
#define AS_MELEE
Definition: g_local.h:145
RANGE_FAR
#define RANGE_FAR
Definition: g_local.h:119
VectorNormalize
vec_t VectorNormalize(vec3_t v)
Definition: shared.c:55
enemy_yaw
float enemy_yaw
Definition: g_ai.c:29
AS_STRAIGHT
#define AS_STRAIGHT
Definition: g_local.h:143
game_locals_t::maxclients
int maxclients
Definition: g_local.h:280