void AI_DeleteSelfFromGroup( gentity_t *self ) { int i; //FIXME: if killed, keep track of how many in group killed? To affect morale? for ( i = 0; i < self->NPC->group->numGroup; i++ ) { if ( self->NPC->group->member[i].number == self->s.number ) { AI_DeleteGroupMember( self->NPC->group, i ); return; } } }
qboolean AI_RefreshGroup( AIGroupInfo_t *group ) { gentity_t *member; int i;//, j; //see if we should merge with another group for ( i = 0; i < MAX_FRAME_GROUPS; i++ ) { if ( &level.groups[i] == group ) { break; } else { if ( level.groups[i].enemy == group->enemy ) {//2 groups with same enemy if ( level.groups[i].numGroup+group->numGroup < (MAX_GROUP_MEMBERS - 1) ) {//combining the members would fit in one group qboolean deleteWhenDone = qtrue; //combine the members of mine into theirs for ( int j = 0; j < group->numGroup; j++ ) { member = &g_entities[group->member[j].number]; if ( level.groups[i].enemy == NULL ) {//special case for groups without enemies, must be in range if ( !AI_ValidateNoEnemyGroupMember( &level.groups[i], member ) ) { deleteWhenDone = qfalse; continue; } } //remove this member from this group AI_DeleteGroupMember( group, j ); //keep marker at same place since we deleted this guy and shifted everyone up one j--; //add them to the earlier group AI_InsertGroupMember( &level.groups[i], member ); } //return and delete this group if ( deleteWhenDone ) { return qfalse; } } } } } //clear numStates for ( i = 0; i < NUM_SQUAD_STATES; i++ ) { group->numState[i] = 0; } //go through group and validate each membership group->commander = NULL; for ( i = 0; i < group->numGroup; i++ ) { /* //this checks for duplicate copies of one member in a group for ( j = 0; j < group->numGroup; j++ ) { if ( i != j ) { if ( group->member[i].number == group->member[j].number ) { break; } } } if ( j < group->numGroup ) {//found a dupe! gi.Printf( S_COLOR_RED"ERROR: member %s(%d) a duplicate group member!!!\n", g_entities[group->member[i].number].targetname, group->member[i].number ); AI_DeleteGroupMember( group, i ); i--; continue; } */ member = &g_entities[group->member[i].number]; //Must be alive if ( member->health <= 0 ) { AI_DeleteGroupMember( group, i ); //keep marker at same place since we deleted this guy and shifted everyone up one i--; } else if ( group->memberValidateTime < level.time && !AI_ValidateGroupMember( group, member ) ) { //remove this one from the group AI_DeleteGroupMember( group, i ); //keep marker at same place since we deleted this guy and shifted everyone up one i--; } else {//membership is valid //keep track of squadStates group->numState[member->NPC->squadState]++; if ( !group->commander || member->NPC->rank > group->commander->NPC->rank ) {//keep track of highest rank group->commander = member; } } } if ( group->memberValidateTime < level.time ) { group->memberValidateTime = level.time + Q_irand( 500, 2500 ); } //Now add any new guys as long as we're not full /* for ( i = 0, member = &g_entities[0]; i < globals.num_entities && group->numGroup < (MAX_GROUP_MEMBERS - 1); i++, member++) { if ( !AI_ValidateGroupMember( group, member ) ) {//FIXME: keep track of those who aren't angry yet and see if we should wake them after we assemble the core group continue; } if ( member->NPC->group == group ) {//DOH, already in our group continue; } //store it AI_InsertGroupMember( group, member ); } */ //calc the morale of this group group->morale = group->moraleAdjust; for ( i = 0; i < group->numGroup; i++ ) { member = &g_entities[group->member[i].number]; if ( member->NPC->rank < RANK_ENSIGN ) {//grunts group->morale++; } else { group->morale += member->NPC->rank; } if ( group->commander && debugNPCAI->integer ) { G_DebugLine( group->commander->currentOrigin, member->currentOrigin, FRAMETIME, 0x00ff00ff, qtrue ); } } if ( group->enemy ) {//modify morale based on enemy health and weapon if ( group->enemy->health < 10 ) { group->morale += 10; } else if ( group->enemy->health < 25 ) { group->morale += 5; } else if ( group->enemy->health < 50 ) { group->morale += 2; } switch( group->enemy->s.weapon ) { case WP_SABER: group->morale -= 5; break; case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: group->morale += 3; break; case WP_DISRUPTOR: group->morale += 2; break; case WP_REPEATER: group->morale -= 1; break; case WP_FLECHETTE: group->morale -= 2; break; case WP_ROCKET_LAUNCHER: group->morale -= 10; break; case WP_CONCUSSION: group->morale -= 12; break; case WP_THERMAL: group->morale -= 5; break; case WP_TRIP_MINE: group->morale -= 3; break; case WP_DET_PACK: group->morale -= 10; break; case WP_MELEE: // Any ol' melee attack group->morale += 20; break; case WP_STUN_BATON: group->morale += 10; break; case WP_EMPLACED_GUN: group->morale -= 8; break; case WP_ATST_MAIN: group->morale -= 8; break; case WP_ATST_SIDE: group->morale -= 20; break; } } if ( group->moraleDebounce < level.time ) {//slowly degrade whatever moraleAdjusters we may have if ( group->moraleAdjust > 0 ) { group->moraleAdjust--; } else if ( group->moraleAdjust < 0 ) { group->moraleAdjust++; } group->moraleDebounce = level.time + 1000;//FIXME: define? } //mark this group as not having been run this frame group->processed = qfalse; return (group->numGroup>0); }