diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/ai_squad.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-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.cpp | 1574 |
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); +} + +//============================================================================= + |