aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_squad.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/ai_squad.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/ai_squad.cpp')
-rw-r--r--mp/src/game/server/ai_squad.cpp787
1 files changed, 787 insertions, 0 deletions
diff --git a/mp/src/game/server/ai_squad.cpp b/mp/src/game/server/ai_squad.cpp
new file mode 100644
index 00000000..1733729b
--- /dev/null
+++ b/mp/src/game/server/ai_squad.cpp
@@ -0,0 +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);
+}
+
+//=============================================================================
+