You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
787 lines
19 KiB
787 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Squad classes |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "ai_squad.h" |
|
#include "ai_squadslot.h" |
|
#include "ai_basenpc.h" |
|
#include "saverestore_bitstring.h" |
|
#include "saverestore_utlvector.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
CAI_SquadManager g_AI_SquadManager; |
|
|
|
//----------------------------------------------------------------------------- |
|
// CAI_SquadManager |
|
// |
|
// Purpose: Manages all the squads in the system |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
CAI_Squad *CAI_SquadManager::FindSquad( string_t squadName ) |
|
{ |
|
CAI_Squad* pSquad = m_pSquads; |
|
|
|
while (pSquad) |
|
{ |
|
if (FStrEq(STRING(squadName),pSquad->GetName())) |
|
{ |
|
return pSquad; |
|
} |
|
pSquad = pSquad->m_pNextSquad; |
|
} |
|
return NULL; |
|
} |
|
|
|
//------------------------------------- |
|
|
|
CAI_Squad *CAI_SquadManager::CreateSquad(string_t squadName) |
|
{ |
|
CAI_Squad *pResult = new CAI_Squad(squadName); |
|
|
|
// --------------------------------- |
|
// Only named squads get added to the squad list |
|
if ( squadName != NULL_STRING ) |
|
{ |
|
pResult->m_pNextSquad = m_pSquads; |
|
m_pSquads = pResult; |
|
} |
|
else |
|
pResult->m_pNextSquad = NULL; |
|
return pResult; |
|
} |
|
|
|
//------------------------------------- |
|
|
|
int CAI_SquadManager::NumSquads() |
|
{ |
|
int nSquads = 0; |
|
CAI_Squad* pSquad = m_pSquads; |
|
|
|
while (pSquad) |
|
{ |
|
nSquads++; |
|
pSquad = pSquad->GetNext(); |
|
} |
|
return nSquads; |
|
} |
|
|
|
//------------------------------------- |
|
|
|
void CAI_SquadManager::DeleteSquad( CAI_Squad *pSquad ) |
|
{ |
|
CAI_Squad *pCurSquad = m_pSquads; |
|
if (pCurSquad == pSquad) |
|
{ |
|
g_AI_SquadManager.m_pSquads = pCurSquad->m_pNextSquad; |
|
} |
|
else |
|
{ |
|
while (pCurSquad) |
|
{ |
|
if (pCurSquad->m_pNextSquad == pSquad) |
|
{ |
|
pCurSquad->m_pNextSquad = pCurSquad->m_pNextSquad->m_pNextSquad; |
|
break; |
|
} |
|
pCurSquad= pCurSquad->m_pNextSquad; |
|
} |
|
} |
|
delete pSquad; |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Delete all the squads (called between levels / loads) |
|
//------------------------------------- |
|
|
|
void CAI_SquadManager::DeleteAllSquads(void) |
|
{ |
|
CAI_Squad *squad = CAI_SquadManager::m_pSquads; |
|
|
|
while (squad) |
|
{ |
|
CAI_Squad *temp = squad->m_pNextSquad; |
|
delete squad; |
|
squad = temp; |
|
} |
|
CAI_SquadManager::m_pSquads = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CAI_Squad |
|
// |
|
// Purpose: Tracks enemies, squad slots, squad members |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
BEGIN_SIMPLE_DATADESC( AISquadEnemyInfo_t ) |
|
|
|
DEFINE_FIELD( hEnemy, FIELD_EHANDLE ), |
|
DEFINE_BITSTRING( slots), |
|
|
|
END_DATADESC() |
|
#endif |
|
|
|
BEGIN_SIMPLE_DATADESC( CAI_Squad ) |
|
|
|
// m_pNextSquad (rebuilt) |
|
// m_Name (rebuilt) |
|
// m_SquadMembers (rebuilt) |
|
// m_SquadMembers.Count() (rebuilt) |
|
DEFINE_FIELD( m_flSquadSoundWaitTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_nSquadSoundPriority, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_hSquadInflictor, FIELD_EHANDLE ), |
|
DEFINE_AUTO_ARRAY( m_SquadData, FIELD_INTEGER ), |
|
// m_pLastFoundEnemyInfo (think transient) |
|
|
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
DEFINE_UTLVECTOR(m_EnemyInfos, FIELD_EMBEDDED ), |
|
DEFINE_FIELD( m_flEnemyInfoCleanupTime, FIELD_TIME ), |
|
#else |
|
DEFINE_EMBEDDED( m_squadSlotsUsed ), |
|
#endif |
|
|
|
END_DATADESC() |
|
|
|
//------------------------------------- |
|
|
|
CAI_Squad::CAI_Squad(string_t newName) |
|
#ifndef PER_ENEMY_SQUADSLOTS |
|
: m_squadSlotsUsed(MAX_SQUADSLOTS) |
|
#endif |
|
{ |
|
Init( newName ); |
|
} |
|
|
|
//------------------------------------- |
|
|
|
CAI_Squad::CAI_Squad() |
|
#ifndef PER_ENEMY_SQUADSLOTS |
|
: m_squadSlotsUsed(MAX_SQUADSLOTS) |
|
#endif |
|
{ |
|
Init( NULL_STRING ); |
|
} |
|
|
|
//------------------------------------- |
|
|
|
void CAI_Squad::Init(string_t newName) |
|
{ |
|
m_Name = AllocPooledString( STRING(newName) ); |
|
m_pNextSquad = NULL; |
|
m_flSquadSoundWaitTime = 0; |
|
m_SquadMembers.RemoveAll(); |
|
|
|
m_flSquadSoundWaitTime = 0; |
|
|
|
SetSquadInflictor( NULL ); |
|
|
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
m_flEnemyInfoCleanupTime = 0; |
|
m_pLastFoundEnemyInfo = NULL; |
|
#endif |
|
|
|
} |
|
|
|
//------------------------------------- |
|
|
|
CAI_Squad::~CAI_Squad(void) |
|
{ |
|
} |
|
|
|
//------------------------------------- |
|
|
|
bool CAI_Squad::IsSilentMember( const CAI_BaseNPC *pNPC ) |
|
{ |
|
if ( !pNPC || ( pNPC->GetMoveType() == MOVETYPE_NONE && pNPC->GetSolid() == SOLID_NONE ) ) // a.k.a., enemy finder |
|
return true; |
|
return pNPC->IsSilentSquadMember(); |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Removes an NPC from a squad |
|
//------------------------------------- |
|
|
|
void CAI_Squad::RemoveFromSquad( CAI_BaseNPC *pNPC, bool bDeath ) |
|
{ |
|
if ( !pNPC ) |
|
return; |
|
|
|
// Find the index of this squad member |
|
int member; |
|
int myIndex = m_SquadMembers.Find(pNPC); |
|
if (myIndex == -1) |
|
{ |
|
DevMsg("ERROR: Attempting to remove non-existing squad membmer!\n"); |
|
return; |
|
} |
|
m_SquadMembers.Remove(myIndex); |
|
|
|
// Notify squad members of death |
|
if ( bDeath ) |
|
{ |
|
for (member = 0; member < m_SquadMembers.Count(); member++) |
|
{ |
|
CAI_BaseNPC* pSquadMem = m_SquadMembers[member]; |
|
if (pSquadMem) |
|
{ |
|
pSquadMem->NotifyDeadFriend(pNPC); |
|
} |
|
} |
|
} |
|
|
|
pNPC->SetSquad(NULL); |
|
pNPC->SetSquadName( NULL_STRING ); |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Addes the given NPC to the squad |
|
//------------------------------------- |
|
void CAI_Squad::AddToSquad(CAI_BaseNPC *pNPC) |
|
{ |
|
if ( !pNPC || !pNPC->IsAlive() ) |
|
{ |
|
Assert(0); |
|
return; |
|
} |
|
|
|
if ( pNPC->GetSquad() == this ) |
|
return; |
|
|
|
if ( pNPC->GetSquad() ) |
|
{ |
|
pNPC->GetSquad()->RemoveFromSquad(pNPC); |
|
} |
|
|
|
if (m_SquadMembers.Count() == MAX_SQUAD_MEMBERS) |
|
{ |
|
DevMsg("Error!! Squad %s is too big!!! Replacing last member\n", STRING( this->m_Name )); |
|
m_SquadMembers.Remove(m_SquadMembers.Count()-1); |
|
} |
|
m_SquadMembers.AddToTail(pNPC); |
|
pNPC->SetSquad( this ); |
|
pNPC->SetSquadName( m_Name ); |
|
|
|
if ( m_SquadMembers.Count() > 1 ) |
|
{ |
|
CAI_BaseNPC *pCopyFrom = m_SquadMembers[0]; |
|
CAI_Enemies *pEnemies = pCopyFrom->GetEnemies(); |
|
AIEnemiesIter_t iter; |
|
AI_EnemyInfo_t *pInfo = pEnemies->GetFirst( &iter ); |
|
while ( pInfo ) |
|
{ |
|
pNPC->UpdateEnemyMemory( pInfo->hEnemy, pInfo->vLastKnownLocation, pCopyFrom ); |
|
pInfo = pEnemies->GetNext( &iter ); |
|
} |
|
} |
|
|
|
} |
|
|
|
//------------------------------------- |
|
|
|
CAI_BaseNPC *CAI_Squad::SquadMemberInRange( const Vector &vecLocation, float flDist ) |
|
{ |
|
for (int i = 0; i < m_SquadMembers.Count(); i++) |
|
{ |
|
if (m_SquadMembers[i] != NULL && (vecLocation - m_SquadMembers[i]->GetAbsOrigin() ).Length2D() <= flDist) |
|
return m_SquadMembers[i]; |
|
} |
|
return NULL; |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Returns the nearest squad member to the given squad member |
|
//------------------------------------- |
|
|
|
CAI_BaseNPC *CAI_Squad::NearestSquadMember( CAI_BaseNPC *pMember ) |
|
{ |
|
float fBestDist = MAX_COORD_RANGE; |
|
CAI_BaseNPC *fNearestEnt = NULL; |
|
Vector fStartLoc = pMember->GetAbsOrigin(); |
|
for (int i = 0; i < m_SquadMembers.Count(); i++) |
|
{ |
|
if (m_SquadMembers[i] != NULL) |
|
{ |
|
float fDist = (fStartLoc - m_SquadMembers[i]->GetAbsOrigin()).Length(); |
|
if (m_SquadMembers[i] != pMember && |
|
fDist < fBestDist ) |
|
{ |
|
fBestDist = fDist; |
|
fNearestEnt = m_SquadMembers[i]; |
|
} |
|
} |
|
} |
|
return fNearestEnt; |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Return the number of squad members visible to the specified member |
|
//------------------------------------- |
|
int CAI_Squad::GetVisibleSquadMembers( CAI_BaseNPC *pMember ) |
|
{ |
|
int iCount = 0; |
|
|
|
for (int i = 0; i < m_SquadMembers.Count(); i++) |
|
{ |
|
// Make sure it's not the specified member |
|
if ( m_SquadMembers[i] != NULL && pMember != m_SquadMembers[i] ) |
|
{ |
|
if ( pMember->FVisible( m_SquadMembers[i] ) ) |
|
{ |
|
iCount++; |
|
} |
|
} |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
//------------------------------------- |
|
// |
|
//------------------------------------- |
|
CAI_BaseNPC *CAI_Squad::GetSquadMemberNearestTo( const Vector &vecLocation ) |
|
{ |
|
CAI_BaseNPC *pNearest = NULL; |
|
float flNearest = FLT_MAX; |
|
|
|
for ( int i = 0; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
float flDist; |
|
flDist = m_SquadMembers[i]->GetAbsOrigin().DistToSqr( vecLocation ); |
|
|
|
if( flDist < flNearest ) |
|
{ |
|
flNearest = flDist; |
|
pNearest = m_SquadMembers[i]; |
|
} |
|
} |
|
|
|
Assert( pNearest != NULL ); |
|
return pNearest; |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Returns true if given entity is in the squad |
|
//------------------------------------- |
|
bool CAI_Squad::SquadIsMember( CBaseEntity *pMember ) |
|
{ |
|
CAI_BaseNPC *pNPC = pMember->MyNPCPointer(); |
|
if ( pNPC && pNPC->GetSquad() == this ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//------------------------------------- |
|
|
|
bool CAI_Squad::IsLeader( CAI_BaseNPC *pNPC ) |
|
{ |
|
if ( IsSilentMember( pNPC ) ) |
|
return false; |
|
|
|
if ( !pNPC ) |
|
return false; |
|
|
|
if ( GetLeader() == pNPC ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//------------------------------------- |
|
|
|
CAI_BaseNPC *CAI_Squad::GetLeader( void ) |
|
{ |
|
CAI_BaseNPC *pLeader = NULL; |
|
int nSilentMembers = 0; |
|
for ( int i = 0; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
if ( !IsSilentMember( m_SquadMembers[i] ) ) |
|
{ |
|
if ( !pLeader ) |
|
pLeader = m_SquadMembers[i]; |
|
} |
|
else |
|
{ |
|
nSilentMembers++; |
|
} |
|
} |
|
return ( m_SquadMembers.Count() - nSilentMembers > 1) ? pLeader : NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
CAI_BaseNPC *CAI_Squad::GetFirstMember( AISquadIter_t *pIter, bool bIgnoreSilentMembers ) |
|
{ |
|
int i = 0; |
|
if ( bIgnoreSilentMembers ) |
|
{ |
|
for ( ; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
if ( !IsSilentMember( m_SquadMembers[i] ) ) |
|
break; |
|
} |
|
} |
|
|
|
if ( pIter ) |
|
*pIter = (AISquadIter_t)i; |
|
if ( i >= m_SquadMembers.Count() ) |
|
return NULL; |
|
|
|
return m_SquadMembers[i]; |
|
} |
|
|
|
//------------------------------------- |
|
|
|
CAI_BaseNPC *CAI_Squad::GetNextMember( AISquadIter_t *pIter, bool bIgnoreSilentMembers ) |
|
{ |
|
int &i = (int &)*pIter; |
|
i++; |
|
if ( bIgnoreSilentMembers ) |
|
{ |
|
for ( ; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
if ( !IsSilentMember( m_SquadMembers[i] ) ) |
|
break; |
|
} |
|
} |
|
|
|
if ( i >= m_SquadMembers.Count() ) |
|
return NULL; |
|
|
|
return m_SquadMembers[i]; |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Alert everyone in the squad to the presence of a new enmey |
|
//------------------------------------- |
|
|
|
int CAI_Squad::NumMembers( bool bIgnoreSilentMembers ) |
|
{ |
|
int nSilentMembers = 0; |
|
if ( bIgnoreSilentMembers ) |
|
{ |
|
for ( int i = 0; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
if ( IsSilentMember( m_SquadMembers[i] ) ) |
|
nSilentMembers++; |
|
} |
|
} |
|
return ( m_SquadMembers.Count() - nSilentMembers ); |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Alert everyone in the squad to the presence of a new enmey |
|
//------------------------------------- |
|
|
|
void CAI_Squad::SquadNewEnemy( CBaseEntity *pEnemy ) |
|
{ |
|
if ( !pEnemy ) |
|
{ |
|
DevMsg( "ERROR: SquadNewEnemy() - pEnemy is NULL!\n" ); |
|
return; |
|
} |
|
|
|
for (int i = 0; i < m_SquadMembers.Count(); i++) |
|
{ |
|
CAI_BaseNPC *pMember = m_SquadMembers[i]; |
|
if (pMember) |
|
{ |
|
// reset members who aren't activly engaged in fighting (only do this if the NPC's using the squad memory, or it'll fail) |
|
if ( !pMember->GetEnemy() || |
|
( pMember->GetEnemy() != pEnemy && |
|
!pMember->HasCondition( COND_SEE_ENEMY) && |
|
gpGlobals->curtime - pMember->GetEnemyLastTimeSeen() > 3.0 ) ) |
|
{ |
|
// give them a new enemy |
|
if( !hl2_episodic.GetBool() || pMember->IsValidEnemy(pEnemy) ) |
|
{ |
|
pMember->SetEnemy( pEnemy ); |
|
} |
|
// pMember->SetLastAttackTime( 0 ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------- |
|
// Purpose: Broadcast a message to all squad members |
|
// Input: messageID - generic message handle |
|
// data - generic data handle |
|
// sender - who sent the message (NULL by default, if not, will not resend to the sender) |
|
//------------------------------------- |
|
|
|
int CAI_Squad::BroadcastInteraction( int interactionType, void *data, CBaseCombatCharacter *sender ) |
|
{ |
|
//Must have a squad |
|
if ( m_SquadMembers.Count() == 0 ) |
|
return false; |
|
|
|
//Broadcast to all members of the squad |
|
for ( int i = 0; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
CAI_BaseNPC *pMember = m_SquadMembers[i]->MyNPCPointer(); |
|
|
|
//Validate and don't send again to the sender |
|
if ( ( pMember != NULL) && ( pMember != sender ) ) |
|
{ |
|
//Send it |
|
pMember->DispatchInteraction( interactionType, data, sender ); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: is it ok to make a sound of the given priority? Check for conflicts |
|
// Input : soundPriority - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CAI_Squad::FOkToMakeSound( int soundPriority ) |
|
{ |
|
if (gpGlobals->curtime <= m_flSquadSoundWaitTime) |
|
{ |
|
if ( soundPriority <= m_nSquadSoundPriority ) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: A squad member made an exclusive sound. Keep track so other squad |
|
// members don't talk over it |
|
// Input : soundPriority - for sorting |
|
// time - |
|
//----------------------------------------------------------------------------- |
|
void CAI_Squad::JustMadeSound( int soundPriority, float time ) |
|
{ |
|
m_flSquadSoundWaitTime = time; |
|
m_nSquadSoundPriority = soundPriority; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Try to get one of a contiguous range of slots |
|
// Input : slotIDStart - start of slot range |
|
// slotIDEnd - end of slot range |
|
// hEnemy - enemy this slot is for |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CAI_Squad::OccupyStrategySlotRange( CBaseEntity *pEnemy, int slotIDStart, int slotIDEnd, int *pSlot ) |
|
{ |
|
#ifndef PER_ENEMY_SQUADSLOTS |
|
// FIXME: combat slots need to be per enemy, not per squad. |
|
// As it is, once a squad is occupied it stops making even simple attacks to other things nearby. |
|
// This code may make soldiers too aggressive |
|
if (GetLeader() && pEnemy != GetLeader()->GetEnemy()) |
|
{ |
|
*pSlot = SQUAD_SLOT_NONE; |
|
return true; |
|
} |
|
#endif |
|
|
|
// If I'm already occupying this slot |
|
if ( *pSlot >= slotIDStart && *pSlot <= slotIDEnd) |
|
return true; |
|
|
|
for ( int i = slotIDStart; i <= slotIDEnd; i++ ) |
|
{ |
|
// Check enemy to see if slot already occupied |
|
if (!IsSlotOccupied(pEnemy, i)) |
|
{ |
|
// Clear any previous spot; |
|
if (*pSlot != SQUAD_SLOT_NONE) |
|
{ |
|
// As a debug measure check to see if slot was filled |
|
if (!IsSlotOccupied(pEnemy, *pSlot)) |
|
{ |
|
DevMsg( "ERROR! Vacating an empty slot!\n"); |
|
} |
|
|
|
// Free the slot |
|
VacateSlot(pEnemy, *pSlot); |
|
} |
|
|
|
// Fill the slot |
|
OccupySlot(pEnemy, i); |
|
*pSlot = i; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
bool CAI_Squad::IsStrategySlotRangeOccupied( CBaseEntity *pEnemy, int slotIDStart, int slotIDEnd ) |
|
{ |
|
for ( int i = slotIDStart; i <= slotIDEnd; i++ ) |
|
{ |
|
if (!IsSlotOccupied(pEnemy, i)) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
void CAI_Squad::VacateStrategySlot( CBaseEntity *pEnemy, int slot) |
|
{ |
|
// If I wasn't taking up a squad slot I'm done |
|
if (slot == SQUAD_SLOT_NONE) |
|
return; |
|
|
|
// As a debug measure check to see if slot was filled |
|
if (!IsSlotOccupied(pEnemy, slot)) |
|
{ |
|
DevMsg( "ERROR! Vacating an empty slot!\n"); |
|
} |
|
|
|
// Free the slot |
|
VacateSlot(pEnemy, slot); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
void CAI_Squad::UpdateEnemyMemory( CAI_BaseNPC *pUpdater, CBaseEntity *pEnemy, const Vector &position ) |
|
{ |
|
//Broadcast to all members of the squad |
|
for ( int i = 0; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
if ( m_SquadMembers[i] != pUpdater ) |
|
{ |
|
m_SquadMembers[i]->UpdateEnemyMemory( pEnemy, position, pUpdater ); |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
|
|
AISquadEnemyInfo_t *CAI_Squad::FindEnemyInfo( CBaseEntity *pEnemy ) |
|
{ |
|
int i; |
|
if ( gpGlobals->curtime > m_flEnemyInfoCleanupTime ) |
|
{ |
|
if ( m_EnemyInfos.Count() ) |
|
{ |
|
m_pLastFoundEnemyInfo = NULL; |
|
CUtlRBTree<CBaseEntity *> activeEnemies; |
|
SetDefLessFunc( activeEnemies ); |
|
|
|
// Gather up the set of active enemies |
|
for ( i = 0; i < m_SquadMembers.Count(); i++ ) |
|
{ |
|
CBaseEntity *pMemberEnemy = m_SquadMembers[i]->GetEnemy(); |
|
if ( pMemberEnemy && activeEnemies.Find( pMemberEnemy ) == activeEnemies.InvalidIndex() ) |
|
{ |
|
activeEnemies.Insert( pMemberEnemy ); |
|
} |
|
} |
|
|
|
// Remove the records for deleted or unused enemies |
|
for ( i = m_EnemyInfos.Count() - 1; i >= 0; --i ) |
|
{ |
|
if ( m_EnemyInfos[i].hEnemy == NULL || activeEnemies.Find( m_EnemyInfos[i].hEnemy ) == activeEnemies.InvalidIndex() ) |
|
{ |
|
m_EnemyInfos.FastRemove( i ); |
|
} |
|
} |
|
} |
|
|
|
m_flEnemyInfoCleanupTime = gpGlobals->curtime + 30; |
|
} |
|
|
|
if ( m_pLastFoundEnemyInfo && m_pLastFoundEnemyInfo->hEnemy == pEnemy ) |
|
return m_pLastFoundEnemyInfo; |
|
|
|
for ( i = 0; i < m_EnemyInfos.Count(); i++ ) |
|
{ |
|
if ( m_EnemyInfos[i].hEnemy == pEnemy ) |
|
{ |
|
m_pLastFoundEnemyInfo = &m_EnemyInfos[i]; |
|
return &m_EnemyInfos[i]; |
|
} |
|
} |
|
|
|
m_pLastFoundEnemyInfo = NULL; |
|
i = m_EnemyInfos.AddToTail(); |
|
m_EnemyInfos[i].hEnemy = pEnemy; |
|
|
|
m_pLastFoundEnemyInfo = &m_EnemyInfos[i]; |
|
return &m_EnemyInfos[i]; |
|
} |
|
|
|
#endif |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
void CAI_Squad::OccupySlot( CBaseEntity *pEnemy, int i ) |
|
{ |
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
AISquadEnemyInfo_t *pInfo = FindEnemyInfo( pEnemy ); |
|
pInfo->slots.Set(i); |
|
#else |
|
m_squadSlotsUsed.Set(i); |
|
#endif |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
void CAI_Squad::VacateSlot( CBaseEntity *pEnemy, int i ) |
|
{ |
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
AISquadEnemyInfo_t *pInfo = FindEnemyInfo( pEnemy ); |
|
pInfo->slots.Clear(i); |
|
#else |
|
m_squadSlotsUsed.Clear(i); |
|
#endif |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
bool CAI_Squad::IsSlotOccupied( CBaseEntity *pEnemy, int i ) const |
|
{ |
|
#ifdef PER_ENEMY_SQUADSLOTS |
|
const AISquadEnemyInfo_t *pInfo = FindEnemyInfo( pEnemy ); |
|
return pInfo->slots.IsBitSet(i); |
|
#else |
|
return m_squadSlotsUsed.IsBitSet(i); |
|
#endif |
|
} |
|
|
|
void CAI_Squad::SquadRemember( int iMemory ) |
|
{ |
|
for (int i = 0; i < m_SquadMembers.Count(); i++) |
|
{ |
|
if (m_SquadMembers[i] != NULL ) |
|
{ |
|
m_SquadMembers[i]->Remember( iMemory ); |
|
} |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
void CAI_Squad::SetSquadInflictor( CBaseEntity *pInflictor ) |
|
{ |
|
m_hSquadInflictor.Set(pInflictor); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
bool CAI_Squad::IsSquadInflictor( CBaseEntity *pInflictor ) |
|
{ |
|
return (m_hSquadInflictor.Get() == pInflictor); |
|
} |
|
|
|
//============================================================================= |
|
|
|
|