00001
00002
00003
00004
00005
00006 #include "b_local.h"
00007 #include "g_nav.h"
00008
00009 #define MAX_RADIUS_ENTS 128
00010 #define DEFAULT_RADIUS 45
00011
00012 extern vmCvar_t d_noGroupAI;
00013 qboolean AI_ValidateGroupMember( AIGroupInfo_t *group, gentity_t *member );
00014
00015 extern void G_TestLine(vec3_t start, vec3_t end, int color, int time);
00016
00017
00018
00019
00020
00021
00022
00023 int AI_GetGroupSize( vec3_t origin, int radius, team_t playerTeam, gentity_t *avoid )
00024 {
00025 int radiusEnts[ MAX_RADIUS_ENTS ];
00026 gentity_t *check;
00027 vec3_t mins, maxs;
00028 int numEnts, realCount = 0;
00029 int i;
00030 int j;
00031
00032
00033 for ( i = 0; i < 3; i++ )
00034 {
00035 mins[i] = origin[i] - radius;
00036 maxs[i] = origin[i] + radius;
00037 }
00038
00039
00040 numEnts = trap_EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
00041
00042
00043 for ( j = 0; j < numEnts; j++ )
00044 {
00045 check = &g_entities[radiusEnts[j]];
00046
00047
00048 if ( check->client == NULL )
00049 continue;
00050
00051
00052 if ( ( avoid != NULL ) && ( check == avoid ) )
00053 continue;
00054
00055
00056 if ( check->client->playerTeam != playerTeam )
00057 continue;
00058
00059
00060 if ( check->health <= 0 )
00061 continue;
00062
00063 realCount++;
00064 }
00065
00066 return realCount;
00067 }
00068
00069
00070
00071 int AI_GetGroupSize2( gentity_t *ent, int radius )
00072 {
00073 if ( ( ent == NULL ) || ( ent->client == NULL ) )
00074 return -1;
00075
00076 return AI_GetGroupSize( ent->r.currentOrigin, radius, ent->client->playerTeam, ent );
00077 }
00078
00079 extern int NAV_FindClosestWaypointForPoint( gentity_t *ent, vec3_t point );
00080 int AI_ClosestGroupEntityNumToPoint( AIGroupInfo_t *group, vec3_t point )
00081 {
00082 int markerWP = WAYPOINT_NONE;
00083 int cost, bestCost = Q3_INFINITE;
00084 int closest = ENTITYNUM_NONE;
00085 int i;
00086
00087 if ( group == NULL || group->numGroup <= 0 )
00088 {
00089 return ENTITYNUM_NONE;
00090 }
00091
00092 markerWP = NAV_FindClosestWaypointForPoint( &g_entities[group->member[0].number], point );
00093
00094 if ( markerWP == WAYPOINT_NONE )
00095 {
00096 return ENTITYNUM_NONE;
00097 }
00098
00099 for ( i = 0; i < group->numGroup; i++ )
00100 {
00101 cost = trap_Nav_GetPathCost( group->member[i].waypoint, markerWP );
00102 if ( cost < bestCost )
00103 {
00104 bestCost = cost;
00105 closest = group->member[i].number;
00106 }
00107 }
00108
00109 return closest;
00110 }
00111
00112 void AI_SetClosestBuddy( AIGroupInfo_t *group )
00113 {
00114 int i, j;
00115 int dist, bestDist;
00116
00117 for ( i = 0; i < group->numGroup; i++ )
00118 {
00119 group->member[i].closestBuddy = ENTITYNUM_NONE;
00120
00121 bestDist = Q3_INFINITE;
00122 for ( j = 0; j < group->numGroup; j++ )
00123 {
00124 dist = DistanceSquared( g_entities[group->member[i].number].r.currentOrigin, g_entities[group->member[j].number].r.currentOrigin );
00125 if ( dist < bestDist )
00126 {
00127 bestDist = dist;
00128 group->member[i].closestBuddy = group->member[j].number;
00129 }
00130 }
00131 }
00132 }
00133
00134 void AI_SortGroupByPathCostToEnemy( AIGroupInfo_t *group )
00135 {
00136 AIGroupMember_t bestMembers[MAX_GROUP_MEMBERS];
00137 int i, j, k;
00138 qboolean sort = qfalse;
00139
00140 if ( group->enemy != NULL )
00141 {
00142 group->enemyWP = NAV_FindClosestWaypointForEnt( group->enemy, WAYPOINT_NONE );
00143 }
00144 else
00145 {
00146 group->enemyWP = WAYPOINT_NONE;
00147 }
00148
00149 for ( i = 0; i < group->numGroup; i++ )
00150 {
00151 if ( group->enemyWP == WAYPOINT_NONE )
00152 {
00153 group->member[i].waypoint = WAYPOINT_NONE;
00154 group->member[i].pathCostToEnemy = Q3_INFINITE;
00155 }
00156 else
00157 {
00158 group->member[i].waypoint = NAV_FindClosestWaypointForEnt( group->enemy, WAYPOINT_NONE );
00159 if ( group->member[i].waypoint != WAYPOINT_NONE )
00160 {
00161 group->member[i].pathCostToEnemy = trap_Nav_GetPathCost( group->member[i].waypoint, group->enemyWP );
00162
00163 sort = qtrue;
00164 }
00165 else
00166 {
00167 group->member[i].pathCostToEnemy = Q3_INFINITE;
00168 }
00169 }
00170 }
00171
00172 if ( sort )
00173 {
00174
00175 for ( j = 0; j < group->numGroup; j++ )
00176 {
00177 bestMembers[j].number = ENTITYNUM_NONE;
00178 }
00179
00180 for ( i = 0; i < group->numGroup; i++ )
00181 {
00182 for ( j = 0; j < group->numGroup; j++ )
00183 {
00184 if ( bestMembers[j].number != ENTITYNUM_NONE )
00185 {
00186 if ( group->member[i].pathCostToEnemy < bestMembers[j].pathCostToEnemy )
00187 {
00188 for ( k = group->numGroup; k > j; k++ )
00189 {
00190 memcpy( &bestMembers[k], &bestMembers[k-1], sizeof( bestMembers[k] ) );
00191 }
00192 memcpy( &bestMembers[j], &group->member[i], sizeof( bestMembers[j] ) );
00193 break;
00194 }
00195 }
00196 else
00197 {
00198 memcpy( &bestMembers[j], &group->member[i], sizeof( bestMembers[j] ) );
00199 break;
00200 }
00201 }
00202 }
00203
00204
00205 for ( i = 0; i < group->numGroup; i++ )
00206 {
00207 memcpy( &group->member[i], &bestMembers[i], sizeof( group->member[i] ) );
00208 }
00209 }
00210 }
00211
00212 qboolean AI_FindSelfInPreviousGroup( gentity_t *self )
00213 {
00214 int i, j;
00215 for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
00216 {
00217 if ( level.groups[i].numGroup )
00218 {
00219 for ( j = 0; j < level.groups[i].numGroup; j++ )
00220 {
00221 if ( level.groups[i].member[j].number == self->s.number )
00222 {
00223 self->NPC->group = &level.groups[i];
00224 return qtrue;
00225 }
00226 }
00227 }
00228 }
00229 return qfalse;
00230 }
00231
00232 void AI_InsertGroupMember( AIGroupInfo_t *group, gentity_t *member )
00233 {
00234 int i;
00235
00236
00237 for ( i = 0; i < group->numGroup; i++ )
00238 {
00239 if ( group->member[i].number == member->s.number )
00240 {
00241 break;
00242 }
00243 }
00244 if ( i < group->numGroup )
00245 {
00246 }
00247 else
00248 {
00249 group->member[group->numGroup++].number = member->s.number;
00250 group->numState[member->NPC->squadState]++;
00251 }
00252 if ( !group->commander || (member->NPC->rank > group->commander->NPC->rank) )
00253 {
00254 group->commander = member;
00255 }
00256 member->NPC->group = group;
00257 }
00258
00259 qboolean AI_TryJoinPreviousGroup( gentity_t *self )
00260 {
00261 int i;
00262 for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
00263 {
00264 if ( level.groups[i].numGroup
00265 && level.groups[i].numGroup < (MAX_GROUP_MEMBERS - 1)
00266
00267 && level.groups[i].enemy == self->enemy )
00268 {
00269 if ( AI_ValidateGroupMember( &level.groups[i], self ) )
00270 {
00271 AI_InsertGroupMember( &level.groups[i], self );
00272 return qtrue;
00273 }
00274 }
00275 }
00276 return qfalse;
00277 }
00278
00279 qboolean AI_GetNextEmptyGroup( gentity_t *self )
00280 {
00281 int i;
00282
00283 if ( AI_FindSelfInPreviousGroup( self ) )
00284 {
00285 return qfalse;
00286 }
00287
00288 if ( AI_TryJoinPreviousGroup( self ) )
00289 {
00290 return qfalse;
00291 }
00292
00293
00294 for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
00295 {
00296 if ( !level.groups[i].numGroup )
00297 {
00298 self->NPC->group = &level.groups[i];
00299 return qtrue;
00300 }
00301 }
00302
00303
00304 {
00305 self->NPC->group = NULL;
00306 return qfalse;
00307 }
00308 }
00309
00310 qboolean AI_ValidateNoEnemyGroupMember( AIGroupInfo_t *group, gentity_t *member )
00311 {
00312 vec3_t center;
00313
00314 if ( !group )
00315 {
00316 return qfalse;
00317 }
00318 if ( group->commander )
00319 {
00320 VectorCopy( group->commander->r.currentOrigin, center );
00321 }
00322 else
00323 {
00324 if ( group->member[0].number < 0 || group->member[0].number >= ENTITYNUM_WORLD )
00325 {
00326 return qfalse;
00327 }
00328 VectorCopy( g_entities[group->member[0].number].r.currentOrigin, center );
00329 }
00330
00331 if ( DistanceSquared( center, member->r.currentOrigin ) > 147456 )
00332 {
00333 return qfalse;
00334 }
00335 if ( !trap_InPVS( member->r.currentOrigin, center ) )
00336 {
00337 return qfalse;
00338 }
00339 return qtrue;
00340 }
00341
00342 qboolean AI_ValidateGroupMember( AIGroupInfo_t *group, gentity_t *member )
00343 {
00344
00345 if ( member == NULL )
00346 return qfalse;
00347
00348
00349 if ( member->client == NULL )
00350 return qfalse;
00351
00352
00353 if ( member->NPC == NULL )
00354 return qfalse;
00355
00356
00357 if ( member->NPC->confusionTime > level.time )
00358 return qfalse;
00359
00360
00361 if ( member->NPC->scriptFlags&SCF_NO_GROUPS )
00362 return qfalse;
00363
00364
00365 if ( member->NPC->group != NULL && member->NPC->group != group )
00366 {
00367 return qfalse;
00368 }
00369
00370
00371 if ( member->health <= 0 )
00372 return qfalse;
00373
00374
00375
00376
00377
00378
00379
00380 if ( member->client->playerTeam != group->team )
00381 return qfalse;
00382
00383 if ( member->client->ps.weapon == WP_SABER ||
00384 member->client->ps.weapon == WP_THERMAL ||
00385 member->client->ps.weapon == WP_DISRUPTOR ||
00386 member->client->ps.weapon == WP_EMPLACED_GUN ||
00387
00388 member->client->ps.weapon == WP_STUN_BATON ||
00389 member->client->ps.weapon == WP_TURRET
00390
00391
00392 )
00393 {
00394 return qfalse;
00395 }
00396
00397 if ( member->client->NPC_class == CLASS_ATST ||
00398 member->client->NPC_class == CLASS_PROBE ||
00399 member->client->NPC_class == CLASS_SEEKER ||
00400 member->client->NPC_class == CLASS_REMOTE ||
00401 member->client->NPC_class == CLASS_SENTRY ||
00402 member->client->NPC_class == CLASS_INTERROGATOR ||
00403 member->client->NPC_class == CLASS_MINEMONSTER ||
00404 member->client->NPC_class == CLASS_HOWLER ||
00405 member->client->NPC_class == CLASS_MARK1 ||
00406 member->client->NPC_class == CLASS_MARK2 )
00407 {
00408 return qfalse;
00409 }
00410
00411
00412 if ( member->enemy != group->enemy )
00413 {
00414 if ( member->enemy != NULL )
00415 {
00416 return qfalse;
00417 }
00418 if ( !trap_InPVS( member->r.currentOrigin, group->enemy->r.currentOrigin ) )
00419 {
00420 return qfalse;
00421 }
00422 }
00423 else if ( group->enemy == NULL )
00424 {
00425 if ( !AI_ValidateNoEnemyGroupMember( group, member ) )
00426 {
00427 return qfalse;
00428 }
00429 }
00430
00431 if ( !TIMER_Done( member, "interrogating" ) )
00432 return qfalse;
00433
00434 return qtrue;
00435 }
00436
00437
00438
00439
00440
00441
00442
00443 void AI_GetGroup( gentity_t *self )
00444 {
00445 int i;
00446 gentity_t *member;
00447
00448
00449 if ( !self || !self->NPC )
00450 {
00451 return;
00452 }
00453
00454 if ( d_noGroupAI.integer )
00455 {
00456 self->NPC->group = NULL;
00457 return;
00458 }
00459
00460 if ( !self->client )
00461 {
00462 self->NPC->group = NULL;
00463 return;
00464 }
00465
00466 if ( self->NPC->scriptFlags&SCF_NO_GROUPS )
00467 {
00468 self->NPC->group = NULL;
00469 return;
00470 }
00471
00472 if ( self->enemy && (!self->enemy->client || (level.time - self->NPC->enemyLastSeenTime > 7000 )))
00473 {
00474 self->NPC->group = NULL;
00475 return;
00476 }
00477
00478 if ( !AI_GetNextEmptyGroup( self ) )
00479 {
00480 return;
00481 }
00482
00483
00484 memset( self->NPC->group, 0, sizeof( AIGroupInfo_t ) );
00485
00486 self->NPC->group->enemy = self->enemy;
00487 self->NPC->group->team = self->client->playerTeam;
00488 self->NPC->group->processed = qfalse;
00489 self->NPC->group->commander = self;
00490 self->NPC->group->memberValidateTime = level.time + 2000;
00491 self->NPC->group->activeMemberNum = 0;
00492
00493 if ( self->NPC->group->enemy )
00494 {
00495 self->NPC->group->lastSeenEnemyTime = level.time;
00496 self->NPC->group->lastClearShotTime = level.time;
00497 VectorCopy( self->NPC->group->enemy->r.currentOrigin, self->NPC->group->enemyLastSeenPos );
00498 }
00499
00500
00501 for ( i = 0; i < level.num_entities ; i++)
00502 {
00503 member = &g_entities[i];
00504
00505 if (!member->inuse)
00506 {
00507 continue;
00508 }
00509
00510 if ( !AI_ValidateGroupMember( self->NPC->group, member ) )
00511 {
00512 continue;
00513 }
00514
00515
00516 AI_InsertGroupMember( self->NPC->group, member );
00517
00518 if ( self->NPC->group->numGroup >= (MAX_GROUP_MEMBERS - 1) )
00519 {
00520 break;
00521 }
00522 }
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 if ( self->NPC->group->numGroup <= 0 )
00544 {
00545 self->NPC->group = NULL;
00546 return;
00547 }
00548
00549 AI_SortGroupByPathCostToEnemy( self->NPC->group );
00550 AI_SetClosestBuddy( self->NPC->group );
00551 }
00552
00553 void AI_SetNewGroupCommander( AIGroupInfo_t *group )
00554 {
00555 gentity_t *member = NULL;
00556 int i;
00557
00558 group->commander = NULL;
00559 for ( i = 0; i < group->numGroup; i++ )
00560 {
00561 member = &g_entities[group->member[i].number];
00562
00563 if ( !group->commander || (member && member->NPC && group->commander->NPC && member->NPC->rank > group->commander->NPC->rank) )
00564 {
00565 group->commander = member;
00566 }
00567 }
00568 }
00569
00570 void AI_DeleteGroupMember( AIGroupInfo_t *group, int memberNum )
00571 {
00572 int i;
00573
00574 if ( group->commander && group->commander->s.number == group->member[memberNum].number )
00575 {
00576 group->commander = NULL;
00577 }
00578 if ( g_entities[group->member[memberNum].number].NPC )
00579 {
00580 g_entities[group->member[memberNum].number].NPC->group = NULL;
00581 }
00582 for ( i = memberNum; i < (group->numGroup-1); i++ )
00583 {
00584 memcpy( &group->member[i], &group->member[i+1], sizeof( group->member[i] ) );
00585 }
00586 if ( memberNum < group->activeMemberNum )
00587 {
00588 group->activeMemberNum--;
00589 if ( group->activeMemberNum < 0 )
00590 {
00591 group->activeMemberNum = 0;
00592 }
00593 }
00594 group->numGroup--;
00595 if ( group->numGroup < 0 )
00596 {
00597 group->numGroup = 0;
00598 }
00599 AI_SetNewGroupCommander( group );
00600 }
00601
00602 void AI_DeleteSelfFromGroup( gentity_t *self )
00603 {
00604 int i;
00605
00606
00607 for ( i = 0; i < self->NPC->group->numGroup; i++ )
00608 {
00609 if ( self->NPC->group->member[i].number == self->s.number )
00610 {
00611 AI_DeleteGroupMember( self->NPC->group, i );
00612 return;
00613 }
00614 }
00615 }
00616
00617 extern void ST_AggressionAdjust( gentity_t *self, int change );
00618 extern void ST_MarkToCover( gentity_t *self );
00619 extern void ST_StartFlee( gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int minTime, int maxTime );
00620 void AI_GroupMemberKilled( gentity_t *self )
00621 {
00622 AIGroupInfo_t *group = self->NPC->group;
00623 gentity_t *member;
00624 qboolean noflee = qfalse;
00625 int i;
00626
00627 if ( !group )
00628 {
00629 return;
00630 }
00631 if ( !self || !self->NPC || self->NPC->rank < RANK_ENSIGN )
00632 {
00633 return;
00634 }
00635
00636 group->moraleAdjust -= self->NPC->rank;
00637
00638 for ( i = 0; i < group->numGroup; i++ )
00639 {
00640 member = &g_entities[group->member[i].number];
00641 if ( member == self )
00642 {
00643 continue;
00644 }
00645 if ( member->NPC->rank > RANK_ENSIGN )
00646 {
00647 noflee = qtrue;
00648 }
00649 else
00650 {
00651 ST_AggressionAdjust( member, -1 );
00652 member->NPC->currentAim -= Q_irand( 0, 10 );
00653 }
00654 }
00655
00656 if ( group->commander != self )
00657 {
00658 return;
00659 }
00660
00661 if ( !noflee )
00662 {
00663 self->NPC->group->speechDebounceTime = 0;
00664 for ( i = 0; i < group->numGroup; i++ )
00665 {
00666 member = &g_entities[group->member[i].number];
00667 if ( member == self )
00668 {
00669 continue;
00670 }
00671 if ( member->NPC->rank < RANK_ENSIGN )
00672 {
00673 if ( group->enemy && DistanceSquared( member->r.currentOrigin, group->enemy->r.currentOrigin ) < 65536 )
00674 {
00675 ST_StartFlee( member, group->enemy, member->r.currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
00676 }
00677 else if ( DistanceSquared( member->r.currentOrigin, self->r.currentOrigin ) < 65536 )
00678 {
00679 ST_StartFlee( member, group->enemy, member->r.currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
00680 }
00681 else
00682 {
00683 if ( Q_irand( 0, self->NPC->rank ) > member->NPC->rank )
00684 {
00685 ST_StartFlee( member, group->enemy, member->r.currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
00686 }
00687 else
00688 {
00689 ST_MarkToCover( member );
00690 }
00691 }
00692 member->NPC->currentAim -= Q_irand( 1, 15 );
00693 }
00694 member->NPC->currentAim -= Q_irand( 1, 15 );
00695 }
00696 }
00697 }
00698
00699 void AI_GroupUpdateEnemyLastSeen( AIGroupInfo_t *group, vec3_t spot )
00700 {
00701 if ( !group )
00702 {
00703 return;
00704 }
00705 group->lastSeenEnemyTime = level.time;
00706 VectorCopy( spot, group->enemyLastSeenPos );
00707 }
00708
00709 void AI_GroupUpdateClearShotTime( AIGroupInfo_t *group )
00710 {
00711 if ( !group )
00712 {
00713 return;
00714 }
00715 group->lastClearShotTime = level.time;
00716 }
00717
00718 void AI_GroupUpdateSquadstates( AIGroupInfo_t *group, gentity_t *member, int newSquadState )
00719 {
00720 int i;
00721
00722 if ( !group )
00723 {
00724 member->NPC->squadState = newSquadState;
00725 return;
00726 }
00727
00728 for ( i = 0; i < group->numGroup; i++ )
00729 {
00730 if ( group->member[i].number == member->s.number )
00731 {
00732 group->numState[member->NPC->squadState]--;
00733 member->NPC->squadState = newSquadState;
00734 group->numState[member->NPC->squadState]++;
00735 return;
00736 }
00737 }
00738 }
00739
00740 qboolean AI_RefreshGroup( AIGroupInfo_t *group )
00741 {
00742 gentity_t *member;
00743 int i;
00744
00745
00746 for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
00747 {
00748 if ( &level.groups[i] == group )
00749 {
00750 break;
00751 }
00752 else
00753 {
00754 if ( level.groups[i].enemy == group->enemy )
00755 {
00756 if ( level.groups[i].numGroup+group->numGroup < (MAX_GROUP_MEMBERS - 1) )
00757 {
00758 qboolean deleteWhenDone = qtrue;
00759 int j;
00760
00761
00762 for ( j = 0; j < group->numGroup; j++ )
00763 {
00764 member = &g_entities[group->member[j].number];
00765 if ( level.groups[i].enemy == NULL )
00766 {
00767 if ( !AI_ValidateNoEnemyGroupMember( &level.groups[i], member ) )
00768 {
00769 deleteWhenDone = qfalse;
00770 continue;
00771 }
00772 }
00773
00774 AI_DeleteGroupMember( group, j );
00775
00776 j--;
00777
00778 AI_InsertGroupMember( &level.groups[i], member );
00779 }
00780
00781 if ( deleteWhenDone )
00782 {
00783 return qfalse;
00784 }
00785 }
00786 }
00787 }
00788 }
00789
00790 for ( i = 0; i < NUM_SQUAD_STATES; i++ )
00791 {
00792 group->numState[i] = 0;
00793 }
00794
00795
00796 group->commander = NULL;
00797 for ( i = 0; i < group->numGroup; i++ )
00798 {
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819 member = &g_entities[group->member[i].number];
00820
00821
00822 if ( member->health <= 0 )
00823 {
00824 AI_DeleteGroupMember( group, i );
00825
00826 i--;
00827 }
00828 else if ( group->memberValidateTime < level.time && !AI_ValidateGroupMember( group, member ) )
00829 {
00830
00831 AI_DeleteGroupMember( group, i );
00832
00833 i--;
00834 }
00835 else
00836 {
00837
00838 group->numState[member->NPC->squadState]++;
00839 if ( !group->commander || member->NPC->rank > group->commander->NPC->rank )
00840 {
00841 group->commander = member;
00842 }
00843 }
00844 }
00845 if ( group->memberValidateTime < level.time )
00846 {
00847 group->memberValidateTime = level.time + Q_irand( 500, 2500 );
00848 }
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868 group->morale = group->moraleAdjust;
00869 for ( i = 0; i < group->numGroup; i++ )
00870 {
00871 member = &g_entities[group->member[i].number];
00872 if ( member->NPC->rank < RANK_ENSIGN )
00873 {
00874 group->morale++;
00875 }
00876 else
00877 {
00878 group->morale += member->NPC->rank;
00879 }
00880 if ( group->commander && debugNPCAI.integer )
00881 {
00882
00883 G_TestLine(group->commander->r.currentOrigin, member->r.currentOrigin, 0x00000ff, FRAMETIME);
00884 }
00885 }
00886 if ( group->enemy )
00887 {
00888 if ( group->enemy->health < 10 )
00889 {
00890 group->morale += 10;
00891 }
00892 else if ( group->enemy->health < 25 )
00893 {
00894 group->morale += 5;
00895 }
00896 else if ( group->enemy->health < 50 )
00897 {
00898 group->morale += 2;
00899 }
00900 switch( group->enemy->s.weapon )
00901 {
00902 case WP_SABER:
00903 group->morale -= 5;
00904 break;
00905 case WP_BRYAR_PISTOL:
00906 group->morale += 3;
00907 break;
00908 case WP_DISRUPTOR:
00909 group->morale += 2;
00910 break;
00911 case WP_REPEATER:
00912 group->morale -= 1;
00913 break;
00914 case WP_FLECHETTE:
00915 group->morale -= 2;
00916 break;
00917 case WP_ROCKET_LAUNCHER:
00918 group->morale -= 10;
00919 break;
00920 case WP_THERMAL:
00921 group->morale -= 5;
00922 break;
00923 case WP_TRIP_MINE:
00924 group->morale -= 3;
00925 break;
00926 case WP_DET_PACK:
00927 group->morale -= 10;
00928 break;
00929
00930
00931
00932 case WP_STUN_BATON:
00933 group->morale += 10;
00934 break;
00935 case WP_EMPLACED_GUN:
00936 group->morale -= 8;
00937 break;
00938
00939
00940
00941
00942
00943
00944 }
00945 }
00946 if ( group->moraleDebounce < level.time )
00947 {
00948 if ( group->moraleAdjust > 0 )
00949 {
00950 group->moraleAdjust--;
00951 }
00952 else if ( group->moraleAdjust < 0 )
00953 {
00954 group->moraleAdjust++;
00955 }
00956 group->moraleDebounce = level.time + 1000;
00957 }
00958
00959 group->processed = qfalse;
00960
00961 return (group->numGroup>0);
00962 }
00963
00964 void AI_UpdateGroups( void )
00965 {
00966 int i;
00967
00968 if ( d_noGroupAI.integer )
00969 {
00970 return;
00971 }
00972
00973 for ( i = 0; i < MAX_FRAME_GROUPS; i++ )
00974 {
00975 if ( !level.groups[i].numGroup || AI_RefreshGroup( &level.groups[i] ) == qfalse )
00976 {
00977 memset( &level.groups[i], 0, sizeof( level.groups[i] ) );
00978 }
00979 }
00980 }
00981
00982 qboolean AI_GroupContainsEntNum( AIGroupInfo_t *group, int entNum )
00983 {
00984 int i;
00985
00986 if ( !group )
00987 {
00988 return qfalse;
00989 }
00990 for ( i = 0; i < group->numGroup; i++ )
00991 {
00992 if ( group->member[i].number == entNum )
00993 {
00994 return qtrue;
00995 }
00996 }
00997 return qfalse;
00998 }
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030 qboolean AI_CheckEnemyCollision( gentity_t *ent, qboolean takeEnemy )
01031 {
01032 navInfo_t info;
01033
01034 if ( ent == NULL )
01035 return qfalse;
01036
01037
01038
01039
01040 NAV_GetLastMove( &info );
01041
01042
01043 if ( ( info.blocker ) && ( info.blocker != ent->enemy ) )
01044 {
01045 if ( ( info.blocker->client ) && ( info.blocker->client->playerTeam == ent->client->enemyTeam ) )
01046 {
01047 if ( takeEnemy )
01048 G_SetEnemy( ent, info.blocker );
01049
01050 return qtrue;
01051 }
01052 }
01053
01054 return qfalse;
01055 }
01056
01057
01058
01059
01060
01061
01062
01063 #define MAX_RADIUS_ENTS 128
01064
01065 gentity_t *AI_DistributeAttack( gentity_t *attacker, gentity_t *enemy, team_t team, int threshold )
01066 {
01067 int radiusEnts[ MAX_RADIUS_ENTS ];
01068 gentity_t *check;
01069 int numEnts;
01070 int numSurrounding;
01071 int i;
01072 int j;
01073 vec3_t mins, maxs;
01074
01075
01076
01077
01078
01079 numSurrounding = AI_GetGroupSize( enemy->r.currentOrigin, 48, team, attacker );
01080
01081
01082 if ( enemy != &g_entities[0] )
01083 {
01084
01085 int aroundPlayer = AI_GetGroupSize( g_entities[0].r.currentOrigin, 48, team, attacker );
01086
01087
01088 if ( aroundPlayer < threshold )
01089 {
01090 return &g_entities[0];
01091 }
01092 }
01093
01094
01095 if ( numSurrounding < threshold )
01096 return enemy;
01097
01098
01099
01100
01101 for ( i = 0; i < 3; i++ )
01102 {
01103 mins[i] = enemy->r.currentOrigin[i] - 512;
01104 maxs[i] = enemy->r.currentOrigin[i] + 512;
01105 }
01106
01107
01108 numEnts = trap_EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
01109
01110
01111 for ( j = 0; j < numEnts; j++ )
01112 {
01113 check = &g_entities[radiusEnts[j]];
01114
01115
01116 if ( check->client == NULL )
01117 continue;
01118
01119
01120 if ( ( check == enemy ) )
01121 continue;
01122
01123
01124 if ( check->client->playerTeam != enemy->client->playerTeam )
01125 continue;
01126
01127
01128 if ( check->health <= 0 )
01129 continue;
01130
01131
01132 if ( AI_GetGroupSize( check->r.currentOrigin, 48, team, attacker ) > threshold )
01133 continue;
01134
01135 return check;
01136 }
01137
01138 return NULL;
01139 }