You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
11 KiB
383 lines
11 KiB
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: System to generate events as specified entities become visible to players. |
|
// This contains some specific early-outs and culling code, so it's not |
|
// exactly general purpose yet. (sjb) |
|
// |
|
// $NoKeywords: $ |
|
//=====================================================================================// |
|
|
|
#include "cbase.h" |
|
#include "cvisibilitymonitor.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
ConVar debug_visibility_monitor("debug_visibility_monitor", "0", FCVAR_CHEAT ); |
|
ConVar vismon_poll_frequency("vismon_poll_frequency", ".5", FCVAR_CHEAT ); |
|
ConVar vismon_trace_limit("vismon_trace_limit", "12", FCVAR_CHEAT ); |
|
|
|
//============================================================================= |
|
// This structure packages up an entity for the visibility monitor. |
|
//============================================================================= |
|
#define NO_VISIBILITY_MEMORY 0 |
|
struct visibility_target_t |
|
{ |
|
EHANDLE entity; |
|
float minDistSqr; |
|
int memory;// A bit vector that remembers which clients have seen this thing already. |
|
VisibilityMonitorCallback pfnCallback; |
|
VisibilityMonitorEvaluator pfnEvaluator; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
class CVisibilityMonitor : public CAutoGameSystemPerFrame |
|
{ |
|
public: |
|
CVisibilityMonitor( char const *name ) : CAutoGameSystemPerFrame( name ) |
|
{ |
|
m_flPollFrequency = 1.0f; |
|
m_flTimeNextPoll = 0.0f; |
|
} |
|
|
|
// Methods of IGameSystem |
|
virtual char const *Name() { return "VisibilityMonitor"; } |
|
|
|
virtual bool Init(); |
|
virtual void FrameUpdatePostEntityThink(); |
|
virtual bool EntityIsVisibleToPlayer( const visibility_target_t &target, CBasePlayer *pPlayer, int *numTraces ); |
|
virtual void Shutdown(); |
|
|
|
virtual void LevelInitPreEntity(); |
|
virtual void LevelInitPostEntity(); |
|
virtual void LevelShutdownPreEntity(); |
|
|
|
// Visibility Monitor Methods |
|
void AddEntity( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator ); |
|
void RemoveEntity( CBaseEntity *pEntity ); |
|
|
|
bool IsTrackingEntity( CBaseEntity *pEntity ); |
|
|
|
private: |
|
CUtlVector<visibility_target_t> m_Entities; |
|
float m_flPollFrequency; |
|
float m_flTimeNextPoll; |
|
|
|
int m_iMaxTracesPerThink; |
|
int m_iMaxEntitiesPerThink; |
|
|
|
int m_iStartElement; |
|
}; |
|
|
|
//========================================================= |
|
// Auto game system instantiation |
|
//========================================================= |
|
CVisibilityMonitor VisibilityMonitor( "CVisibilityMonitor" ); |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
bool CVisibilityMonitor::Init() |
|
{ |
|
return true; |
|
} |
|
|
|
//--------------------------------------------------------- |
|
// Purpose: See if it is time to poll and do so. |
|
// |
|
// SOON: We need to load-balance this. |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::FrameUpdatePostEntityThink() |
|
{ |
|
if( gpGlobals->curtime < m_flTimeNextPoll ) |
|
return; |
|
|
|
m_flTimeNextPoll = gpGlobals->curtime + vismon_poll_frequency.GetFloat(); |
|
|
|
int iDebugging = debug_visibility_monitor.GetInt(); |
|
|
|
if( m_Entities.Count() > m_iMaxEntitiesPerThink ) |
|
m_iMaxEntitiesPerThink = m_Entities.Count(); |
|
|
|
if( iDebugging > 1 ) |
|
{ |
|
Msg("\nVisMon: Polling now. (Frequency: %f)\n", m_flPollFrequency ); |
|
Msg("VisMon: Time: %f - Tracking %d Entities. (Max:%d)\n", gpGlobals->curtime, m_Entities.Count(), m_iMaxEntitiesPerThink ); |
|
} |
|
|
|
// Cleanup, dump entities that have been removed since we last polled. |
|
for ( int i = 0 ; i < m_Entities.Count() ; i++ ) |
|
{ |
|
if ( m_Entities[i].entity == NULL ) |
|
{ |
|
m_Entities.FastRemove(i); |
|
if ( i >= m_Entities.Count() ) |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
|
|
int numTraces = 0; |
|
bool bHitTraceLimit = false; |
|
|
|
if( m_iStartElement >= m_Entities.Count() ) |
|
{ |
|
if( iDebugging > 1 ) |
|
{ |
|
Msg("VisMon: RESET\n"); |
|
} |
|
|
|
m_iStartElement = 0; |
|
} |
|
|
|
if( iDebugging > 1 ) |
|
{ |
|
Msg("VisMon: Starting at element: %d\n", m_iStartElement ); |
|
} |
|
|
|
for( int i = m_iStartElement ; i < m_Entities.Count() ; i++ ) |
|
{ |
|
for( int j = 1 ; j <= gpGlobals->maxClients ; j++ ) |
|
{ |
|
CBasePlayer *pPlayer =UTIL_PlayerByIndex( j ); |
|
|
|
if( pPlayer != NULL && pPlayer->IsAlive() && !pPlayer->IsBot() ) |
|
{ |
|
int memoryBit = 1 << j; // The bit that is used to remember whether a given entity has been seen by a given player. |
|
|
|
CBaseEntity *pSeeEntity = m_Entities[ i ].entity.Get(); |
|
|
|
if( pSeeEntity == NULL ) |
|
{ |
|
continue; |
|
} |
|
|
|
if( !(m_Entities[i].memory & memoryBit) ) |
|
{ |
|
// If this player hasn't seen this entity yet, check it. |
|
if( EntityIsVisibleToPlayer( m_Entities[i], pPlayer, &numTraces ) ) |
|
{ |
|
bool bIgnore = false; |
|
|
|
if( m_Entities[i].pfnEvaluator != NULL && !m_Entities[i].pfnEvaluator( m_Entities[i].entity, pPlayer ) ) |
|
{ |
|
bIgnore = true; |
|
} |
|
|
|
// See it! Generate our event. |
|
if( iDebugging > 0 ) |
|
{ |
|
if( bIgnore ) |
|
{ |
|
Msg("VisMon: Player %s IGNORING VISIBILE Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() ); |
|
NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 255, 0, 0, false, 10.0f ); |
|
} |
|
else |
|
{ |
|
Msg("VisMon: Player %s sees Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() ); |
|
NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 0, 255, 0, false, 10.0f ); |
|
} |
|
} |
|
|
|
if( !bIgnore ) |
|
{ |
|
bool bGenerateEvent = true; |
|
|
|
if( m_Entities[i].pfnCallback != NULL ) |
|
{ |
|
// Make the callback, and let it determine whether to generate the simple event. |
|
bGenerateEvent = m_Entities[i].pfnCallback( m_Entities[i].entity, pPlayer ); |
|
} |
|
|
|
if( bGenerateEvent ) |
|
{ |
|
// No callback, generate the generic game event. |
|
IGameEvent * event = gameeventmanager->CreateEvent( "entity_visible" ); |
|
if ( event ) |
|
{ |
|
event->SetInt( "userid", pPlayer->GetUserID() ); |
|
event->SetInt( "subject", pSeeEntity->entindex() ); |
|
event->SetString( "classname", pSeeEntity->GetClassname() ); |
|
event->SetString( "entityname", STRING( pSeeEntity->GetEntityName() ) ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
|
|
// Remember that this entity was visible to the player |
|
m_Entities[i].memory |= memoryBit; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if( numTraces >= vismon_trace_limit.GetInt() ) |
|
{ |
|
if( iDebugging > 1 ) |
|
{ |
|
Msg("VisMon: MAX Traces. Stopping after element %d\n", i ); |
|
} |
|
|
|
m_iStartElement = i + 1; // Pick up here next think. |
|
bHitTraceLimit = true; |
|
break; |
|
} |
|
} |
|
|
|
if( !bHitTraceLimit ) |
|
{ |
|
m_iStartElement = 0; |
|
} |
|
|
|
if( numTraces > m_iMaxTracesPerThink ) |
|
m_iMaxTracesPerThink = numTraces; |
|
|
|
if( iDebugging > 1 ) |
|
{ |
|
Msg("VisMon: %d traces performed during this polling cycle (Max: %d)\n\n", numTraces, m_iMaxTracesPerThink ); |
|
} |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
bool CVisibilityMonitor::EntityIsVisibleToPlayer( const visibility_target_t &target, CBasePlayer *pPlayer, int *numTraces ) |
|
{ |
|
CBaseCombatCharacter *pEyeEntity = pPlayer->ActivePlayerCombatCharacter(); |
|
|
|
Vector vecTargetOrigin = target.entity->WorldSpaceCenter(); |
|
Vector vecPlayerOrigin = pEyeEntity->EyePosition(); |
|
|
|
float flDistSqr = vecPlayerOrigin.DistToSqr( vecTargetOrigin ); |
|
|
|
if( flDistSqr <= target.minDistSqr ) |
|
{ |
|
// Increment the counter of traces done during this polling cycle |
|
*numTraces += 1; |
|
|
|
trace_t tr; |
|
int mask = MASK_BLOCKLOS_AND_NPCS & ~CONTENTS_BLOCKLOS; |
|
UTIL_TraceLine( vecPlayerOrigin, vecTargetOrigin, mask, pEyeEntity, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if( tr.fraction == 1.0f || tr.m_pEnt == target.entity ) |
|
return true; |
|
|
|
if( debug_visibility_monitor.GetInt() > 1 ) |
|
{ |
|
NDebugOverlay::Line( vecPlayerOrigin, vecTargetOrigin, 255, 0, 0, false, vismon_poll_frequency.GetFloat() ); |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::Shutdown() |
|
{ |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::LevelInitPreEntity() |
|
{ |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::LevelInitPostEntity() |
|
{ |
|
m_iMaxTracesPerThink = 0; |
|
m_iMaxEntitiesPerThink = 0; |
|
m_iStartElement = 0; |
|
m_flTimeNextPoll = gpGlobals->curtime + vismon_poll_frequency.GetFloat(); |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::LevelShutdownPreEntity() |
|
{ |
|
m_Entities.RemoveAll(); |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::AddEntity( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator ) |
|
{ |
|
Assert( pEntity != NULL ); |
|
|
|
if( !IsTrackingEntity( pEntity ) ) |
|
{ |
|
visibility_target_t newTarget; |
|
|
|
newTarget.entity = pEntity; |
|
newTarget.minDistSqr = Square(flMinDist); |
|
newTarget.memory = NO_VISIBILITY_MEMORY; |
|
newTarget.pfnCallback = pfnCallback; |
|
newTarget.pfnEvaluator = pfnEvaluator; |
|
|
|
m_Entities.AddToTail( newTarget ); |
|
|
|
if( debug_visibility_monitor.GetBool() ) |
|
{ |
|
Msg("VisMon: Added Entity: %s (%s)\n", pEntity->GetClassname(), pEntity->GetDebugName() ); |
|
} |
|
} |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
void CVisibilityMonitor::RemoveEntity( CBaseEntity *pEntity ) |
|
{ |
|
Assert( pEntity != NULL ); |
|
|
|
for( int i = 0 ; i < m_Entities.Count() ; i++ ) |
|
{ |
|
if( m_Entities[i].entity == pEntity ) |
|
{ |
|
m_Entities.Remove( i ); |
|
|
|
if( debug_visibility_monitor.GetBool() ) |
|
{ |
|
Msg("VisMon: Removed Entity: %s (%s)\n", pEntity->GetClassname(), pEntity->GetDebugName() ); |
|
} |
|
|
|
return; |
|
} |
|
} |
|
} |
|
|
|
//--------------------------------------------------------- |
|
//--------------------------------------------------------- |
|
bool CVisibilityMonitor::IsTrackingEntity( CBaseEntity *pEntity ) |
|
{ |
|
Assert( pEntity != NULL ); |
|
|
|
for( int i = 0 ; i < m_Entities.Count() ; i++ ) |
|
{ |
|
if( m_Entities[i].entity == pEntity ) |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//--------------------------------------------------------- |
|
// Purpose: Adds an entity to the list of entities that |
|
// the visibility monitor is tracking. |
|
//--------------------------------------------------------- |
|
void VisibilityMonitor_AddEntity( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator ) |
|
{ |
|
VisibilityMonitor.AddEntity( pEntity, flMinDist, pfnCallback, pfnEvaluator ); |
|
} |
|
|
|
//--------------------------------------------------------- |
|
// Purpose: Remove (stop looking for) this entity |
|
//--------------------------------------------------------- |
|
void VisibilityMonitor_RemoveEntity( CBaseEntity *pEntity ) |
|
{ |
|
VisibilityMonitor.RemoveEntity( pEntity ); |
|
} |
|
|
|
|