2013-12-02 19:31:46 -08:00
|
|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
|
|
//
|
|
|
|
// Purpose: An NPC's memory of potential enemies
|
|
|
|
//
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "isaverestore.h"
|
|
|
|
#include "ai_debug.h"
|
|
|
|
#include "ai_memory.h"
|
|
|
|
#include "ai_basenpc.h"
|
|
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
#define EMEMORY_POOL_SIZE 64
|
|
|
|
#define AI_FREE_KNOWLEDGE_DURATION 1.75
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// AI_EnemyInfo_t
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AI_EnemyInfo_t::AI_EnemyInfo_t(void)
|
|
|
|
{
|
|
|
|
hEnemy = NULL;
|
|
|
|
vLastKnownLocation = vec3_origin;
|
|
|
|
vLastSeenLocation = vec3_origin;
|
|
|
|
timeLastSeen = 0;
|
|
|
|
timeFirstSeen = 0;
|
|
|
|
timeLastReacquired = 0;
|
|
|
|
timeValidEnemy = 0;
|
|
|
|
timeLastReceivedDamageFrom = 0;
|
|
|
|
timeAtFirstHand = AI_INVALID_TIME;
|
|
|
|
bDangerMemory = 0;
|
|
|
|
bEludedMe = 0;
|
|
|
|
bUnforgettable = 0;
|
|
|
|
bMobbedMe = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// CAI_EnemiesListSaveRestoreOps
|
|
|
|
//
|
|
|
|
// Purpose: Handles save and load for enemy memories
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class CAI_EnemiesListSaveRestoreOps : public CDefSaveRestoreOps
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CAI_EnemiesListSaveRestoreOps()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
|
|
|
|
{
|
|
|
|
CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField;
|
|
|
|
|
|
|
|
int nMemories = pMemMap->Count();
|
|
|
|
pSave->WriteInt( &nMemories );
|
|
|
|
|
|
|
|
for ( CAI_Enemies::CMemMap::IndexType_t i = pMemMap->FirstInorder(); i != pMemMap->InvalidIndex(); i = pMemMap->NextInorder( i ) )
|
|
|
|
{
|
|
|
|
pSave->WriteAll( (*pMemMap)[i] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
|
|
|
|
{
|
|
|
|
CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField;
|
|
|
|
Assert( pMemMap->Count() == 0 );
|
|
|
|
|
|
|
|
int nMemories = pRestore->ReadInt();
|
|
|
|
|
|
|
|
while ( nMemories-- )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pAddMemory = new AI_EnemyInfo_t;
|
|
|
|
|
|
|
|
pRestore->ReadAll( pAddMemory );
|
|
|
|
|
|
|
|
if ( pAddMemory->hEnemy != NULL )
|
|
|
|
{
|
|
|
|
pMemMap->Insert( pAddMemory->hEnemy, pAddMemory );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
delete pAddMemory;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
|
|
|
|
{
|
|
|
|
CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField;
|
|
|
|
|
|
|
|
for ( CAI_Enemies::CMemMap::IndexType_t i = pMemMap->FirstInorder(); i != pMemMap->InvalidIndex(); i = pMemMap->NextInorder( i ) )
|
|
|
|
{
|
|
|
|
delete (*pMemMap)[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
pMemMap->RemoveAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
|
|
|
|
{
|
|
|
|
CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField;
|
|
|
|
return ( pMemMap->Count() == 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
} g_AI_MemoryListSaveRestoreOps;
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// CAI_Enemies
|
|
|
|
//
|
|
|
|
// Purpose: Stores a set of AI_EnemyInfo_t's
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
BEGIN_SIMPLE_DATADESC( CAI_Enemies )
|
|
|
|
|
|
|
|
DEFINE_CUSTOM_FIELD( m_Map, &g_AI_MemoryListSaveRestoreOps ),
|
|
|
|
DEFINE_FIELD( m_flFreeKnowledgeDuration, FIELD_FLOAT ),
|
|
|
|
DEFINE_FIELD( m_flEnemyDiscardTime, FIELD_FLOAT ),
|
|
|
|
DEFINE_FIELD( m_vecDefaultLKP, FIELD_POSITION_VECTOR ),
|
|
|
|
DEFINE_FIELD( m_vecDefaultLSP, FIELD_POSITION_VECTOR ),
|
|
|
|
DEFINE_FIELD( m_serial, FIELD_INTEGER ),
|
|
|
|
|
|
|
|
END_DATADESC()
|
|
|
|
|
|
|
|
BEGIN_SIMPLE_DATADESC( AI_EnemyInfo_t )
|
|
|
|
DEFINE_FIELD( vLastKnownLocation, FIELD_POSITION_VECTOR ),
|
|
|
|
DEFINE_FIELD( vLastSeenLocation, FIELD_POSITION_VECTOR ),
|
|
|
|
DEFINE_FIELD( hEnemy, FIELD_EHANDLE ),
|
|
|
|
DEFINE_FIELD( timeLastSeen, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( timeFirstSeen, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( timeLastReacquired, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( timeValidEnemy, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( timeLastReceivedDamageFrom, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( timeAtFirstHand, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( bDangerMemory, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_FIELD( bEludedMe, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_FIELD( bUnforgettable, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_FIELD( bMobbedMe, FIELD_BOOLEAN ),
|
|
|
|
// NOT SAVED nextEMemory
|
|
|
|
END_DATADESC()
|
|
|
|
|
2020-05-22 20:26:31 -05:00
|
|
|
#ifdef MAPBASE_VSCRIPT
|
|
|
|
#define DEFINE_ENEMY_INFO_SCRIPTFUNCS(name, desc) \
|
|
|
|
DEFINE_SCRIPTFUNC_NAMED( Get##name, #name, "Get " desc ) \
|
|
|
|
DEFINE_SCRIPTFUNC( Set##name, "Set " desc )
|
|
|
|
|
|
|
|
BEGIN_SCRIPTDESC_ROOT( AI_EnemyInfo_t, "Accessor for information about an enemy." )
|
|
|
|
DEFINE_SCRIPTFUNC( Enemy, "Get the enemy." )
|
|
|
|
DEFINE_SCRIPTFUNC( SetEnemy, "Set the enemy." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastKnownLocation, "the enemy's last known location." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastSeenLocation, "the enemy's last seen location." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastSeen, "the last time the enemy was seen." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeFirstSeen, "the first time the enemy was seen." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReacquired, "the last time the enemy was reaquired." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeValidEnemy, "the time at which the enemy can be selected (reaction delay)." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReceivedDamageFrom, "the last time damage was received from this enemy." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeAtFirstHand, "the time at which the enemy was seen firsthand." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( DangerMemory, "the memory of danger position w/o enemy pointer." )
|
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( EludedMe, "whether the enemy is not at the last known location." )
|
Mapbase v5.0
- Added keyvalue to hl2_gamerules which allows respawning in singleplayer
- Added the game instructor system (including env_instructor_hint) from later Valve games using a VDC tutorial which adjusts the version from the Alien Swarm SDK to FPS rules and a Source 2013 environment; Also added new KV and icons for further control from mappers (tutorial mentioned by Maestra Fenix)
- Added L4D/TF2 glows + point_glow entity as an all-purpose SDK-based off-shoot of tf_glow
- Fixed weapon pickup sound not playing (reported by Sl0th and later Cvoxulary)
- Fixed env_projectedtextures not updating on save/load
- Added func_fake_worldportal, a spatial point_camera inspired by linked_portal_door based on SDK code alone (WIP, may be changed a lot in future updates)
- Added option for point_camera and func_reflective_glass to use different render targets, therefore allowing multiple cameras and mirrors to be active at the same time
- Added additional RT camera textures to choose from with a default of 3, but also controllable through a -numcameratextures command line param
- Added adjustable convars for main view NearZ and skybox NearZ (suggested by someone recently, also suggested by Klems over a year ago)
- Fixed map-specific localization files, cleaned up map-specific file code
- Added a new block to gameinfo.txt which allows mods to automatically append their own command line parameters
- Fixed math_lightpattern corruption when setting pattern/style while active
- Fixed the "Touch" input crashing when given no entity
- Added a way to add EFlags via keyvalue (suggested by Niker107)
- Fixed ai_script_conditions not working without a NPC actor (reported by MetroHam)
- Fixed point_radiation_source causing huge problems when intensity is 0, even though it was already advised against (reported by beefbacon)
- Added "Mapbase" header to Mapbase-specific code files
- Fixed an issue with updating sky_camera not obtaining area correctly, causing some entities to not draw in the skybox
- Added "CopyFogController" and "CopyFogControllerWithScale" inputs to sky_camera, which copy fog parameters directly from a fog controller
- Added "SetScale" input to sky_camera for live scale changing
- Added convar to control player crouch speed multiplier (suggested by ArtyIF)
- Added a ton of fixes for people running the Debug configuration of the codebase (partial credit to stepa2)
- Added support for pre-defined enums and constants in VScript, starting with various values from the SDK code (damage types, trace masks, etc.)
- Added limited support for Valve's Quaternion class in VScript
- Added new instance helper capabilities, destructible game instances, and other misc. changes to VScript library
- Replaced most of the VScript "accessor" classes with direct references to the original classes, as they were getting complicated fast and adding new VScript-only functions to the original classes might not be as bad as previously thought
- Added base NPC hooks for AI sensing in VScript (allows control over sight and hearing), also exposed CSound for it
- Added various functions and hooks for VPhysics integration in VScript
- Added VScript-based custom suit devices
- Expanded trace info exposed to VScript to allow plane and surface access (suggested by krassell)
- Added ability to insert localization strings through VScript
- Added various misc. VScript functions with various purposes, including reading/writing EFlags, movetypes, collision groups, etc.
- Fixed VBSP not being able to correctly parse parallax corrected cubemaps in maps with instances
2020-08-14 21:21:25 +00:00
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( Unforgettable, "whether the enemy is unforgettable." )
|
2020-05-22 20:26:31 -05:00
|
|
|
DEFINE_ENEMY_INFO_SCRIPTFUNCS( MobbedMe, "whether the enemy was part of a mob at some point." )
|
|
|
|
END_SCRIPTDESC();
|
|
|
|
#endif
|
|
|
|
|
2013-12-02 19:31:46 -08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
CAI_Enemies::CAI_Enemies(void)
|
|
|
|
{
|
|
|
|
m_flFreeKnowledgeDuration = AI_FREE_KNOWLEDGE_DURATION;
|
|
|
|
m_flEnemyDiscardTime = AI_DEF_ENEMY_DISCARD_TIME;
|
|
|
|
m_vecDefaultLKP = vec3_invalid;
|
|
|
|
m_vecDefaultLSP = vec3_invalid;
|
|
|
|
m_serial = 0;
|
|
|
|
SetDefLessFunc( m_Map );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
CAI_Enemies::~CAI_Enemies()
|
|
|
|
{
|
|
|
|
for ( CMemMap::IndexType_t i = m_Map.FirstInorder(); i != m_Map.InvalidIndex(); i = m_Map.NextInorder( i ) )
|
|
|
|
{
|
|
|
|
delete m_Map[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Purges any dead enemies from memory
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *CAI_Enemies::GetFirst( AIEnemiesIter_t *pIter )
|
|
|
|
{
|
|
|
|
CMemMap::IndexType_t i = m_Map.FirstInorder();
|
|
|
|
*pIter = (AIEnemiesIter_t)(unsigned)i;
|
|
|
|
|
|
|
|
if ( i == m_Map.InvalidIndex() )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( m_Map[i]->hEnemy == NULL )
|
|
|
|
return GetNext( pIter );
|
|
|
|
|
|
|
|
return m_Map[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *CAI_Enemies::GetNext( AIEnemiesIter_t *pIter )
|
|
|
|
{
|
|
|
|
CMemMap::IndexType_t i = (CMemMap::IndexType_t)((unsigned)(*pIter));
|
|
|
|
|
|
|
|
if ( i == m_Map.InvalidIndex() )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
i = m_Map.NextInorder( i );
|
|
|
|
*pIter = (AIEnemiesIter_t)(unsigned)i;
|
|
|
|
if ( i == m_Map.InvalidIndex() )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( m_Map[i]->hEnemy == NULL )
|
|
|
|
return GetNext( pIter );
|
|
|
|
|
|
|
|
return m_Map[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *CAI_Enemies::Find( CBaseEntity *pEntity, bool bTryDangerMemory )
|
|
|
|
{
|
|
|
|
if ( pEntity == AI_UNKNOWN_ENEMY )
|
|
|
|
pEntity = NULL;
|
|
|
|
|
|
|
|
CMemMap::IndexType_t i = m_Map.Find( pEntity );
|
|
|
|
if ( i == m_Map.InvalidIndex() )
|
|
|
|
{
|
|
|
|
if ( !bTryDangerMemory || ( i = m_Map.Find( NULL ) ) == m_Map.InvalidIndex() )
|
|
|
|
return NULL;
|
|
|
|
Assert(m_Map[i]->bDangerMemory == true);
|
|
|
|
}
|
|
|
|
return m_Map[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *CAI_Enemies::GetDangerMemory()
|
|
|
|
{
|
|
|
|
CMemMap::IndexType_t i = m_Map.Find( NULL );
|
|
|
|
if ( i == m_Map.InvalidIndex() )
|
|
|
|
return NULL;
|
|
|
|
Assert(m_Map[i]->bDangerMemory == true);
|
|
|
|
return m_Map[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool CAI_Enemies::ShouldDiscardMemory( AI_EnemyInfo_t *pMemory )
|
|
|
|
{
|
|
|
|
CBaseEntity *pEnemy = pMemory->hEnemy;
|
|
|
|
|
|
|
|
if ( pEnemy )
|
|
|
|
{
|
|
|
|
CAI_BaseNPC *pEnemyNPC = pEnemy->MyNPCPointer();
|
|
|
|
if ( pEnemyNPC && pEnemyNPC->GetState() == NPC_STATE_DEAD )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( !pMemory->bDangerMemory )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !pMemory->bUnforgettable &&
|
|
|
|
gpGlobals->curtime > pMemory->timeLastSeen + m_flEnemyDiscardTime )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CAI_Enemies::RefreshMemories(void)
|
|
|
|
{
|
|
|
|
AI_PROFILE_SCOPE(CAI_Enemies_RefreshMemories);
|
|
|
|
|
|
|
|
if ( m_flFreeKnowledgeDuration >= m_flEnemyDiscardTime )
|
|
|
|
{
|
|
|
|
m_flFreeKnowledgeDuration = m_flEnemyDiscardTime - .1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -------------------
|
|
|
|
// Check each record
|
|
|
|
// -------------------
|
|
|
|
|
|
|
|
CMemMap::IndexType_t i = m_Map.FirstInorder();
|
|
|
|
while ( i != m_Map.InvalidIndex() )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = m_Map[i];
|
|
|
|
|
|
|
|
CMemMap::IndexType_t iNext = m_Map.NextInorder( i ); // save so can remove
|
|
|
|
if ( ShouldDiscardMemory( pMemory ) )
|
|
|
|
{
|
|
|
|
delete pMemory;
|
|
|
|
m_Map.RemoveAt(i);
|
|
|
|
}
|
|
|
|
else if ( pMemory->hEnemy )
|
|
|
|
{
|
|
|
|
if ( gpGlobals->curtime <= pMemory->timeLastSeen + m_flFreeKnowledgeDuration )
|
|
|
|
{
|
|
|
|
// Free knowledge is ignored if the target has notarget on
|
|
|
|
if ( !(pMemory->hEnemy->GetFlags() & FL_NOTARGET) )
|
|
|
|
{
|
|
|
|
pMemory->vLastKnownLocation = pMemory->hEnemy->GetAbsOrigin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( gpGlobals->curtime <= pMemory->timeLastSeen )
|
|
|
|
{
|
|
|
|
pMemory->vLastSeenLocation = pMemory->hEnemy->GetAbsOrigin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i = iNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Updates information about our enemies
|
|
|
|
// Output : Returns true if new enemy, false if already know of enemy
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool CAI_Enemies::UpdateMemory(CAI_Network* pAINet, CBaseEntity *pEnemy, const Vector &vPosition, float reactionDelay, bool firstHand )
|
|
|
|
{
|
|
|
|
if ( pEnemy == AI_UNKNOWN_ENEMY )
|
|
|
|
pEnemy = NULL;
|
|
|
|
|
|
|
|
const float DIST_TRIGGER_REACQUIRE_SQ = Square(20.0 * 12.0);
|
|
|
|
const float TIME_TRIGGER_REACQUIRE = 4.0;
|
|
|
|
const float MIN_DIST_TIME_TRIGGER_REACQUIRE_SQ = Square(4.0 * 12.0);
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy );
|
|
|
|
// -------------------------------------------
|
|
|
|
// Otherwise just update my own
|
|
|
|
// -------------------------------------------
|
|
|
|
// Update enemy information
|
|
|
|
if ( pMemory )
|
|
|
|
{
|
|
|
|
Assert(pEnemy || pMemory->bDangerMemory == true);
|
|
|
|
|
|
|
|
if ( firstHand )
|
|
|
|
pMemory->timeLastSeen = gpGlobals->curtime;
|
|
|
|
pMemory->bEludedMe = false;
|
|
|
|
|
|
|
|
float deltaDist = (pMemory->vLastKnownLocation - vPosition).LengthSqr();
|
|
|
|
|
|
|
|
if (deltaDist>DIST_TRIGGER_REACQUIRE_SQ || ( deltaDist>MIN_DIST_TIME_TRIGGER_REACQUIRE_SQ && ( gpGlobals->curtime - pMemory->timeLastSeen ) > TIME_TRIGGER_REACQUIRE ) )
|
|
|
|
{
|
|
|
|
pMemory->timeLastReacquired = gpGlobals->curtime;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only update if the enemy has moved
|
|
|
|
if (deltaDist>Square(12.0))
|
|
|
|
{
|
|
|
|
pMemory->vLastKnownLocation = vPosition;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the time at which we first saw him firsthand
|
|
|
|
if ( firstHand && pMemory->timeAtFirstHand == AI_INVALID_TIME )
|
|
|
|
{
|
|
|
|
pMemory->timeAtFirstHand = gpGlobals->curtime;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not on my list of enemies add it
|
|
|
|
AI_EnemyInfo_t *pAddMemory = new AI_EnemyInfo_t;
|
|
|
|
pAddMemory->vLastKnownLocation = vPosition;
|
|
|
|
|
|
|
|
if ( firstHand )
|
|
|
|
{
|
|
|
|
pAddMemory->timeLastReacquired = pAddMemory->timeFirstSeen = pAddMemory->timeLastSeen = pAddMemory->timeAtFirstHand = gpGlobals->curtime;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Block free knowledge
|
|
|
|
pAddMemory->timeLastReacquired = pAddMemory->timeFirstSeen = pAddMemory->timeLastSeen = ( gpGlobals->curtime - (m_flFreeKnowledgeDuration + 0.01) );
|
|
|
|
pAddMemory->timeAtFirstHand = AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( reactionDelay > 0.0 )
|
|
|
|
pAddMemory->timeValidEnemy = gpGlobals->curtime + reactionDelay;
|
|
|
|
|
|
|
|
pAddMemory->bEludedMe = false;
|
|
|
|
|
|
|
|
// I'm either remembering a postion of an enmey of just a danger position
|
|
|
|
pAddMemory->hEnemy = pEnemy;
|
|
|
|
pAddMemory->bDangerMemory = ( pEnemy == NULL );
|
|
|
|
|
|
|
|
// add to the list
|
|
|
|
m_Map.Insert( pEnemy, pAddMemory );
|
|
|
|
m_serial++;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Purpose : Returns true if this enemy is part of my memory
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void CAI_Enemies::OnTookDamageFrom( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
pMemory->timeLastReceivedDamageFrom = gpGlobals->curtime;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Purpose : Returns true if this enemy is part of my memory
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool CAI_Enemies::HasMemory( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
return ( Find( pEnemy ) != NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Clear information about our enemy
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CAI_Enemies::ClearMemory(CBaseEntity *pEnemy)
|
|
|
|
{
|
|
|
|
CMemMap::IndexType_t i = m_Map.Find( pEnemy );
|
|
|
|
if ( i != m_Map.InvalidIndex() )
|
|
|
|
{
|
|
|
|
delete m_Map[i];
|
|
|
|
m_Map.RemoveAt( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Notes that the given enemy has eluded me
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CAI_Enemies::MarkAsEluded( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy );
|
|
|
|
if ( pMemory )
|
|
|
|
{
|
|
|
|
pMemory->bEludedMe = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Returns last known posiiton of given enemy
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const Vector &CAI_Enemies::LastKnownPosition( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
{
|
|
|
|
m_vecDefaultLKP = pMemory->vLastKnownLocation;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DevWarning( 2,"Asking LastKnownPosition for enemy that's not in my memory!!\n");
|
|
|
|
}
|
|
|
|
return m_vecDefaultLKP;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Returns the last position the enemy was SEEN at. This will always be
|
|
|
|
// different than LastKnownPosition() when the enemy is out of sight, because
|
|
|
|
// the last KNOWN position will be updated for a number of seconds after the
|
|
|
|
// player disappears.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const Vector &CAI_Enemies::LastSeenPosition( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
{
|
|
|
|
m_vecDefaultLSP = pMemory->vLastSeenLocation;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DevWarning( 2,"Asking LastSeenPosition for enemy that's not in my memory!!\n");
|
|
|
|
}
|
|
|
|
return m_vecDefaultLSP;
|
|
|
|
}
|
|
|
|
|
|
|
|
float CAI_Enemies::TimeLastReacquired( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
// I've never seen something that doesn't exist
|
|
|
|
if (!pEnemy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
return pMemory->timeLastReacquired;
|
|
|
|
|
|
|
|
if ( pEnemy != AI_UNKNOWN_ENEMY )
|
|
|
|
DevWarning( 2,"Asking TimeLastReacquired for enemy that's not in my memory!!\n");
|
|
|
|
return AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Sets position to the last known position of an enemy. If enemy
|
|
|
|
// was not found returns last memory of danger position if it exists
|
|
|
|
// Output : Returns false is no position is known
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
float CAI_Enemies::LastTimeSeen( CBaseEntity *pEnemy, bool bCheckDangerMemory /*= true*/ )
|
|
|
|
{
|
|
|
|
// I've never seen something that doesn't exist
|
|
|
|
if (!pEnemy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, bCheckDangerMemory );
|
|
|
|
if ( pMemory )
|
|
|
|
return pMemory->timeLastSeen;
|
|
|
|
|
|
|
|
if ( pEnemy != AI_UNKNOWN_ENEMY )
|
|
|
|
DevWarning( 2,"Asking LastTimeSeen for enemy that's not in my memory!!\n");
|
|
|
|
return AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Get the time at which the enemy was first seen.
|
|
|
|
// Output : Returns false is no position is known
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
float CAI_Enemies::FirstTimeSeen( CBaseEntity *pEnemy)
|
|
|
|
{
|
|
|
|
// I've never seen something that doesn't exist
|
|
|
|
if (!pEnemy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
return pMemory->timeFirstSeen;
|
|
|
|
|
|
|
|
if ( pEnemy != AI_UNKNOWN_ENEMY )
|
|
|
|
DevWarning( 2,"Asking FirstTimeSeen for enemy that's not in my memory!!\n");
|
|
|
|
return AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose:
|
|
|
|
// Input : *pEnemy -
|
|
|
|
// Output : Returns true on success, false on failure.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool CAI_Enemies::HasFreeKnowledgeOf( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
// I've never seen something that doesn't exist
|
|
|
|
if (!pEnemy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
{
|
|
|
|
float flFreeKnowledgeTime = pMemory->timeLastSeen + m_flFreeKnowledgeDuration;
|
|
|
|
return ( gpGlobals->curtime < flFreeKnowledgeTime );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( pEnemy != AI_UNKNOWN_ENEMY )
|
|
|
|
DevWarning( 2,"Asking HasFreeKnowledgeOf for enemy that's not in my memory!!\n");
|
|
|
|
return AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
float CAI_Enemies::LastTimeTookDamageFrom( CBaseEntity *pEnemy)
|
|
|
|
{
|
|
|
|
// I've never seen something that doesn't exist
|
|
|
|
if (!pEnemy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
return pMemory->timeLastReceivedDamageFrom;
|
|
|
|
|
|
|
|
if ( pEnemy != AI_UNKNOWN_ENEMY )
|
|
|
|
DevWarning( 2,"Asking LastTimeTookDamageFrom for enemy that's not in my memory!!\n");
|
|
|
|
return AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Returns the time at which the enemy was first seen firsthand
|
|
|
|
// Input : *pEnemy -
|
|
|
|
// Output : float
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
float CAI_Enemies::TimeAtFirstHand( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
// I've never seen something that doesn't exist
|
|
|
|
if (!pEnemy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy, true );
|
|
|
|
if ( pMemory )
|
|
|
|
return pMemory->timeAtFirstHand;
|
|
|
|
|
|
|
|
if ( pEnemy != AI_UNKNOWN_ENEMY )
|
|
|
|
DevWarning( 2,"Asking TimeAtFirstHand for enemy that's not in my memory!!\n");
|
|
|
|
return AI_INVALID_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Purpose: Sets position to the last known position of an enemy. If enemy
|
|
|
|
// was not found returns last memory of danger position if it exists
|
|
|
|
// Output : Returns false is no position is known
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool CAI_Enemies::HasEludedMe( CBaseEntity *pEnemy )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy );
|
|
|
|
if ( pMemory )
|
|
|
|
return pMemory->bEludedMe;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CAI_Enemies::SetTimeValidEnemy( CBaseEntity *pEnemy, float flTime )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy );
|
|
|
|
if ( pMemory )
|
|
|
|
pMemory->timeValidEnemy = flTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CAI_Enemies::SetUnforgettable( CBaseEntity *pEnemy, bool bUnforgettable )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy );
|
|
|
|
if ( pMemory )
|
|
|
|
pMemory->bUnforgettable = bUnforgettable;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CAI_Enemies::SetMobbedMe( CBaseEntity *pEnemy, bool bMobbedMe )
|
|
|
|
{
|
|
|
|
AI_EnemyInfo_t *pMemory = Find( pEnemy );
|
|
|
|
if ( pMemory )
|
|
|
|
pMemory->bMobbedMe = bMobbedMe;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CAI_Enemies::SetFreeKnowledgeDuration( float flDuration )
|
|
|
|
{
|
|
|
|
m_flFreeKnowledgeDuration = flDuration;
|
|
|
|
|
|
|
|
if ( m_flFreeKnowledgeDuration >= m_flEnemyDiscardTime )
|
|
|
|
{
|
|
|
|
// If your free knowledge time is greater than your discard time,
|
|
|
|
// you'll forget about secondhand enemies passed to you by squadmates
|
|
|
|
// as soon as you're given them.
|
|
|
|
Assert( m_flFreeKnowledgeDuration < m_flEnemyDiscardTime );
|
|
|
|
|
|
|
|
m_flFreeKnowledgeDuration = m_flEnemyDiscardTime - .1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CAI_Enemies::SetEnemyDiscardTime( float flTime )
|
|
|
|
{
|
|
|
|
m_flEnemyDiscardTime = flTime;
|
|
|
|
|
|
|
|
if ( m_flFreeKnowledgeDuration >= m_flEnemyDiscardTime )
|
|
|
|
{
|
|
|
|
// If your free knowledge time is greater than your discard time,
|
|
|
|
// you'll forget about secondhand enemies passed to you by squadmates
|
|
|
|
// as soon as you're given them.
|
|
|
|
Assert( m_flFreeKnowledgeDuration < m_flEnemyDiscardTime );
|
|
|
|
|
|
|
|
m_flFreeKnowledgeDuration = m_flEnemyDiscardTime - .1;
|
|
|
|
}
|
|
|
|
}
|