Counter Strike : Global Offensive Source Code
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.

402 lines
12 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: System to generate events as specified entities become visible to players.
  4. // This contains some specific early-outs and culling code, so it's not
  5. // exactly general purpose yet. (sjb)
  6. //
  7. // $NoKeywords: $
  8. //=====================================================================================//
  9. #include "cbase.h"
  10. #include "cvisibilitymonitor.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. ConVar debug_visibility_monitor("debug_visibility_monitor", "0", FCVAR_CHEAT );
  14. ConVar vismon_poll_frequency("vismon_poll_frequency", ".5", FCVAR_CHEAT );
  15. ConVar vismon_trace_limit("vismon_trace_limit", "12", FCVAR_CHEAT );
  16. //=============================================================================
  17. // This structure packages up an entity for the visibility monitor.
  18. //=============================================================================
  19. #define NO_VISIBILITY_MEMORY 0
  20. struct visibility_target_t
  21. {
  22. EHANDLE entity;
  23. float minDistSqr;
  24. int memory;// A bit vector that remembers which clients have seen this thing already.
  25. bool bNotVisibleThroughGlass;
  26. VisibilityMonitorCallback pfnCallback;
  27. VisibilityMonitorEvaluator pfnEvaluator;
  28. };
  29. //-----------------------------------------------------------------------------
  30. //-----------------------------------------------------------------------------
  31. class CVisibilityMonitor : public CAutoGameSystemPerFrame
  32. {
  33. public:
  34. CVisibilityMonitor( char const *name ) : CAutoGameSystemPerFrame( name )
  35. {
  36. m_flPollFrequency = 1.0f;
  37. m_flTimeNextPoll = 0.0f;
  38. }
  39. // Methods of IGameSystem
  40. virtual char const *Name() { return "VisibilityMonitor"; }
  41. virtual bool Init();
  42. virtual void FrameUpdatePostEntityThink();
  43. virtual bool EntityIsVisibleToPlayer( const visibility_target_t &target, CBasePlayer *pPlayer, int *numTraces );
  44. virtual void Shutdown();
  45. virtual void LevelInitPreEntity();
  46. virtual void LevelInitPostEntity();
  47. virtual void LevelShutdownPreEntity();
  48. // Visibility Monitor Methods
  49. void AddEntity( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator, bool bNotVisibleThroughGlass = false );
  50. void RemoveEntity( CBaseEntity *pEntity );
  51. bool IsTrackingEntity( CBaseEntity *pEntity );
  52. private:
  53. CUtlVector< visibility_target_t > m_Entities;
  54. float m_flPollFrequency;
  55. float m_flTimeNextPoll;
  56. int m_iMaxTracesPerThink;
  57. int m_iMaxEntitiesPerThink;
  58. int m_iStartElement;
  59. };
  60. //=========================================================
  61. // Auto game system instantiation
  62. //=========================================================
  63. CVisibilityMonitor VisibilityMonitor( "CVisibilityMonitor" );
  64. //---------------------------------------------------------
  65. //---------------------------------------------------------
  66. bool CVisibilityMonitor::Init()
  67. {
  68. return true;
  69. }
  70. //---------------------------------------------------------
  71. // Purpose: See if it is time to poll and do so.
  72. //
  73. // SOON: We need to load-balance this.
  74. //---------------------------------------------------------
  75. void CVisibilityMonitor::FrameUpdatePostEntityThink()
  76. {
  77. if( gpGlobals->curtime < m_flTimeNextPoll )
  78. return;
  79. m_flTimeNextPoll = gpGlobals->curtime + vismon_poll_frequency.GetFloat();
  80. int iDebugging = debug_visibility_monitor.GetInt();
  81. if( m_Entities.Count() > m_iMaxEntitiesPerThink )
  82. m_iMaxEntitiesPerThink = m_Entities.Count();
  83. if( iDebugging > 1 )
  84. {
  85. Msg("\nVisMon: Polling now. (Frequency: %f)\n", m_flPollFrequency );
  86. Msg("VisMon: Time: %f - Tracking %d Entities. (Max:%d)\n", gpGlobals->curtime, m_Entities.Count(), m_iMaxEntitiesPerThink );
  87. }
  88. // Cleanup, dump entities that have been removed since we last polled.
  89. for ( int i = 0 ; i < m_Entities.Count() ; i++ )
  90. {
  91. if ( m_Entities[i].entity == NULL )
  92. {
  93. m_Entities.FastRemove(i);
  94. if ( i >= m_Entities.Count() )
  95. {
  96. break;
  97. }
  98. }
  99. }
  100. int numTraces = 0;
  101. bool bHitTraceLimit = false;
  102. if( m_iStartElement >= m_Entities.Count() )
  103. {
  104. if( iDebugging > 1 )
  105. {
  106. Msg("VisMon: RESET\n");
  107. }
  108. m_iStartElement = 0;
  109. }
  110. if( iDebugging > 1 )
  111. {
  112. Msg("VisMon: Starting at element: %d\n", m_iStartElement );
  113. }
  114. for( int i = m_iStartElement ; i < m_Entities.Count() ; i++ )
  115. {
  116. for( int j = 1 ; j <= gpGlobals->maxClients ; j++ )
  117. {
  118. CBasePlayer *pPlayer =UTIL_PlayerByIndex( j );
  119. if( pPlayer != NULL && pPlayer->IsAlive() && !pPlayer->IsBot() )
  120. {
  121. int memoryBit = 1 << j; // The bit that is used to remember whether a given entity has been seen by a given player.
  122. CBaseEntity *pSeeEntity = m_Entities[ i ].entity.Get();
  123. if( pSeeEntity == NULL )
  124. {
  125. continue;
  126. }
  127. if( !(m_Entities[i].memory & memoryBit) )
  128. {
  129. // If this player hasn't seen this entity yet, check it.
  130. if( EntityIsVisibleToPlayer( m_Entities[i], pPlayer, &numTraces ) )
  131. {
  132. bool bIgnore = false;
  133. if( m_Entities[i].pfnEvaluator != NULL && !m_Entities[i].pfnEvaluator( m_Entities[i].entity, pPlayer ) )
  134. {
  135. bIgnore = true;
  136. }
  137. // See it! Generate our event.
  138. if( iDebugging > 0 )
  139. {
  140. if( bIgnore )
  141. {
  142. Msg("VisMon: Player %s IGNORING VISIBILE Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() );
  143. NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 255, 0, 0, false, 10.0f );
  144. }
  145. else
  146. {
  147. Msg("VisMon: Player %s sees Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() );
  148. NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 0, 255, 0, false, 10.0f );
  149. }
  150. }
  151. if( !bIgnore )
  152. {
  153. bool bGenerateEvent = true;
  154. if( m_Entities[i].pfnCallback != NULL )
  155. {
  156. // Make the callback, and let it determine whether to generate the simple event.
  157. bGenerateEvent = m_Entities[i].pfnCallback( m_Entities[i].entity, pPlayer );
  158. }
  159. if( bGenerateEvent )
  160. {
  161. // No callback, generate the generic game event.
  162. IGameEvent * event = gameeventmanager->CreateEvent( "entity_visible" );
  163. if ( event )
  164. {
  165. event->SetInt( "userid", pPlayer->GetUserID() );
  166. event->SetInt( "subject", pSeeEntity->entindex() );
  167. event->SetString( "classname", pSeeEntity->GetClassname() );
  168. event->SetString( "entityname", STRING( pSeeEntity->GetEntityName() ) );
  169. gameeventmanager->FireEvent( event );
  170. }
  171. }
  172. // Remember that this entity was visible to the player
  173. m_Entities[i].memory |= memoryBit;
  174. }
  175. }
  176. }
  177. }
  178. }
  179. if( numTraces >= vismon_trace_limit.GetInt() )
  180. {
  181. if( iDebugging > 1 )
  182. {
  183. Msg("VisMon: MAX Traces. Stopping after element %d\n", i );
  184. }
  185. m_iStartElement = i + 1; // Pick up here next think.
  186. bHitTraceLimit = true;
  187. break;
  188. }
  189. }
  190. if( !bHitTraceLimit )
  191. {
  192. m_iStartElement = 0;
  193. }
  194. if( numTraces > m_iMaxTracesPerThink )
  195. m_iMaxTracesPerThink = numTraces;
  196. if( iDebugging > 1 )
  197. {
  198. Msg("VisMon: %d traces performed during this polling cycle (Max: %d)\n\n", numTraces, m_iMaxTracesPerThink );
  199. }
  200. }
  201. //---------------------------------------------------------
  202. //---------------------------------------------------------
  203. bool CVisibilityMonitor::EntityIsVisibleToPlayer( const visibility_target_t &target, CBasePlayer *pPlayer, int *numTraces )
  204. {
  205. // if it's both invisible and not solid or interactive, we don't see it
  206. if ( target.entity->m_nRenderMode == kRenderNone && !target.entity->IsSolidFlagSet( FSOLID_TRIGGER ) )
  207. {
  208. // It's invisible
  209. return false;
  210. }
  211. CBaseCombatCharacter *pEyeEntity = pPlayer->ActivePlayerCombatCharacter();
  212. Vector vecTargetOrigin = target.entity->WorldSpaceCenter();
  213. Vector vecPlayerOrigin = pEyeEntity->EyePosition();
  214. float flDistSqr = vecPlayerOrigin.DistToSqr( vecTargetOrigin );
  215. if( flDistSqr <= target.minDistSqr )
  216. {
  217. // Increment the counter of traces done during this polling cycle
  218. *numTraces += 1;
  219. int mask = MASK_BLOCKLOS_AND_NPCS & ~CONTENTS_BLOCKLOS;
  220. if ( target.bNotVisibleThroughGlass )
  221. {
  222. mask |= CONTENTS_WINDOW;
  223. }
  224. trace_t tr;
  225. UTIL_TraceLine( vecPlayerOrigin, vecTargetOrigin, mask, pEyeEntity, COLLISION_GROUP_NONE, &tr );
  226. if( tr.fraction == 1.0f || tr.m_pEnt == target.entity )
  227. return true;
  228. if( debug_visibility_monitor.GetInt() > 1 )
  229. {
  230. NDebugOverlay::Line( vecPlayerOrigin, vecTargetOrigin, 255, 0, 0, false, vismon_poll_frequency.GetFloat() );
  231. }
  232. }
  233. return false;
  234. }
  235. //---------------------------------------------------------
  236. //---------------------------------------------------------
  237. void CVisibilityMonitor::Shutdown()
  238. {
  239. }
  240. //---------------------------------------------------------
  241. //---------------------------------------------------------
  242. void CVisibilityMonitor::LevelInitPreEntity()
  243. {
  244. }
  245. //---------------------------------------------------------
  246. //---------------------------------------------------------
  247. void CVisibilityMonitor::LevelInitPostEntity()
  248. {
  249. m_iMaxTracesPerThink = 0;
  250. m_iMaxEntitiesPerThink = 0;
  251. m_iStartElement = 0;
  252. m_flTimeNextPoll = gpGlobals->curtime + vismon_poll_frequency.GetFloat();
  253. }
  254. //---------------------------------------------------------
  255. //---------------------------------------------------------
  256. void CVisibilityMonitor::LevelShutdownPreEntity()
  257. {
  258. m_Entities.RemoveAll();
  259. }
  260. //---------------------------------------------------------
  261. //---------------------------------------------------------
  262. void CVisibilityMonitor::AddEntity( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator, bool bNotVisibleThroughGlass )
  263. {
  264. Assert( pEntity != NULL );
  265. if( !IsTrackingEntity( pEntity ) )
  266. {
  267. visibility_target_t newTarget;
  268. newTarget.entity = pEntity;
  269. newTarget.minDistSqr = Square(flMinDist);
  270. newTarget.memory = NO_VISIBILITY_MEMORY;
  271. newTarget.pfnCallback = pfnCallback;
  272. newTarget.pfnEvaluator = pfnEvaluator;
  273. newTarget.bNotVisibleThroughGlass = bNotVisibleThroughGlass;
  274. m_Entities.AddToTail( newTarget );
  275. if( debug_visibility_monitor.GetBool() )
  276. {
  277. Msg("VisMon: Added Entity: %s (%s)\n", pEntity->GetClassname(), pEntity->GetDebugName() );
  278. }
  279. }
  280. }
  281. //---------------------------------------------------------
  282. //---------------------------------------------------------
  283. void CVisibilityMonitor::RemoveEntity( CBaseEntity *pEntity )
  284. {
  285. Assert( pEntity != NULL );
  286. for( int i = 0 ; i < m_Entities.Count() ; i++ )
  287. {
  288. if( m_Entities[i].entity == pEntity )
  289. {
  290. m_Entities.Remove( i );
  291. if( debug_visibility_monitor.GetBool() )
  292. {
  293. Msg("VisMon: Removed Entity: %s (%s)\n", pEntity->GetClassname(), pEntity->GetDebugName() );
  294. }
  295. return;
  296. }
  297. }
  298. }
  299. //---------------------------------------------------------
  300. //---------------------------------------------------------
  301. bool CVisibilityMonitor::IsTrackingEntity( CBaseEntity *pEntity )
  302. {
  303. Assert( pEntity != NULL );
  304. for( int i = 0 ; i < m_Entities.Count() ; i++ )
  305. {
  306. if( m_Entities[i].entity == pEntity )
  307. {
  308. return true;
  309. }
  310. }
  311. return false;
  312. }
  313. //---------------------------------------------------------
  314. // Purpose: Adds an entity to the list of entities that
  315. // the visibility monitor is tracking.
  316. //---------------------------------------------------------
  317. void VisibilityMonitor_AddEntity( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator )
  318. {
  319. VisibilityMonitor.AddEntity( pEntity, flMinDist, pfnCallback, pfnEvaluator );
  320. }
  321. void VisibilityMonitor_AddEntity_NotVisibleThroughGlass( CBaseEntity *pEntity, float flMinDist, VisibilityMonitorCallback pfnCallback, VisibilityMonitorEvaluator pfnEvaluator )
  322. {
  323. VisibilityMonitor.AddEntity( pEntity, flMinDist, pfnCallback, pfnEvaluator, true );
  324. }
  325. //---------------------------------------------------------
  326. // Purpose: Remove (stop looking for) this entity
  327. //---------------------------------------------------------
  328. void VisibilityMonitor_RemoveEntity( CBaseEntity *pEntity )
  329. {
  330. VisibilityMonitor.RemoveEntity( pEntity );
  331. }