Team Fortress 2 Source Code as on 22/4/2020
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.

802 lines
22 KiB

  1. // NextBotVisionInterface.cpp
  2. // Implementation of common vision system
  3. // Author: Michael Booth, May 2006
  4. //========= Copyright Valve Corporation, All rights reserved. ============//
  5. #include "cbase.h"
  6. #include "nav.h"
  7. #include "functorutils.h"
  8. #include "NextBot.h"
  9. #include "NextBotVisionInterface.h"
  10. #include "NextBotBodyInterface.h"
  11. #include "NextBotUtil.h"
  12. #ifdef TERROR
  13. #include "querycache.h"
  14. #endif
  15. #include "tier0/vprof.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. ConVar nb_blind( "nb_blind", "0", FCVAR_CHEAT, "Disable vision" );
  19. ConVar nb_debug_known_entities( "nb_debug_known_entities", "0", FCVAR_CHEAT, "Show the 'known entities' for the bot that is the current spectator target" );
  20. //------------------------------------------------------------------------------------------
  21. IVision::IVision( INextBot *bot ) : INextBotComponent( bot )
  22. {
  23. Reset();
  24. }
  25. //------------------------------------------------------------------------------------------
  26. /**
  27. * Reset to initial state
  28. */
  29. void IVision::Reset( void )
  30. {
  31. INextBotComponent::Reset();
  32. m_knownEntityVector.RemoveAll();
  33. m_lastVisionUpdateTimestamp = 0.0f;
  34. m_primaryThreat = NULL;
  35. m_FOV = GetDefaultFieldOfView();
  36. m_cosHalfFOV = cos( 0.5f * m_FOV * M_PI / 180.0f );
  37. for( int i=0; i<MAX_TEAMS; ++i )
  38. {
  39. m_notVisibleTimer[i].Invalidate();
  40. }
  41. }
  42. //------------------------------------------------------------------------------------------
  43. /**
  44. * Ask the current behavior to select the most dangerous threat from
  45. * our set of currently known entities
  46. * TODO: Find a semantically better place for this to live.
  47. */
  48. const CKnownEntity *IVision::GetPrimaryKnownThreat( bool onlyVisibleThreats ) const
  49. {
  50. if ( m_knownEntityVector.Count() == 0 )
  51. return NULL;
  52. const CKnownEntity *threat = NULL;
  53. int i;
  54. // find the first valid entity
  55. for( i=0; i<m_knownEntityVector.Count(); ++i )
  56. {
  57. const CKnownEntity &firstThreat = m_knownEntityVector[i];
  58. // check in case status changes between updates
  59. if ( IsAwareOf( firstThreat ) && !firstThreat.IsObsolete() && !IsIgnored( firstThreat.GetEntity() ) && GetBot()->IsEnemy( firstThreat.GetEntity() ) )
  60. {
  61. if ( !onlyVisibleThreats || firstThreat.IsVisibleRecently() )
  62. {
  63. threat = &firstThreat;
  64. break;
  65. }
  66. }
  67. }
  68. if ( threat == NULL )
  69. {
  70. m_primaryThreat = NULL;
  71. return NULL;
  72. }
  73. for( ++i; i<m_knownEntityVector.Count(); ++i )
  74. {
  75. const CKnownEntity &newThreat = m_knownEntityVector[i];
  76. // check in case status changes between updates
  77. if ( IsAwareOf( newThreat ) && !newThreat.IsObsolete() && !IsIgnored( newThreat.GetEntity() ) && GetBot()->IsEnemy( newThreat.GetEntity() ) )
  78. {
  79. if ( !onlyVisibleThreats || newThreat.IsVisibleRecently() )
  80. {
  81. threat = GetBot()->GetIntentionInterface()->SelectMoreDangerousThreat( GetBot(), GetBot()->GetEntity(), threat, &newThreat );
  82. }
  83. }
  84. }
  85. // cache off threat
  86. m_primaryThreat = threat ? threat->GetEntity() : NULL;
  87. return threat;
  88. }
  89. //------------------------------------------------------------------------------------------
  90. /**
  91. * Return the closest recognized entity
  92. */
  93. const CKnownEntity *IVision::GetClosestKnown( int team ) const
  94. {
  95. const Vector &myPos = GetBot()->GetPosition();
  96. const CKnownEntity *close = NULL;
  97. float closeRange = 999999999.9f;
  98. for( int i=0; i < m_knownEntityVector.Count(); ++i )
  99. {
  100. const CKnownEntity &known = m_knownEntityVector[i];
  101. if ( !known.IsObsolete() && IsAwareOf( known ) )
  102. {
  103. if ( team == TEAM_ANY || known.GetEntity()->GetTeamNumber() == team )
  104. {
  105. Vector to = known.GetLastKnownPosition() - myPos;
  106. float rangeSq = to.LengthSqr();
  107. if ( rangeSq < closeRange )
  108. {
  109. close = &known;
  110. closeRange = rangeSq;
  111. }
  112. }
  113. }
  114. }
  115. return close;
  116. }
  117. //------------------------------------------------------------------------------------------
  118. /**
  119. * Return the closest recognized entity that passes the given filter
  120. */
  121. const CKnownEntity *IVision::GetClosestKnown( const INextBotEntityFilter &filter ) const
  122. {
  123. const Vector &myPos = GetBot()->GetPosition();
  124. const CKnownEntity *close = NULL;
  125. float closeRange = 999999999.9f;
  126. for( int i=0; i < m_knownEntityVector.Count(); ++i )
  127. {
  128. const CKnownEntity &known = m_knownEntityVector[i];
  129. if ( !known.IsObsolete() && IsAwareOf( known ) )
  130. {
  131. if ( filter.IsAllowed( known.GetEntity() ) )
  132. {
  133. Vector to = known.GetLastKnownPosition() - myPos;
  134. float rangeSq = to.LengthSqr();
  135. if ( rangeSq < closeRange )
  136. {
  137. close = &known;
  138. closeRange = rangeSq;
  139. }
  140. }
  141. }
  142. }
  143. return close;
  144. }
  145. //------------------------------------------------------------------------------------------
  146. /**
  147. * Given an entity, return our known version of it (or NULL if we don't know of it)
  148. */
  149. const CKnownEntity *IVision::GetKnown( const CBaseEntity *entity ) const
  150. {
  151. if ( entity == NULL )
  152. return NULL;
  153. for( int i=0; i < m_knownEntityVector.Count(); ++i )
  154. {
  155. const CKnownEntity &known = m_knownEntityVector[i];
  156. if ( known.GetEntity() && known.GetEntity()->entindex() == entity->entindex() && !known.IsObsolete() )
  157. {
  158. return &known;
  159. }
  160. }
  161. return NULL;
  162. }
  163. //------------------------------------------------------------------------------------------
  164. /**
  165. * Introduce a known entity into the system. Its position is assumed to be known
  166. * and will be updated, and it is assumed to not yet have been seen by us, allowing for learning
  167. * of known entities by being told about them, hearing them, etc.
  168. */
  169. void IVision::AddKnownEntity( CBaseEntity *entity )
  170. {
  171. if ( entity == NULL || entity->IsWorld() )
  172. {
  173. // the world is not an entity we can deal with
  174. return;
  175. }
  176. CKnownEntity known( entity );
  177. // only add it if we don't already know of it
  178. if ( m_knownEntityVector.Find( known ) == m_knownEntityVector.InvalidIndex() )
  179. {
  180. m_knownEntityVector.AddToTail( known );
  181. }
  182. }
  183. //------------------------------------------------------------------------------------------
  184. // Remove the given entity from our awareness (whether we know if it or not)
  185. // Useful if we've moved to where we last saw the entity, but it's not there any longer.
  186. void IVision::ForgetEntity( CBaseEntity *forgetMe )
  187. {
  188. if ( !forgetMe )
  189. return;
  190. FOR_EACH_VEC( m_knownEntityVector, it )
  191. {
  192. const CKnownEntity &known = m_knownEntityVector[ it ];
  193. if ( known.GetEntity() && known.GetEntity()->entindex() == forgetMe->entindex() )
  194. {
  195. m_knownEntityVector.FastRemove( it );
  196. return;
  197. }
  198. }
  199. }
  200. //------------------------------------------------------------------------------------------
  201. void IVision::ForgetAllKnownEntities( void )
  202. {
  203. m_knownEntityVector.RemoveAll();
  204. }
  205. //------------------------------------------------------------------------------------------
  206. /**
  207. * Return the number of entity on the given team known to us closer than rangeLimit
  208. */
  209. int IVision::GetKnownCount( int team, bool onlyVisible, float rangeLimit ) const
  210. {
  211. int count = 0;
  212. FOR_EACH_VEC( m_knownEntityVector, it )
  213. {
  214. const CKnownEntity &known = m_knownEntityVector[ it ];
  215. if ( !known.IsObsolete() && IsAwareOf( known ) )
  216. {
  217. if ( team == TEAM_ANY || known.GetEntity()->GetTeamNumber() == team )
  218. {
  219. if ( !onlyVisible || known.IsVisibleRecently() )
  220. {
  221. if ( rangeLimit < 0.0f || GetBot()->IsRangeLessThan( known.GetLastKnownPosition(), rangeLimit ) )
  222. {
  223. ++count;
  224. }
  225. }
  226. }
  227. }
  228. }
  229. return count;
  230. }
  231. //------------------------------------------------------------------------------------------
  232. class PopulateVisibleVector
  233. {
  234. public:
  235. PopulateVisibleVector( CUtlVector< CBaseEntity * > *potentiallyVisible )
  236. {
  237. m_potentiallyVisible = potentiallyVisible;
  238. }
  239. bool operator() ( CBaseEntity *actor )
  240. {
  241. m_potentiallyVisible->AddToTail( actor );
  242. return true;
  243. }
  244. CUtlVector< CBaseEntity * > *m_potentiallyVisible;
  245. };
  246. //------------------------------------------------------------------------------------------
  247. /**
  248. * Populate "potentiallyVisible" with the set of all entities we could potentially see.
  249. * Entities in this set will be tested for visibility/recognition in IVision::Update()
  250. */
  251. void IVision::CollectPotentiallyVisibleEntities( CUtlVector< CBaseEntity * > *potentiallyVisible )
  252. {
  253. potentiallyVisible->RemoveAll();
  254. // by default, only consider players and other bots as potentially visible
  255. PopulateVisibleVector populate( potentiallyVisible );
  256. ForEachActor( populate );
  257. }
  258. //------------------------------------------------------------------------------------------
  259. class CollectVisible
  260. {
  261. public:
  262. CollectVisible( IVision *vision )
  263. {
  264. m_vision = vision;
  265. }
  266. bool operator() ( CBaseEntity *entity )
  267. {
  268. if ( entity &&
  269. !m_vision->IsIgnored( entity ) &&
  270. entity->IsAlive() &&
  271. entity != m_vision->GetBot()->GetEntity() &&
  272. m_vision->IsAbleToSee( entity, IVision::USE_FOV ) )
  273. {
  274. m_recognized.AddToTail( entity );
  275. }
  276. return true;
  277. }
  278. bool Contains( CBaseEntity *entity ) const
  279. {
  280. for( int i=0; i < m_recognized.Count(); ++i )
  281. {
  282. if ( entity->entindex() == m_recognized[ i ]->entindex() )
  283. {
  284. return true;
  285. }
  286. }
  287. return false;
  288. }
  289. IVision *m_vision;
  290. CUtlVector< CBaseEntity * > m_recognized;
  291. };
  292. //------------------------------------------------------------------------------------------
  293. void IVision::UpdateKnownEntities( void )
  294. {
  295. VPROF_BUDGET( "IVision::UpdateKnownEntities", "NextBot" );
  296. // construct set of potentially visible objects
  297. CUtlVector< CBaseEntity * > potentiallyVisible;
  298. CollectPotentiallyVisibleEntities( &potentiallyVisible );
  299. // collect set of visible and recognized entities at this moment
  300. CollectVisible visibleNow( this );
  301. FOR_EACH_VEC( potentiallyVisible, pit )
  302. {
  303. VPROF_BUDGET( "IVision::UpdateKnownEntities( collect visible )", "NextBot" );
  304. if ( visibleNow( potentiallyVisible[ pit ] ) == false )
  305. break;
  306. }
  307. // update known set with new data
  308. { VPROF_BUDGET( "IVision::UpdateKnownEntities( update status )", "NextBot" );
  309. int i;
  310. for( i=0; i < m_knownEntityVector.Count(); ++i )
  311. {
  312. CKnownEntity &known = m_knownEntityVector[i];
  313. // clear out obsolete knowledge
  314. if ( known.GetEntity() == NULL || known.IsObsolete() )
  315. {
  316. m_knownEntityVector.Remove( i );
  317. --i;
  318. continue;
  319. }
  320. if ( visibleNow.Contains( known.GetEntity() ) )
  321. {
  322. // this visible entity was already known (but perhaps not visible until now)
  323. known.UpdatePosition();
  324. known.UpdateVisibilityStatus( true );
  325. // has our reaction time just elapsed?
  326. if ( gpGlobals->curtime - known.GetTimeWhenBecameVisible() >= GetMinRecognizeTime() &&
  327. m_lastVisionUpdateTimestamp - known.GetTimeWhenBecameVisible() < GetMinRecognizeTime() )
  328. {
  329. if ( GetBot()->IsDebugging( NEXTBOT_VISION ) )
  330. {
  331. ConColorMsg( Color( 0, 255, 0, 255 ), "%3.2f: %s caught sight of %s(#%d)\n",
  332. gpGlobals->curtime,
  333. GetBot()->GetDebugIdentifier(),
  334. known.GetEntity()->GetClassname(),
  335. known.GetEntity()->entindex() );
  336. NDebugOverlay::Line( GetBot()->GetBodyInterface()->GetEyePosition(), known.GetLastKnownPosition(), 255, 255, 0, false, 0.2f );
  337. }
  338. GetBot()->OnSight( known.GetEntity() );
  339. }
  340. // restart 'not seen' timer
  341. m_notVisibleTimer[ known.GetEntity()->GetTeamNumber() ].Start();
  342. }
  343. else // known entity is not currently visible
  344. {
  345. if ( known.IsVisibleInFOVNow() )
  346. {
  347. // previously known and visible entity is now no longer visible
  348. known.UpdateVisibilityStatus( false );
  349. // lost sight of this entity
  350. if ( GetBot()->IsDebugging( NEXTBOT_VISION ) )
  351. {
  352. ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Lost sight of %s(#%d)\n",
  353. gpGlobals->curtime,
  354. GetBot()->GetDebugIdentifier(),
  355. known.GetEntity()->GetClassname(),
  356. known.GetEntity()->entindex() );
  357. }
  358. GetBot()->OnLostSight( known.GetEntity() );
  359. }
  360. if ( !known.HasLastKnownPositionBeenSeen() )
  361. {
  362. // can we see the entity's last know position?
  363. if ( IsAbleToSee( known.GetLastKnownPosition(), IVision::USE_FOV ) )
  364. {
  365. known.MarkLastKnownPositionAsSeen();
  366. }
  367. }
  368. }
  369. }
  370. }
  371. // check for new recognizes that were not in the known set
  372. { VPROF_BUDGET( "IVision::UpdateKnownEntities( new recognizes )", "NextBot" );
  373. int i, j;
  374. for( i=0; i < visibleNow.m_recognized.Count(); ++i )
  375. {
  376. for( j=0; j < m_knownEntityVector.Count(); ++j )
  377. {
  378. if ( visibleNow.m_recognized[i] == m_knownEntityVector[j].GetEntity() )
  379. {
  380. break;
  381. }
  382. }
  383. if ( j == m_knownEntityVector.Count() )
  384. {
  385. // recognized a previously unknown entity (emit OnSight() event after reaction time has passed)
  386. CKnownEntity known( visibleNow.m_recognized[i] );
  387. known.UpdatePosition();
  388. known.UpdateVisibilityStatus( true );
  389. m_knownEntityVector.AddToTail( known );
  390. }
  391. }
  392. }
  393. // debugging
  394. if ( nb_debug_known_entities.GetBool() )
  395. {
  396. CBasePlayer *watcher = UTIL_GetListenServerHost();
  397. if ( watcher )
  398. {
  399. CBaseEntity *subject = watcher->GetObserverTarget();
  400. if ( subject && GetBot()->IsSelf( subject ) )
  401. {
  402. CUtlVector< CKnownEntity > knownVector;
  403. CollectKnownEntities( &knownVector );
  404. for( int i=0; i < knownVector.Count(); ++i )
  405. {
  406. CKnownEntity &known = knownVector[i];
  407. if ( GetBot()->IsFriend( known.GetEntity() ) )
  408. {
  409. if ( IsAwareOf( known ) )
  410. {
  411. if ( known.IsVisibleInFOVNow() )
  412. NDebugOverlay::HorzArrow( GetBot()->GetEntity()->GetAbsOrigin(), known.GetLastKnownPosition(), 5.0f, 0, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  413. else
  414. NDebugOverlay::HorzArrow( GetBot()->GetEntity()->GetAbsOrigin(), known.GetLastKnownPosition(), 2.0f, 0, 100, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  415. }
  416. else
  417. {
  418. NDebugOverlay::HorzArrow( GetBot()->GetEntity()->GetAbsOrigin(), known.GetLastKnownPosition(), 1.0f, 0, 100, 0, 128, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  419. }
  420. }
  421. else
  422. {
  423. if ( IsAwareOf( known ) )
  424. {
  425. if ( known.IsVisibleInFOVNow() )
  426. NDebugOverlay::HorzArrow( GetBot()->GetEntity()->GetAbsOrigin(), known.GetLastKnownPosition(), 5.0f, 255, 0, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  427. else
  428. NDebugOverlay::HorzArrow( GetBot()->GetEntity()->GetAbsOrigin(), known.GetLastKnownPosition(), 2.0f, 100, 0, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  429. }
  430. else
  431. {
  432. NDebugOverlay::HorzArrow( GetBot()->GetEntity()->GetAbsOrigin(), known.GetLastKnownPosition(), 1.0f, 100, 0, 0, 128, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  433. }
  434. }
  435. }
  436. }
  437. }
  438. }
  439. }
  440. //------------------------------------------------------------------------------------------
  441. /**
  442. * Update internal state
  443. */
  444. void IVision::Update( void )
  445. {
  446. VPROF_BUDGET( "IVision::Update", "NextBotExpensive" );
  447. /* This adds significantly to bot's reaction times
  448. // throttle update rate
  449. if ( !m_scanTimer.IsElapsed() )
  450. {
  451. return;
  452. }
  453. m_scanTimer.Start( 0.5f * GetMinRecognizeTime() );
  454. */
  455. if ( nb_blind.GetBool() )
  456. {
  457. m_knownEntityVector.RemoveAll();
  458. return;
  459. }
  460. UpdateKnownEntities();
  461. m_lastVisionUpdateTimestamp = gpGlobals->curtime;
  462. }
  463. //------------------------------------------------------------------------------------------
  464. bool IVision::IsAbleToSee( CBaseEntity *subject, FieldOfViewCheckType checkFOV, Vector *visibleSpot ) const
  465. {
  466. VPROF_BUDGET( "IVision::IsAbleToSee", "NextBotExpensive" );
  467. if ( GetBot()->IsRangeGreaterThan( subject, GetMaxVisionRange() ) )
  468. {
  469. return false;
  470. }
  471. if ( GetBot()->GetEntity()->IsHiddenByFog( subject ) )
  472. {
  473. // lost in the fog
  474. return false;
  475. }
  476. if ( checkFOV == USE_FOV && !IsInFieldOfView( subject ) )
  477. {
  478. return false;
  479. }
  480. CBaseCombatCharacter *combat = subject->MyCombatCharacterPointer();
  481. if ( combat )
  482. {
  483. CNavArea *subjectArea = combat->GetLastKnownArea();
  484. CNavArea *myArea = GetBot()->GetEntity()->GetLastKnownArea();
  485. if ( myArea && subjectArea )
  486. {
  487. if ( !myArea->IsPotentiallyVisible( subjectArea ) )
  488. {
  489. // subject is not potentially visible, skip the expensive raycast
  490. return false;
  491. }
  492. }
  493. }
  494. // do actual line-of-sight trace
  495. if ( !IsLineOfSightClearToEntity( subject ) )
  496. {
  497. return false;
  498. }
  499. return IsVisibleEntityNoticed( subject );
  500. }
  501. //------------------------------------------------------------------------------------------
  502. bool IVision::IsAbleToSee( const Vector &pos, FieldOfViewCheckType checkFOV ) const
  503. {
  504. VPROF_BUDGET( "IVision::IsAbleToSee", "NextBotExpensive" );
  505. if ( GetBot()->IsRangeGreaterThan( pos, GetMaxVisionRange() ) )
  506. {
  507. return false;
  508. }
  509. if ( GetBot()->GetEntity()->IsHiddenByFog( pos ) )
  510. {
  511. // lost in the fog
  512. return false;
  513. }
  514. if ( checkFOV == USE_FOV && !IsInFieldOfView( pos ) )
  515. {
  516. return false;
  517. }
  518. // do actual line-of-sight trace
  519. return IsLineOfSightClear( pos );
  520. }
  521. //------------------------------------------------------------------------------------------
  522. /**
  523. * Angle given in degrees
  524. */
  525. void IVision::SetFieldOfView( float horizAngle )
  526. {
  527. m_FOV = horizAngle;
  528. m_cosHalfFOV = cos( 0.5f * m_FOV * M_PI / 180.0f );
  529. }
  530. //------------------------------------------------------------------------------------------
  531. bool IVision::IsInFieldOfView( const Vector &pos ) const
  532. {
  533. #ifdef CHECK_OLD_CODE_AGAINST_NEW
  534. bool bCheck = PointWithinViewAngle( GetBot()->GetBodyInterface()->GetEyePosition(), pos, GetBot()->GetBodyInterface()->GetViewVector(), m_cosHalfFOV );
  535. Vector to = pos - GetBot()->GetBodyInterface()->GetEyePosition();
  536. to.NormalizeInPlace();
  537. float cosDiff = DotProduct( GetBot()->GetBodyInterface()->GetViewVector(), to );
  538. if ( ( cosDiff > m_cosHalfFOV ) != bCheck )
  539. {
  540. Assert(0);
  541. bool bCheck2 =
  542. PointWithinViewAngle( GetBot()->GetBodyInterface()->GetEyePosition(), pos, GetBot()->GetBodyInterface()->GetViewVector(), m_cosHalfFOV );
  543. }
  544. return ( cosDiff > m_cosHalfFOV );
  545. #else
  546. return PointWithinViewAngle( GetBot()->GetBodyInterface()->GetEyePosition(), pos, GetBot()->GetBodyInterface()->GetViewVector(), m_cosHalfFOV );
  547. #endif
  548. return true;
  549. }
  550. //------------------------------------------------------------------------------------------
  551. bool IVision::IsInFieldOfView( CBaseEntity *subject ) const
  552. {
  553. /// @todo check more points
  554. if ( IsInFieldOfView( subject->WorldSpaceCenter() ) )
  555. {
  556. return true;
  557. }
  558. return IsInFieldOfView( subject->EyePosition() );
  559. }
  560. //------------------------------------------------------------------------------------------
  561. /**
  562. * Return true if the ray to the given point is unobstructed
  563. */
  564. bool IVision::IsLineOfSightClear( const Vector &pos ) const
  565. {
  566. VPROF_BUDGET( "IVision::IsLineOfSightClear", "NextBot" );
  567. VPROF_INCREMENT_COUNTER( "IVision::IsLineOfSightClear", 1 );
  568. trace_t result;
  569. NextBotVisionTraceFilter filter( GetBot()->GetEntity(), COLLISION_GROUP_NONE );
  570. UTIL_TraceLine( GetBot()->GetBodyInterface()->GetEyePosition(), pos, MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &filter, &result );
  571. return ( result.fraction >= 1.0f && !result.startsolid );
  572. }
  573. //------------------------------------------------------------------------------------------
  574. bool IVision::IsLineOfSightClearToEntity( const CBaseEntity *subject, Vector *visibleSpot ) const
  575. {
  576. #ifdef TERROR
  577. // TODO: Integration querycache & its dependencies
  578. VPROF_INCREMENT_COUNTER( "IVision::IsLineOfSightClearToEntity", 1 );
  579. VPROF_BUDGET( "IVision::IsLineOfSightClearToEntity", "NextBotSpiky" );
  580. bool bClear = IsLineOfSightBetweenTwoEntitiesClear( GetBot()->GetBodyInterface()->GetEntity(), EOFFSET_MODE_EYEPOSITION,
  581. subject, EOFFSET_MODE_WORLDSPACE_CENTER,
  582. subject, COLLISION_GROUP_NONE,
  583. MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, VisionTraceFilterFunction, 1.0 );
  584. #ifdef USE_NON_CACHE_QUERY
  585. trace_t result;
  586. NextBotTraceFilterIgnoreActors filter( subject, COLLISION_GROUP_NONE );
  587. UTIL_TraceLine( GetBot()->GetBodyInterface()->GetEyePosition(), subject->WorldSpaceCenter(), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &filter, &result );
  588. Assert( result.DidHit() != bClear );
  589. if ( subject->IsPlayer() && ! bClear )
  590. {
  591. UTIL_TraceLine( GetBot()->GetBodyInterface()->GetEyePosition(), subject->EyePosition(), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &filter, &result );
  592. bClear = IsLineOfSightBetweenTwoEntitiesClear( GetBot()->GetEntity(),
  593. EOFFSET_MODE_EYEPOSITION,
  594. subject, EOFFSET_MODE_EYEPOSITION,
  595. subject, COLLISION_GROUP_NONE,
  596. MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE,
  597. IgnoreActorsTraceFilterFunction, 1.0 );
  598. // this WILL assert - the query interface happens at a different time, and has hysteresis.
  599. Assert( result.DidHit() != bClear );
  600. }
  601. #endif
  602. return bClear;
  603. #else
  604. // TODO: Use plain-old traces until querycache/etc gets integrated
  605. VPROF_BUDGET( "IVision::IsLineOfSightClearToEntity", "NextBot" );
  606. trace_t result;
  607. NextBotTraceFilterIgnoreActors filter( subject, COLLISION_GROUP_NONE );
  608. UTIL_TraceLine( GetBot()->GetBodyInterface()->GetEyePosition(), subject->WorldSpaceCenter(), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &filter, &result );
  609. if ( result.DidHit() )
  610. {
  611. UTIL_TraceLine( GetBot()->GetBodyInterface()->GetEyePosition(), subject->EyePosition(), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &filter, &result );
  612. if ( result.DidHit() )
  613. {
  614. UTIL_TraceLine( GetBot()->GetBodyInterface()->GetEyePosition(), subject->GetAbsOrigin(), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &filter, &result );
  615. }
  616. }
  617. if ( visibleSpot )
  618. {
  619. *visibleSpot = result.endpos;
  620. }
  621. return ( result.fraction >= 1.0f && !result.startsolid );
  622. #endif
  623. }
  624. //------------------------------------------------------------------------------------------
  625. /**
  626. * Are we looking directly at the given position
  627. */
  628. bool IVision::IsLookingAt( const Vector &pos, float cosTolerance ) const
  629. {
  630. Vector to = pos - GetBot()->GetBodyInterface()->GetEyePosition();
  631. to.NormalizeInPlace();
  632. Vector forward;
  633. AngleVectors( GetBot()->GetEntity()->EyeAngles(), &forward );
  634. return DotProduct( to, forward ) > cosTolerance;
  635. }
  636. //------------------------------------------------------------------------------------------
  637. /**
  638. * Are we looking directly at the given actor
  639. */
  640. bool IVision::IsLookingAt( const CBaseCombatCharacter *actor, float cosTolerance ) const
  641. {
  642. return IsLookingAt( actor->EyePosition(), cosTolerance );
  643. }