aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_squad.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/ai_squad.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/ai_squad.cpp')
-rw-r--r--mp/src/game/server/ai_squad.cpp1574
1 files changed, 787 insertions, 787 deletions
diff --git a/mp/src/game/server/ai_squad.cpp b/mp/src/game/server/ai_squad.cpp
index 1733729b..17e0c18f 100644
--- a/mp/src/game/server/ai_squad.cpp
+++ b/mp/src/game/server/ai_squad.cpp
@@ -1,787 +1,787 @@
-//========= 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);
-}
-
-//=============================================================================
-
+//========= 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);
+}
+
+//=============================================================================
+