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.

747 lines
23 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ammodef.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. //-----------------------------------------------------------------------------
  12. // Purpose: Switches to the best weapon that is also better than the given weapon.
  13. // Input : pCurrent - The current weapon used by the player.
  14. // Output : Returns true if the weapon was switched, false if there was no better
  15. // weapon to switch to.
  16. //-----------------------------------------------------------------------------
  17. bool CBaseCombatCharacter::SwitchToNextBestWeapon(CBaseCombatWeapon *pCurrent)
  18. {
  19. CBaseCombatWeapon *pNewWeapon = g_pGameRules->GetNextBestWeapon(this, pCurrent);
  20. if ( ( pNewWeapon != NULL ) && ( pNewWeapon != pCurrent ) )
  21. {
  22. return Weapon_Switch( pNewWeapon );
  23. }
  24. return false;
  25. }
  26. //-----------------------------------------------------------------------------
  27. // Purpose: Switches to the given weapon (providing it has ammo)
  28. // Input :
  29. // Output : true is switch succeeded
  30. //-----------------------------------------------------------------------------
  31. bool CBaseCombatCharacter::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex /*=0*/ )
  32. {
  33. if ( pWeapon == NULL )
  34. return false;
  35. // Already have it out?
  36. if ( m_hActiveWeapon.Get() == pWeapon )
  37. {
  38. if ( !m_hActiveWeapon->IsWeaponVisible() || m_hActiveWeapon->IsHolstered() )
  39. return m_hActiveWeapon->Deploy( );
  40. return false;
  41. }
  42. if (!Weapon_CanSwitchTo(pWeapon))
  43. {
  44. return false;
  45. }
  46. if ( m_hActiveWeapon )
  47. {
  48. if ( !m_hActiveWeapon->Holster( pWeapon ) )
  49. return false;
  50. }
  51. m_hActiveWeapon = pWeapon;
  52. return pWeapon->Deploy( );
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Returns whether or not we can switch to the given weapon.
  56. // Input : pWeapon -
  57. //-----------------------------------------------------------------------------
  58. bool CBaseCombatCharacter::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
  59. {
  60. if (IsPlayer())
  61. {
  62. CBasePlayer *pPlayer = (CBasePlayer *)this;
  63. #if !defined( CLIENT_DLL )
  64. IServerVehicle *pVehicle = pPlayer->GetVehicle();
  65. #else
  66. IClientVehicle *pVehicle = pPlayer->GetVehicle();
  67. #endif
  68. if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle())
  69. return false;
  70. }
  71. if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) )
  72. return false;
  73. if ( !pWeapon->CanDeploy() )
  74. return false;
  75. if ( m_hActiveWeapon )
  76. {
  77. if ( !m_hActiveWeapon->CanHolster() && !pWeapon->ForceWeaponSwitch() )
  78. return false;
  79. if ( IsPlayer() )
  80. {
  81. CBasePlayer *pPlayer = (CBasePlayer *)this;
  82. // check if active weapon force the last weapon to switch
  83. if ( m_hActiveWeapon->ForceWeaponSwitch() )
  84. {
  85. // last weapon wasn't allowed to switch, don't allow to switch to new weapon
  86. CBaseCombatWeapon *pLastWeapon = pPlayer->GetLastWeapon();
  87. if ( pLastWeapon && pWeapon != pLastWeapon && !pLastWeapon->CanHolster() && !pWeapon->ForceWeaponSwitch() )
  88. {
  89. return false;
  90. }
  91. }
  92. }
  93. }
  94. return true;
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. // Output : CBaseCombatWeapon
  99. //-----------------------------------------------------------------------------
  100. CBaseCombatWeapon *CBaseCombatCharacter::GetActiveWeapon() const
  101. {
  102. return m_hActiveWeapon;
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose:
  106. // Input : iCount -
  107. // iAmmoIndex -
  108. //-----------------------------------------------------------------------------
  109. void CBaseCombatCharacter::RemoveAmmo( int iCount, int iAmmoIndex )
  110. {
  111. if (iCount <= 0)
  112. return;
  113. // Infinite ammo?
  114. if ( GetAmmoDef()->MaxCarry( iAmmoIndex ) == INFINITE_AMMO )
  115. return;
  116. // Ammo pickup sound
  117. m_iAmmo.Set( iAmmoIndex, MAX( m_iAmmo[iAmmoIndex] - iCount, 0 ) );
  118. }
  119. void CBaseCombatCharacter::RemoveAmmo( int iCount, const char *szName )
  120. {
  121. RemoveAmmo( iCount, GetAmmoDef()->Index(szName) );
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Purpose:
  125. //-----------------------------------------------------------------------------
  126. void CBaseCombatCharacter::RemoveAllAmmo( )
  127. {
  128. for ( int i = 0; i < MAX_AMMO_SLOTS; i++ )
  129. {
  130. m_iAmmo.Set( i, 0 );
  131. }
  132. }
  133. //-----------------------------------------------------------------------------
  134. // FIXME: This is a sort of hack back-door only used by physgun!
  135. //-----------------------------------------------------------------------------
  136. void CBaseCombatCharacter::SetAmmoCount( int iCount, int iAmmoIndex )
  137. {
  138. // NOTE: No sound, no max check! Seems pretty bogus to me!
  139. m_iAmmo.Set( iAmmoIndex, iCount );
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Purpose: Returns the amount of ammunition of a particular type owned
  143. // owned by the character
  144. // Input : Ammo Index
  145. // Output : The amount of ammo
  146. //-----------------------------------------------------------------------------
  147. int CBaseCombatCharacter::GetAmmoCount( int iAmmoIndex ) const
  148. {
  149. if ( iAmmoIndex == -1 )
  150. return 0;
  151. // Infinite ammo?
  152. if ( GetAmmoDef()->MaxCarry( iAmmoIndex ) == INFINITE_AMMO )
  153. return 999;
  154. return m_iAmmo[ iAmmoIndex ];
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Returns the amount of ammunition of the specified type the character's carrying
  158. //-----------------------------------------------------------------------------
  159. int CBaseCombatCharacter::GetAmmoCount( char *szName ) const
  160. {
  161. return GetAmmoCount( GetAmmoDef()->Index(szName) );
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose: Returns weapon if already owns a weapon of this class
  165. //-----------------------------------------------------------------------------
  166. CBaseCombatWeapon* CBaseCombatCharacter::Weapon_OwnsThisType( const char *pszWeapon, int iSubType ) const
  167. {
  168. // Check for duplicates
  169. for (int i=0;i<MAX_WEAPONS;i++)
  170. {
  171. if ( m_hMyWeapons[i].Get() && FClassnameIs( m_hMyWeapons[i], pszWeapon ) )
  172. {
  173. // Make sure it matches the subtype
  174. if ( m_hMyWeapons[i]->GetSubType() == iSubType )
  175. return m_hMyWeapons[i];
  176. }
  177. }
  178. return NULL;
  179. }
  180. int CBaseCombatCharacter::BloodColor()
  181. {
  182. return m_bloodColor;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Blood color (see BLOOD_COLOR_* macros in baseentity.h)
  186. //-----------------------------------------------------------------------------
  187. void CBaseCombatCharacter::SetBloodColor( int nBloodColor )
  188. {
  189. m_bloodColor = nBloodColor;
  190. }
  191. //-----------------------------------------------------------------------------
  192. /**
  193. The main visibility check. Checks all the entity specific reasons that could
  194. make IsVisible fail. Then checks points in space to get environmental reasons.
  195. This is LOS, plus invisibility and fog and smoke and such.
  196. */
  197. enum VisCacheResult_t
  198. {
  199. VISCACHE_UNKNOWN = 0,
  200. VISCACHE_IS_VISIBLE,
  201. VISCACHE_IS_NOT_VISIBLE,
  202. };
  203. enum
  204. {
  205. VIS_CACHE_INVALID = 0x80000000
  206. };
  207. #define VIS_CACHE_ENTRY_LIFE .090f
  208. class CCombatCharVisCache : public CAutoGameSystemPerFrame
  209. {
  210. public:
  211. virtual void FrameUpdatePreEntityThink();
  212. virtual void LevelShutdownPreEntity();
  213. int LookupVisibility( const CBaseCombatCharacter *pChar1, CBaseCombatCharacter *pChar2 );
  214. VisCacheResult_t HasVisibility( int iCache ) const;
  215. void RegisterVisibility( int iCache, bool bChar1SeesChar2, bool bChar2SeesChar1 );
  216. private:
  217. struct VisCacheEntry_t
  218. {
  219. CHandle< CBaseCombatCharacter > m_hEntity1;
  220. CHandle< CBaseCombatCharacter > m_hEntity2;
  221. float m_flTime;
  222. bool m_bEntity1CanSeeEntity2;
  223. bool m_bEntity2CanSeeEntity1;
  224. };
  225. class CVisCacheEntryLess
  226. {
  227. public:
  228. CVisCacheEntryLess( int ) {}
  229. bool operator!() const { return false; }
  230. bool operator()( const VisCacheEntry_t &lhs, const VisCacheEntry_t &rhs ) const
  231. {
  232. return ( memcmp( &lhs, &rhs, offsetof( VisCacheEntry_t, m_flTime ) ) < 0 );
  233. }
  234. };
  235. CUtlRBTree< VisCacheEntry_t, unsigned short, CVisCacheEntryLess > m_VisCache;
  236. mutable int m_nTestCount;
  237. mutable int m_nHitCount;
  238. };
  239. void CCombatCharVisCache::FrameUpdatePreEntityThink()
  240. {
  241. // Msg( "test: %d/%d\n", m_nHitCount, m_nTestCount );
  242. // Lazy retirement of vis cache
  243. // NOTE: 256 was chosen heuristically based on a playthrough where 200
  244. // was the max # in the viscache where nothing could be retired.
  245. if ( m_VisCache.Count() < 256 )
  246. return;
  247. int nMaxIndex = m_VisCache.MaxElement() - 1;
  248. for ( int i = 0; i < 8; ++i )
  249. {
  250. int n = RandomInt( 0, nMaxIndex );
  251. if ( !m_VisCache.IsValidIndex( n ) )
  252. continue;
  253. const VisCacheEntry_t &entry = m_VisCache[n];
  254. if ( !entry.m_hEntity1.IsValid() || !entry.m_hEntity2.IsValid() || ( gpGlobals->curtime - entry.m_flTime > 10.0f ) )
  255. {
  256. m_VisCache.RemoveAt( n );
  257. }
  258. }
  259. }
  260. void CCombatCharVisCache::LevelShutdownPreEntity()
  261. {
  262. m_VisCache.Purge();
  263. }
  264. int CCombatCharVisCache::LookupVisibility( const CBaseCombatCharacter *pChar1, CBaseCombatCharacter *pChar2 )
  265. {
  266. VisCacheEntry_t cacheEntry;
  267. if ( pChar1 < pChar2 )
  268. {
  269. cacheEntry.m_hEntity1 = pChar1;
  270. cacheEntry.m_hEntity2 = pChar2;
  271. }
  272. else
  273. {
  274. cacheEntry.m_hEntity1 = pChar2;
  275. cacheEntry.m_hEntity2 = pChar1;
  276. }
  277. int iCache = m_VisCache.Find( cacheEntry );
  278. if ( iCache == m_VisCache.InvalidIndex() )
  279. {
  280. if ( m_VisCache.Count() == m_VisCache.InvalidIndex() )
  281. return VIS_CACHE_INVALID;
  282. iCache = m_VisCache.Insert( cacheEntry );
  283. m_VisCache[iCache].m_flTime = gpGlobals->curtime - 2.0f * VIS_CACHE_ENTRY_LIFE;
  284. }
  285. return ( pChar1 < pChar2 ) ? iCache : - iCache - 1;
  286. }
  287. VisCacheResult_t CCombatCharVisCache::HasVisibility( int iCache ) const
  288. {
  289. if ( iCache == VIS_CACHE_INVALID )
  290. return VISCACHE_UNKNOWN;
  291. m_nTestCount++;
  292. bool bReverse = ( iCache < 0 );
  293. if ( bReverse )
  294. {
  295. iCache = - iCache - 1;
  296. }
  297. const VisCacheEntry_t &entry = m_VisCache[iCache];
  298. if ( gpGlobals->curtime - entry.m_flTime > VIS_CACHE_ENTRY_LIFE )
  299. return VISCACHE_UNKNOWN;
  300. m_nHitCount++;
  301. bool bIsVisible = !bReverse ? entry.m_bEntity1CanSeeEntity2 : entry.m_bEntity2CanSeeEntity1;
  302. return bIsVisible ? VISCACHE_IS_VISIBLE : VISCACHE_IS_NOT_VISIBLE;
  303. }
  304. void CCombatCharVisCache::RegisterVisibility( int iCache, bool bEntity1CanSeeEntity2, bool bEntity2CanSeeEntity1 )
  305. {
  306. if ( iCache == VIS_CACHE_INVALID )
  307. return;
  308. bool bReverse = ( iCache < 0 );
  309. if ( bReverse )
  310. {
  311. iCache = - iCache - 1;
  312. }
  313. VisCacheEntry_t &entry = m_VisCache[iCache];
  314. entry.m_flTime = gpGlobals->curtime;
  315. if ( !bReverse )
  316. {
  317. entry.m_bEntity1CanSeeEntity2 = bEntity1CanSeeEntity2;
  318. entry.m_bEntity2CanSeeEntity1 = bEntity2CanSeeEntity1;
  319. }
  320. else
  321. {
  322. entry.m_bEntity1CanSeeEntity2 = bEntity2CanSeeEntity1;
  323. entry.m_bEntity2CanSeeEntity1 = bEntity1CanSeeEntity2;
  324. }
  325. }
  326. static CCombatCharVisCache s_CombatCharVisCache;
  327. bool CBaseCombatCharacter::IsAbleToSee( const CBaseEntity *pEntity, FieldOfViewCheckType checkFOV )
  328. {
  329. CBaseCombatCharacter *pBCC = const_cast<CBaseEntity *>( pEntity )->MyCombatCharacterPointer();
  330. if ( pBCC )
  331. return IsAbleToSee( pBCC, checkFOV );
  332. // Test this every time; it's cheap.
  333. Vector vecEyePosition = EyePosition();
  334. Vector vecTargetPosition = pEntity->WorldSpaceCenter();
  335. #ifdef GAME_DLL
  336. Vector vecEyeToTarget;
  337. VectorSubtract( vecTargetPosition, vecEyePosition, vecEyeToTarget );
  338. float flDistToOther = VectorNormalize( vecEyeToTarget );
  339. // We can't see because they are too far in the fog
  340. if ( IsHiddenByFog( flDistToOther ) )
  341. return false;
  342. #endif
  343. if ( !ComputeLOS( vecEyePosition, vecTargetPosition ) )
  344. return false;
  345. #if defined(GAME_DLL) && defined(TERROR)
  346. if ( flDistToOther > NavObscureRange.GetFloat() )
  347. {
  348. const float flMaxDistance = 100.0f;
  349. TerrorNavArea *pTargetArea = static_cast< TerrorNavArea* >( TheNavMesh->GetNearestNavArea( vecTargetPosition, false, flMaxDistance ) );
  350. if ( !pTargetArea || pTargetArea->HasSpawnAttributes( TerrorNavArea::SPAWN_OBSCURED ) )
  351. return false;
  352. if ( ComputeTargetIsInDarkness( vecEyePosition, pTargetArea, vecTargetPosition ) )
  353. return false;
  354. }
  355. #endif
  356. return ( checkFOV != USE_FOV || IsInFieldOfView( vecTargetPosition ) );
  357. }
  358. static void ComputeSeeTestPosition( Vector *pEyePosition, CBaseCombatCharacter *pBCC )
  359. {
  360. #if defined(GAME_DLL) && defined(TERROR)
  361. if ( pBCC->IsPlayer() )
  362. {
  363. CTerrorPlayer *pPlayer = ToTerrorPlayer( pBCC );
  364. *pEyePosition = !pPlayer->IsDead() ? pPlayer->EyePosition() : pPlayer->GetDeathPosition();
  365. }
  366. else
  367. #endif
  368. {
  369. *pEyePosition = pBCC->EyePosition();
  370. }
  371. }
  372. bool CBaseCombatCharacter::IsAbleToSee( CBaseCombatCharacter *pBCC, FieldOfViewCheckType checkFOV )
  373. {
  374. Vector vecEyePosition, vecOtherEyePosition;
  375. ComputeSeeTestPosition( &vecEyePosition, this );
  376. ComputeSeeTestPosition( &vecOtherEyePosition, pBCC );
  377. #ifdef GAME_DLL
  378. Vector vecEyeToTarget;
  379. VectorSubtract( vecOtherEyePosition, vecEyePosition, vecEyeToTarget );
  380. float flDistToOther = VectorNormalize( vecEyeToTarget );
  381. // Test this every time; it's cheap.
  382. // We can't see because they are too far in the fog
  383. if ( IsHiddenByFog( flDistToOther ) )
  384. return false;
  385. #ifdef TERROR
  386. // Check this every time also, it's cheap; check to see if the enemy is in an obscured area.
  387. bool bIsInNavObscureRange = ( flDistToOther > NavObscureRange.GetFloat() );
  388. if ( bIsInNavObscureRange )
  389. {
  390. TerrorNavArea *pOtherNavArea = static_cast< TerrorNavArea* >( pBCC->GetLastKnownArea() );
  391. if ( !pOtherNavArea || pOtherNavArea->HasSpawnAttributes( TerrorNavArea::SPAWN_OBSCURED ) )
  392. return false;
  393. }
  394. #endif // TERROR
  395. #endif
  396. // Check if we have a cached-off visibility
  397. int iCache = s_CombatCharVisCache.LookupVisibility( this, pBCC );
  398. VisCacheResult_t nResult = s_CombatCharVisCache.HasVisibility( iCache );
  399. // Compute symmetric visibility
  400. if ( nResult == VISCACHE_UNKNOWN )
  401. {
  402. bool bThisCanSeeOther = false, bOtherCanSeeThis = false;
  403. if ( ComputeLOS( vecEyePosition, vecOtherEyePosition ) )
  404. {
  405. #if defined(GAME_DLL) && defined(TERROR)
  406. if ( !bIsInNavObscureRange )
  407. {
  408. bThisCanSeeOther = true, bOtherCanSeeThis = true;
  409. }
  410. else
  411. {
  412. bThisCanSeeOther = !ComputeTargetIsInDarkness( vecEyePosition, pBCC->GetLastKnownArea(), vecOtherEyePosition );
  413. bOtherCanSeeThis = !ComputeTargetIsInDarkness( vecOtherEyePosition, GetLastKnownArea(), vecEyePosition );
  414. }
  415. #else
  416. bThisCanSeeOther = true, bOtherCanSeeThis = true;
  417. #endif
  418. }
  419. s_CombatCharVisCache.RegisterVisibility( iCache, bThisCanSeeOther, bOtherCanSeeThis );
  420. nResult = bThisCanSeeOther ? VISCACHE_IS_VISIBLE : VISCACHE_IS_NOT_VISIBLE;
  421. }
  422. if ( nResult == VISCACHE_IS_VISIBLE )
  423. return ( checkFOV != USE_FOV || IsInFieldOfView( pBCC ) );
  424. return false;
  425. }
  426. class CTraceFilterNoCombatCharacters : public CTraceFilterSimple
  427. {
  428. public:
  429. CTraceFilterNoCombatCharacters( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE )
  430. : CTraceFilterSimple( passentity, collisionGroup )
  431. {
  432. }
  433. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  434. {
  435. if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) )
  436. {
  437. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  438. if ( !pEntity )
  439. return NULL;
  440. if ( pEntity->MyCombatCharacterPointer() || pEntity->MyCombatWeaponPointer() )
  441. return false;
  442. // Honor BlockLOS - this lets us see through partially-broken doors, etc
  443. if ( !pEntity->BlocksLOS() )
  444. return false;
  445. return true;
  446. }
  447. return false;
  448. }
  449. };
  450. bool CBaseCombatCharacter::ComputeLOS( const Vector &vecEyePosition, const Vector &vecTarget ) const
  451. {
  452. // We simply can't see because the world is in the way.
  453. trace_t result;
  454. CTraceFilterNoCombatCharacters traceFilter( NULL, COLLISION_GROUP_NONE );
  455. UTIL_TraceLine( vecEyePosition, vecTarget, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_MONSTER, &traceFilter, &result );
  456. return ( result.fraction == 1.0f );
  457. }
  458. #if defined(GAME_DLL) && defined(TERROR)
  459. bool CBaseCombatCharacter::ComputeTargetIsInDarkness( const Vector &vecEyePosition, CNavArea *pTargetNavArea, const Vector &vecTargetPos ) const
  460. {
  461. if ( GetTeamNumber() != TEAM_SURVIVOR )
  462. return false;
  463. // Check light info
  464. const float flMinLightIntensity = 0.1f;
  465. if ( !pTargetNavArea || ( pTargetNavArea->GetLightIntensity() >= flMinLightIntensity ) )
  466. return false;
  467. CTraceFilterNoNPCsOrPlayer lightingFilter( this, COLLISION_GROUP_NONE );
  468. Vector vecSightDirection;
  469. VectorSubtract( vecTargetPos, vecEyePosition, vecSightDirection );
  470. VectorNormalize( vecSightDirection );
  471. trace_t result;
  472. UTIL_TraceLine( vecTargetPos, vecTargetPos + vecSightDirection * 32768.0f, MASK_L4D_VISION, &lightingFilter, &result );
  473. if ( ( result.fraction < 1.0f ) && ( ( result.surface.flags & SURF_SKY ) == 0 ) )
  474. {
  475. const float flMaxDistance = 100.0f;
  476. TerrorNavArea *pFarArea = (TerrorNavArea *)TheNavMesh->GetNearestNavArea( result.endpos, false, flMaxDistance );
  477. // Target is in darkness, the wall behind him is too, and we are too far away
  478. if ( pFarArea && pFarArea->GetLightIntensity() < flMinLightIntensity )
  479. return true;
  480. }
  481. return false;
  482. }
  483. #endif
  484. //-----------------------------------------------------------------------------
  485. /**
  486. Return true if our view direction is pointing at the given target,
  487. within the cosine of the angular tolerance. LINE OF SIGHT IS NOT CHECKED.
  488. */
  489. bool CBaseCombatCharacter::IsLookingTowards( const CBaseEntity *target, float cosTolerance ) const
  490. {
  491. return IsLookingTowards( target->WorldSpaceCenter(), cosTolerance ) || IsLookingTowards( target->EyePosition(), cosTolerance ) || IsLookingTowards( target->GetAbsOrigin(), cosTolerance );
  492. }
  493. //-----------------------------------------------------------------------------
  494. /**
  495. Return true if our view direction is pointing at the given target,
  496. within the cosine of the angular tolerance. LINE OF SIGHT IS NOT CHECKED.
  497. */
  498. bool CBaseCombatCharacter::IsLookingTowards( const Vector &target, float cosTolerance ) const
  499. {
  500. Vector toTarget = target - EyePosition();
  501. toTarget.NormalizeInPlace();
  502. Vector forward;
  503. AngleVectors( EyeAngles(), &forward );
  504. return ( DotProduct( forward, toTarget ) >= cosTolerance );
  505. }
  506. //-----------------------------------------------------------------------------
  507. /**
  508. Returns true if we are looking towards something within a tolerence determined
  509. by our field of view
  510. */
  511. bool CBaseCombatCharacter::IsInFieldOfView( CBaseEntity *entity ) const
  512. {
  513. CBasePlayer *pPlayer = ToBasePlayer( const_cast< CBaseCombatCharacter* >( this ) );
  514. float flTolerance = pPlayer ? cos( (float)pPlayer->GetFOV() * 0.5f ) : BCC_DEFAULT_LOOK_TOWARDS_TOLERANCE;
  515. Vector vecForward;
  516. Vector vecEyePosition = EyePosition();
  517. AngleVectors( EyeAngles(), &vecForward );
  518. // FIXME: Use a faster check than this!
  519. // Check 3 spots, or else when standing right next to someone looking at their eyes,
  520. // the angle will be too great to see their center.
  521. Vector vecToTarget = entity->GetAbsOrigin() - vecEyePosition;
  522. vecToTarget.NormalizeInPlace();
  523. if ( DotProduct( vecForward, vecToTarget ) >= flTolerance )
  524. return true;
  525. vecToTarget = entity->WorldSpaceCenter() - vecEyePosition;
  526. vecToTarget.NormalizeInPlace();
  527. if ( DotProduct( vecForward, vecToTarget ) >= flTolerance )
  528. return true;
  529. vecToTarget = entity->EyePosition() - vecEyePosition;
  530. vecToTarget.NormalizeInPlace();
  531. return ( DotProduct( vecForward, vecToTarget ) >= flTolerance );
  532. }
  533. //-----------------------------------------------------------------------------
  534. /**
  535. Returns true if we are looking towards something within a tolerence determined
  536. by our field of view
  537. */
  538. bool CBaseCombatCharacter::IsInFieldOfView( const Vector &pos ) const
  539. {
  540. CBasePlayer *pPlayer = ToBasePlayer( const_cast< CBaseCombatCharacter* >( this ) );
  541. if ( pPlayer )
  542. return IsLookingTowards( pos, cos( (float)pPlayer->GetFOV() * 0.5f ) );
  543. return IsLookingTowards( pos );
  544. }
  545. //-----------------------------------------------------------------------------
  546. /**
  547. Strictly checks Line of Sight only.
  548. */
  549. bool CBaseCombatCharacter::IsLineOfSightClear( CBaseEntity *entity, LineOfSightCheckType checkType ) const
  550. {
  551. #ifdef CLIENT_DLL
  552. if ( entity->MyCombatCharacterPointer() )
  553. return IsLineOfSightClear( entity->EyePosition(), checkType, entity );
  554. return IsLineOfSightClear( entity->WorldSpaceCenter(), checkType, entity );
  555. #else
  556. // FIXME: Should we do the same check here as the client does?
  557. return IsLineOfSightClear( entity->WorldSpaceCenter(), checkType, entity ) || IsLineOfSightClear( entity->EyePosition(), checkType, entity ) || IsLineOfSightClear( entity->GetAbsOrigin(), checkType, entity );
  558. #endif
  559. }
  560. //-----------------------------------------------------------------------------
  561. /**
  562. Strictly checks Line of Sight only.
  563. */
  564. static bool TraceFilterNoCombatCharacters( IHandleEntity *pServerEntity, int contentsMask )
  565. {
  566. // Honor BlockLOS also to allow seeing through partially-broken doors
  567. CBaseEntity *entity = EntityFromEntityHandle( pServerEntity );
  568. return ( entity->MyCombatCharacterPointer() == NULL && !entity->MyCombatWeaponPointer() && entity->BlocksLOS() );
  569. }
  570. bool CBaseCombatCharacter::IsLineOfSightClear( const Vector &pos, LineOfSightCheckType checkType, CBaseEntity *entityToIgnore ) const
  571. {
  572. #if defined(GAME_DLL) && defined(COUNT_BCC_LOS)
  573. static int count, frame;
  574. if ( frame != gpGlobals->framecount )
  575. {
  576. Msg( ">> %d\n", count );
  577. frame = gpGlobals->framecount;
  578. count = 0;
  579. }
  580. count++;
  581. #endif
  582. if( checkType == IGNORE_ACTORS )
  583. {
  584. // use the query cache unless it causes problems
  585. #if defined(GAME_DLL) && defined(TERROR)
  586. return IsLineOfSightBetweenTwoEntitiesClear( const_cast<CBaseCombatCharacter *>(this), EOFFSET_MODE_EYEPOSITION,
  587. entityToIgnore, EOFFSET_MODE_WORLDSPACE_CENTER,
  588. entityToIgnore, COLLISION_GROUP_NONE,
  589. MASK_L4D_VISION, TraceFilterNoCombatCharacters, 1.0 );
  590. #else
  591. trace_t trace;
  592. CTraceFilterNoCombatCharacters traceFilter( entityToIgnore, COLLISION_GROUP_NONE );
  593. UTIL_TraceLine( EyePosition(), pos, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_MONSTER, &traceFilter, &trace );
  594. return trace.fraction == 1.0f;
  595. #endif
  596. }
  597. else
  598. {
  599. trace_t trace;
  600. CTraceFilterSkipTwoEntities traceFilter( this, entityToIgnore, COLLISION_GROUP_NONE );
  601. UTIL_TraceLine( EyePosition(), pos, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE, &traceFilter, &trace );
  602. return trace.fraction == 1.0f;
  603. }
  604. }
  605. /*
  606. //---------------------------------------------------------------------------------------------------------------------------
  607. surfacedata_t * CBaseCombatCharacter::GetGroundSurface( void ) const
  608. {
  609. Vector start( vec3_origin );
  610. Vector end( 0, 0, -64 );
  611. Vector vecMins, vecMaxs;
  612. CollisionProp()->WorldSpaceAABB( &vecMins, &vecMaxs );
  613. Ray_t ray;
  614. ray.Init( start, end, vecMins, vecMaxs );
  615. trace_t trace;
  616. UTIL_TraceRay( ray, MASK_SOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  617. if ( trace.fraction == 1.0f )
  618. return NULL; // no ground
  619. return physprops->GetSurfaceData( trace.surface.surfaceProps );
  620. }
  621. */