00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "g_local.h"
00027 #include "g_ai.h"
00028
00035 static qboolean AI_CheckFF (const edict_t * ent, const vec3_t target, float spread)
00036 {
00037 edict_t *check = NULL;
00038 vec3_t dtarget, dcheck, back;
00039 float cosSpread;
00040
00041
00042 if (spread < 1.0)
00043 spread = 1.0;
00044 spread *= torad;
00045 cosSpread = cos(spread);
00046 VectorSubtract(target, ent->origin, dtarget);
00047 VectorNormalize(dtarget);
00048 VectorScale(dtarget, PLAYER_WIDTH / spread, back);
00049
00050 while ((check = G_EdictsGetNextLivingActorOfTeam(check, ent->team))) {
00051 if (ent != check) {
00052
00053 VectorSubtract(check->origin, ent->origin, dcheck);
00054 if (DotProduct(dtarget, dcheck) > 0.0) {
00055
00056 VectorAdd(dcheck, back, dcheck);
00057 VectorNormalize(dcheck);
00058 if (DotProduct(dtarget, dcheck) > cosSpread)
00059 return qtrue;
00060 }
00061 }
00062 }
00063
00064
00065 return qfalse;
00066 }
00067
00073 static qboolean AI_FighterCheckShoot (const edict_t* ent, const edict_t* check, const fireDef_t* fd, float *dist)
00074 {
00075
00076 *dist = VectorDist(ent->origin, check->origin);
00077 if (*dist > fd->range)
00078 return qfalse;
00079
00080 else if (*dist < fd->splrad)
00081 return qfalse;
00082
00083
00084 if (!G_IsInsane(ent) && AI_CheckFF(ent, check->origin, fd->spread[0]))
00085 return qfalse;
00086
00087 return qtrue;
00088 }
00089
00099 qboolean AI_CheckUsingDoor (const edict_t *ent, const edict_t *door)
00100 {
00101
00102 if (frand() < 0.3)
00103 return qfalse;
00104
00105
00106 if (!G_FrustumVis(door, ent->origin))
00107 return qfalse;
00108
00109
00110
00111 if (ent->hiding && door->doorState == STATE_OPENED)
00112 return qtrue;
00113
00114
00115 switch (ent->team) {
00116 case TEAM_ALIEN: {
00117
00118 edict_t *check = NULL;
00119
00120
00121 while ((check = G_EdictsGetNextLivingActor(check))) {
00122 float actorVis;
00123
00124 if (check->team == ent->team)
00125 continue;
00126
00127
00128 if (!G_FrustumVis(check, ent->origin))
00129 continue;
00130
00131 if (VectorDist(check->origin, ent->origin) > MAX_SPOT_DIST)
00132 continue;
00133 actorVis = G_ActorVis(check->origin, ent, qtrue);
00134
00135 if (actorVis > ACTOR_VIS_0)
00136 return qfalse;
00137 }
00138 }
00139 break;
00140 case TEAM_CIVILIAN:
00141
00142
00143 break;
00144 default:
00145 gi.DPrintf("Invalid team in AI_CheckUsingDoor: %i for ent type: %i\n",
00146 ent->team, ent->type);
00147 break;
00148 }
00149 return qtrue;
00150 }
00151
00157 static qboolean AI_CheckCrouch (const edict_t *ent)
00158 {
00159 edict_t *check = NULL;
00160
00161
00162 while ((check = G_EdictsGetNextLivingActor(check))) {
00163 float actorVis;
00164
00165 if (check->team == ent->team || G_IsCivilian(check))
00166 continue;
00167
00168
00169 if (!G_FrustumVis(check, ent->origin))
00170 continue;
00171
00172 if (VectorDist(check->origin, ent->origin) > MAX_SPOT_DIST)
00173 continue;
00174 actorVis = G_ActorVis(check->origin, ent, qtrue);
00175 if (actorVis >= ACTOR_VIS_50)
00176 return qtrue;
00177 }
00178 return qfalse;
00179 }
00180
00187 static qboolean AI_HideNeeded (const edict_t *ent)
00188 {
00189
00190 if (ent->morale > mor_brave->integer) {
00191 edict_t *from = NULL;
00192
00193 while ((from = G_EdictsGetNextLivingActor(from))) {
00194 if (from->team == ent->team)
00195 continue;
00196
00197 if (G_IsCivilian(from))
00198 continue;
00199
00200 if (G_IsVisibleForTeam(from, ent->team)) {
00201 const invList_t *invlist = RIGHT(from);
00202 const fireDef_t *fd = NULL;
00203 if (invlist && invlist->item.t) {
00204 fd = FIRESH_FiredefForWeapon(&invlist->item);
00205 } else {
00206 invlist = LEFT(from);
00207 if (invlist && invlist->item.t)
00208 fd = FIRESH_FiredefForWeapon(&invlist->item);
00209 }
00210
00211 if (fd != NULL && fd->range * fd->range >= VectorDistSqr(ent->origin, from->origin)) {
00212 const int damage = max(0, fd->damage[0] + (fd->damage[1] * crand()));
00213 if (damage >= ent->HP / 3) {
00214 const int hidingTeam = AI_GetHidingTeam(ent);
00215
00216 if (G_Vis(hidingTeam, ent, from, VT_NOFRUSTUM))
00217 return qtrue;
00218 }
00219 }
00220 }
00221 }
00222 return qfalse;
00223 }
00224 return qtrue;
00225 }
00226
00233 static inline const item_t* AI_GetItemFromInventory (const invList_t *ic)
00234 {
00235 if (ic != NULL) {
00236 const item_t *item = &ic->item;
00237 if (item->m && item->t->weapon && (!item->t->reload || item->a > 0))
00238 return item;
00239 }
00240 return NULL;
00241 }
00242
00250 const item_t *AI_GetItemForShootType (shoot_types_t shootType, const edict_t *ent)
00251 {
00252
00253 if (IS_SHOT_REACTION(shootType))
00254 return NULL;
00255
00256
00257
00258 if (IS_SHOT_RIGHT(shootType)) {
00259 const invList_t *ic = RIGHT(ent);
00260 return AI_GetItemFromInventory(ic);
00261 } else if (IS_SHOT_LEFT(shootType)) {
00262 const invList_t *ic = LEFT(ent);
00263 return AI_GetItemFromInventory(ic);
00264 } else if (IS_SHOT_HEADGEAR(shootType)) {
00265 return NULL;
00266 }
00267
00268 return NULL;
00269 }
00270
00279 int AI_GetHidingTeam (const edict_t *ent)
00280 {
00281 if (G_IsCivilian(ent))
00282 return TEAM_ALIEN;
00283 return -ent->team;
00284 }
00285
00295 qboolean AI_FindHidingLocation (int team, edict_t *ent, const pos3_t from, int *tuLeft)
00296 {
00297
00298 static pathing_t hidePathingTable;
00299 byte minX, maxX, minY, maxY;
00300 const byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
00301 const int distance = min(*tuLeft, HIDE_DIST * 2);
00302
00303
00304 G_MoveCalcLocal(&hidePathingTable, 0, ent, from, crouchingState, distance);
00305 ent->pos[2] = from[2];
00306 minX = max(from[0] - HIDE_DIST, 0);
00307 minY = max(from[1] - HIDE_DIST, 0);
00308 maxX = min(from[0] + HIDE_DIST, PATHFINDING_WIDTH - 1);
00309 maxY = min(from[1] + HIDE_DIST, PATHFINDING_WIDTH - 1);
00310
00311 for (ent->pos[1] = minY; ent->pos[1] <= maxY; ent->pos[1]++) {
00312 for (ent->pos[0] = minX; ent->pos[0] <= maxX; ent->pos[0]++) {
00313
00314 const pos_t delta = gi.MoveLength(&hidePathingTable, ent->pos, crouchingState, qfalse);
00315 if (delta > *tuLeft || delta == ROUTING_NOT_REACHABLE)
00316 continue;
00317
00318
00319 G_EdictCalcOrigin(ent);
00320 if (G_TestVis(team, ent, VT_PERISH | VT_NOFRUSTUM) & VIS_YES)
00321 continue;
00322
00323 *tuLeft -= delta;
00324 return qtrue;
00325 }
00326 }
00327
00328 return qfalse;
00329 }
00330
00340 qboolean AI_FindHerdLocation (edict_t *ent, const pos3_t from, const vec3_t target, int tu)
00341 {
00342 static pathing_t hidePathingTable;
00343 byte minX, maxX, minY, maxY;
00344 const byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
00345 const int distance = min(tu, HERD_DIST * 2);
00346 vec_t length;
00347 vec_t bestlength = 0.0f;
00348 pos3_t bestpos;
00349 edict_t* next = NULL;
00350 edict_t* enemy = NULL;
00351 vec3_t vfriend, venemy;
00352
00353
00354 while ((next = G_EdictsGetNextLivingActorOfTeam(next, AI_GetHidingTeam(ent)))) {
00355 length = VectorDistSqr(target, next->origin);
00356 if (!bestlength || length < bestlength) {
00357 enemy = next;
00358 bestlength = length;
00359 }
00360 }
00361 assert(enemy);
00362
00363
00364 G_MoveCalcLocal(&hidePathingTable, 0, ent, from, crouchingState, distance);
00365 ent->pos[2] = from[2];
00366 minX = max(from[0] - HERD_DIST, 0);
00367 minY = max(from[1] - HERD_DIST, 0);
00368 maxX = min(from[0] + HERD_DIST, PATHFINDING_WIDTH - 1);
00369 maxY = min(from[1] + HERD_DIST, PATHFINDING_WIDTH - 1);
00370
00371
00372 VectorCopy(from, bestpos);
00373 bestlength = VectorDistSqr(target, ent->origin);
00374 for (ent->pos[1] = minY; ent->pos[1] <= maxY; ent->pos[1]++) {
00375 for (ent->pos[0] = minX; ent->pos[0] <= maxX; ent->pos[0]++) {
00376
00377 const pos_t delta = gi.MoveLength(&hidePathingTable, ent->pos, crouchingState, qfalse);
00378 if (delta > tu || delta == ROUTING_NOT_REACHABLE)
00379 continue;
00380
00381 G_EdictCalcOrigin(ent);
00382 length = VectorDistSqr(ent->origin, target);
00383 if (length < bestlength) {
00384
00385 VectorSubtract(target, ent->origin, vfriend);
00386 VectorNormalize(vfriend);
00387 VectorSubtract(enemy->origin, ent->origin, venemy);
00388 VectorNormalize(venemy);
00389 if (DotProduct(vfriend, venemy) > 0.5) {
00390 bestlength = length;
00391 VectorCopy(ent->pos, bestpos);
00392 }
00393 }
00394 }
00395 }
00396
00397 if (!VectorCompare(from, bestpos)) {
00398 VectorCopy(bestpos, ent->pos);
00399 return qtrue;
00400 }
00401
00402 return qfalse;
00403 }
00404
00411 static edict_t *AI_SearchDestroyableObject (const edict_t *ent, const fireDef_t *fd)
00412 {
00413 #if 0
00414
00415 edict_t *check = NULL;
00416 float dist;
00417
00418 while ((check = G_EdictsGetNextInUse(check))) {
00419 if (G_IsBreakable(check)) {
00420 float vis;
00421
00422 if (!AI_FighterCheckShoot(ent, check, fd, &dist))
00423 continue;
00424
00425
00426 vis = G_ActorVis(ent->origin, check, qtrue);
00427 if (vis < ACTOR_VIS_0)
00428 continue;
00429
00430
00431 return check;
00432 }
00433 }
00434 #endif
00435 return NULL;
00436 }
00437
00441 static void AI_SearchBestTarget (aiAction_t *aia, const edict_t *ent, edict_t *check, const item_t *item, shoot_types_t shootType, int tu, float *maxDmg, int *bestTime, const fireDef_t *fdArray)
00442 {
00443 const objDef_t *ad;
00444 qboolean visChecked = qfalse;
00445 fireDefIndex_t fdIdx;
00446 float dist;
00447
00448 for (fdIdx = 0; fdIdx < item->m->numFiredefs[fdArray->weapFdsIdx]; fdIdx++) {
00449 const fireDef_t *fd = &fdArray[fdIdx];
00450 const float nspread = SPREAD_NORM((fd->spread[0] + fd->spread[1]) * 0.5 +
00451 GET_ACC(ent->chr.score.skills[ABILITY_ACCURACY], fd->weaponSkill));
00452
00453 const int shots = tu / fd->time;
00454 if (shots) {
00455 float dmg;
00456 float vis = ACTOR_VIS_0;
00457 if (!AI_FighterCheckShoot(ent, check, fd, &dist))
00458 continue;
00459
00460
00461 if (!visChecked) {
00462 vis = G_ActorVis(ent->origin, check, qtrue);
00463 visChecked = qtrue;
00464 }
00465 if (vis == ACTOR_VIS_0)
00466 continue;
00467
00468
00469 dmg = vis * (fd->damage[0] + fd->spldmg[0]) * fd->shots * shots;
00470 if (nspread && dist > nspread)
00471 dmg *= nspread / dist;
00472
00473
00474 if (CONTAINER(check, gi.csi->idArmour)) {
00475 ad = CONTAINER(check, gi.csi->idArmour)->item.t;
00476 dmg *= 1.0 - ad->protection[ad->dmgtype] * 0.01;
00477 }
00478
00479 if (dmg > check->HP && G_IsReaction(check))
00480
00481 dmg = check->HP + GUETE_KILL + GUETE_REACTION_ERADICATION;
00482 else if (dmg > check->HP)
00483
00484 dmg = check->HP + GUETE_KILL;
00485
00486
00487 if ((dmg < 25.0 && vis < 0.2)
00488 || (dmg < 10.0 && vis < 0.6)
00489 || dmg < 0.1)
00490 continue;
00491
00492
00493 if (G_IsCivilian(check) && !G_IsInsane(ent))
00494 dmg *= GUETE_CIV_FACTOR;
00495
00496
00497 if (dmg > 0)
00498 dmg += GUETE_RANDOM * frand();
00499
00500
00501 if (dmg > *maxDmg) {
00502 *maxDmg = dmg;
00503 *bestTime = fd->time * shots;
00504 aia->shootType = shootType;
00505 aia->shots = shots;
00506 aia->target = check;
00507 aia->fd = fd;
00508 }
00509
00510 if (!aia->target) {
00511 aia->target = AI_SearchDestroyableObject(ent, fd);
00512 if (aia->target) {
00513
00514
00515
00516 *maxDmg = (fd->damage[0] + fd->spldmg[0]);
00517 *bestTime = fd->time * shots;
00518 aia->shootType = shootType;
00519 aia->shots = shots;
00520 aia->fd = fd;
00521 }
00522 }
00523 }
00524 }
00525 }
00526
00532 static float AI_FighterCalcBestAction (edict_t * ent, pos3_t to, aiAction_t * aia)
00533 {
00534 edict_t *check = NULL;
00535 int tu;
00536 pos_t move;
00537 shoot_types_t shootType;
00538 float minDist;
00539 float bestActionPoints, maxDmg;
00540 int bestTime = -1;
00541
00542 move = gi.MoveLength(&level.pathingMap, to,
00543 G_IsCrouched(ent) ? 1 : 0, qtrue);
00544 tu = ent->TU - move;
00545
00546
00547 if (tu < 0 || move == ROUTING_NOT_REACHABLE)
00548 return AI_ACTION_NOTHING_FOUND;
00549
00550 bestActionPoints = 0.0;
00551 memset(aia, 0, sizeof(*aia));
00552
00553
00554 VectorCopy(to, aia->to);
00555 VectorCopy(to, aia->stop);
00556 G_EdictSetOrigin(ent, to);
00557
00558 maxDmg = 0.0;
00559
00560 while ((check = G_EdictsGetNextLivingActor(check))) {
00561 if (ent != check && (check->team != ent->team || G_IsInsane(ent))) {
00562
00563 if (G_IsCivilian(check) && sv_maxclients->integer > 1 && !G_IsInsane(ent))
00564 continue;
00565
00566
00567 maxDmg = 0.0;
00568 for (shootType = ST_RIGHT; shootType < ST_NUM_SHOOT_TYPES; shootType++) {
00569 const item_t *item;
00570 const fireDef_t *fdArray;
00571
00572 item = AI_GetItemForShootType(shootType, ent);
00573 if (!item)
00574 continue;
00575
00576 fdArray = FIRESH_FiredefForWeapon(item);
00577 if (fdArray == NULL)
00578 continue;
00579
00580 AI_SearchBestTarget(aia, ent, check, item, shootType, tu, &maxDmg, &bestTime, fdArray);
00581 }
00582 }
00583 }
00584
00585 if (aia->target) {
00586 bestActionPoints += maxDmg;
00587 assert(bestTime > 0);
00588 tu -= bestTime;
00589 }
00590
00591 if (!G_IsRaged(ent)) {
00592 const int hidingTeam = AI_GetHidingTeam(ent);
00593
00594 if (!AI_HideNeeded(ent) || !(G_TestVis(hidingTeam, ent, VT_PERISH | VT_NOFRUSTUM) & VIS_YES)) {
00595
00596 bestActionPoints += GUETE_HIDE + (aia->target ? GUETE_CLOSE_IN : 0);
00597 } else if (aia->target && tu >= TU_MOVE_STRAIGHT) {
00598
00605 bestActionPoints += max(GUETE_CLOSE_IN - move, 0);
00606
00607 if (!AI_FindHidingLocation(hidingTeam, ent, to, &tu)) {
00608
00609 G_EdictSetOrigin(ent, to);
00611 } else {
00612
00613 VectorCopy(ent->pos, aia->stop);
00614 bestActionPoints += GUETE_HIDE;
00617 }
00618 }
00619 }
00620
00621
00622 minDist = CLOSE_IN_DIST;
00623 check = NULL;
00624 while ((check = G_EdictsGetNextLivingActor(check))) {
00625 if (check->team != ent->team) {
00626 const float dist = VectorDist(ent->origin, check->origin);
00627 minDist = min(dist, minDist);
00628 }
00629 }
00630 bestActionPoints += GUETE_CLOSE_IN * (1.0 - minDist / CLOSE_IN_DIST);
00631
00632 return bestActionPoints;
00633 }
00634
00643 static float AI_CivilianCalcBestAction (edict_t * ent, pos3_t to, aiAction_t * aia)
00644 {
00645 edict_t *check = NULL;
00646 int tu;
00647 pos_t move;
00648 float minDist, minDistCivilian, minDistFighter;
00649 float bestActionPoints;
00650 float reactionTrap = 0.0;
00651 float delta = 0.0;
00652 const byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
00653
00654
00655 bestActionPoints = 0.0;
00656 memset(aia, 0, sizeof(*aia));
00657 VectorCopy(to, aia->to);
00658 VectorCopy(to, aia->stop);
00659 G_EdictSetOrigin(ent, to);
00660
00661 move = gi.MoveLength(&level.pathingMap, to, crouchingState, qtrue);
00662 tu = ent->TU - move;
00663
00664
00665 if (tu < 0 || move == ROUTING_NOT_REACHABLE)
00666 return AI_ACTION_NOTHING_FOUND;
00667
00668
00669 if (ent->chr.teamDef) {
00670 const teamDef_t* teamDef = ent->chr.teamDef;
00671 if (!G_IsPaniced(ent) && teamDef->weapons)
00672 return AI_FighterCalcBestAction(ent, to, aia);
00673 } else
00674 gi.DPrintf("AI_CivilianCalcBestAction: Error - civilian team with no teamdef\n");
00675
00676
00677 minDist = minDistCivilian = minDistFighter = RUN_AWAY_DIST * UNIT_SIZE;
00678
00679 while ((check = G_EdictsGetNextLivingActor(check))) {
00680 float dist;
00681 if (ent == check)
00682 continue;
00683 dist = VectorDist(ent->origin, check->origin);
00684
00685 if (!dist)
00686 return AI_ACTION_NOTHING_FOUND;
00687 switch (check->team) {
00688 case TEAM_ALIEN:
00689 if (dist < minDist)
00690 minDist = dist;
00691 break;
00692 case TEAM_CIVILIAN:
00693 if (dist < minDistCivilian)
00694 minDistCivilian = dist;
00695 break;
00696 case TEAM_PHALANX:
00697 if (dist < minDistFighter)
00698 minDistFighter = dist;
00699 break;
00700 }
00701 }
00702
00703 minDist /= UNIT_SIZE;
00704 minDistCivilian /= UNIT_SIZE;
00705 minDistFighter /= UNIT_SIZE;
00706
00707 if (minDist < 8.0) {
00708
00709 delta = 4.0 * minDist;
00710 } else if (minDist < 16.0) {
00711
00712 delta = 24.0 + minDist;
00713 } else if (minDist < 24.0) {
00714
00715 delta = 40.0 + (minDist - 16) / 4;
00716 } else {
00717 delta = 42.0;
00718 }
00719
00720 if (minDistCivilian < 10.0)
00721 delta += (10.0 - minDistCivilian) / 3.0;
00722
00723 if (minDistFighter < 15.0)
00724 delta += (15.0 - minDistFighter) / 5.0;
00725
00726 if (minDistFighter < 2.0)
00727 delta /= 10.0;
00728
00729
00730 check = NULL;
00731 while ((check = G_EdictsGetNextLivingActor(check))) {
00732 if (ent == check)
00733 continue;
00734 if (!(G_IsAlien(check) || G_IsInsane(ent)))
00735 continue;
00736
00737 if (!G_IsVisibleForTeam(check, ent->team))
00738 continue;
00739
00740 if (G_ActorVis(check->origin, ent, qtrue) > 0.25)
00741 reactionTrap += 25.0;
00742 }
00743 delta -= reactionTrap;
00744 bestActionPoints += delta;
00745
00746
00747 if (ent->TU)
00748 bestActionPoints += GUETE_CIV_LAZINESS * tu / ent->TU;
00749
00750 bestActionPoints += GUETE_CIV_RANDOM * frand();
00751
00752 return bestActionPoints;
00753 }
00754
00761 static int AI_CheckForMissionTargets (const player_t* player, edict_t *ent, aiAction_t *aia)
00762 {
00763 int bestActionPoints = AI_ACTION_NOTHING_FOUND;
00764 const byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
00765
00766
00767 memset(aia, 0, sizeof(*aia));
00768
00769 if (ent->team == TEAM_CIVILIAN) {
00770 edict_t *checkPoint = NULL;
00771 int length;
00772 int i = 0;
00773
00774
00775 for (checkPoint = ai_waypointList; checkPoint != NULL; checkPoint = checkPoint->groupChain) {
00776 if (checkPoint->inuse)
00777 continue;
00778
00779
00780 if (checkPoint->count < ent->count) {
00781 if (VectorDist(ent->origin, checkPoint->origin) <= WAYPOINT_CIV_DIST) {
00782 const pos_t move = gi.MoveLength(&level.pathingMap, checkPoint->pos, crouchingState, qtrue);
00783 i++;
00784 if (move == ROUTING_NOT_REACHABLE)
00785 continue;
00786
00787
00788 length = ent->TU - move;
00789 bestActionPoints = GUETE_MISSION_TARGET + length;
00790
00791 ent->count = checkPoint->count;
00792 VectorCopy(checkPoint->pos, aia->to);
00793 }
00794 }
00795 }
00796
00797 if (!i)
00798 ent->count = 100;
00799 } else if (ent->team == TEAM_ALIEN) {
00800
00801 edict_t *mission = NULL;
00802 while ((mission = G_EdictsGetNextInUse(mission))) {
00803 if (mission->type == ET_MISSION) {
00804 VectorCopy(mission->pos, aia->to);
00805 aia->target = mission;
00806 if (player->pers.team == mission->team) {
00807 bestActionPoints = GUETE_MISSION_TARGET;
00808 break;
00809 } else {
00810
00811 bestActionPoints = GUETE_MISSION_OPPONENT_TARGET;
00812 }
00813 }
00814 }
00815 }
00816
00817 return bestActionPoints;
00818 }
00819
00820 #define AI_MAX_DIST 30
00821
00827 static aiAction_t AI_PrepBestAction (const player_t *player, edict_t * ent)
00828 {
00829 aiAction_t aia, bestAia;
00830 pos3_t oldPos, to;
00831 vec3_t oldOrigin;
00832 int xl, yl, xh, yh;
00833 int dist;
00834 float bestActionPoints, best;
00835 const byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
00836
00837
00838 G_MoveCalc(0, ent, ent->pos, crouchingState, ent->TU);
00839 Com_DPrintf(DEBUG_ENGINE, "AI_PrepBestAction: Called MoveMark.\n");
00840 gi.MoveStore(&level.pathingMap);
00841
00842
00843 dist = (ent->TU + 1) / 2;
00844 xl = max((int) ent->pos[0] - dist, 0);
00845 yl = max((int) ent->pos[1] - dist, 0);
00846 xh = min((int) ent->pos[0] + dist, PATHFINDING_WIDTH);
00847 yh = min((int) ent->pos[1] + dist, PATHFINDING_WIDTH);
00848
00849
00850 best = AI_ACTION_NOTHING_FOUND;
00851 VectorCopy(ent->pos, oldPos);
00852 VectorCopy(ent->origin, oldOrigin);
00853
00854
00855
00856 for (to[2] = 0; to[2] < PATHFINDING_HEIGHT; to[2]++)
00857 for (to[1] = yl; to[1] < yh; to[1]++)
00858 for (to[0] = xl; to[0] < xh; to[0]++) {
00859 const pos_t move = gi.MoveLength(&level.pathingMap, to, crouchingState, qtrue);
00860 if (move != ROUTING_NOT_REACHABLE && move <= ent->TU) {
00861 if (G_IsCivilian(ent) || G_IsPaniced(ent))
00862 bestActionPoints = AI_CivilianCalcBestAction(ent, to, &aia);
00863 else
00864 bestActionPoints = AI_FighterCalcBestAction(ent, to, &aia);
00865
00866 if (bestActionPoints > best) {
00867 bestAia = aia;
00868 best = bestActionPoints;
00869 }
00870 }
00871 }
00872
00873 VectorCopy(oldPos, ent->pos);
00874 VectorCopy(oldOrigin, ent->origin);
00875
00876 bestActionPoints = AI_CheckForMissionTargets(player, ent, &aia);
00877 if (bestActionPoints > best) {
00878 bestAia = aia;
00879 best = bestActionPoints;
00880 }
00881
00882
00883 if (best == AI_ACTION_NOTHING_FOUND) {
00884 bestAia.target = NULL;
00885 return bestAia;
00886 }
00887
00888
00889 if (G_IsCrouched(ent))
00890 G_ClientStateChange(player, ent, STATE_CROUCHED, qtrue);
00891
00892
00893 G_ClientMove(player, 0, ent, bestAia.to);
00894
00895
00896 if (G_IsDead(ent))
00897 memset(&bestAia, 0, sizeof(bestAia));
00898
00899 return bestAia;
00900 }
00901
00902 edict_t *ai_waypointList;
00903
00904 void G_AddToWayPointList (edict_t *ent)
00905 {
00906 int i = 1;
00907
00908 if (!ai_waypointList)
00909 ai_waypointList = ent;
00910 else {
00911 edict_t *e = ai_waypointList;
00912 while (e->groupChain) {
00913 e = e->groupChain;
00914 i++;
00915 }
00916 i++;
00917 e->groupChain = ent;
00918 }
00919 }
00920
00927 void AI_TurnIntoDirection (edict_t *ent, const pos3_t pos)
00928 {
00929 int dv;
00930 const byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
00931
00932 G_MoveCalc(ent->team, ent, pos, crouchingState, ent->TU);
00933
00934 dv = gi.MoveNext(&level.pathingMap, pos, crouchingState);
00935 if (dv != ROUTING_UNREACHABLE) {
00936 const byte dir = getDVdir(dv);
00937
00938 if (dir < CORE_DIRECTIONS || dir >= FLYING_DIRECTIONS)
00939 G_ActorDoTurn(ent, dir & (CORE_DIRECTIONS - 1));
00940 }
00941 }
00942
00946 static void AI_TryToReloadWeapon (edict_t *ent, containerIndex_t containerID)
00947 {
00948 if (G_ClientCanReload(ent, containerID)) {
00949 G_ActorReload(ent, INVDEF(containerID));
00950 } else {
00951 G_ActorInvMove(ent, INVDEF(containerID), CONTAINER(ent, containerID), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue);
00952 }
00953 }
00954
00964 void AI_ActorThink (player_t * player, edict_t * ent)
00965 {
00966 aiAction_t bestAia;
00967
00968
00969 if (!G_IsPaniced(ent)) {
00970 if (RIGHT(ent) && RIGHT(ent)->item.t->reload && RIGHT(ent)->item.a == 0)
00971 AI_TryToReloadWeapon(ent, gi.csi->idRight);
00972 if (LEFT(ent) && LEFT(ent)->item.t->reload && LEFT(ent)->item.a == 0)
00973 AI_TryToReloadWeapon(ent, gi.csi->idLeft);
00974 }
00975
00976
00977
00978 if (!LEFT(ent) && !RIGHT(ent))
00979 G_ClientGetWeaponFromInventory(ent);
00980
00981 bestAia = AI_PrepBestAction(player, ent);
00982
00983
00984 if (bestAia.target) {
00985 const fireDefIndex_t fdIdx = bestAia.fd ? bestAia.fd->fdIdx : 0;
00986
00987 while (bestAia.shots) {
00988 G_ClientShoot(player, ent, bestAia.target->pos, bestAia.shootType, fdIdx, NULL, qtrue, bestAia.z_align);
00989 bestAia.shots--;
00990
00991 if (G_IsDead(ent))
00992 return;
00993
00994 if (G_IsDead(bestAia.target)) {
00995
00996 bestAia = AI_PrepBestAction(player, ent);
00997
00998 if (!bestAia.target)
00999 return;
01000 }
01001 }
01002 ent->hiding = qtrue;
01003
01004
01005
01006 G_ClientMove(player, ent->team, ent, bestAia.stop);
01007
01008
01009
01010
01011 if (AI_CheckCrouch(ent))
01012 G_ClientStateChange(player, ent, STATE_CROUCHED, qfalse);
01013
01014
01015
01016 AI_TurnIntoDirection(ent, bestAia.target->pos);
01017
01020
01021
01022 ent->hiding = qfalse;
01023 }
01024 }
01025
01026
01031 void AI_Run (void)
01032 {
01033 player_t *player;
01034 int i;
01035
01036
01037 if (level.framenum % 10)
01038 return;
01039
01040
01041 for (i = 0, player = game.players + game.sv_maxplayersperteam; i < game.sv_maxplayersperteam; i++, player++)
01042 if (player->inuse && G_IsAIPlayer(player) && level.activeTeam == player->pers.team) {
01043
01044 edict_t *ent = player->pers.last;
01045
01046 while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, player->pers.team))) {
01047 if (ent->TU) {
01048 if (g_ailua->integer)
01049 AIL_ActorThink(player, ent);
01050 else
01051 AI_ActorThink(player, ent);
01052 player->pers.last = ent;
01053 return;
01054 }
01055 }
01056
01057
01058 G_ClientEndRound(player);
01059 player->pers.last = NULL;
01060 return;
01061 }
01062 }
01063
01068 static void AI_SetStats (edict_t * ent)
01069 {
01070 CHRSH_CharGenAbilitySkills(&ent->chr, sv_maxclients->integer >= 2);
01071
01072 ent->HP = ent->chr.HP;
01073 ent->morale = ent->chr.morale;
01074 ent->STUN = 0;
01075
01076 G_ActorGiveTimeUnits(ent);
01077 }
01078
01079
01085 static void AI_SetCharacterValues (edict_t * ent, int team)
01086 {
01087
01088 const char *teamDefintion;
01089 if (team != TEAM_CIVILIAN) {
01090 if (gi.csi->numAlienTeams) {
01091 const int alienTeam = rand() % gi.csi->numAlienTeams;
01092 assert(gi.csi->alienTeams[alienTeam]);
01093 teamDefintion = gi.csi->alienTeams[alienTeam]->id;
01094 } else
01095 teamDefintion = gi.Cvar_String("ai_alien");
01096 } else if (team == TEAM_CIVILIAN) {
01097 teamDefintion = gi.Cvar_String("ai_civilian");
01098 }
01099 gi.GetCharacterValues(teamDefintion, &ent->chr);
01100 if (!ent->chr.teamDef)
01101 gi.Error("Could not set teamDef for character: '%s'", teamDefintion);
01102 }
01103
01104
01110 static void AI_SetEquipment (edict_t * ent, const equipDef_t * ed)
01111 {
01112
01113 if (ent->chr.teamDef->weapons)
01114 game.i.EquipActor(&game.i, &ent->chr.i, ed, ent->chr.teamDef);
01115 else if (ent->chr.teamDef)
01116
01117 game.i.EquipActorMelee(&game.i, &ent->chr.i, ent->chr.teamDef);
01118 else
01119 gi.DPrintf("AI_InitPlayer: actor with no equipment\n");
01120 }
01121
01122
01129 static void AI_InitPlayer (const player_t * player, edict_t * ent, const equipDef_t * ed)
01130 {
01131 const int team = player->pers.team;
01132
01133
01134 AI_SetCharacterValues(ent, team);
01135
01136
01137 AI_SetStats(ent);
01138
01139
01140 if (ed != NULL)
01141 AI_SetEquipment(ent, ed);
01142
01143
01144 ent->body = gi.ModelIndex(CHRSH_CharGetBody(&ent->chr));
01145 ent->head = gi.ModelIndex(CHRSH_CharGetHead(&ent->chr));
01146
01147
01148 if (frand() < 0.75f)
01149 G_ClientStateChange(player, ent, STATE_REACTION_ONCE, qfalse);
01150
01151
01152 if (team == TEAM_CIVILIAN)
01153 AIL_InitActor(ent, "civilian", "default");
01154 else if (team == TEAM_ALIEN)
01155 AIL_InitActor(ent, "alien", "default");
01156 else
01157 gi.DPrintf("AI_InitPlayer: unknown team AI\n");
01158 }
01159
01166 static void G_SpawnAIPlayer (const player_t * player, int numSpawn)
01167 {
01168 const equipDef_t *ed;
01169 int i;
01170 const int team = player->pers.team;
01171
01172
01173 if (team != TEAM_CIVILIAN) {
01174 char name[MAX_VAR];
01175 Q_strncpyz(name, gi.Cvar_String("ai_equipment"), sizeof(name));
01176 for (i = 0, ed = gi.csi->eds; i < gi.csi->numEDs; i++, ed++)
01177 if (!strcmp(name, ed->name))
01178 break;
01179 if (i == gi.csi->numEDs)
01180 ed = &gi.csi->eds[0];
01181 } else {
01182 ed = NULL;
01183 }
01184
01185
01186 for (i = 0; i < numSpawn; i++) {
01187 edict_t *ent = G_ClientGetFreeSpawnPointForActorSize(player, ACTOR_SIZE_NORMAL);
01188 if (!ent) {
01189 gi.DPrintf("Not enough spawn points for team %i\n", team);
01190 break;
01191 }
01192
01193
01194 AI_InitPlayer(player, ent, ed);
01195 }
01196
01197 G_ClearVisFlags(team);
01198 G_CheckVis(NULL, qfalse);
01199 }
01200
01201
01209 player_t *AI_CreatePlayer (int team)
01210 {
01211 player_t *p;
01212 int i;
01213
01214 if (!sv_ai->integer) {
01215 gi.DPrintf("AI deactivated - set sv_ai cvar to 1 to activate it\n");
01216 return NULL;
01217 }
01218
01219
01220 for (i = 0, p = game.players + game.sv_maxplayersperteam; i < game.sv_maxplayersperteam; i++, p++)
01221 if (!p->inuse) {
01222 memset(p, 0, sizeof(*p));
01223 p->inuse = qtrue;
01224 p->num = p - game.players;
01225 p->pers.ai = qtrue;
01226 G_SetTeamForPlayer(p, team);
01227 if (p->pers.team == TEAM_CIVILIAN)
01228 G_SpawnAIPlayer(p, ai_numcivilians->integer);
01229 else if (sv_maxclients->integer == 1)
01230 G_SpawnAIPlayer(p, ai_numaliens->integer);
01231 else
01232 G_SpawnAIPlayer(p, ai_numactors->integer);
01233 gi.DPrintf("Created AI player (team %i)\n", p->pers.team);
01234 return p;
01235 }
01236
01237
01238 return NULL;
01239 }