UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_ai.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "g_ai.h"
27 #include "g_actor.h"
28 #include "g_client.h"
29 #include "g_combat.h"
30 #include "g_edicts.h"
31 #include "g_health.h"
32 #include "g_inventory.h"
33 #include "g_move.h"
34 #include "g_utils.h"
35 #include "g_vis.h"
36 #include "g_reaction.h"
37 
38 class AiAction {
39 public:
45  const fireDef_t* fd;
46  int z_align;
48  inline void reset() {
49  OBJZERO(*this);
50  }
51 };
52 
58 }
59 
63 AiAreaSearch::AiAreaSearch(const pos3_t origin, int radius, bool flat) {
64  plotArea(origin, radius, flat);
65 }
66 
71  _area.clear();
72 }
73 
80  return _area.dequeue(pos);
81 }
82 
85  clear();
86 }
87 
93  qnode_s* node = static_cast<qnode_s*>(G_TagMalloc(sizeof(qnode_s), TAG_LEVEL));
94  VectorCopy(data, node->data);
95  node->next = nullptr;
96 
97  if (isEmpty())
98  _head = _tail = node;
99  else {
100  _tail->next = node;
101  _tail = node;
102  }
103  _count++;
104 }
105 
112  if (isEmpty())
113  return false;
114 
115  VectorCopy(_head->data, data);
116  qnode_s* node = _head;
117  _head = _head->next;
118  _count--;
119  G_MemFree(node);
120 
121  return true;
122 }
123 
128  qnode_s* next = nullptr;
129  for (qnode_s* node = _head; node; node = next) {
130  next = node->next;
131  G_MemFree(node);
132  }
133  _head = _tail = nullptr;
134  _count = 0;
135 }
136 
143 void AiAreaSearch::plotArea(const pos3_t origin, int radius, bool flat) {
144  /* Starting with a sphere with radius 0 (just the center pos) out to the given radius */
145  for (int i = 0; i <= radius; ++i) {
146  /* Battlescape has max PATHFINDING_HEIGHT levels, so cap the z offset to the max levels
147  * the unit can currently go up or down */
148  const int zOfsMax = std::min(std::max(static_cast<int>(origin[2]), PATHFINDING_HEIGHT - origin[2] - 1), i);
149  /* Starting a the center z level up to zOfsMax levels */
150  if (flat)
151  plotCircle(origin, i);
152  else
153  for (int zOfs = 0; zOfs <= zOfsMax; ++zOfs) {
154  pos3_t center = {origin[0], origin[1], static_cast<pos_t>(origin[2] + zOfs)};
155  plotCircle(center, i - zOfs);
156  /* We need to cover both up and down directions, so we Flip the sign (if there's actually a z offset) */
157  if (zOfs != 0) {
158  center[2] = origin[2] - zOfs;
159  plotCircle(center, i - zOfs);
160  }
161  }
162  }
163 }
164 
165 void AiAreaSearch::plotCircle(const pos3_t origin, int radius) {
166  /* There are always more levels in one direction than the other (since the number is even)
167  * so check if out of bounds */
168  if (origin[2] >= PATHFINDING_HEIGHT)
169  return;
170  /* This is the main loop of the midpoint circle algorithm this specific variation
171  * circles of consecutive integer radius won't overlap in any point or leave gaps in between,
172  * Coincidentally (at time of writing) a circle of radius n will be exactly the farthest a normal actor can walk
173  * with n * 2 TU, in ideal conditions (standing, no obstacles, no penalties) */
174  for (int dy = 1, xOfs = 0, yOfs = radius; xOfs <= yOfs; ++xOfs, dy += 1, yOfs = radius - (dy >> 1)) {
175  /* Have the offsets, now to fill the octants */
176  plotPos(origin, xOfs, yOfs);
177  /* Need to Flip the signs, for each one */
178  if (xOfs != 0) {
179  plotPos(origin, -xOfs, yOfs);
180  }
181  if (yOfs != 0) {
182  plotPos(origin, xOfs, -yOfs);
183  }
184  if (xOfs != 0 && yOfs != 0) {
185  plotPos(origin, -xOfs, -yOfs);
186  }
187  /* And need to Flip the x and y offsets too */
188  if (xOfs != yOfs) {
189  plotPos(origin, yOfs, xOfs);
190  if (yOfs != 0) {
191  plotPos(origin, -yOfs, xOfs);
192  }
193  if (xOfs != 0) {
194  plotPos(origin, yOfs, -xOfs);
195  }
196  if (yOfs != 0 && xOfs != 0) {
197  plotPos(origin, -yOfs, -xOfs);
198  }
199  }
200  }
201 }
202 
203 void AiAreaSearch::plotPos(const pos3_t origin, int xOfs, int dy) {
204  /* There's more distance to one (possible) edge of the map than the other
205  * check if out of bounds to be safe */
206  if (origin[0] + xOfs < 0 || origin[0] + xOfs >= PATHFINDING_WIDTH || origin[1] + dy < 0
207  || origin[1] + dy >= PATHFINDING_WIDTH)
208  return;
209  const pos3_t pos = {static_cast<pos_t>(origin[0] + xOfs), static_cast<pos_t>(origin[1] + dy), origin[2]};
210  /* Most maps won't use the full space available so check against the actual map area */
211  vec3_t vec;
212  PosToVec(pos, vec);
213  if (!gi.isOnMap(vec))
214  return;
215  _area.enqueue(pos);
216 }
217 
218 #define SCORE_HIDE 60
219 #define SCORE_CLOSE_IN 20
220 #define SCORE_KILL 30
221 #define SCORE_RANDOM 10
222 #define SCORE_REACTION_ERADICATION 30
223 #define SCORE_REACTION_FEAR_FACTOR 20
224 #define SCORE_NONHIDING_PLACE_PENALTY 25
225 #define SCORE_RAGE 40
226 #define SCORE_DAMAGE 100.0f
227 #define SCORE_DAMAGE_FACTOR 1.25f
228 #define SCORE_CIV_FACTOR 0.25f
229 #define SCORE_DISABLED_FACTOR 0.25f
230 #define SCORE_DAMAGE_WORTH_FACTOR 0.1f
231 
232 #define SCORE_CIV_RANDOM 10
233 #define SCORE_RUN_AWAY 50
234 #define SCORE_CIV_LAZINESS 5
235 #define RUN_AWAY_DIST 160
236 #define WAYPOINT_CIV_DIST 768
237 #define HERD_THRESHOLD 128.0f
238 #define SCORE_HERDING_PENALTY 100
239 #define SCORE_NOSAFE_POSITION_PENALTY 500
240 
241 #define SCORE_MISSION_OPPONENT_TARGET 50
242 #define SCORE_MISSION_TARGET 60
243 #define SCORE_MISSION_HOLD 25
244 #define MISSION_HOLD_DIST 96
245 
246 #define SCORE_PANIC_RUN_TO_FRIENDS 300.0f
247 #define SCORE_PANIC_FLEE_FROM_STRANGERS 500.0f
248 #define SCORE_PANIC_RANDOM 25.0f
249 
250 #define AI_ACTION_NOTHING_FOUND -10000.0f
251 
252 #define CLOSE_IN_DIST 1200.0f
253 #define SPREAD_FACTOR 8.0f
254 #define SPREAD_NORM(x) ((x) > 0 ? SPREAD_FACTOR/((x)*torad) : 0)
255 
256 #define HIDE_DIST 25
257 #define HERD_DIST 25
258 #define HOLD_DIST 3
259 
260 #define CALC_DAMAGE_SAMPLES 10.0f
261 #define INVDEF_FOR_SHOOTTYPE(st) (IS_SHOT_RIGHT(st)?INVDEF(CID_RIGHT):IS_SHOT_LEFT(st)?INVDEF(CID_LEFT):IS_SHOT_HEADGEAR(st)?INVDEF(CID_HEADGEAR):nullptr)
262 
265 
266 void AI_Init (void)
267 {
268  hidePathingTable = nullptr;
269  herdPathingTable = nullptr;
270 }
271 
275 bool AI_HasLineOfFire (const Actor* actor, const Edict* target)
276 {
277  for (shoot_types_t shootType = ST_RIGHT; shootType < ST_NUM_SHOOT_TYPES; shootType++) {
278  const Item* item = AI_GetItemForShootType(shootType, actor);
279  if (item == nullptr)
280  continue;
281 
282  const fireDef_t* fdArray = item->getFiredefs();
283  if (fdArray == nullptr)
284  continue;
285 
286  for (fireDefIndex_t fdIdx = 0; fdIdx < item->ammoDef()->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
287  const fireDef_t* fd = &fdArray[fdIdx];
288  if (AI_CheckLineOfFire(actor, target, fd, 1))
289  return true;
290  }
291  }
292  return false;
293 }
294 
298 static bool AI_IsExposed (int team, Actor* check)
299 {
300  if (G_TestVis(team, check, VT_PERISHCHK | VT_NOFRUSTUM) & VS_YES)
301  return true;
302 
303  Actor* from = nullptr;
304  while ((from = G_EdictsGetNextLivingActor(from))) {
305  const int fromTeam = from->getTeam();
306  if ((team >= 0 && fromTeam != team) || (team < 0 && fromTeam == -team))
307  continue;
308 
309  if (AI_HasLineOfFire(from, check))
310  return true;
311  }
312 
313  return false;
314 }
322 static bool AI_CheckFF (const Edict* ent, const vec3_t target, float spread, float radius)
323 {
324  /* spread data */
325  spread *= 2;
326  if (spread < 1.0)
327  spread = 1.0;
328  spread *= torad;
329 
330  const float cosSpread = cos(spread);
331  vec3_t dtarget;
332  VectorSubtract(target, ent->origin, dtarget);
333  VectorNormalizeFast(dtarget);
334  vec3_t back;
335  VectorScale(dtarget, PLAYER_WIDTH / spread, back);
336 
337  Actor* check = nullptr;
338  while ((check = G_EdictsGetNextLivingActorOfTeam(check, ent->getTeam()))) {
339  if (!ent->isSameAs(check)) {
340  vec3_t dcheck;
341  /* found ally */
342  VectorSubtract(check->origin, ent->origin, dcheck);
343  if (DotProduct(dtarget, dcheck) > 0.0) {
344  /* ally in front of player */
345  VectorAdd(dcheck, back, dcheck);
346  VectorNormalizeFast(dcheck);
347  if (DotProduct(dtarget, dcheck) > cosSpread)
348  return true;
349  }
350  }
351  if (VectorDist(target, check->origin) < radius + UNIT_SIZE)
352  return true;
353  }
354 
355  /* no ally in danger */
356  return false;
357 }
358 
364 bool AI_FighterCheckShoot (const Actor* actor, const Edict* check, const fireDef_t* fd, float dist)
365 {
366  /* check range */
367  if (dist > fd->range)
368  return false;
369 
370  /* if insane, we don't check more */
371  if (actor->isInsane())
372  return true;
373 
374  /* don't shoot - we are too close */
375  if (dist < fd->splrad)
376  return false;
377 
378  /* check FF */
379  vec2_t effSpread;
380  G_CalcEffectiveSpread(actor, fd, effSpread);
381  if (AI_CheckFF(actor, check->origin, effSpread[0], fd->splrad))
382  return false;
383 
384  return true;
385 }
386 
396 bool AI_CheckUsingDoor (const Edict* ent, const Edict* door)
397 {
398  /* don't try to use the door in every case */
399  if (frand() < 0.3f)
400  return false;
401 
402  /* not in the view frustum - don't use the door while not seeing it */
403  if (!G_FrustumVis(door, ent->origin))
404  return false;
405 
406  /* if the alien is trying to hide and the door is
407  * still opened, close it */
408  if (ent->hiding && door->doorState == STATE_OPENED)
409  return true;
410 
411  /* aliens and civilians need different handling */
412  switch (ent->getTeam()) {
413  case TEAM_ALIEN: {
414  /* only use the door when there is no civilian or phalanx to kill */
415  Actor* check = nullptr;
416 
417  /* see if there are enemies */
418  while ((check = G_EdictsGetNextLivingActor(check))) {
419  /* don't check for aliens */
420  if (check->isSameTeamAs(ent))
421  continue;
422  /* check whether the origin of the enemy is inside the
423  * AI actors view frustum */
424  if (!G_FrustumVis(check, ent->origin))
425  continue;
426  /* check whether the enemy is close enough to change the state */
427  if (VectorDist(check->origin, ent->origin) > G_VisCheckDist(ent))
428  continue;
429  const float actorVis = G_ActorVis(check, ent, true);
430  /* there is a visible enemy, don't use that door */
431  if (actorVis > ACTOR_VIS_0)
432  return false;
433  }
434  }
435  break;
436  case TEAM_CIVILIAN:
437  /* don't use any door if no alien is inside the viewing angle - but
438  * try to hide behind the door when there is an alien */
439  break;
440  default:
441  gi.DPrintf("Invalid team in AI_CheckUsingDoor: %i for ent type: %i\n", ent->getTeam(), ent->type);
442  break;
443  }
444  return true;
445 }
446 
452 static bool AI_CheckCrouch (const Actor* actor)
453 {
454  if (G_IsCrouched(actor))
455  return false;
456 
457  Actor* check = nullptr;
458 
459  /* see if we are very well visible by an enemy */
460  while ((check = G_EdictsGetNextLivingActor(check))) {
461  /* don't check for civilians or aliens */
462  if (check->isSameTeamAs(actor) || G_IsCivilian(check))
463  continue;
464  /* check whether the origin of the enemy is inside the
465  * AI actors view frustum */
466  if (!G_FrustumVis(check, actor->origin))
467  continue;
468  /* check whether the enemy is close enough to change the state */
469  if (VectorDist(check->origin, actor->origin) > G_VisCheckDist(actor))
470  continue;
471  const float actorVis = G_ActorVis(check, actor, true);
472  if (actorVis >= ACTOR_VIS_50)
473  return true;
474  }
475  return false;
476 }
477 
484 bool AI_HideNeeded (const Actor* actor)
485 {
486  /* aliens will consider hiding if they are not brave, or there is a dangerous enemy in sight */
487  const bool brave = actor->morale > mor_brave->integer;
488 
489  Actor* from = nullptr;
490  /* test if actor is visible */
491  while ((from = G_EdictsGetNextLivingActor(from))) {
492  if (!AI_IsHostile(actor, from))
493  continue;
494 
495  const Item* item = from->getRightHandItem();
496  if (!item)
497  item = from->getLeftHandItem();
498  if (!item)
499  continue;
500 
501  const fireDef_t* fd = item->getFiredefs();
502  /* search the (visible) inventory (by just checking the weapon in the hands of the enemy) */
503  const bool inRange = fd != nullptr && fd->range * fd->range >= VectorDistSqr(actor->origin, from->origin);
504  const int damageRand = !inRange ? 0 : fd->damage[0] + fd->spldmg[0] + ((fd->damage[1] + fd->spldmg[1]) * crand());
505  const int damage = std::max(0, damageRand);
506  if (!brave || damage >= actor->HP / 3) {
507  const int hidingTeam = AI_GetHidingTeam(actor);
508  /* now check whether this enemy is visible for this alien */
509  if (G_Vis(hidingTeam, actor, from, VT_NOFRUSTUM) || AI_HasLineOfFire(from, actor))
510  return true;
511  }
512  }
513  return false;
514 }
515 
522 static inline const Item* AI_GetItemFromInventory (const Item* ic)
523 {
524  if (ic == nullptr)
525  return nullptr;
526 
527  const Item* item = ic;
528  if (item->ammoDef() && item->isWeapon() && !item->mustReload())
529  return item;
530 
531  return nullptr;
532 }
533 
541 const Item* AI_GetItemForShootType (shoot_types_t shootType, const Edict* ent)
542 {
543  /* optimization: reaction fire is automatic */
544  if (IS_SHOT_REACTION(shootType))
545  return nullptr;
546 
547  /* check that the current selected shoot type also has a valid item in its
548  * corresponding hand slot of the inventory. */
549  if (IS_SHOT_RIGHT(shootType)) {
550  const Item* item = ent->getRightHandItem();
551  return AI_GetItemFromInventory(item);
552  } else if (IS_SHOT_LEFT(shootType)) {
553  const Item* item = ent->getLeftHandItem();
554  return AI_GetItemFromInventory(item);
555  } else if (IS_SHOT_HEADGEAR(shootType)) {
556  return nullptr;
557  }
558 
559  return nullptr;
560 }
561 
570 int AI_GetHidingTeam (const Edict* ent)
571 {
572  if (G_IsCivilian(ent))
573  return TEAM_ALIEN;
574  return -ent->getTeam();
575 }
576 
581 bool AI_CheckPosition (const Actor* const actor, const pos3_t pos)
582 {
583  if (actor->isInsane())
584  return true;
585 
586  /* Don't stand on hurt triggers or fire/stun gas */
587  Edict* check = nullptr;
588  while ((check = G_EdictsGetNextInUse(check))) {
589  if (!check->isSamePosAs(pos) || check->dmg <= 0)
590  continue;
591  if (G_ApplyProtection(actor, check->dmgtype, check->dmg) > 0)
592  return false;
593  }
594  return true;
595 }
596 
606 bool AI_FindHidingLocation (int team, Actor* actor, const pos3_t from, int tuLeft)
607 {
608  /* We need a local table to calculate the hiding steps */
609  if (!hidePathingTable)
610  hidePathingTable = (pathing_t*) G_TagMalloc(sizeof(*hidePathingTable), TAG_LEVEL);
611 
612  /* search hiding spot */
613  const int maxTUs = std::min(tuLeft, HIDE_DIST * 2);
614  const int distance = (maxTUs + 1) / TU_MOVE_STRAIGHT;
615  G_MoveCalcLocal(hidePathingTable, 0, actor, from, maxTUs);
616 
617  int bestScore = AI_ACTION_NOTHING_FOUND;
618  pos3_t bestPos = {0, 0, PATHFINDING_HEIGHT};
619  AiAreaSearch searchArea(from, distance, true);
620  while (searchArea.getNext(actor->pos)) {
621  /* Don't have TUs to walk there */
622  const pos_t delta = G_ActorMoveLength(actor, hidePathingTable, actor->pos, false);
623  if (delta > tuLeft || delta == ROUTING_NOT_REACHABLE)
624  continue;
625 
626  /* Don't stand on dangerous terrain! */
627  if (!AI_CheckPosition(actor, actor->pos))
628  continue;
629 
630  /* If enemies see this position, it doesn't qualify as hiding spot */
631  actor->calcOrigin();
632  if (AI_IsExposed(team, actor))
633  continue;
634 
635  const int score = tuLeft - delta;
636  if (score > bestScore) {
637  bestScore = score;
638  VectorCopy(actor->pos, bestPos);
639  }
640  }
641 
642  if (bestPos[2] != PATHFINDING_HEIGHT) {
643  VectorCopy(bestPos, actor->pos);
644  return true;
645  }
646  return false;
647 }
648 
659 bool AI_FindHerdLocation (Actor* actor, const pos3_t from, const vec3_t target, int tu, bool inverse)
660 {
661  if (!herdPathingTable)
662  herdPathingTable = (pathing_t*) G_TagMalloc(sizeof(*herdPathingTable), TAG_LEVEL);
663 
664  /* find the nearest enemy actor to the target*/
665  vec_t bestLength = -1.0f;
666  Actor* next = nullptr;
667  Actor* enemy = nullptr;
668  int team = AI_GetHidingTeam(actor);
669  const bool invTeam = team < 0;
670  team = std::abs(team);
671  while ((next = G_EdictsGetNextLivingActor(next))) {
672  if (next->getTeam() == team ? invTeam : !invTeam)
673  continue;
674  const vec_t length = VectorDistSqr(target, next->origin);
675  if (length < bestLength || bestLength < 0.0f) {
676  enemy = next;
677  bestLength = length;
678  }
679  }
680  if (!enemy)
681  return false;
682 
683  /* calculate move table */
684  const int maxTUs = std::min(tu, HERD_DIST * 2);
685  const int distance = (maxTUs + 1) / TU_MOVE_STRAIGHT;
686  G_MoveCalcLocal(herdPathingTable, 0, actor, from, maxTUs);
687 
688  /* search the location */
689  pos3_t bestPos = {0, 0, PATHFINDING_HEIGHT};
690  bestLength = VectorDistSqr(target, actor->origin);
691  AiAreaSearch searchArea(from, distance, true);
692  while (searchArea.getNext(actor->pos)) {
693  /* time */
694  const pos_t delta = G_ActorMoveLength(actor, herdPathingTable, actor->pos, false);
695  if (delta > tu || delta == ROUTING_NOT_REACHABLE)
696  continue;
697 
698  /* Don't stand on dangerous terrain! */
699  if (!AI_CheckPosition(actor, actor->pos))
700  continue;
701 
702  actor->calcOrigin();
703  const vec_t length = VectorDistSqr(actor->origin, target);
704  /* Don't pack them too close */
705  if (length < HERD_THRESHOLD * HERD_THRESHOLD)
706  continue;
707 
708  if (length < bestLength || bestPos[2] == PATHFINDING_HEIGHT) {
709  vec3_t vfriend, venemy;
710  /* check this position to locate behind target from enemy */
711  VectorSubtract(target, actor->origin, vfriend);
712  VectorNormalizeFast(vfriend);
713  VectorSubtract(enemy->origin, actor->origin, venemy);
714  VectorNormalizeFast(venemy);
715  const float dotProd = DotProduct(vfriend, venemy);
716  if ((inverse ? -dotProd : dotProd) > 0.5f) {
717  bestLength = length;
718  VectorCopy(actor->pos, bestPos);
719  }
720  }
721  }
722 
723  if (bestPos[2] != PATHFINDING_HEIGHT) {
724  VectorCopy(bestPos, actor->pos);
725  return true;
726  }
727 
728  return false;
729 }
730 
737 static Edict* AI_SearchDestroyableObject (const Actor* actor, const fireDef_t* fd)
738 {
739 #if 0
740  /* search best none human target */
741  Edict* check = nullptr;
742  float dist;
743 
744  while ((check = G_EdictsGetNextInUse(check))) {
745  if (G_IsBreakable(check)) {
746  if (!AI_FighterCheckShoot(actor, check, fd, &dist))
747  continue;
748 
749  /* check whether target is visible enough */
750  const float vis = G_ActorVis(actor->origin, check, true);
751  if (vis < ACTOR_VIS_0)
752  continue;
753 
754  /* take the first best breakable or door and try to shoot it */
755  return check;
756  }
757  }
758 #endif
759  return nullptr;
760 }
761 
762 #define LOF_CHECK_PARTITIONS 4
763 bool AI_CheckLineOfFire (const Actor* shooter, const Edict* target, const fireDef_t* fd, int shots)
764 {
765  vec3_t dir, origin;
766  VectorSubtract(target->origin, shooter->origin, dir);
767  fd->getShotOrigin(shooter->origin, dir, shooter->isCrouched(), origin);
768  if (!fd->gravity) {
769  /* gun-to-target line free? */
770  const trace_t trace = G_Trace(Line(origin, target->origin), shooter, MASK_SHOT);
771  const Edict* trEnt = G_EdictsGetByNum(trace.entNum);
772  const bool hitBreakable = trEnt && G_IsBrushModel(trEnt) && G_IsBreakable(trEnt);
773  const bool shotBreakable = hitBreakable && (fd->shots > 1 || shots > 1)
774  && trEnt->HP < fd->damage[0] && !(fd->splrad > 0.0f) && !fd->bounce;
775  if (trace.fraction < 1.0f && (!trEnt || (!VectorCompare(trEnt->pos, target->pos) && !shotBreakable)))
776  return false;
777  } else {
778  /* gun-to-target *parabola* free? */
779  vec3_t at, v;
780  VectorCopy(target->origin, at);
781  /* Grenades are targeted at the ground in G_ShootGrenade */
782  at[2] -= GROUND_DELTA;
783  const float dt = gi.GrenadeTarget(origin, at, fd->range, fd->launched, fd->rolled, v) / LOF_CHECK_PARTITIONS;
784  if (!dt)
785  return false;
786  VectorSubtract(at, origin, dir);
787  VectorScale(dir, 1.0f / LOF_CHECK_PARTITIONS, dir);
788  dir[2] = 0;
789  float vz = v[2];
790  int i;
791  for (i = 0; i < LOF_CHECK_PARTITIONS; i++) {
792  VectorAdd(origin, dir, at);
793  at[2] += dt * (vz - 0.5f * GRAVITY * dt);
794  vz -= GRAVITY * dt;
795  const trace_t trace = G_Trace(Line(origin, at), shooter, MASK_SHOT);
796  const Edict* trEnt = G_EdictsGetByNum(trace.entNum);
797  if (trace.fraction < 1.0f && (!trEnt || !VectorCompare(trEnt->pos, target->pos))) {
798  break;
799  }
800  VectorCopy(at, origin);
801  }
802  if (i < LOF_CHECK_PARTITIONS)
803  return false;
804  }
805  return true;
806 }
807 
811 float AI_CalcShotDamage (Actor* actor, const Actor* target, const fireDef_t* fd, shoot_types_t shotType)
812 {
813  const int shots = ceil(CALC_DAMAGE_SAMPLES / fd->shots);
814  const int zAlign = !fd->gravity && (fd->splrad > 0.0f || target->isStunned()) ? GROUND_DELTA : 0;
815  shot_mock_t mock;
816  for (int i = 0; i < shots; ++i)
817  G_ClientShoot(actor->getPlayer(), actor, target->pos, shotType, fd->fdIdx, &mock, false, zAlign);
818 
819  if (mock.damage == 0)
820  return 0.0f;
821 
822  const int totalCount = mock.enemyCount + mock.friendCount + mock.civilian;
823  const int eCount = totalCount - ((actor->isInsane() ? 0 : mock.friendCount)
824  + (G_IsAlien(actor) || actor->isInsane() ? 0 : mock.civilian));
825  return mock.damage * (static_cast<float>(eCount) / totalCount) / shots;
826 }
827 
831 static void AI_FindBestFiredef (AiAction* aia, Actor* actor, Actor* check, const Item* item, shoot_types_t shootType, int tu, float* maxDmg, int* bestTime, const fireDef_t* fdArray)
832 {
833  bool hasLineOfFire = false;
834  int shotChecked = NONE;
835 
836  const float dist = VectorDist(actor->origin, check->origin);
837  for (fireDefIndex_t fdIdx = 0; fdIdx < item->ammoDef()->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
838  const fireDef_t* fd = &fdArray[fdIdx];
839  const int time = G_ActorGetModifiedTimeForFiredef(actor, fd, false);
840  /* how many shoots can this actor do */
841  const int shots = tu / time;
842  if (shots) {
843  const bool stunWeapon = (item->def()->dmgtype == gi.csi->damStunElectro || item->def()->dmgtype == gi.csi->damStunGas);
844  if (stunWeapon && !actor->isInsane() && (check->isStunned() || CHRSH_IsTeamDefRobot(check->chr.teamDef)))
845  return;
846 
847  if (!AI_FighterCheckShoot(actor, check, fd, dist))
848  continue;
849 
850  /*
851  * check weapon can hit, we only want to do this once unless the LoF actually changes
852  * between shots, only hand grenades seem to do this (rolled vs thrown)
853  */
854  const int shotFlags = fd->gravity | (fd->launched << 1) | (fd->rolled << 2);
855  if (shotChecked != shotFlags) {
856  shotChecked = shotFlags;
857  hasLineOfFire = AI_CheckLineOfFire(actor, check, fd, shots);
858  }
859  if (!hasLineOfFire)
860  continue;
861 
862  /* calculate expected damage */
863  float dmg = AI_CalcShotDamage(actor, check, fd, shootType) * shots;
864 
865  /* It is said that there is no kill like overkill but... */
866  dmg = std::min(dmg, check->HP * SCORE_DAMAGE_FACTOR) * SCORE_DAMAGE / check->HP;
867 
868  if (dmg > check->HP && check->isReaction())
869  /* reaction shooters eradication bonus */
870  dmg = check->HP + SCORE_KILL + SCORE_REACTION_ERADICATION;
871  else if (dmg > check->HP)
872  /* standard kill bonus */
873  dmg = check->HP + SCORE_KILL;
874 
875  /* ammo is limited and shooting gives away your position */
876  if (dmg < check->HP * SCORE_DAMAGE_WORTH_FACTOR)
877  continue;
878 
879  /* Reaction fire malus */
880  if (!actor->isInsane() && check->isReaction() && G_ActorVis(check, actor, true) > ACTOR_VIS_0)
882 
883  /* civilian malus */
884  if (G_IsCivilian(check) && !actor->isRaged())
885  dmg *= SCORE_CIV_FACTOR;
886 
887  /* Stunned malus */
888  if (check->isStunned() && !actor->isRaged())
889  dmg *= SCORE_DISABLED_FACTOR;
890 
891  /* add random effects */
892  if (dmg > 0)
893  dmg += SCORE_RANDOM * frand();
894 
895  /* check if most damage can be done here */
896  if (dmg > *maxDmg) {
897  *maxDmg = dmg;
898  *bestTime = time * shots;
899  aia->shootType = shootType;
900  aia->shots = shots;
901  aia->target = check;
902  aia->fd = fd;
903  if (!fd->gravity && (fd->splrad > 0.0f || check->isStunned()))
904  aia->z_align = GROUND_DELTA;
905  else
906  aia->z_align = 0;
907  }
908 
909  if (!aia->target) {
910  aia->target = AI_SearchDestroyableObject(actor, fd);
911  if (aia->target) {
912  /* don't take vis into account, don't multiply with amount of shots
913  * others (human victims) should be preferred, that's why we don't
914  * want a too high value here */
915  *maxDmg = (fd->damage[0] + fd->spldmg[0]);
916  *bestTime = time * shots;
917  aia->shootType = shootType;
918  aia->shots = shots;
919  aia->fd = fd;
920  }
921  }
922  }
923  }
924 }
925 
933 bool AI_IsHostile (const Actor* actor, const Edict* target)
934 {
935  if (actor == target)
936  return false;
937 
938  if (actor->isInsane())
939  return true;
940 
941  if (!target->isOpponent(actor))
942  return false;
943 
944  /* don't shoot civs in multiplayer */
945  if (G_IsMultiPlayer())
946  return !G_IsCivilian(target);
947 
948  return true;
949 }
950 
954 const invDef_t* AI_SearchGrenade (const Actor* actor, Item** ip)
955 {
956  /* search for grenades and select the one that is available easily */
957  const Container* cont = nullptr;
958  const invDef_t* bestContainer = nullptr;
959  Item* weapon = nullptr;
960  int cost = 100;
961  while ((cont = actor->chr.inv.getNextCont(cont, true))) {
962  if (cont->def()->out >= cost)
963  continue;
964  Item* item = nullptr;
965  while ((item = cont->getNextItem(item))) {
966  assert(item->def());
967  const objDef_t* obj = item->def();
968  if (item->isWeapon() && !item->mustReload() && ((obj->thrown && obj->oneshot && obj->deplete)
969  || Q_streq(obj->type, "grenade"))) {
970  weapon = item;
971  bestContainer = cont->def();
972  cost = bestContainer->out;
973  break;
974  }
975  }
976  }
977 
978  *ip = weapon;
979  return bestContainer;
980 }
981 
987 static bool AI_IsHandForForShootTypeFree (shoot_types_t shootType, Actor* actor)
988 {
989  if (!IS_SHOT_REACTION(shootType)) {
990  if (IS_SHOT_RIGHT(shootType)) {
991  const Item* item = actor->getRightHandItem();
992  return item == nullptr;
993  }
994  if (IS_SHOT_LEFT(shootType)) {
995  const Item* left = actor->getLeftHandItem();
996  const Item* right = actor->getRightHandItem();
997  return left == nullptr && (right == nullptr || !right->isHeldTwoHanded());
998  }
999  }
1000 
1001  return false;
1002 }
1003 
1010 static int AI_CheckForMissionTargets (Actor* actor, const pos3_t pos)
1011 {
1012  int bestActionScore = AI_ACTION_NOTHING_FOUND;
1013  int actionScore = 0;
1014  pos3_t oldPos;
1015 
1016  VectorCopy(pos, oldPos);
1017  actor->setOrigin(pos);
1018  if (G_IsCivilian(actor)) {
1019  Edict* checkPoint = nullptr;
1020  int i = 0;
1021  /* find waypoints in a closer distance - if civilians are not close enough, let them walk
1022  * around until they came close */
1023  for (checkPoint = level.ai_waypointList; checkPoint != nullptr; checkPoint = checkPoint->groupChain) {
1024  if (!checkPoint->inuse)
1025  continue;
1026 
1027  /* the lower the count value - the nearer the final target */
1028  if (checkPoint->count < actor->count) {
1029  if (VectorDist(actor->origin, checkPoint->origin) <= WAYPOINT_CIV_DIST) {
1030  const int actorTUs = actor->getUsableTUs();
1031  const int length = actorTUs - G_ActorMoveLength(actor, level.pathingMap, actor->pos, true);
1032  i++;
1033 
1034  /* test for time and distance */
1035  actionScore = SCORE_MISSION_TARGET + length;
1036 
1037  /* Don't walk to enemy ambush */
1038  Actor* check = nullptr;
1039  while ((check = G_EdictsGetNextLivingActorOfTeam(check, TEAM_ALIEN))) {
1040  const float dist = VectorDist(actor->origin, check->origin);
1041  /* @todo add visibility check here? */
1042  if (dist < RUN_AWAY_DIST)
1043  actionScore -= SCORE_RUN_AWAY;
1044  }
1045  if (actionScore > bestActionScore) {
1046  bestActionScore = actionScore;
1047  actor->count = checkPoint->count;
1048  }
1049  }
1050  }
1051  }
1052  /* reset the count value for this civilian to restart the search */
1053  if (!i)
1054  actor->count = 100;
1055  } else if (G_IsAlien(actor)) {
1056  /* search for a mission edict */
1057  Edict* mission = nullptr;
1058  while ((mission = G_EdictsGetNextInUse(mission))) {
1059  if (mission->type != ET_MISSION)
1060  continue;
1061  if (mission->pos[2] != actor->pos[2])
1062  continue;
1063  const int radius = mission->radius / (UNIT_SIZE + 1);
1064  const int distX = std::abs(actor->pos[0] - mission->pos[0]);
1065  const int distY = std::abs(actor->pos[1] - mission->pos[1]);
1066  const int dists = std::max(distX, distY);
1067  if (actor->getTeam() == mission->getTeam()) {
1068  actionScore = SCORE_MISSION_TARGET / (dists > radius ? dists - radius : 1);
1069  } else {
1070  /* try to prevent the phalanx from reaching their mission target */
1071  actionScore = SCORE_MISSION_OPPONENT_TARGET / (dists > radius ? dists - radius : 1);
1072  }
1073 
1074  if (actionScore > bestActionScore) {
1075  bestActionScore = actionScore;
1076  }
1077  }
1078  }
1079 
1080  actor->setOrigin(oldPos);
1081  return bestActionScore > AI_ACTION_NOTHING_FOUND ? bestActionScore : 0;
1082 }
1083 
1089 static float AI_FighterCalcActionScore (Actor* actor, const pos3_t to, AiAction* aia)
1090 {
1091  const pos_t move = G_ActorMoveLength(actor, level.pathingMap, to, true);
1092  if (move == ROUTING_NOT_REACHABLE)
1093  return AI_ACTION_NOTHING_FOUND;
1094 
1095  int tu = actor->getUsableTUs() - move;
1096  /* test for time */
1097  if (tu < 0)
1098  return AI_ACTION_NOTHING_FOUND;
1099 
1100  /* set basic parameters */
1101  aia->reset();
1102  VectorCopy(to, aia->to);
1103  VectorCopy(to, aia->stop);
1104  actor->setOrigin(aia->to);
1105 
1106  /* pre-find a grenade */
1107  Item* grenade = nullptr;
1108  const invDef_t* fromCont = AI_SearchGrenade(actor, &grenade);
1109 
1110  /* search best target */
1111  float maxDmg = 0.0f;
1112  float bestActionScore = 0.0f;
1113  int bestTime = -1;
1114  Actor* check = nullptr;
1115 
1116  while ((check = G_EdictsGetNextLivingActor(check))) {
1117  if (check->isSamePosAs(aia->to) || !AI_IsHostile(actor, check))
1118  continue;
1119 
1120  if (!G_IsVisibleForTeam(check, actor->getTeam()) && G_ActorVis(actor, check, true) < ACTOR_VIS_10)
1121  continue;
1122 
1123  /* shooting */
1124  for (shoot_types_t shootType = ST_RIGHT; shootType < ST_NUM_SHOOT_TYPES; shootType++) {
1125  const bool freeHand = AI_IsHandForForShootTypeFree(shootType, actor);
1126  const Item* item = freeHand ? grenade : AI_GetItemForShootType(shootType, actor);
1127  if (!item)
1128  continue;
1129 
1130  const fireDef_t* fdArray = item->getFiredefs();
1131  if (fdArray == nullptr)
1132  continue;
1133 
1134  const invDef_t* toCont = INVDEF_FOR_SHOOTTYPE(shootType);
1135  const int invMoveCost = freeHand && grenade ? fromCont->out + toCont->in : 0;
1136  AI_FindBestFiredef(aia, actor, check, item, shootType, tu - invMoveCost, &maxDmg, &bestTime, fdArray);
1137  if (aia->shootType == shootType)
1138  bestTime += invMoveCost;
1139  }
1140  }
1141  /* add damage to bestActionScore */
1142  if (aia->target) {
1143  bestActionScore += maxDmg;
1144  assert(bestTime > 0);
1145  tu -= bestTime;
1146  }
1147 
1148  /* Try not to stand in dangerous terrain (eg. fireField) */
1149  if (!AI_CheckPosition(actor, aia->to))
1150  bestActionScore -= SCORE_NOSAFE_POSITION_PENALTY;
1151 
1152  if (!actor->isRaged()) {
1153  const int hidingTeam = AI_GetHidingTeam(actor);
1154  /* hide */
1155  if (!AI_HideNeeded(actor)) {
1156  /* is a hiding spot */
1157  bestActionScore += SCORE_HIDE + (aia->target ? SCORE_CLOSE_IN + SCORE_REACTION_FEAR_FACTOR : 0);
1158  } else if (aia->target && tu >= TU_MOVE_STRAIGHT) {
1159  /* reward short walking to shooting spot, when seen by enemies; */
1166  bestActionScore += std::max(SCORE_CLOSE_IN - move, 0);
1167 
1168  if (!AI_FindHidingLocation(hidingTeam, actor, aia->to, tu)) {
1169  /* nothing found */
1170  actor->setOrigin(aia->to);
1171  } else {
1172  /* found a hiding spot */
1173  VectorCopy(actor->pos, aia->stop);
1174  actor->calcOrigin();
1175  bestActionScore += SCORE_HIDE;
1178  }
1179  }
1180  } else {
1181  if (aia->target)
1182  bestActionScore += aia->shots * SCORE_RAGE - move;
1183  else
1184  bestActionScore += move;
1185  }
1186 
1187  if (aia->target) {
1188  const float dist = VectorDist(actor->origin, aia->target->origin);
1189  bestActionScore += SCORE_CLOSE_IN * (1.0f - dist / CLOSE_IN_DIST);
1190  } else if (actor->isRaged()) {
1191  /* reward closing in */
1192  float minDist = CLOSE_IN_DIST;
1193  check = nullptr;
1194  while ((check = G_EdictsGetNextLivingActor(check))) {
1195  if (!check->isSameTeamAs(actor)) {
1196  const float dist = VectorDist(actor->origin, check->origin);
1197  minDist = std::min(dist, minDist);
1198  }
1199  }
1200  bestActionScore += SCORE_CLOSE_IN * (1.0f - minDist / CLOSE_IN_DIST);
1201  } else {
1202  /* if no target available let them wander around until they find one */
1203  bestActionScore += SCORE_RANDOM * frand();
1204  }
1205 
1206  /* Reward getting to mission objectives */
1207  if (!actor->isRaged())
1208  bestActionScore += AI_CheckForMissionTargets(actor, to);
1209 
1210  /* penalize herding */
1211  if (!actor->isRaged()) {
1212  check = nullptr;
1213  while ((check = G_EdictsGetNextLivingActorOfTeam(check, actor->getTeam()))) {
1214  const float dist = VectorDist(actor->origin, check->origin);
1215  if (dist < HERD_THRESHOLD)
1216  bestActionScore -= SCORE_HERDING_PENALTY;
1217  }
1218  }
1219 
1220  return bestActionScore;
1221 }
1222 
1231 static float AI_CivilianCalcActionScore (Actor* actor, const pos3_t to, AiAction* aia)
1232 {
1233  const pos_t move = G_ActorMoveLength(actor, level.pathingMap, to, true);
1234  const int tu = actor->getUsableTUs() - move;
1235 
1236  /* test for time */
1237  if (tu < 0 || move == ROUTING_NOT_REACHABLE)
1238  return AI_ACTION_NOTHING_FOUND;
1239 
1240  /* set basic parameters */
1241  aia->reset();
1242  VectorCopy(to, aia->to);
1243  VectorCopy(to, aia->stop);
1244  actor->setOrigin(to);
1245 
1246  /* check whether this civilian can use weapons */
1247  if (actor->chr.teamDef) {
1248  const teamDef_t* teamDef = actor->chr.teamDef;
1249  if (!actor->isPanicked() && teamDef->weapons)
1250  return AI_FighterCalcActionScore(actor, to, aia);
1251  } else
1252  gi.DPrintf("AI_CivilianCalcActionScore: Error - civilian team with no teamdef\n");
1253 
1254  /* run away */
1255  float minDist, minDistCivilian, minDistFighter;
1256  minDist = minDistCivilian = minDistFighter = RUN_AWAY_DIST * UNIT_SIZE;
1257 
1258  Actor* check = nullptr;
1259  while ((check = G_EdictsGetNextLivingActor(check))) {
1260  float dist;
1261  if (actor == check)
1262  continue;
1263  dist = VectorDist(actor->origin, check->origin);
1264  /* if we are trying to walk to a position that is occupied by another actor already we just return */
1265  if (!dist)
1266  return AI_ACTION_NOTHING_FOUND;
1267  switch (check->getTeam()) {
1268  case TEAM_ALIEN:
1269  if (dist < minDist)
1270  minDist = dist;
1271  break;
1272  case TEAM_CIVILIAN:
1273  if (dist < minDistCivilian)
1274  minDistCivilian = dist;
1275  break;
1276  case TEAM_PHALANX:
1277  if (dist < minDistFighter)
1278  minDistFighter = dist;
1279  break;
1280  }
1281  }
1282 
1283  minDist /= UNIT_SIZE;
1284  minDistCivilian /= UNIT_SIZE;
1285  minDistFighter /= UNIT_SIZE;
1286 
1287  float delta;
1288  if (minDist < 8.0) {
1289  /* very near an alien: run away fast */
1290  delta = 4.0 * minDist;
1291  } else if (minDist < 16.0) {
1292  /* near an alien: run away */
1293  delta = 24.0 + minDist;
1294  } else if (minDist < 24.0) {
1295  /* near an alien: run away slower */
1296  delta = 40.0 + (minDist - 16) / 4;
1297  } else {
1298  delta = 42.0;
1299  }
1300  /* near a civilian: join him (1/3) */
1301  if (minDistCivilian < 10.0)
1302  delta += (10.0 - minDistCivilian) / 3.0;
1303  /* near a fighter: join him (1/5) */
1304  if (minDistFighter < 15.0)
1305  delta += (15.0 - minDistFighter) / 5.0;
1306  /* don't go close to a fighter to let him move */
1307  if (minDistFighter < 2.0)
1308  delta /= 10.0;
1309 
1310  /* try to hide */
1311  float reactionTrap = 0.0;
1312  if (!actor->isInsane()) {
1313  check = nullptr;
1314  while ((check = G_EdictsGetNextLivingActor(check))) {
1315  if (actor == check)
1316  continue;
1317  if (!(G_IsAlien(check)))
1318  continue;
1319 
1320  if (G_ActorVis(check, actor, true) > ACTOR_VIS_10)
1321  reactionTrap += SCORE_NONHIDING_PLACE_PENALTY;
1322  }
1323  }
1324  delta -= reactionTrap;
1325  float bestActionScore = delta;
1326 
1327  /* Try not to stand in dangerous terrain */
1328  if (!AI_CheckPosition(actor, actor->pos))
1329  bestActionScore -= SCORE_NOSAFE_POSITION_PENALTY;
1330 
1331  /* Approach waypoints */
1332  bestActionScore += AI_CheckForMissionTargets(actor, actor->pos);
1333 
1334  /* add laziness */
1335  if (actor->getTus())
1336  bestActionScore += SCORE_CIV_LAZINESS * tu / actor->getTus();
1337  /* add random effects */
1338  bestActionScore += SCORE_CIV_RANDOM * frand();
1339 
1340  return bestActionScore;
1341 }
1342 
1351 static float AI_PanicCalcActionScore (Actor* actor, const pos3_t to, AiAction* aia)
1352 {
1353  const pos_t move = G_ActorMoveLength(actor, level.pathingMap, to, true);
1354  const int tu = actor->getUsableTUs() - move;
1355 
1356  /* test for time */
1357  if (tu < 0 || move == ROUTING_NOT_REACHABLE)
1358  return AI_ACTION_NOTHING_FOUND;
1359 
1360  /* set basic parameters */
1361  aia->reset();
1362  VectorCopy(to, aia->to);
1363  VectorCopy(to, aia->stop);
1364  actor->setOrigin(to);
1365 
1366  /* run away */
1367  float minDistFriendly, minDistOthers;
1368  minDistFriendly = minDistOthers = RUN_AWAY_DIST * UNIT_SIZE;
1369 
1370  Actor* check = nullptr;
1371  while ((check = G_EdictsGetNextLivingActor(check))) {
1372  float dist;
1373  if (actor == check)
1374  continue;
1375  dist = VectorDist(actor->origin, check->origin);
1376  /* if we are trying to walk to a position that is occupied by another actor already we just return */
1377  if (!dist)
1378  return AI_ACTION_NOTHING_FOUND;
1379  if (check->isSameTeamAs(actor)) {
1380  if (dist < minDistFriendly)
1381  minDistFriendly = dist;
1382  } else {
1383  if (dist < minDistOthers)
1384  minDistOthers = dist;
1385  }
1386  }
1387 
1388  minDistFriendly /= UNIT_SIZE;
1389  minDistOthers /= UNIT_SIZE;
1390 
1391  float bestActionScore = SCORE_PANIC_RUN_TO_FRIENDS / minDistFriendly;
1392  bestActionScore -= SCORE_PANIC_FLEE_FROM_STRANGERS / minDistOthers;
1393 
1394  /* try to hide */
1395  check = nullptr;
1396  if (!actor->isInsane())
1397  while ((check = G_EdictsGetNextLivingActor(check))) {
1398  if (actor == check)
1399  continue;
1400 
1401  if (G_ActorVis(check, actor, true) > ACTOR_VIS_10)
1402  bestActionScore -= SCORE_NONHIDING_PLACE_PENALTY;
1403  }
1404 
1405  /* Try not to stand in dangerous terrain */
1406  if (!AI_CheckPosition(actor, actor->pos))
1407  bestActionScore -= SCORE_NOSAFE_POSITION_PENALTY;
1408 
1409  /* add random effects */
1410  bestActionScore += SCORE_PANIC_RANDOM * frand();
1411 
1412  return bestActionScore;
1413 }
1414 
1423 bool AI_FindMissionLocation (Actor* actor, const pos3_t to, int tus, int radius)
1424 {
1425  int bestDist = ROUTING_NOT_REACHABLE;
1426  pos3_t bestPos = {to[0], to[1], to[2]};
1427 
1428  AiAreaSearch searchArea(to, radius, true);
1429  while (searchArea.getNext(actor->pos)) {
1430  const pos_t length = G_ActorMoveLength(actor, level.pathingMap, actor->pos, true);
1431  /* Can't walk there */
1432  if (length == ROUTING_NOT_REACHABLE || length > tus)
1433  continue;
1434  /* Don't stand on dangerous terrain! */
1435  if (!AI_CheckPosition(actor, actor->pos))
1436  continue;
1437 
1438  const int distX = std::abs(actor->pos[0] - to[0]);
1439  const int distY = std::abs(actor->pos[1] - to[1]);
1440  const int dist = std::max(distX, distY);
1441  if (dist < bestDist) {
1442  bestDist = dist;
1443  VectorCopy(actor->pos, bestPos);
1444  }
1445  }
1446  if (!VectorCompare(to, bestPos))
1447  VectorCopy(bestPos, actor->pos);
1448 
1449  return bestDist < ROUTING_NOT_REACHABLE;
1450 }
1451 
1458 static AiAction AI_PrepBestAction (const Player& player, Actor* actor)
1459 {
1460  /* calculate move table */
1461  const int maxTU = actor->getUsableTUs();
1462  G_MoveCalc(0, actor, actor->pos, maxTU);
1463  Com_DPrintf(DEBUG_ENGINE, "AI_PrepBestAction: Called MoveMark.\n");
1464  gi.MoveStore(level.pathingMap);
1465 
1466  /* set borders */
1467  const int dist = (maxTU + 1) / TU_MOVE_STRAIGHT;
1468 
1469  /* search best action */
1470  pos3_t oldPos;
1471  vec3_t oldOrigin;
1472  VectorCopy(actor->pos, oldPos);
1473  VectorCopy(actor->origin, oldOrigin);
1474 
1475  /* evaluate moving to every possible location in the search area,
1476  * including combat considerations */
1477  float bestActionScore, best = AI_ACTION_NOTHING_FOUND;
1478  AiAction aia, bestAia;
1479  pos3_t to;
1480  AiAreaSearch searchArea(oldPos, dist);
1481  while (searchArea.getNext(to)) {
1482  const pos_t move = G_ActorMoveLength(actor, level.pathingMap, to, true);
1483  if (move >= ROUTING_NOT_REACHABLE)
1484  continue;
1485  if (move > maxTU)
1486  continue;
1487 
1488  if (G_IsCivilian(actor))
1489  bestActionScore = AI_CivilianCalcActionScore(actor, to, &aia);
1490  else if (actor->isPanicked())
1491  bestActionScore = AI_PanicCalcActionScore(actor, to, &aia);
1492  else
1493  bestActionScore = AI_FighterCalcActionScore(actor, to, &aia);
1494 
1495  if (bestActionScore > best) {
1496  bestAia = aia;
1497  best = bestActionScore;
1498  }
1499  }
1500 
1501  VectorCopy(oldPos, actor->pos);
1502  VectorCopy(oldOrigin, actor->origin);
1503 
1504  /* nothing found to do */
1505  if (best == AI_ACTION_NOTHING_FOUND) {
1506  bestAia.target = nullptr;
1507  return bestAia;
1508  }
1509 
1510  /* do the move */
1511  for (;;) {
1512  if (actor->isDead())
1513  break;
1514  G_ClientMove(player, 0, actor, bestAia.to);
1515  if (actor->isSamePosAs(bestAia.to))
1516  break;
1517  const pos_t length = G_ActorMoveLength(actor, level.pathingMap, bestAia.to, false);
1518  if (length > actor->getUsableTUs() || length >= ROUTING_NOT_REACHABLE)
1519  break;
1520  }
1521  /* test for possible death during move. reset bestAia due to dead status */
1522  if (actor->isDead())
1523  bestAia.reset();
1524 
1525  /* if we are throwing a grenade from the inventory grab it now */
1526  if (bestAia.target && AI_IsHandForForShootTypeFree(bestAia.shootType, actor)) {
1527  Item* grenade = nullptr;
1528  const invDef_t* fromCont = AI_SearchGrenade(actor, &grenade);
1529  const invDef_t* toCont = INVDEF_FOR_SHOOTTYPE(bestAia.shootType);
1530  if (!grenade || !fromCont || !toCont || !G_ActorInvMove(actor, fromCont, grenade, toCont, NONE, NONE, true))
1531  bestAia.target = nullptr;
1532  }
1533 
1534  return bestAia;
1535 }
1536 
1538 {
1539  if (!level.ai_waypointList) {
1540  level.ai_waypointList = ent;
1541  return;
1542  }
1543 
1545  while (e->groupChain) {
1546  e = e->groupChain;
1547  }
1548  e->groupChain = ent;
1549 }
1550 
1557 void AI_TurnIntoDirection (Actor* actor, const pos3_t pos)
1558 {
1559  const byte crouchingState = actor->isCrouched() ? 1 : 0;
1560  G_MoveCalc(actor->getTeam(), actor, pos, actor->getUsableTUs());
1561 
1562  const int dvec = gi.MoveNext(level.pathingMap, pos, crouchingState);
1563  if (dvec != ROUTING_UNREACHABLE) {
1564  const byte dir = getDVdir(dvec);
1565  /* Only attempt to turn if the direction is not a vertical only action */
1566  if (dir < CORE_DIRECTIONS || dir >= FLYING_DIRECTIONS)
1567  G_ActorDoTurn(actor, dir & (CORE_DIRECTIONS - 1));
1568  }
1569 }
1570 
1574 bool AI_TryToReloadWeapon (Actor* actor, containerIndex_t containerID)
1575 {
1576  if (G_ClientCanReload(actor, containerID)) {
1577  return G_ActorReload(actor, INVDEF(containerID));
1578  } else {
1579  G_ActorInvMove(actor, INVDEF(containerID), actor->getContainer(containerID), INVDEF(CID_FLOOR), NONE, NONE, true);
1580  G_ReactionFireSettingsUpdate(actor, actor->chr.RFmode.getFmIdx(), actor->chr.RFmode.getHand(), actor->chr.RFmode.getWeapon());
1581  }
1582  return false;
1583 }
1584 
1592 static void AI_ActorThink (Player& player, Actor* actor)
1593 {
1594  /* if a weapon can be reloaded we attempt to do so if TUs permit, otherwise drop it */
1595  Item* rightH = actor->getRightHandItem();
1596  Item* leftH = actor->getLeftHandItem();
1597  if (!actor->isPanicked()) {
1598  if (rightH && rightH->mustReload())
1600  if (leftH && leftH->mustReload())
1602  }
1603 
1604  /* if both hands are empty, attempt to get a weapon out of backpack or the
1605  * floor (if TUs permit) */
1607  if (!actor->getLeftHandItem() && !actor->getRightHandItem())
1609 
1610  AiAction bestAia = AI_PrepBestAction(player, actor);
1611 
1612  /* shoot and hide */
1613  if (bestAia.target) {
1614  const fireDefIndex_t fdIdx = bestAia.fd ? bestAia.fd->fdIdx : 0;
1615  /* shoot until no shots are left or target died */
1616  while (bestAia.shots) {
1617  G_ClientShoot(player, actor, bestAia.target->pos, bestAia.shootType, fdIdx, nullptr, true, bestAia.z_align);
1618  bestAia.shots--;
1619  /* died by our own shot? */
1620  if (actor->isDead())
1621  return;
1622  /* check for target's death */
1623  if (G_IsDead(bestAia.target)) {
1624  /* search another target now */
1625  bestAia = AI_PrepBestAction(player, actor);
1626  /* no other target found - so no need to hide */
1627  if (!bestAia.target)
1628  return;
1629  }
1630  }
1631  actor->hiding = true;
1632 
1633  /* now hide - for this we use the team of the alien actor because a phalanx soldier
1634  * might become visible during the hide movement */
1635  G_ClientMove(player, actor->getTeam(), actor, bestAia.stop);
1636  /* no shots left, but possible targets left - maybe they shoot back
1637  * or maybe they are still close after hiding */
1638 
1639  /* decide whether the actor wants to crouch */
1640  if (AI_CheckCrouch(actor))
1641  G_ClientStateChange(player, actor, STATE_CROUCHED, true);
1642 
1643  /* actor is still alive - try to turn into the appropriate direction to see the target
1644  * actor once he sees the ai, too */
1645  AI_TurnIntoDirection(actor, bestAia.target->pos);
1646 
1649  /* G_ClientStateChange(player, actor->number, STATE_REACTION_ONCE, true); */
1650 
1651  actor->hiding = false;
1652  }
1653 }
1654 
1655 void AI_ActorRun (Player& player, Actor* actor)
1656 {
1657  if (g_ailua->integer)
1658  AIL_ActorThink(player, actor);
1659  else
1660  AI_ActorThink(player, actor);
1661 }
1662 
1663 #if 0
1664 #include "g_ai2.cpp"
1665 #else
1666 static bool AI_TeamThink (Player& player)
1667 {
1668  return false;
1669 }
1670 #endif
1671 
1672 static void AI_PlayerRun (Player& player)
1673 {
1674  if (level.activeTeam != player.getTeam() || player.roundDone)
1675  return;
1676 
1677  if (g_ailua->integer > 1) {
1678  if (AIL_TeamThink(player))
1679  /* did some thinking, come back next time */
1680  return;
1681  /* finished thinking, end round */
1682  }
1684  else if (player.getTeam() == TEAM_PHALANX && g_aihumans->integer == 2) {
1685  if (AI_TeamThink(player))
1686  return; /* did some thinking, come back next frame */
1687  /* finished thinking, end round */
1688  }
1689  else {
1690  /* find next actor to handle */
1691  Actor* actor = player.pers.getLastActor();
1692  while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, player.getTeam()))) {
1693  const int beforeTUs = actor->getTus();
1694  if (beforeTUs > 0) {
1695  AI_ActorRun(player, actor);
1696  player.pers.setLastActor(actor);
1697 
1698  if (beforeTUs > actor->getTus())
1699  return;
1700  }
1701  }
1702  }
1703 
1704  /* nothing left to do, request endround */
1705  G_ClientEndRound(player);
1706  player.pers.setLastActor(nullptr);
1707 }
1708 
1713 void AI_Run (void)
1714 {
1715  /* don't run this too often to prevent overflows */
1716  if (level.framenum % 10)
1717  return;
1718 
1719  /* set players to ai players and cycle over all of them */
1720  Player* player = nullptr;
1721  while ((player = G_PlayerGetNextActiveAI(player))) {
1722  AI_PlayerRun(*player);
1723  }
1724 
1725  if (g_aihumans->integer) {
1726  player = nullptr;
1727  while ((player = G_PlayerGetNextActiveHuman(player))) {
1728  AI_PlayerRun(*player);
1729  }
1730  }
1731 }
1732 
1738 static void AI_SetStats (Actor* actor, int team)
1739 {
1740  const char* templateId = "";
1741  if (team != TEAM_CIVILIAN && gi.csi->numAlienTeams) {
1742  for (int i = 0; i < gi.csi->numAlienTeams; ++i) {
1743  if (gi.csi->alienTeams[i] == actor->chr.teamDef && gi.csi->alienChrTemplates[i]) {
1744  templateId = gi.csi->alienChrTemplates[i]->id;
1745  break;
1746  }
1747  }
1748  }
1749 
1750  CHRSH_CharGenAbilitySkills(&actor->chr, G_IsMultiPlayer(), templateId);
1751 
1752  actor->HP = actor->chr.HP;
1753  actor->setMorale(actor->chr.morale);
1754  actor->setStun(0);
1755 
1756  /* hurt aliens in ufo crash missions (5%: almost dead, 10%: wounded, 15%: stunned) */
1757  if (level.hurtAliens && CHRSH_IsTeamDefAlien(actor->chr.teamDef)) {
1758  const float random = frand();
1759  int damage = 0, stun = 0;
1760  if (random <= 0.05f) {
1761  damage = actor->HP * 0.95f;
1762  } else if (random <= 0.15f) {
1763  stun = actor->HP * 0.3f;
1764  damage = actor->HP * 0.5f;
1765  } else if (random <= 0.3f) {
1766  stun = actor->HP * 0.75f;
1767  }
1768  actor->HP -= damage;
1769  if (!CHRSH_IsTeamDefRobot(actor->chr.teamDef))
1770  actor->setStun(stun);
1771 
1772  for (int i = 0; i < actor->chr.teamDef->bodyTemplate->numBodyParts(); ++i)
1774  }
1775 
1776  G_ActorGiveTimeUnits(actor);
1777 }
1778 
1784 static void AI_SetCharacterValues (Edict* ent, int team)
1785 {
1786  /* Set model. */
1787  const char* teamDefinition;
1788  if (team != TEAM_CIVILIAN) {
1789  if (gi.csi->numAlienTeams) {
1790  const int alienTeam = rand() % gi.csi->numAlienTeams;
1791  const teamDef_t* td = gi.csi->alienTeams[alienTeam];
1792  assert(td);
1793  teamDefinition = td->id;
1794  } else {
1795  teamDefinition = gi.Cvar_String("ai_alienteam");
1796  }
1797  } else {
1798  teamDefinition = gi.Cvar_String("ai_civilianteam");
1799  }
1800  gi.GetCharacterValues(teamDefinition, &ent->chr);
1801  if (!ent->chr.teamDef)
1802  gi.Error("Could not set teamDef for character: '%s'", teamDefinition);
1803 }
1804 
1805 
1811 static void AI_SetEquipment (Edict* ent, const equipDef_t* ed)
1812 {
1813  /* Pack equipment. */
1815 }
1816 
1823 static void AI_InitPlayer (const Player& player, Actor* actor, const equipDef_t* ed)
1824 {
1825  const int team = player.getTeam();
1826 
1827  /* Set the model and chose alien race. */
1828  AI_SetCharacterValues(actor, team);
1829 
1830  /* Calculate stats. */
1831  AI_SetStats(actor, team);
1832 
1833  /* Give equipment. */
1834  if (ed != nullptr)
1835  AI_SetEquipment(actor, ed);
1836 
1837  /* after equipping the actor we can also get the model indices */
1838  actor->setBody(gi.ModelIndex(CHRSH_CharGetBody(&actor->chr)));
1839  actor->setHead(gi.ModelIndex(CHRSH_CharGetHead(&actor->chr)));
1840 
1841  /* no need to call G_SendStats for the AI - reaction fire is serverside only for the AI */
1842  if (frand() < 0.75f) {
1843  G_ClientStateChange(player, actor, STATE_REACTION, false);
1844  }
1845 
1846  /* initialize the LUA AI now */
1847  AIL_InitActor(actor);
1848 }
1849 
1850 static const equipDef_t* G_GetEquipmentForAISpawn (int team)
1851 {
1852  /* prepare equipment */
1853  if (team == TEAM_CIVILIAN)
1854  return nullptr;
1855 
1856  const char* equipID = gi.Cvar_String("ai_equipment");
1857  const equipDef_t* ed = G_GetEquipDefByID(equipID);
1858  if (ed == nullptr)
1859  ed = &gi.csi->eds[0];
1860  return ed;
1861 }
1862 
1863 static Actor* G_SpawnAIPlayer (const Player& player, const equipDef_t* ed)
1864 {
1866  if (!actor) {
1867  gi.DPrintf("Not enough spawn points for team %i\n", player.getTeam());
1868  return nullptr;
1869  }
1870 
1871  /* initialize the new actor */
1872  AI_InitPlayer(player, actor, ed);
1873 
1874  G_TouchTriggers(actor);
1875 
1876  gi.DPrintf("Spawned ai player for team %i with entnum %i (%s)\n", actor->getTeam(), actor->getIdNum(), actor->chr.name);
1877  G_CheckVis(actor, VT_PERISHCHK | VT_NEW);
1878  G_CheckVisTeamAll(actor->getTeam(), 0, actor);
1879 
1880  return actor;
1881 }
1882 
1889 static void G_SpawnAIPlayers (const Player& player, int numSpawn)
1890 {
1891  const equipDef_t* ed = G_GetEquipmentForAISpawn(player.getTeam());
1892 
1893  for (int i = 0; i < numSpawn; i++) {
1894  if (G_SpawnAIPlayer(player, ed) == nullptr)
1895  break;
1896  }
1897 
1898  /* show visible actors */
1899  G_VisFlagsClear(player.getTeam());
1900  G_CheckVis(nullptr, 0);
1901 }
1902 
1908 void AI_CheckRespawn (int team)
1909 {
1910  if (!g_endlessaliens->integer)
1911  return;
1912 
1913  if (team != TEAM_ALIEN)
1914  return;
1915 
1916  const int spawned = level.initialAlienActorsSpawned;
1917  const int alive = level.num_alive[team];
1918  int diff = spawned - alive;
1919  const equipDef_t* ed = G_GetEquipmentForAISpawn(team);
1920 
1921  while (diff > 0) {
1922  const Player* player = G_GetPlayerForTeam(team);
1923  Actor* actor = G_SpawnAIPlayer(*player, ed);
1924  if (actor == nullptr)
1925  break;
1926 
1927  /* Some events need the actor added to the client before they are even *parsed*
1928  * - namely all the ones that rely on step times */
1929  G_EventActorAdd(PM_ALL, *actor, true);
1930  const playermask_t playerMask = G_VisToPM(actor->visflags);
1931  G_AppearPerishEvent(playerMask, true, *actor, nullptr);
1932 
1933  diff--;
1934  }
1935 }
1936 
1944 Player* AI_CreatePlayer (int team)
1945 {
1946  if (!sv_ai->integer) {
1947  gi.DPrintf("AI deactivated - set sv_ai cvar to 1 to activate it\n");
1948  return nullptr;
1949  }
1950 
1951  /* set players to ai players and cycle over all of them */
1952  Player* p = nullptr;
1953  while ((p = G_PlayerGetNextAI(p))) {
1954  if (p->isInUse())
1955  continue;
1956  p->reset();
1957  p->setInUse(true);
1958  p->setNum(p - game.players);
1959  p->pers.ai = true;
1960  G_SetTeamForPlayer(*p, team);
1961  if (p->getTeam() == TEAM_CIVILIAN) {
1963  } else {
1964  if (G_IsSinglePlayer())
1966  else
1968 
1970  }
1971 
1972  gi.DPrintf("Created AI player (team %i)\n", p->getTeam());
1973  return p;
1974  }
1975 
1976  /* nothing free */
1977  return nullptr;
1978 }
#define SCORE_REACTION_ERADICATION
Definition: g_ai.cpp:222
#define SCORE_RUN_AWAY
Definition: g_ai.cpp:233
#define ST_RIGHT
The right hand should be used for shooting.
Definition: q_shared.h:211
const objDef_t * onlyWeapon
Definition: chr_shared.h:325
bool AI_HideNeeded(const Actor *actor)
Checks whether the given alien should try to hide because there are enemies close enough to shoot the...
Definition: g_ai.cpp:484
static void AI_SetStats(Actor *actor, int team)
Initializes the actor's stats like morals, strength and so on.
Definition: g_ai.cpp:1738
Edict * G_EdictsGetNextInUse(Edict *lastEnt)
Iterate through the entities that are in use.
Definition: g_edicts.cpp:166
int getIdNum() const
Definition: g_edict.h:231
bool mustReload() const
Definition: inv_shared.h:483
float AI_CalcShotDamage(Actor *actor, const Actor *target, const fireDef_t *fd, shoot_types_t shotType)
Calculate estimated damage per single shoot.
Definition: g_ai.cpp:811
#define PLAYER_WIDTH
Definition: q_sizes.h:10
int G_TouchTriggers(Edict *ent, const entity_type_t type)
Check the world against triggers for the current entity.
Definition: g_utils.cpp:547
Reaction fire system.
#define VectorCopy(src, dest)
Definition: vector.h:51
#define BODYPART_MAXTYPE
Definition: chr_shared.h:255
const objDef_t * getWeapon() const
Definition: chr_shared.h:154
int damStunElectro
Definition: q_shared.h:535
void G_AddToWayPointList(Edict *ent)
Definition: g_ai.cpp:1537
bool G_ClientGetWeaponFromInventory(Actor *actor)
Retrieve or collect a loaded weapon from any linked container for the actor's right hand...
Definition: g_client.cpp:568
void AI_Run(void)
Every server frame one single actor is handled - always in the same order.
Definition: g_ai.cpp:1713
bool isSameTeamAs(const Edict *other) const
Definition: g_edict.h:278
#define ACTOR_VIS_10
Definition: g_vis.h:63
void AI_Init(void)
Definition: g_ai.cpp:266
#define SCORE_DAMAGE_WORTH_FACTOR
Definition: g_ai.cpp:230
trace_t G_Trace(const Line &trLine, const Edict *passent, int contentmask)
collision detection - this version is more accurate and includes entity tests
Definition: g_utils.cpp:265
#define IS_SHOT_REACTION(x)
Determine whether the selected shoot type is for reaction fire.
Definition: q_shared.h:239
Player * G_PlayerGetNextActiveHuman(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:110
static float AI_FighterCalcActionScore(Actor *actor, const pos3_t to, AiAction *aia)
Definition: g_ai.cpp:1089
bool AIL_TeamThink(Player &player)
The team think function for the ai controlled players.
Definition: g_ai_lua.cpp:2156
cvar_t * g_endlessaliens
Definition: g_main.cpp:111
chrScoreGlobal_t score
Definition: chr_shared.h:387
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
#define SCORE_KILL
Definition: g_ai.cpp:220
#define SCORE_MISSION_TARGET
Definition: g_ai.cpp:242
static void AI_InitPlayer(const Player &player, Actor *actor, const equipDef_t *ed)
Initializes the actor.
Definition: g_ai.cpp:1823
bool hiding
Definition: g_edict.h:142
void AI_TurnIntoDirection(Actor *actor, const pos3_t pos)
This function will turn the AI actor into the direction that is needed to walk to the given location...
Definition: g_ai.cpp:1557
char id[MAX_VAR]
Definition: chr_shared.h:298
#define TEAM_PHALANX
Definition: q_shared.h:62
void getShotOrigin(const vec3_t from, const vec3_t dir, bool crouching, vec3_t shotOrigin) const
bool AI_FighterCheckShoot(const Actor *actor, const Edict *check, const fireDef_t *fd, float dist)
Check whether the fighter should perform the shoot.
Definition: g_ai.cpp:364
void calcOrigin()
Calculate the edict's origin vector from it's grid position.
Definition: g_edict.h:216
bool AI_FindHidingLocation(int team, Actor *actor, const pos3_t from, int tuLeft)
Tries to search a hiding spot.
Definition: g_ai.cpp:606
static void G_SpawnAIPlayers(const Player &player, int numSpawn)
Spawn civilians and aliens.
Definition: g_ai.cpp:1889
#define SCORE_DISABLED_FACTOR
Definition: g_ai.cpp:229
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
#define HERD_THRESHOLD
Definition: g_ai.cpp:237
#define TEAM_ALIEN
Definition: q_shared.h:63
bool isRaged() const
Definition: g_edict.h:358
void G_CheckVis(Edict *check, const vischeckflags_t visFlags)
Check if the edict appears/perishes for the other teams. If they appear for other teams...
Definition: g_vis.cpp:409
bool hurtAliens
Definition: g_local.h:89
byte dmgtype
Definition: g_edict.h:139
int G_ActorDoTurn(Edict *ent, byte dir)
Turns an actor around.
Definition: g_actor.cpp:154
const teamDef_t * teamDef
Definition: chr_shared.h:394
Player * players
Definition: g_local.h:70
int damStunGas
Definition: q_shared.h:533
Artificial Intelligence functions.
int civilian
Definition: g_combat.h:34
Misc utility functions for game module.
weaponFireDefIndex_t weapFdsIdx
Definition: inv_shared.h:126
bool AI_CheckPosition(const Actor *const actor, const pos3_t pos)
Checks if the given position is safe to stand on.
Definition: g_ai.cpp:581
Edict * ai_waypointList
Definition: g_local.h:118
static void AI_SetEquipment(Edict *ent, const equipDef_t *ed)
Sets the actor's equipment.
Definition: g_ai.cpp:1811
~AiAreaSearch(void)
Clear AiAreaSearch internal data.
Definition: g_ai.cpp:70
void G_ClientStateChange(const Player &player, Actor *actor, int reqState, bool checkaction)
Changes the state of a player/soldier.
Definition: g_client.cpp:473
voidpf uLong int origin
Definition: ioapi.h:45
#define STATE_REACTION
Definition: q_shared.h:272
#define INVDEF_FOR_SHOOTTYPE(st)
Definition: g_ai.cpp:261
unsigned int playermask_t
Definition: g_events.h:34
bool isPanicked() const
Definition: g_edict.h:356
vec2_t spldmg
Definition: inv_shared.h:160
static AiAction AI_PrepBestAction(const Player &player, Actor *actor)
Attempts to find the best action for an alien. Moves the alien into the starting position for that ac...
Definition: g_ai.cpp:1458
int activeTeam
Definition: g_local.h:101
bool isDead() const
Definition: g_edict.h:362
character_t chr
Definition: g_edict.h:116
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
Player * G_PlayerGetNextAI(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:84
int doorState
Definition: g_edict.h:158
#define STATE_OPENED
Definition: defines.h:87
float vec_t
Definition: ufotypes.h:37
bool G_SetTeamForPlayer(Player &player, const int team)
Set the used team for the given player.
Definition: g_client.cpp:852
int enemyCount
Definition: g_combat.h:32
static bool AI_CheckFF(const Edict *ent, const vec3_t target, float spread, float radius)
Check whether friendly units are in the line of fire when shooting.
Definition: g_ai.cpp:322
void G_ClientMove(const Player &player, int visTeam, Actor *actor, const pos3_t to)
Generates the client events that are send over the netchannel to move an actor.
Definition: g_move.cpp:307
bool inuse
Definition: g_edict.h:47
void plotCircle(const pos3_t origin, int radius)
Definition: g_ai.cpp:165
bool isStunned() const
Definition: g_edict.h:355
void G_ClientEndRound(Player &player)
Definition: g_round.cpp:184
const objDef_t * def(void) const
Definition: inv_shared.h:469
#define SCORE_HERDING_PENALTY
Definition: g_ai.cpp:238
#define VectorDist(a, b)
Definition: vector.h:69
#define ACTOR_VIS_50
Definition: g_vis.h:62
static pathing_t * hidePathingTable
Definition: g_ai.cpp:263
int AI_GetHidingTeam(const Edict *ent)
Returns the value for the vis check whenever an ai actor tries to hide. For aliens this is the invers...
Definition: g_ai.cpp:570
bool G_ClientCanReload(Actor *actor, containerIndex_t containerID)
Returns true if actor can reload weapon.
Definition: g_client.cpp:536
bool AI_FindMissionLocation(Actor *actor, const pos3_t to, int tus, int radius)
Try to go close to a mission edict.
Definition: g_ai.cpp:1423
equipDef_t eds[MAX_EQUIPDEFS]
Definition: q_shared.h:540
static int oldPos
void clear(void)
Remove all data from the queue.
Definition: g_ai.cpp:127
#define PM_ALL
Definition: g_events.h:36
void setBody(unsigned int body_)
Definition: g_edict.h:387
InventoryInterface invi
Definition: g_local.h:76
bool CHRSH_IsTeamDefAlien(const teamDef_t *const td)
Check if a team definition is alien.
Definition: chr_shared.cpp:82
LQueue _area
Definition: g_ai.h:70
#define TEAM_CIVILIAN
Definition: q_shared.h:61
void reset()
Definition: g_ai.cpp:48
int getFmIdx() const
Definition: chr_shared.h:150
bool AI_CheckUsingDoor(const Edict *ent, const Edict *door)
Checks whether the AI controlled actor wants to use a door.
Definition: g_ai.cpp:396
#define G_IsCrouched(ent)
Definition: g_actor.h:32
#define G_IsBrushModel(ent)
Definition: g_local.h:138
void VectorNormalizeFast(vec3_t v)
fast vector normalize routine that does not check to make sure that length != 0, nor does it return l...
Definition: mathlib.cpp:762
const char * CHRSH_CharGetBody(const character_t *const chr)
Returns the body model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:296
cvar_t * ai_singleplayeraliens
Definition: g_main.cpp:72
static Actor * G_SpawnAIPlayer(const Player &player, const equipDef_t *ed)
Definition: g_ai.cpp:1863
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
#define TU_MOVE_STRAIGHT
Definition: defines.h:74
byte dmgtype
Definition: inv_shared.h:325
#define STATE_CROUCHED
Definition: q_shared.h:263
void G_AppearPerishEvent(playermask_t playerMask, bool appear, Edict &check, const Edict *ent)
Send the appear or perish event to the affected clients.
Definition: g_client.cpp:245
int integer
Definition: cvar.h:81
#define VT_NOFRUSTUM
Definition: g_vis.h:55
#define VectorScale(in, scale, out)
Definition: vector.h:79
void G_ReactionFireSettingsUpdate(Actor *actor, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t *od)
Updates the reaction fire settings in case something was moved into a hand or from a hand that would ...
Definition: g_reaction.cpp:555
#define G_MemFree(ptr)
Definition: g_local.h:65
void G_VisFlagsClear(int team)
Reset the visflags for all edicts in the global list for the given team - and only for the given team...
Definition: g_vis.cpp:424
float G_ActorVis(const Edict *ent, const Edict *check, bool full)
calculate how much check is "visible" by ent
Definition: g_vis.cpp:100
const csi_t * csi
Definition: game.h:176
Player & getPlayer() const
Definition: g_edict.h:265
AiAreaSearch class, used to get an area of the map around a certain position for the AI to check poss...
Definition: g_ai.h:33
float fraction
Definition: tracing.h:58
#define SCORE_PANIC_FLEE_FROM_STRANGERS
Definition: g_ai.cpp:247
Item * getContainer(const containerIndex_t idx) const
Definition: g_edict.h:243
cvar_t * g_aihumans
Definition: g_main.cpp:113
bool isWeapon() const
Definition: inv_shared.h:486
void G_ActorGiveTimeUnits(Actor *actor)
Set time units for the given edict. Based on speed skills.
Definition: g_actor.cpp:260
Item * getRightHandItem() const
Definition: g_edict.h:249
cvar_t * g_ailua
Definition: g_main.cpp:112
static int AI_CheckForMissionTargets(Actor *actor, const pos3_t pos)
Searches the map for mission edicts and try to get there.
Definition: g_ai.cpp:1010
game_locals_t game
Definition: g_main.cpp:37
static void AI_SetCharacterValues(Edict *ent, int team)
Sets an actor's character values.
Definition: g_ai.cpp:1784
const teamDef_t * alienTeams[MAX_TEAMS_PER_MISSION]
Definition: q_shared.h:553
#define IS_SHOT_RIGHT(x)
Determine whether the selected shoot type is for the item in the right hand, either shooting or react...
Definition: q_shared.h:243
#define SCORE_PANIC_RUN_TO_FRIENDS
Definition: g_ai.cpp:246
int getTeam() const
Definition: g_edict.h:269
#define CID_LEFT
Definition: inv_shared.h:48
int getUsableTUs() const
Calculates the amount of usable TUs. This is without the reserved TUs.
Definition: g_edict.h:400
#define VT_NEW
Definition: g_vis.h:58
item instance data, with linked list capability
Definition: inv_shared.h:402
bool AI_CheckLineOfFire(const Actor *shooter, const Edict *target, const fireDef_t *fd, int shots)
Definition: g_ai.cpp:763
int framenum
Definition: g_local.h:81
static bool AI_CheckCrouch(const Actor *actor)
Checks whether it would be smart to change the state to STATE_CROUCHED.
Definition: g_ai.cpp:452
~LQueue(void)
Clear LQueue internal data.
Definition: g_ai.cpp:84
#define G_IsMultiPlayer()
Definition: g_local.h:145
void CHRSH_CharGenAbilitySkills(character_t *chr, bool multiplayer, const char *templateId)
Generates a skill and ability set for any character.
Definition: chr_shared.cpp:220
used in shot probability calculations (pseudo shots)
Definition: g_combat.h:31
Player * G_PlayerGetNextActiveAI(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:126
#define PATHFINDING_WIDTH
absolute max
Definition: defines.h:292
bool dequeue(pos3_t data)
Retrieve an entry form the queue.
Definition: g_ai.cpp:111
static void AI_ActorThink(Player &player, Actor *actor)
The think function for the ai controlled aliens or panicked humans.
Definition: g_ai.cpp:1592
#define FLYING_DIRECTIONS
Definition: mathlib.h:89
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
#define TAG_LEVEL
Definition: g_local.h:59
Player * G_GetPlayerForTeam(int team)
Gets player for given team.
Definition: g_utils.cpp:188
bool launched
Definition: inv_shared.h:137
int treatmentLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:352
#define SCORE_REACTION_FEAR_FACTOR
Definition: g_ai.cpp:223
#define SCORE_MISSION_OPPONENT_TARGET
Definition: g_ai.cpp:241
#define DEBUG_ENGINE
Definition: defines.h:56
#define CID_RIGHT
Definition: inv_shared.h:47
int damage
Definition: g_combat.h:36
fireDefIndex_t numFiredefs[MAX_WEAPONS_PER_OBJDEF]
Definition: inv_shared.h:315
bool gravity
Definition: inv_shared.h:136
int friendCount
Definition: g_combat.h:33
game_import_t gi
Definition: g_main.cpp:39
#define ACTOR_VIS_0
Definition: g_vis.h:64
#define OBJZERO(obj)
Definition: shared.h:178
void plotPos(const pos3_t origin, int xOfs, int yOfs)
Definition: g_ai.cpp:203
#define VT_PERISHCHK
Definition: g_vis.h:53
int32_t shoot_types_t
Available shoot types - also see the ST_ constants.
Definition: q_shared.h:206
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
void EquipActor(character_t *const chr, const equipDef_t *ed, const objDef_t *weapon, int maxWeight)
Definition: inventory.cpp:947
int dmg
Definition: g_edict.h:138
static Edict * AI_SearchDestroyableObject(const Actor *actor, const fireDef_t *fd)
Definition: g_ai.cpp:737
#define G_IsCivilian(ent)
Definition: g_local.h:148
#define IS_SHOT_HEADGEAR(x)
Determine whether the selected shoot type is for the item in the headgear slot.
Definition: q_shared.h:245
int radius
Definition: g_edict.h:123
cvar_t * ai_multiplayeraliens
Definition: g_main.cpp:74
teammask_t visflags
Definition: g_edict.h:82
int AIL_InitActor(Actor *actor)
Initializes the lua AI for an actor.
Definition: g_ai_lua.cpp:2203
All parts of the main game logic that are combat related.
#define IS_SHOT_LEFT(x)
Determine whether the selected shoot type is for the item in the left hand, either shooting or reacti...
Definition: q_shared.h:241
const invDef_t * AI_SearchGrenade(const Actor *actor, Item **ip)
Search the edict's inventory for a grenade or other one-use weapon.
Definition: g_ai.cpp:954
bool isOpponent(const Actor *actor) const
Check if given actor is an enemy.
Definition: g_edicts.cpp:383
static bool AI_IsExposed(int team, Actor *check)
Test if check is exposed to the enemy team.
Definition: g_ai.cpp:298
int32_t fireDefIndex_t
Definition: inv_shared.h:78
bool isSamePosAs(const pos3_t cmpPos)
Check whether the edict is on the given position.
Definition: g_edict.h:286
int G_ActorGetModifiedTimeForFiredef(const Edict *const ent, const fireDef_t *const fd, const bool reaction)
Definition: g_actor.cpp:764
AiAreaSearch()
Initializes an AiAreaSearch object to default values.
Definition: g_ai.cpp:56
pos3_t pos
Definition: g_edict.h:55
fireDefIndex_t fdIdx
Definition: inv_shared.h:130
cvar_t * ai_numcivilians
Definition: g_main.cpp:73
#define G_TagMalloc(size, tag)
Definition: g_local.h:64
#define PosToVec(p, v)
Pos boundary size is +/- 128 - to get into the positive area we add the possible max negative value a...
Definition: mathlib.h:110
static bool AI_TeamThink(Player &player)
Definition: g_ai.cpp:1666
An Edict of type Actor.
Definition: g_edict.h:348
#define CID_FLOOR
Definition: inv_shared.h:55
bool isInsane() const
Definition: g_edict.h:359
byte num_alive[MAX_TEAMS]
Definition: g_local.h:115
#define SCORE_DAMAGE
Definition: g_ai.cpp:226
#define CLOSE_IN_DIST
Definition: g_ai.cpp:252
bool CHRSH_IsTeamDefRobot(const teamDef_t *const td)
Check if a team definition is a robot.
Definition: chr_shared.cpp:102
pos_t G_ActorMoveLength(const Actor *actor, const pathing_t *path, const pos3_t to, bool stored)
Return the needed TUs to walk to a given position.
Definition: g_move.cpp:270
Definition: line.h:31
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition: g_client.cpp:186
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
bool AI_IsHostile(const Actor *actor, const Edict *target)
Check if actor perceives target as hostile.
Definition: g_ai.cpp:933
const invDef_t * def() const
Definition: inv_shared.cpp:667
Definition: grid.h:83
#define GRAVITY
Definition: q_shared.h:276
#define INVDEF(containerID)
Definition: cl_shared.h:47
const equipDef_t * G_GetEquipDefByID(const char *equipID)
Definition: g_inventory.cpp:31
Item * getNextItem(const Item *prev) const
Definition: inv_shared.cpp:671
pos_t pos3_t[3]
Definition: ufotypes.h:58
#define RUN_AWAY_DIST
Definition: g_ai.cpp:235
vec2_t damage
Definition: inv_shared.h:158
byte shots
Definition: g_ai.cpp:43
pos3_t stop
Definition: g_ai.cpp:41
int numAlienTeams
Definition: q_shared.h:555
pos3_t to
Definition: g_ai.cpp:40
const char * CHRSH_CharGetHead(const character_t *const chr)
Returns the head model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:318
bool G_ActorInvMove(Actor *actor, const invDef_t *fromContType, Item *fItem, const invDef_t *toContType, int tx, int ty, bool checkaction)
Moves an item inside an inventory. Floors are handled special.
Definition: g_actor.cpp:506
#define SCORE_CLOSE_IN
Definition: g_ai.cpp:219
bool G_ActorReload(Actor *actor, const invDef_t *invDef)
Reload weapon with actor.
Definition: g_actor.cpp:714
#define SCORE_CIV_RANDOM
Definition: g_ai.cpp:232
#define UNIT_SIZE
Definition: defines.h:121
#define MASK_SHOT
Definition: defines.h:275
void setOrigin(const pos3_t newPos)
Set the edict's pos and origin vector to the given grid position.
Definition: g_edict.h:223
#define VectorCompare(a, b)
Definition: vector.h:63
woundInfo_t wounds
Definition: chr_shared.h:383
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
int count
Definition: g_edict.h:135
int32_t containerIndex_t
Definition: inv_shared.h:46
int G_CheckVisTeamAll(const int team, const vischeckflags_t visFlags, const Edict *ent)
Do G_CheckVisTeam for all entities ent is the one that is looking at the others.
Definition: g_vis.cpp:376
int G_TestVis(const int team, Edict *check, const vischeckflags_t flags)
test if check is visible by team (or if visibility changed?)
Definition: g_vis.cpp:255
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
#define VectorAdd(a, b, dest)
Definition: vector.h:47
void setMorale(int mor)
Definition: g_edict.h:311
int z_align
Definition: g_ai.cpp:46
void setStun(int stu)
Definition: g_edict.h:302
#define G_IsSinglePlayer()
Definition: g_local.h:146
#define SCORE_HIDE
Definition: g_ai.cpp:218
pathing_t * pathingMap
Definition: g_local.h:106
bool rolled
Definition: inv_shared.h:138
float range
Definition: inv_shared.h:152
FiremodeSettings RFmode
Definition: chr_shared.h:397
cvar_t *IMPORT const char *IMPORT * Cvar_String(const char *varName)
short numBodyParts(void) const
Definition: chr_shared.cpp:388
static float AI_PanicCalcActionScore(Actor *actor, const pos3_t to, AiAction *aia)
Calculates possible actions for a panicking unit.
Definition: g_ai.cpp:1351
#define LOF_CHECK_PARTITIONS
Definition: g_ai.cpp:762
bool G_ClientShoot(const Player &player, Actor *actor, const pos3_t at, shoot_types_t shootType, fireDefIndex_t firemode, shot_mock_t *mock, bool allowReaction, int z_align)
Setup for shooting, either real or mock.
Definition: g_combat.cpp:1183
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
#define VS_YES
Definition: g_vis.h:46
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define HIDE_DIST
distance for (ai) hiding in grid tiles
Definition: g_ai.cpp:256
int getTus() const
Definition: g_edict.h:318
bool AI_FindHerdLocation(Actor *actor, const pos3_t from, const vec3_t target, int tu, bool inverse)
Tries to search a spot where actor will be more closer to the target and behind the target from enemy...
Definition: g_ai.cpp:659
#define WAYPOINT_CIV_DIST
Definition: g_ai.cpp:236
#define G_IsDead(ent)
Definition: g_actor.h:34
static pathing_t * herdPathingTable
Definition: g_ai.cpp:264
bool isCrouched() const
Definition: g_edict.h:361
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
Definition: inv_shared.cpp:722
static const equipDef_t * G_GetEquipmentForAISpawn(int team)
Definition: g_ai.cpp:1850
#define G_IsAlien(ent)
Definition: g_local.h:149
void AI_CheckRespawn(int team)
If the cvar g_endlessaliens is set we will endlessly respawn aliens.
Definition: g_ai.cpp:1908
entity_type_t type
Definition: g_edict.h:81
#define GROUND_DELTA
Definition: defines.h:115
#define CORE_DIRECTIONS
Definition: mathlib.h:88
#define SCORE_DAMAGE_FACTOR
Definition: g_ai.cpp:227
const BodyData * bodyTemplate
Definition: chr_shared.h:339
static float AI_CivilianCalcActionScore(Actor *actor, const pos3_t to, AiAction *aia)
Calculates possible actions for a civilian.
Definition: g_ai.cpp:1231
#define CALC_DAMAGE_SAMPLES
Definition: g_ai.cpp:260
bool G_FrustumVis(const Edict *from, const vec3_t point)
Checks whether a point is "visible" from the edicts position.
Definition: g_vis.cpp:38
Edict * target
Definition: g_ai.cpp:44
functions to handle the storage and lifecycle of all edicts in the game module.
unsigned num_spawned[MAX_TEAMS]
Definition: g_local.h:112
vec3_t origin
Definition: g_edict.h:53
bool G_Vis(const int team, const Edict *from, const Edict *check, const vischeckflags_t flags)
test if check is visible by from
Definition: g_vis.cpp:183
bool AI_TryToReloadWeapon(Actor *actor, containerIndex_t containerID)
if a weapon can be reloaded we attempt to do so if TUs permit, otherwise drop it
Definition: g_ai.cpp:1574
bool isSameAs(const Edict *other) const
Definition: g_edict.h:275
bool isHeldTwoHanded() const
Definition: inv_shared.h:476
float crand(void)
Return random values between -1 and 1.
Definition: mathlib.cpp:517
bool isReaction() const
Definition: g_edict.h:357
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
int initialAlienActorsSpawned
Definition: g_local.h:111
vec_t vec3_t[3]
Definition: ufotypes.h:39
inventory definition for our menus
Definition: inv_shared.h:371
void G_EventActorAdd(playermask_t playerMask, const Edict &ent, const bool instant)
Definition: g_events.cpp:511
#define torad
Definition: mathlib.h:50
vec_t vec2_t[2]
Definition: ufotypes.h:38
static bool AI_IsHandForForShootTypeFree(shoot_types_t shootType, Actor *actor)
Check if the hand for the given shoot type is free.
Definition: g_ai.cpp:987
#define HERD_DIST
Definition: g_ai.cpp:257
float splrad
Definition: inv_shared.h:161
#define SCORE_RAGE
Definition: g_ai.cpp:225
const Item * AI_GetItemForShootType(shoot_types_t shootType, const Edict *ent)
Definition: g_ai.cpp:541
#define SCORE_CIV_FACTOR
Definition: g_ai.cpp:228
char id[MAX_VAR]
Definition: chr_shared.h:57
Inventory inv
Definition: chr_shared.h:392
shoot_types_t shootType
Definition: g_ai.cpp:42
cvar_t * mor_brave
Definition: g_main.cpp:104
char name[MAX_VAR]
Definition: chr_shared.h:371
#define getDVdir(dv)
Definition: mathlib.h:249
int morale
Definition: g_edict.h:91
#define G_IsVisibleForTeam(ent, team)
Definition: g_local.h:144
Player * AI_CreatePlayer(int team)
Spawn civilians and aliens.
Definition: g_ai.cpp:1944
GLsizei const GLvoid * data
Definition: r_gl.h:152
#define ROUTING_UNREACHABLE
Definition: defines.h:284
static void AI_FindBestFiredef(AiAction *aia, Actor *actor, Actor *check, const Item *item, shoot_types_t shootType, int tu, float *maxDmg, int *bestTime, const fireDef_t *fdArray)
Definition: g_ai.cpp:831
#define SCORE_CIV_LAZINESS
Definition: g_ai.cpp:234
void G_MoveCalc(int team, const Actor *movingActor, const pos3_t from, int distance)
Precalculates a move table for a given team and a given starting position. This will calculate a rout...
Definition: g_move.cpp:88
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition: g_edicts.cpp:83
#define NONE
Definition: defines.h:68
#define Q_streq(a, b)
Definition: shared.h:136
const chrTemplate_t * alienChrTemplates[MAX_TEAMS_PER_MISSION]
Definition: q_shared.h:554
actorHands_t getHand() const
Definition: chr_shared.h:158
void setHead(unsigned int head_)
Definition: g_edict.h:393
void AI_ActorRun(Player &player, Actor *actor)
Definition: g_ai.cpp:1655
#define AI_ACTION_NOTHING_FOUND
Definition: g_ai.cpp:250
int bounce
Definition: inv_shared.h:149
void G_CalcEffectiveSpread(const Actor *shooter, const fireDef_t *fd, vec2_t effSpread)
Calculate the effective spread for the given actor and firemode.
Definition: g_combat.cpp:671
#define ST_NUM_SHOOT_TYPES
Amount of shoottypes available.
Definition: q_shared.h:236
void enqueue(const pos3_t data)
Add an entry to the queue.
Definition: g_ai.cpp:92
Definition: g_edict.h:45
Actor * G_ClientGetFreeSpawnPointForActorSize(const Player &player, const actorSizeEnum_t actorSize)
Searches a free spawning point for a given actor size and turns it into an actor. ...
Definition: g_client.cpp:1039
short getRandomBodyPart(void) const
Definition: chr_shared.cpp:340
#define ACTOR_SIZE_NORMAL
Definition: defines.h:302
#define ROUTING_NOT_REACHABLE
Definition: defines.h:283
void plotArea(const pos3_t origin, int radius, bool flat=false)
Calculate the search area.
Definition: g_ai.cpp:143
bool AI_HasLineOfFire(const Actor *actor, const Edict *target)
Check if actor has a line of fire to the target given.
Definition: g_ai.cpp:275
Item * getLeftHandItem() const
Definition: g_edict.h:252
bool weapons
Definition: chr_shared.h:324
uint8_t byte
Definition: ufotypes.h:34
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
void AIL_ActorThink(Player &player, Actor *actor)
The think function for the ai controlled players.
Definition: g_ai_lua.cpp:2108
static void AI_PlayerRun(Player &player)
Definition: g_ai.cpp:1672
#define VectorDistSqr(a, b)
Definition: vector.h:68
#define SCORE_PANIC_RANDOM
Definition: g_ai.cpp:248
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
const fireDef_t * fd
Definition: g_ai.cpp:45
#define SCORE_RANDOM
Definition: g_ai.cpp:221
Edict * groupChain
Definition: g_edict.h:167
cvar_t * sv_ai
Definition: g_main.cpp:60
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
bool getNext(pos3_t pos)
Get next position in the search area.
Definition: g_ai.cpp:79
int G_VisCheckDist(const Edict *const ent)
Definition: g_vis.cpp:163
Interface for g_client.cpp.
static const Item * AI_GetItemFromInventory(const Item *ic)
Returns useable item from the given inventory list. That means that the 'weapon' has ammunition left ...
Definition: g_ai.cpp:522
byte pos_t
Definition: ufotypes.h:57
int entNum
Definition: tracing.h:67
#define G_IsBreakable(ent)
Definition: g_local.h:137
level_locals_t level
Definition: g_main.cpp:38
void G_MoveCalcLocal(pathing_t *pt, int team, const Edict *movingActor, const pos3_t from, int distance)
Same as G_MoveCalc, except that it uses the pathing table passed as the first param.
Definition: g_move.cpp:101
int HP
Definition: g_edict.h:89
int G_ApplyProtection(const Edict *target, const byte dmgWeight, int damage)
Reduces damage by armour and natural protection.
Definition: g_combat.cpp:362
const pos3_t pos3_origin
Definition: mathlib.cpp:37
#define SCORE_NONHIDING_PLACE_PENALTY
Definition: g_ai.cpp:224
#define SCORE_NOSAFE_POSITION_PENALTY
Definition: g_ai.cpp:239