From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/shared/basecombatcharacter_shared.cpp | 1464 ++++++++++----------- 1 file changed, 732 insertions(+), 732 deletions(-) (limited to 'mp/src/game/shared/basecombatcharacter_shared.cpp') diff --git a/mp/src/game/shared/basecombatcharacter_shared.cpp b/mp/src/game/shared/basecombatcharacter_shared.cpp index d7fad6da..c032cd54 100644 --- a/mp/src/game/shared/basecombatcharacter_shared.cpp +++ b/mp/src/game/shared/basecombatcharacter_shared.cpp @@ -1,732 +1,732 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "ammodef.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - - -//----------------------------------------------------------------------------- -// Purpose: Switches to the best weapon that is also better than the given weapon. -// Input : pCurrent - The current weapon used by the player. -// Output : Returns true if the weapon was switched, false if there was no better -// weapon to switch to. -//----------------------------------------------------------------------------- -bool CBaseCombatCharacter::SwitchToNextBestWeapon(CBaseCombatWeapon *pCurrent) -{ - CBaseCombatWeapon *pNewWeapon = g_pGameRules->GetNextBestWeapon(this, pCurrent); - - if ( ( pNewWeapon != NULL ) && ( pNewWeapon != pCurrent ) ) - { - return Weapon_Switch( pNewWeapon ); - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Switches to the given weapon (providing it has ammo) -// Input : -// Output : true is switch succeeded -//----------------------------------------------------------------------------- -bool CBaseCombatCharacter::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex /*=0*/ ) -{ - if ( pWeapon == NULL ) - return false; - - // Already have it out? - if ( m_hActiveWeapon.Get() == pWeapon ) - { - if ( !m_hActiveWeapon->IsWeaponVisible() || m_hActiveWeapon->IsHolstered() ) - return m_hActiveWeapon->Deploy( ); - return false; - } - - if (!Weapon_CanSwitchTo(pWeapon)) - { - return false; - } - - if ( m_hActiveWeapon ) - { - if ( !m_hActiveWeapon->Holster( pWeapon ) ) - return false; - } - - m_hActiveWeapon = pWeapon; - - return pWeapon->Deploy( ); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns whether or not we can switch to the given weapon. -// Input : pWeapon - -//----------------------------------------------------------------------------- -bool CBaseCombatCharacter::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) -{ - if (IsPlayer()) - { - CBasePlayer *pPlayer = (CBasePlayer *)this; -#if !defined( CLIENT_DLL ) - IServerVehicle *pVehicle = pPlayer->GetVehicle(); -#else - IClientVehicle *pVehicle = pPlayer->GetVehicle(); -#endif - if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle()) - return false; - } - - if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) ) - return false; - - if ( !pWeapon->CanDeploy() ) - return false; - - if ( m_hActiveWeapon ) - { - if ( !m_hActiveWeapon->CanHolster() ) - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CBaseCombatWeapon -//----------------------------------------------------------------------------- -CBaseCombatWeapon *CBaseCombatCharacter::GetActiveWeapon() const -{ - return m_hActiveWeapon; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : iCount - -// iAmmoIndex - -//----------------------------------------------------------------------------- -void CBaseCombatCharacter::RemoveAmmo( int iCount, int iAmmoIndex ) -{ - if (iCount <= 0) - return; - - // Infinite ammo? - if ( GetAmmoDef()->MaxCarry( iAmmoIndex ) == INFINITE_AMMO ) - return; - - // Ammo pickup sound - m_iAmmo.Set( iAmmoIndex, MAX( m_iAmmo[iAmmoIndex] - iCount, 0 ) ); -} - -void CBaseCombatCharacter::RemoveAmmo( int iCount, const char *szName ) -{ - RemoveAmmo( iCount, GetAmmoDef()->Index(szName) ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBaseCombatCharacter::RemoveAllAmmo( ) -{ - for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) - { - m_iAmmo.Set( i, 0 ); - } -} - -//----------------------------------------------------------------------------- -// FIXME: This is a sort of hack back-door only used by physgun! -//----------------------------------------------------------------------------- -void CBaseCombatCharacter::SetAmmoCount( int iCount, int iAmmoIndex ) -{ - // NOTE: No sound, no max check! Seems pretty bogus to me! - m_iAmmo.Set( iAmmoIndex, iCount ); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the amount of ammunition of a particular type owned -// owned by the character -// Input : Ammo Index -// Output : The amount of ammo -//----------------------------------------------------------------------------- -int CBaseCombatCharacter::GetAmmoCount( int iAmmoIndex ) const -{ - if ( iAmmoIndex == -1 ) - return 0; - - // Infinite ammo? - if ( GetAmmoDef()->MaxCarry( iAmmoIndex ) == INFINITE_AMMO ) - return 999; - - return m_iAmmo[ iAmmoIndex ]; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the amount of ammunition of the specified type the character's carrying -//----------------------------------------------------------------------------- -int CBaseCombatCharacter::GetAmmoCount( char *szName ) const -{ - return GetAmmoCount( GetAmmoDef()->Index(szName) ); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns weapon if already owns a weapon of this class -//----------------------------------------------------------------------------- -CBaseCombatWeapon* CBaseCombatCharacter::Weapon_OwnsThisType( const char *pszWeapon, int iSubType ) const -{ - // Check for duplicates - for (int i=0;iGetSubType() == iSubType ) - return m_hMyWeapons[i]; - } - } - return NULL; -} - - -int CBaseCombatCharacter::BloodColor() -{ - return m_bloodColor; -} - - -//----------------------------------------------------------------------------- -// Blood color (see BLOOD_COLOR_* macros in baseentity.h) -//----------------------------------------------------------------------------- -void CBaseCombatCharacter::SetBloodColor( int nBloodColor ) -{ - m_bloodColor = nBloodColor; -} - -//----------------------------------------------------------------------------- -/** - The main visibility check. Checks all the entity specific reasons that could - make IsVisible fail. Then checks points in space to get environmental reasons. - This is LOS, plus invisibility and fog and smoke and such. -*/ - -enum VisCacheResult_t -{ - VISCACHE_UNKNOWN = 0, - VISCACHE_IS_VISIBLE, - VISCACHE_IS_NOT_VISIBLE, -}; - -enum -{ - VIS_CACHE_INVALID = 0x80000000 -}; - -#define VIS_CACHE_ENTRY_LIFE .090f - -class CCombatCharVisCache : public CAutoGameSystemPerFrame -{ -public: - virtual void FrameUpdatePreEntityThink(); - virtual void LevelShutdownPreEntity(); - - int LookupVisibility( const CBaseCombatCharacter *pChar1, CBaseCombatCharacter *pChar2 ); - VisCacheResult_t HasVisibility( int iCache ) const; - void RegisterVisibility( int iCache, bool bChar1SeesChar2, bool bChar2SeesChar1 ); - -private: - struct VisCacheEntry_t - { - CHandle< CBaseCombatCharacter > m_hEntity1; - CHandle< CBaseCombatCharacter > m_hEntity2; - float m_flTime; - bool m_bEntity1CanSeeEntity2; - bool m_bEntity2CanSeeEntity1; - }; - - class CVisCacheEntryLess - { - public: - CVisCacheEntryLess( int ) {} - bool operator!() const { return false; } - bool operator()( const VisCacheEntry_t &lhs, const VisCacheEntry_t &rhs ) const - { - return ( memcmp( &lhs, &rhs, offsetof( VisCacheEntry_t, m_flTime ) ) < 0 ); - } - }; - - CUtlRBTree< VisCacheEntry_t, unsigned short, CVisCacheEntryLess > m_VisCache; - - mutable int m_nTestCount; - mutable int m_nHitCount; -}; - -void CCombatCharVisCache::FrameUpdatePreEntityThink() -{ - // Msg( "test: %d/%d\n", m_nHitCount, m_nTestCount ); - - // Lazy retirement of vis cache - // NOTE: 256 was chosen heuristically based on a playthrough where 200 - // was the max # in the viscache where nothing could be retired. - if ( m_VisCache.Count() < 256 ) - return; - - int nMaxIndex = m_VisCache.MaxElement() - 1; - for ( int i = 0; i < 8; ++i ) - { - int n = RandomInt( 0, nMaxIndex ); - if ( !m_VisCache.IsValidIndex( n ) ) - continue; - - const VisCacheEntry_t &entry = m_VisCache[n]; - if ( !entry.m_hEntity1.IsValid() || !entry.m_hEntity2.IsValid() || ( gpGlobals->curtime - entry.m_flTime > 10.0f ) ) - { - m_VisCache.RemoveAt( n ); - } - } -} - -void CCombatCharVisCache::LevelShutdownPreEntity() -{ - m_VisCache.Purge(); -} - -int CCombatCharVisCache::LookupVisibility( const CBaseCombatCharacter *pChar1, CBaseCombatCharacter *pChar2 ) -{ - VisCacheEntry_t cacheEntry; - if ( pChar1 < pChar2 ) - { - cacheEntry.m_hEntity1 = pChar1; - cacheEntry.m_hEntity2 = pChar2; - } - else - { - cacheEntry.m_hEntity1 = pChar2; - cacheEntry.m_hEntity2 = pChar1; - } - - int iCache = m_VisCache.Find( cacheEntry ); - if ( iCache == m_VisCache.InvalidIndex() ) - { - if ( m_VisCache.Count() == m_VisCache.InvalidIndex() ) - return VIS_CACHE_INVALID; - - iCache = m_VisCache.Insert( cacheEntry ); - m_VisCache[iCache].m_flTime = gpGlobals->curtime - 2.0f * VIS_CACHE_ENTRY_LIFE; - } - - return ( pChar1 < pChar2 ) ? iCache : - iCache - 1; -} - -VisCacheResult_t CCombatCharVisCache::HasVisibility( int iCache ) const -{ - if ( iCache == VIS_CACHE_INVALID ) - return VISCACHE_UNKNOWN; - - m_nTestCount++; - - bool bReverse = ( iCache < 0 ); - if ( bReverse ) - { - iCache = - iCache - 1; - } - - const VisCacheEntry_t &entry = m_VisCache[iCache]; - if ( gpGlobals->curtime - entry.m_flTime > VIS_CACHE_ENTRY_LIFE ) - return VISCACHE_UNKNOWN; - - m_nHitCount++; - - bool bIsVisible = !bReverse ? entry.m_bEntity1CanSeeEntity2 : entry.m_bEntity2CanSeeEntity1; - return bIsVisible ? VISCACHE_IS_VISIBLE : VISCACHE_IS_NOT_VISIBLE; -} - -void CCombatCharVisCache::RegisterVisibility( int iCache, bool bEntity1CanSeeEntity2, bool bEntity2CanSeeEntity1 ) -{ - if ( iCache == VIS_CACHE_INVALID ) - return; - - bool bReverse = ( iCache < 0 ); - if ( bReverse ) - { - iCache = - iCache - 1; - } - - VisCacheEntry_t &entry = m_VisCache[iCache]; - entry.m_flTime = gpGlobals->curtime; - if ( !bReverse ) - { - entry.m_bEntity1CanSeeEntity2 = bEntity1CanSeeEntity2; - entry.m_bEntity2CanSeeEntity1 = bEntity2CanSeeEntity1; - } - else - { - entry.m_bEntity1CanSeeEntity2 = bEntity2CanSeeEntity1; - entry.m_bEntity2CanSeeEntity1 = bEntity1CanSeeEntity2; - } -} - -static CCombatCharVisCache s_CombatCharVisCache; - -bool CBaseCombatCharacter::IsAbleToSee( const CBaseEntity *pEntity, FieldOfViewCheckType checkFOV ) -{ - CBaseCombatCharacter *pBCC = const_cast( pEntity )->MyCombatCharacterPointer(); - if ( pBCC ) - return IsAbleToSee( pBCC, checkFOV ); - - // Test this every time; it's cheap. - Vector vecEyePosition = EyePosition(); - Vector vecTargetPosition = pEntity->WorldSpaceCenter(); - -#ifdef GAME_DLL - Vector vecEyeToTarget; - VectorSubtract( vecTargetPosition, vecEyePosition, vecEyeToTarget ); - float flDistToOther = VectorNormalize( vecEyeToTarget ); - - // We can't see because they are too far in the fog - if ( IsHiddenByFog( flDistToOther ) ) - return false; -#endif - - if ( !ComputeLOS( vecEyePosition, vecTargetPosition ) ) - return false; - -#if defined(GAME_DLL) && defined(TERROR) - if ( flDistToOther > NavObscureRange.GetFloat() ) - { - const float flMaxDistance = 100.0f; - TerrorNavArea *pTargetArea = static_cast< TerrorNavArea* >( TheNavMesh->GetNearestNavArea( vecTargetPosition, false, flMaxDistance ) ); - if ( !pTargetArea || pTargetArea->HasSpawnAttributes( TerrorNavArea::SPAWN_OBSCURED ) ) - return false; - - if ( ComputeTargetIsInDarkness( vecEyePosition, pTargetArea, vecTargetPosition ) ) - return false; - } -#endif - - return ( checkFOV != USE_FOV || IsInFieldOfView( vecTargetPosition ) ); -} - -static void ComputeSeeTestPosition( Vector *pEyePosition, CBaseCombatCharacter *pBCC ) -{ -#if defined(GAME_DLL) && defined(TERROR) - if ( pBCC->IsPlayer() ) - { - CTerrorPlayer *pPlayer = ToTerrorPlayer( pBCC ); - *pEyePosition = !pPlayer->IsDead() ? pPlayer->EyePosition() : pPlayer->GetDeathPosition(); - } - else -#endif - { - *pEyePosition = pBCC->EyePosition(); - } -} - -bool CBaseCombatCharacter::IsAbleToSee( CBaseCombatCharacter *pBCC, FieldOfViewCheckType checkFOV ) -{ - Vector vecEyePosition, vecOtherEyePosition; - ComputeSeeTestPosition( &vecEyePosition, this ); - ComputeSeeTestPosition( &vecOtherEyePosition, pBCC ); - -#ifdef GAME_DLL - Vector vecEyeToTarget; - VectorSubtract( vecOtherEyePosition, vecEyePosition, vecEyeToTarget ); - float flDistToOther = VectorNormalize( vecEyeToTarget ); - - // Test this every time; it's cheap. - // We can't see because they are too far in the fog - if ( IsHiddenByFog( flDistToOther ) ) - return false; - -#ifdef TERROR - // Check this every time also, it's cheap; check to see if the enemy is in an obscured area. - bool bIsInNavObscureRange = ( flDistToOther > NavObscureRange.GetFloat() ); - if ( bIsInNavObscureRange ) - { - TerrorNavArea *pOtherNavArea = static_cast< TerrorNavArea* >( pBCC->GetLastKnownArea() ); - if ( !pOtherNavArea || pOtherNavArea->HasSpawnAttributes( TerrorNavArea::SPAWN_OBSCURED ) ) - return false; - } -#endif // TERROR -#endif - - // Check if we have a cached-off visibility - int iCache = s_CombatCharVisCache.LookupVisibility( this, pBCC ); - VisCacheResult_t nResult = s_CombatCharVisCache.HasVisibility( iCache ); - - // Compute symmetric visibility - if ( nResult == VISCACHE_UNKNOWN ) - { - bool bThisCanSeeOther = false, bOtherCanSeeThis = false; - if ( ComputeLOS( vecEyePosition, vecOtherEyePosition ) ) - { -#if defined(GAME_DLL) && defined(TERROR) - if ( !bIsInNavObscureRange ) - { - bThisCanSeeOther = true, bOtherCanSeeThis = true; - } - else - { - bThisCanSeeOther = !ComputeTargetIsInDarkness( vecEyePosition, pBCC->GetLastKnownArea(), vecOtherEyePosition ); - bOtherCanSeeThis = !ComputeTargetIsInDarkness( vecOtherEyePosition, GetLastKnownArea(), vecEyePosition ); - } -#else - bThisCanSeeOther = true, bOtherCanSeeThis = true; -#endif - } - - s_CombatCharVisCache.RegisterVisibility( iCache, bThisCanSeeOther, bOtherCanSeeThis ); - nResult = bThisCanSeeOther ? VISCACHE_IS_VISIBLE : VISCACHE_IS_NOT_VISIBLE; - } - - if ( nResult == VISCACHE_IS_VISIBLE ) - return ( checkFOV != USE_FOV || IsInFieldOfView( pBCC ) ); - - return false; -} - -class CTraceFilterNoCombatCharacters : public CTraceFilterSimple -{ -public: - CTraceFilterNoCombatCharacters( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE ) - : CTraceFilterSimple( passentity, collisionGroup ) - { - } - - virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) - { - if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) ) - { - CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); - if ( !pEntity ) - return NULL; - - if ( pEntity->MyCombatCharacterPointer() || pEntity->MyCombatWeaponPointer() ) - return false; - - // Honor BlockLOS - this lets us see through partially-broken doors, etc - if ( !pEntity->BlocksLOS() ) - return false; - - return true; - } - - return false; - } -}; - -bool CBaseCombatCharacter::ComputeLOS( const Vector &vecEyePosition, const Vector &vecTarget ) const -{ - // We simply can't see because the world is in the way. - trace_t result; - CTraceFilterNoCombatCharacters traceFilter( NULL, COLLISION_GROUP_NONE ); - UTIL_TraceLine( vecEyePosition, vecTarget, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_MONSTER, &traceFilter, &result ); - return ( result.fraction == 1.0f ); -} - -#if defined(GAME_DLL) && defined(TERROR) -bool CBaseCombatCharacter::ComputeTargetIsInDarkness( const Vector &vecEyePosition, CNavArea *pTargetNavArea, const Vector &vecTargetPos ) const -{ - if ( GetTeamNumber() != TEAM_SURVIVOR ) - return false; - - // Check light info - const float flMinLightIntensity = 0.1f; - - if ( !pTargetNavArea || ( pTargetNavArea->GetLightIntensity() >= flMinLightIntensity ) ) - return false; - - CTraceFilterNoNPCsOrPlayer lightingFilter( this, COLLISION_GROUP_NONE ); - - Vector vecSightDirection; - VectorSubtract( vecTargetPos, vecEyePosition, vecSightDirection ); - VectorNormalize( vecSightDirection ); - - trace_t result; - UTIL_TraceLine( vecTargetPos, vecTargetPos + vecSightDirection * 32768.0f, MASK_L4D_VISION, &lightingFilter, &result ); - if ( ( result.fraction < 1.0f ) && ( ( result.surface.flags & SURF_SKY ) == 0 ) ) - { - const float flMaxDistance = 100.0f; - TerrorNavArea *pFarArea = (TerrorNavArea *)TheNavMesh->GetNearestNavArea( result.endpos, false, flMaxDistance ); - - // Target is in darkness, the wall behind him is too, and we are too far away - if ( pFarArea && pFarArea->GetLightIntensity() < flMinLightIntensity ) - return true; - } - - return false; -} -#endif - - -//----------------------------------------------------------------------------- -/** - Return true if our view direction is pointing at the given target, - within the cosine of the angular tolerance. LINE OF SIGHT IS NOT CHECKED. -*/ -bool CBaseCombatCharacter::IsLookingTowards( const CBaseEntity *target, float cosTolerance ) const -{ - return IsLookingTowards( target->WorldSpaceCenter(), cosTolerance ) || IsLookingTowards( target->EyePosition(), cosTolerance ) || IsLookingTowards( target->GetAbsOrigin(), cosTolerance ); -} - - -//----------------------------------------------------------------------------- -/** - Return true if our view direction is pointing at the given target, - within the cosine of the angular tolerance. LINE OF SIGHT IS NOT CHECKED. -*/ -bool CBaseCombatCharacter::IsLookingTowards( const Vector &target, float cosTolerance ) const -{ - Vector toTarget = target - EyePosition(); - toTarget.NormalizeInPlace(); - - Vector forward; - AngleVectors( EyeAngles(), &forward ); - - return ( DotProduct( forward, toTarget ) >= cosTolerance ); -} - - -//----------------------------------------------------------------------------- -/** - Returns true if we are looking towards something within a tolerence determined - by our field of view -*/ -bool CBaseCombatCharacter::IsInFieldOfView( CBaseEntity *entity ) const -{ - CBasePlayer *pPlayer = ToBasePlayer( const_cast< CBaseCombatCharacter* >( this ) ); - float flTolerance = pPlayer ? cos( (float)pPlayer->GetFOV() * 0.5f ) : BCC_DEFAULT_LOOK_TOWARDS_TOLERANCE; - - Vector vecForward; - Vector vecEyePosition = EyePosition(); - AngleVectors( EyeAngles(), &vecForward ); - - // FIXME: Use a faster check than this! - - // Check 3 spots, or else when standing right next to someone looking at their eyes, - // the angle will be too great to see their center. - Vector vecToTarget = entity->GetAbsOrigin() - vecEyePosition; - vecToTarget.NormalizeInPlace(); - if ( DotProduct( vecForward, vecToTarget ) >= flTolerance ) - return true; - - vecToTarget = entity->WorldSpaceCenter() - vecEyePosition; - vecToTarget.NormalizeInPlace(); - if ( DotProduct( vecForward, vecToTarget ) >= flTolerance ) - return true; - - vecToTarget = entity->EyePosition() - vecEyePosition; - vecToTarget.NormalizeInPlace(); - return ( DotProduct( vecForward, vecToTarget ) >= flTolerance ); -} - -//----------------------------------------------------------------------------- -/** - Returns true if we are looking towards something within a tolerence determined - by our field of view -*/ -bool CBaseCombatCharacter::IsInFieldOfView( const Vector &pos ) const -{ - CBasePlayer *pPlayer = ToBasePlayer( const_cast< CBaseCombatCharacter* >( this ) ); - - if ( pPlayer ) - return IsLookingTowards( pos, cos( (float)pPlayer->GetFOV() * 0.5f ) ); - - return IsLookingTowards( pos ); -} - -//----------------------------------------------------------------------------- -/** - Strictly checks Line of Sight only. -*/ - -bool CBaseCombatCharacter::IsLineOfSightClear( CBaseEntity *entity, LineOfSightCheckType checkType ) const -{ -#ifdef CLIENT_DLL - if ( entity->MyCombatCharacterPointer() ) - return IsLineOfSightClear( entity->EyePosition(), checkType, entity ); - return IsLineOfSightClear( entity->WorldSpaceCenter(), checkType, entity ); -#else - // FIXME: Should we do the same check here as the client does? - return IsLineOfSightClear( entity->WorldSpaceCenter(), checkType, entity ) || IsLineOfSightClear( entity->EyePosition(), checkType, entity ) || IsLineOfSightClear( entity->GetAbsOrigin(), checkType, entity ); -#endif -} - -//----------------------------------------------------------------------------- -/** - Strictly checks Line of Sight only. -*/ -static bool TraceFilterNoCombatCharacters( IHandleEntity *pServerEntity, int contentsMask ) -{ - // Honor BlockLOS also to allow seeing through partially-broken doors - CBaseEntity *entity = EntityFromEntityHandle( pServerEntity ); - return ( entity->MyCombatCharacterPointer() == NULL && !entity->MyCombatWeaponPointer() && entity->BlocksLOS() ); -} - -bool CBaseCombatCharacter::IsLineOfSightClear( const Vector &pos, LineOfSightCheckType checkType, CBaseEntity *entityToIgnore ) const -{ -#if defined(GAME_DLL) && defined(COUNT_BCC_LOS) - static int count, frame; - if ( frame != gpGlobals->framecount ) - { - Msg( ">> %d\n", count ); - frame = gpGlobals->framecount; - count = 0; - } - count++; -#endif - if( checkType == IGNORE_ACTORS ) - { - - // use the query cache unless it causes problems -#if defined(GAME_DLL) && defined(TERROR) - return IsLineOfSightBetweenTwoEntitiesClear( const_cast(this), EOFFSET_MODE_EYEPOSITION, - entityToIgnore, EOFFSET_MODE_WORLDSPACE_CENTER, - entityToIgnore, COLLISION_GROUP_NONE, - MASK_L4D_VISION, TraceFilterNoCombatCharacters, 1.0 ); -#else - trace_t trace; - CTraceFilterNoCombatCharacters traceFilter( entityToIgnore, COLLISION_GROUP_NONE ); - UTIL_TraceLine( EyePosition(), pos, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_MONSTER, &traceFilter, &trace ); - - return trace.fraction == 1.0f; -#endif - } - else - { - trace_t trace; - CTraceFilterSkipTwoEntities traceFilter( this, entityToIgnore, COLLISION_GROUP_NONE ); - UTIL_TraceLine( EyePosition(), pos, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE, &traceFilter, &trace ); - - return trace.fraction == 1.0f; - } -} - - -/* -//--------------------------------------------------------------------------------------------------------------------------- -surfacedata_t * CBaseCombatCharacter::GetGroundSurface( void ) const -{ - Vector start( vec3_origin ); - Vector end( 0, 0, -64 ); - - Vector vecMins, vecMaxs; - CollisionProp()->WorldSpaceAABB( &vecMins, &vecMaxs ); - - Ray_t ray; - ray.Init( start, end, vecMins, vecMaxs ); - - trace_t trace; - UTIL_TraceRay( ray, MASK_SOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); - - if ( trace.fraction == 1.0f ) - return NULL; // no ground - - return physprops->GetSurfaceData( trace.surface.surfaceProps ); -} -*/ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "ammodef.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Purpose: Switches to the best weapon that is also better than the given weapon. +// Input : pCurrent - The current weapon used by the player. +// Output : Returns true if the weapon was switched, false if there was no better +// weapon to switch to. +//----------------------------------------------------------------------------- +bool CBaseCombatCharacter::SwitchToNextBestWeapon(CBaseCombatWeapon *pCurrent) +{ + CBaseCombatWeapon *pNewWeapon = g_pGameRules->GetNextBestWeapon(this, pCurrent); + + if ( ( pNewWeapon != NULL ) && ( pNewWeapon != pCurrent ) ) + { + return Weapon_Switch( pNewWeapon ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Switches to the given weapon (providing it has ammo) +// Input : +// Output : true is switch succeeded +//----------------------------------------------------------------------------- +bool CBaseCombatCharacter::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex /*=0*/ ) +{ + if ( pWeapon == NULL ) + return false; + + // Already have it out? + if ( m_hActiveWeapon.Get() == pWeapon ) + { + if ( !m_hActiveWeapon->IsWeaponVisible() || m_hActiveWeapon->IsHolstered() ) + return m_hActiveWeapon->Deploy( ); + return false; + } + + if (!Weapon_CanSwitchTo(pWeapon)) + { + return false; + } + + if ( m_hActiveWeapon ) + { + if ( !m_hActiveWeapon->Holster( pWeapon ) ) + return false; + } + + m_hActiveWeapon = pWeapon; + + return pWeapon->Deploy( ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not we can switch to the given weapon. +// Input : pWeapon - +//----------------------------------------------------------------------------- +bool CBaseCombatCharacter::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) +{ + if (IsPlayer()) + { + CBasePlayer *pPlayer = (CBasePlayer *)this; +#if !defined( CLIENT_DLL ) + IServerVehicle *pVehicle = pPlayer->GetVehicle(); +#else + IClientVehicle *pVehicle = pPlayer->GetVehicle(); +#endif + if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle()) + return false; + } + + if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) ) + return false; + + if ( !pWeapon->CanDeploy() ) + return false; + + if ( m_hActiveWeapon ) + { + if ( !m_hActiveWeapon->CanHolster() ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CBaseCombatWeapon +//----------------------------------------------------------------------------- +CBaseCombatWeapon *CBaseCombatCharacter::GetActiveWeapon() const +{ + return m_hActiveWeapon; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : iCount - +// iAmmoIndex - +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::RemoveAmmo( int iCount, int iAmmoIndex ) +{ + if (iCount <= 0) + return; + + // Infinite ammo? + if ( GetAmmoDef()->MaxCarry( iAmmoIndex ) == INFINITE_AMMO ) + return; + + // Ammo pickup sound + m_iAmmo.Set( iAmmoIndex, MAX( m_iAmmo[iAmmoIndex] - iCount, 0 ) ); +} + +void CBaseCombatCharacter::RemoveAmmo( int iCount, const char *szName ) +{ + RemoveAmmo( iCount, GetAmmoDef()->Index(szName) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::RemoveAllAmmo( ) +{ + for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) + { + m_iAmmo.Set( i, 0 ); + } +} + +//----------------------------------------------------------------------------- +// FIXME: This is a sort of hack back-door only used by physgun! +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::SetAmmoCount( int iCount, int iAmmoIndex ) +{ + // NOTE: No sound, no max check! Seems pretty bogus to me! + m_iAmmo.Set( iAmmoIndex, iCount ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the amount of ammunition of a particular type owned +// owned by the character +// Input : Ammo Index +// Output : The amount of ammo +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::GetAmmoCount( int iAmmoIndex ) const +{ + if ( iAmmoIndex == -1 ) + return 0; + + // Infinite ammo? + if ( GetAmmoDef()->MaxCarry( iAmmoIndex ) == INFINITE_AMMO ) + return 999; + + return m_iAmmo[ iAmmoIndex ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the amount of ammunition of the specified type the character's carrying +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::GetAmmoCount( char *szName ) const +{ + return GetAmmoCount( GetAmmoDef()->Index(szName) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns weapon if already owns a weapon of this class +//----------------------------------------------------------------------------- +CBaseCombatWeapon* CBaseCombatCharacter::Weapon_OwnsThisType( const char *pszWeapon, int iSubType ) const +{ + // Check for duplicates + for (int i=0;iGetSubType() == iSubType ) + return m_hMyWeapons[i]; + } + } + return NULL; +} + + +int CBaseCombatCharacter::BloodColor() +{ + return m_bloodColor; +} + + +//----------------------------------------------------------------------------- +// Blood color (see BLOOD_COLOR_* macros in baseentity.h) +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::SetBloodColor( int nBloodColor ) +{ + m_bloodColor = nBloodColor; +} + +//----------------------------------------------------------------------------- +/** + The main visibility check. Checks all the entity specific reasons that could + make IsVisible fail. Then checks points in space to get environmental reasons. + This is LOS, plus invisibility and fog and smoke and such. +*/ + +enum VisCacheResult_t +{ + VISCACHE_UNKNOWN = 0, + VISCACHE_IS_VISIBLE, + VISCACHE_IS_NOT_VISIBLE, +}; + +enum +{ + VIS_CACHE_INVALID = 0x80000000 +}; + +#define VIS_CACHE_ENTRY_LIFE .090f + +class CCombatCharVisCache : public CAutoGameSystemPerFrame +{ +public: + virtual void FrameUpdatePreEntityThink(); + virtual void LevelShutdownPreEntity(); + + int LookupVisibility( const CBaseCombatCharacter *pChar1, CBaseCombatCharacter *pChar2 ); + VisCacheResult_t HasVisibility( int iCache ) const; + void RegisterVisibility( int iCache, bool bChar1SeesChar2, bool bChar2SeesChar1 ); + +private: + struct VisCacheEntry_t + { + CHandle< CBaseCombatCharacter > m_hEntity1; + CHandle< CBaseCombatCharacter > m_hEntity2; + float m_flTime; + bool m_bEntity1CanSeeEntity2; + bool m_bEntity2CanSeeEntity1; + }; + + class CVisCacheEntryLess + { + public: + CVisCacheEntryLess( int ) {} + bool operator!() const { return false; } + bool operator()( const VisCacheEntry_t &lhs, const VisCacheEntry_t &rhs ) const + { + return ( memcmp( &lhs, &rhs, offsetof( VisCacheEntry_t, m_flTime ) ) < 0 ); + } + }; + + CUtlRBTree< VisCacheEntry_t, unsigned short, CVisCacheEntryLess > m_VisCache; + + mutable int m_nTestCount; + mutable int m_nHitCount; +}; + +void CCombatCharVisCache::FrameUpdatePreEntityThink() +{ + // Msg( "test: %d/%d\n", m_nHitCount, m_nTestCount ); + + // Lazy retirement of vis cache + // NOTE: 256 was chosen heuristically based on a playthrough where 200 + // was the max # in the viscache where nothing could be retired. + if ( m_VisCache.Count() < 256 ) + return; + + int nMaxIndex = m_VisCache.MaxElement() - 1; + for ( int i = 0; i < 8; ++i ) + { + int n = RandomInt( 0, nMaxIndex ); + if ( !m_VisCache.IsValidIndex( n ) ) + continue; + + const VisCacheEntry_t &entry = m_VisCache[n]; + if ( !entry.m_hEntity1.IsValid() || !entry.m_hEntity2.IsValid() || ( gpGlobals->curtime - entry.m_flTime > 10.0f ) ) + { + m_VisCache.RemoveAt( n ); + } + } +} + +void CCombatCharVisCache::LevelShutdownPreEntity() +{ + m_VisCache.Purge(); +} + +int CCombatCharVisCache::LookupVisibility( const CBaseCombatCharacter *pChar1, CBaseCombatCharacter *pChar2 ) +{ + VisCacheEntry_t cacheEntry; + if ( pChar1 < pChar2 ) + { + cacheEntry.m_hEntity1 = pChar1; + cacheEntry.m_hEntity2 = pChar2; + } + else + { + cacheEntry.m_hEntity1 = pChar2; + cacheEntry.m_hEntity2 = pChar1; + } + + int iCache = m_VisCache.Find( cacheEntry ); + if ( iCache == m_VisCache.InvalidIndex() ) + { + if ( m_VisCache.Count() == m_VisCache.InvalidIndex() ) + return VIS_CACHE_INVALID; + + iCache = m_VisCache.Insert( cacheEntry ); + m_VisCache[iCache].m_flTime = gpGlobals->curtime - 2.0f * VIS_CACHE_ENTRY_LIFE; + } + + return ( pChar1 < pChar2 ) ? iCache : - iCache - 1; +} + +VisCacheResult_t CCombatCharVisCache::HasVisibility( int iCache ) const +{ + if ( iCache == VIS_CACHE_INVALID ) + return VISCACHE_UNKNOWN; + + m_nTestCount++; + + bool bReverse = ( iCache < 0 ); + if ( bReverse ) + { + iCache = - iCache - 1; + } + + const VisCacheEntry_t &entry = m_VisCache[iCache]; + if ( gpGlobals->curtime - entry.m_flTime > VIS_CACHE_ENTRY_LIFE ) + return VISCACHE_UNKNOWN; + + m_nHitCount++; + + bool bIsVisible = !bReverse ? entry.m_bEntity1CanSeeEntity2 : entry.m_bEntity2CanSeeEntity1; + return bIsVisible ? VISCACHE_IS_VISIBLE : VISCACHE_IS_NOT_VISIBLE; +} + +void CCombatCharVisCache::RegisterVisibility( int iCache, bool bEntity1CanSeeEntity2, bool bEntity2CanSeeEntity1 ) +{ + if ( iCache == VIS_CACHE_INVALID ) + return; + + bool bReverse = ( iCache < 0 ); + if ( bReverse ) + { + iCache = - iCache - 1; + } + + VisCacheEntry_t &entry = m_VisCache[iCache]; + entry.m_flTime = gpGlobals->curtime; + if ( !bReverse ) + { + entry.m_bEntity1CanSeeEntity2 = bEntity1CanSeeEntity2; + entry.m_bEntity2CanSeeEntity1 = bEntity2CanSeeEntity1; + } + else + { + entry.m_bEntity1CanSeeEntity2 = bEntity2CanSeeEntity1; + entry.m_bEntity2CanSeeEntity1 = bEntity1CanSeeEntity2; + } +} + +static CCombatCharVisCache s_CombatCharVisCache; + +bool CBaseCombatCharacter::IsAbleToSee( const CBaseEntity *pEntity, FieldOfViewCheckType checkFOV ) +{ + CBaseCombatCharacter *pBCC = const_cast( pEntity )->MyCombatCharacterPointer(); + if ( pBCC ) + return IsAbleToSee( pBCC, checkFOV ); + + // Test this every time; it's cheap. + Vector vecEyePosition = EyePosition(); + Vector vecTargetPosition = pEntity->WorldSpaceCenter(); + +#ifdef GAME_DLL + Vector vecEyeToTarget; + VectorSubtract( vecTargetPosition, vecEyePosition, vecEyeToTarget ); + float flDistToOther = VectorNormalize( vecEyeToTarget ); + + // We can't see because they are too far in the fog + if ( IsHiddenByFog( flDistToOther ) ) + return false; +#endif + + if ( !ComputeLOS( vecEyePosition, vecTargetPosition ) ) + return false; + +#if defined(GAME_DLL) && defined(TERROR) + if ( flDistToOther > NavObscureRange.GetFloat() ) + { + const float flMaxDistance = 100.0f; + TerrorNavArea *pTargetArea = static_cast< TerrorNavArea* >( TheNavMesh->GetNearestNavArea( vecTargetPosition, false, flMaxDistance ) ); + if ( !pTargetArea || pTargetArea->HasSpawnAttributes( TerrorNavArea::SPAWN_OBSCURED ) ) + return false; + + if ( ComputeTargetIsInDarkness( vecEyePosition, pTargetArea, vecTargetPosition ) ) + return false; + } +#endif + + return ( checkFOV != USE_FOV || IsInFieldOfView( vecTargetPosition ) ); +} + +static void ComputeSeeTestPosition( Vector *pEyePosition, CBaseCombatCharacter *pBCC ) +{ +#if defined(GAME_DLL) && defined(TERROR) + if ( pBCC->IsPlayer() ) + { + CTerrorPlayer *pPlayer = ToTerrorPlayer( pBCC ); + *pEyePosition = !pPlayer->IsDead() ? pPlayer->EyePosition() : pPlayer->GetDeathPosition(); + } + else +#endif + { + *pEyePosition = pBCC->EyePosition(); + } +} + +bool CBaseCombatCharacter::IsAbleToSee( CBaseCombatCharacter *pBCC, FieldOfViewCheckType checkFOV ) +{ + Vector vecEyePosition, vecOtherEyePosition; + ComputeSeeTestPosition( &vecEyePosition, this ); + ComputeSeeTestPosition( &vecOtherEyePosition, pBCC ); + +#ifdef GAME_DLL + Vector vecEyeToTarget; + VectorSubtract( vecOtherEyePosition, vecEyePosition, vecEyeToTarget ); + float flDistToOther = VectorNormalize( vecEyeToTarget ); + + // Test this every time; it's cheap. + // We can't see because they are too far in the fog + if ( IsHiddenByFog( flDistToOther ) ) + return false; + +#ifdef TERROR + // Check this every time also, it's cheap; check to see if the enemy is in an obscured area. + bool bIsInNavObscureRange = ( flDistToOther > NavObscureRange.GetFloat() ); + if ( bIsInNavObscureRange ) + { + TerrorNavArea *pOtherNavArea = static_cast< TerrorNavArea* >( pBCC->GetLastKnownArea() ); + if ( !pOtherNavArea || pOtherNavArea->HasSpawnAttributes( TerrorNavArea::SPAWN_OBSCURED ) ) + return false; + } +#endif // TERROR +#endif + + // Check if we have a cached-off visibility + int iCache = s_CombatCharVisCache.LookupVisibility( this, pBCC ); + VisCacheResult_t nResult = s_CombatCharVisCache.HasVisibility( iCache ); + + // Compute symmetric visibility + if ( nResult == VISCACHE_UNKNOWN ) + { + bool bThisCanSeeOther = false, bOtherCanSeeThis = false; + if ( ComputeLOS( vecEyePosition, vecOtherEyePosition ) ) + { +#if defined(GAME_DLL) && defined(TERROR) + if ( !bIsInNavObscureRange ) + { + bThisCanSeeOther = true, bOtherCanSeeThis = true; + } + else + { + bThisCanSeeOther = !ComputeTargetIsInDarkness( vecEyePosition, pBCC->GetLastKnownArea(), vecOtherEyePosition ); + bOtherCanSeeThis = !ComputeTargetIsInDarkness( vecOtherEyePosition, GetLastKnownArea(), vecEyePosition ); + } +#else + bThisCanSeeOther = true, bOtherCanSeeThis = true; +#endif + } + + s_CombatCharVisCache.RegisterVisibility( iCache, bThisCanSeeOther, bOtherCanSeeThis ); + nResult = bThisCanSeeOther ? VISCACHE_IS_VISIBLE : VISCACHE_IS_NOT_VISIBLE; + } + + if ( nResult == VISCACHE_IS_VISIBLE ) + return ( checkFOV != USE_FOV || IsInFieldOfView( pBCC ) ); + + return false; +} + +class CTraceFilterNoCombatCharacters : public CTraceFilterSimple +{ +public: + CTraceFilterNoCombatCharacters( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE ) + : CTraceFilterSimple( passentity, collisionGroup ) + { + } + + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) ) + { + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + if ( !pEntity ) + return NULL; + + if ( pEntity->MyCombatCharacterPointer() || pEntity->MyCombatWeaponPointer() ) + return false; + + // Honor BlockLOS - this lets us see through partially-broken doors, etc + if ( !pEntity->BlocksLOS() ) + return false; + + return true; + } + + return false; + } +}; + +bool CBaseCombatCharacter::ComputeLOS( const Vector &vecEyePosition, const Vector &vecTarget ) const +{ + // We simply can't see because the world is in the way. + trace_t result; + CTraceFilterNoCombatCharacters traceFilter( NULL, COLLISION_GROUP_NONE ); + UTIL_TraceLine( vecEyePosition, vecTarget, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_MONSTER, &traceFilter, &result ); + return ( result.fraction == 1.0f ); +} + +#if defined(GAME_DLL) && defined(TERROR) +bool CBaseCombatCharacter::ComputeTargetIsInDarkness( const Vector &vecEyePosition, CNavArea *pTargetNavArea, const Vector &vecTargetPos ) const +{ + if ( GetTeamNumber() != TEAM_SURVIVOR ) + return false; + + // Check light info + const float flMinLightIntensity = 0.1f; + + if ( !pTargetNavArea || ( pTargetNavArea->GetLightIntensity() >= flMinLightIntensity ) ) + return false; + + CTraceFilterNoNPCsOrPlayer lightingFilter( this, COLLISION_GROUP_NONE ); + + Vector vecSightDirection; + VectorSubtract( vecTargetPos, vecEyePosition, vecSightDirection ); + VectorNormalize( vecSightDirection ); + + trace_t result; + UTIL_TraceLine( vecTargetPos, vecTargetPos + vecSightDirection * 32768.0f, MASK_L4D_VISION, &lightingFilter, &result ); + if ( ( result.fraction < 1.0f ) && ( ( result.surface.flags & SURF_SKY ) == 0 ) ) + { + const float flMaxDistance = 100.0f; + TerrorNavArea *pFarArea = (TerrorNavArea *)TheNavMesh->GetNearestNavArea( result.endpos, false, flMaxDistance ); + + // Target is in darkness, the wall behind him is too, and we are too far away + if ( pFarArea && pFarArea->GetLightIntensity() < flMinLightIntensity ) + return true; + } + + return false; +} +#endif + + +//----------------------------------------------------------------------------- +/** + Return true if our view direction is pointing at the given target, + within the cosine of the angular tolerance. LINE OF SIGHT IS NOT CHECKED. +*/ +bool CBaseCombatCharacter::IsLookingTowards( const CBaseEntity *target, float cosTolerance ) const +{ + return IsLookingTowards( target->WorldSpaceCenter(), cosTolerance ) || IsLookingTowards( target->EyePosition(), cosTolerance ) || IsLookingTowards( target->GetAbsOrigin(), cosTolerance ); +} + + +//----------------------------------------------------------------------------- +/** + Return true if our view direction is pointing at the given target, + within the cosine of the angular tolerance. LINE OF SIGHT IS NOT CHECKED. +*/ +bool CBaseCombatCharacter::IsLookingTowards( const Vector &target, float cosTolerance ) const +{ + Vector toTarget = target - EyePosition(); + toTarget.NormalizeInPlace(); + + Vector forward; + AngleVectors( EyeAngles(), &forward ); + + return ( DotProduct( forward, toTarget ) >= cosTolerance ); +} + + +//----------------------------------------------------------------------------- +/** + Returns true if we are looking towards something within a tolerence determined + by our field of view +*/ +bool CBaseCombatCharacter::IsInFieldOfView( CBaseEntity *entity ) const +{ + CBasePlayer *pPlayer = ToBasePlayer( const_cast< CBaseCombatCharacter* >( this ) ); + float flTolerance = pPlayer ? cos( (float)pPlayer->GetFOV() * 0.5f ) : BCC_DEFAULT_LOOK_TOWARDS_TOLERANCE; + + Vector vecForward; + Vector vecEyePosition = EyePosition(); + AngleVectors( EyeAngles(), &vecForward ); + + // FIXME: Use a faster check than this! + + // Check 3 spots, or else when standing right next to someone looking at their eyes, + // the angle will be too great to see their center. + Vector vecToTarget = entity->GetAbsOrigin() - vecEyePosition; + vecToTarget.NormalizeInPlace(); + if ( DotProduct( vecForward, vecToTarget ) >= flTolerance ) + return true; + + vecToTarget = entity->WorldSpaceCenter() - vecEyePosition; + vecToTarget.NormalizeInPlace(); + if ( DotProduct( vecForward, vecToTarget ) >= flTolerance ) + return true; + + vecToTarget = entity->EyePosition() - vecEyePosition; + vecToTarget.NormalizeInPlace(); + return ( DotProduct( vecForward, vecToTarget ) >= flTolerance ); +} + +//----------------------------------------------------------------------------- +/** + Returns true if we are looking towards something within a tolerence determined + by our field of view +*/ +bool CBaseCombatCharacter::IsInFieldOfView( const Vector &pos ) const +{ + CBasePlayer *pPlayer = ToBasePlayer( const_cast< CBaseCombatCharacter* >( this ) ); + + if ( pPlayer ) + return IsLookingTowards( pos, cos( (float)pPlayer->GetFOV() * 0.5f ) ); + + return IsLookingTowards( pos ); +} + +//----------------------------------------------------------------------------- +/** + Strictly checks Line of Sight only. +*/ + +bool CBaseCombatCharacter::IsLineOfSightClear( CBaseEntity *entity, LineOfSightCheckType checkType ) const +{ +#ifdef CLIENT_DLL + if ( entity->MyCombatCharacterPointer() ) + return IsLineOfSightClear( entity->EyePosition(), checkType, entity ); + return IsLineOfSightClear( entity->WorldSpaceCenter(), checkType, entity ); +#else + // FIXME: Should we do the same check here as the client does? + return IsLineOfSightClear( entity->WorldSpaceCenter(), checkType, entity ) || IsLineOfSightClear( entity->EyePosition(), checkType, entity ) || IsLineOfSightClear( entity->GetAbsOrigin(), checkType, entity ); +#endif +} + +//----------------------------------------------------------------------------- +/** + Strictly checks Line of Sight only. +*/ +static bool TraceFilterNoCombatCharacters( IHandleEntity *pServerEntity, int contentsMask ) +{ + // Honor BlockLOS also to allow seeing through partially-broken doors + CBaseEntity *entity = EntityFromEntityHandle( pServerEntity ); + return ( entity->MyCombatCharacterPointer() == NULL && !entity->MyCombatWeaponPointer() && entity->BlocksLOS() ); +} + +bool CBaseCombatCharacter::IsLineOfSightClear( const Vector &pos, LineOfSightCheckType checkType, CBaseEntity *entityToIgnore ) const +{ +#if defined(GAME_DLL) && defined(COUNT_BCC_LOS) + static int count, frame; + if ( frame != gpGlobals->framecount ) + { + Msg( ">> %d\n", count ); + frame = gpGlobals->framecount; + count = 0; + } + count++; +#endif + if( checkType == IGNORE_ACTORS ) + { + + // use the query cache unless it causes problems +#if defined(GAME_DLL) && defined(TERROR) + return IsLineOfSightBetweenTwoEntitiesClear( const_cast(this), EOFFSET_MODE_EYEPOSITION, + entityToIgnore, EOFFSET_MODE_WORLDSPACE_CENTER, + entityToIgnore, COLLISION_GROUP_NONE, + MASK_L4D_VISION, TraceFilterNoCombatCharacters, 1.0 ); +#else + trace_t trace; + CTraceFilterNoCombatCharacters traceFilter( entityToIgnore, COLLISION_GROUP_NONE ); + UTIL_TraceLine( EyePosition(), pos, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_MONSTER, &traceFilter, &trace ); + + return trace.fraction == 1.0f; +#endif + } + else + { + trace_t trace; + CTraceFilterSkipTwoEntities traceFilter( this, entityToIgnore, COLLISION_GROUP_NONE ); + UTIL_TraceLine( EyePosition(), pos, MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE, &traceFilter, &trace ); + + return trace.fraction == 1.0f; + } +} + + +/* +//--------------------------------------------------------------------------------------------------------------------------- +surfacedata_t * CBaseCombatCharacter::GetGroundSurface( void ) const +{ + Vector start( vec3_origin ); + Vector end( 0, 0, -64 ); + + Vector vecMins, vecMaxs; + CollisionProp()->WorldSpaceAABB( &vecMins, &vecMaxs ); + + Ray_t ray; + ray.Init( start, end, vecMins, vecMaxs ); + + trace_t trace; + UTIL_TraceRay( ray, MASK_SOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + if ( trace.fraction == 1.0f ) + return NULL; // no ground + + return physprops->GetSurfaceData( trace.surface.surfaceProps ); +} +*/ -- cgit v1.2.3