//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: A game system for tracking and updating entity spot state // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "cs_entity_spotting.h" #include "cs_player.h" #include "cs_bot.h" #include "sensorgrenade_projectile.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define ENTITY_SPOT_FREQUENCY 0.5f static CCSEntitySpotting s_EntitySpotting("CCSEntitySpotting"); CCSEntitySpotting * g_EntitySpotting = &s_EntitySpotting; ConVar radarvis( "radarvismethod", "1", FCVAR_CHEAT, "0 for traditional method, 1 for more realistic method", true, 0, true, 1 ); ConVar radarpow( "radarvispow", ".4", FCVAR_CHEAT, "the degree to which you can point away from a target, and still see them on radar." ); ConVar radardist( "radarvisdistance", "1000.0f", FCVAR_CHEAT, "at this distance and beyond you need to be point right at someone to see them", true, 10, false, 0 ); ConVar radarmaxdot( "radarvismaxdot", ".996", FCVAR_CHEAT, "how closely you have to point at someone to see them beyond max distance", true, 0, true, 1.0f ); //-------------------------------------------------------------------------------------------------------- // Functors //-------------------------------------------------------------------------------------------------------- template < typename SpotFunctor > bool ForEachEntitySpotter( SpotFunctor &func ) { VPROF( "ForEachEntitySpotter" ); for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast< CBasePlayer * >( UTIL_PlayerByIndex( i ) ); if ( player == NULL ) continue; if ( FNullEnt( player->edict() ) ) continue; if ( !player->IsPlayer() ) continue; if ( !player->IsConnected() ) continue; if ( func( player ) == false ) return false; } //ActiveGrenadeList activeGrenadeList = TheBots->m_activeGrenadeList; FOR_EACH_LL( TheCSBots()->m_activeGrenadeList, it ) { ActiveGrenade *ag = TheCSBots()->m_activeGrenadeList[it]; if ( ag->IsSensor() == false ) continue; CBaseGrenade *grenade = ag->GetEntity(); if ( grenade == NULL ) continue; if ( FNullEnt( grenade->edict() ) ) continue; if ( func( grenade ) == false ) return false; } /* CBaseEntity * pEntity = gEntList.FirstEnt(); while ( pEntity ) { if ( pEntity == NULL || FNullEnt( pEntity->edict() ) ) { pEntity = gEntList.NextEnt( pEntity ); continue; } if ( FNullEnt( pEntity->edict() ) ) { pEntity = gEntList.NextEnt( pEntity ); continue; } if ( pEntity->IsPlayer() ) { CBasePlayer *player = static_cast< CBasePlayer * >( pEntity ); if ( !player->IsConnected() ) { pEntity = gEntList.NextEnt( pEntity ); continue; } } if ( func( pEntity ) == false ) return false; // else if ( dynamic_cast( pEntity ) ) // { // if ( pThrower->IsOtherEnemy( pPlayer ) ) // { // Vector vDelta = pPlayer->EyePosition() - GetAbsOrigin(); // float flDistance = vDelta.Length(); // // float flMaxDrawDist = 1024; // if ( flDistance <= flMaxDrawDist ) // { // trace_t tr; // //if ( pCSPlayer->IsAlive() && ( flTargetIDCone > flViewCone ) && !bShowAllNamesForSpec ) // { // if ( TheCSBots()->IsLineBlockedBySmoke( pPlayer->EyePosition(), GetAbsOrigin(), 1.0f ) ) // { // continue; // } // // UTIL_TraceLine( pPlayer->EyePosition(), GetAbsOrigin(), MASK_VISIBLE, pPlayer, COLLISION_GROUP_DEBRIS, &tr ); // if ( tr.fraction != 1 ) // { // trace_t tr2; // UTIL_TraceLine( pPlayer->GetAbsOrigin() + Vector( 0, 0, 16 ), GetAbsOrigin(), MASK_VISIBLE, pPlayer, COLLISION_GROUP_DEBRIS, &tr2 ); // if ( tr2.fraction != 1 ) // { // continue; // } // } // // pPlayer->SetIsSpotted( true ); // pPlayer->SetIsSpottedBy( nThrowerIndex ); // } // } // } // } pEntity = gEntList.NextEnt( pEntity ); }*/ return true; } //================================================== // - CanPlayerSeeTargetEntityFunctor - // // Determine if a player has spotted a target entity. // Query IsSpotted() for result //================================================== class CanPlayerSeeTargetEntityFunctor { public: CanPlayerSeeTargetEntityFunctor( CBaseEntity *entity, int spottingTeam ) { m_targetEntity = entity; m_target = entity->EyePosition(); m_team = spottingTeam; m_spotted = false; } bool operator()( CBaseEntity *spotter ) { CCSPlayer *csPlayer = ToCSPlayer( spotter ); if ( (csPlayer && csPlayer->IsAlive() == false ) || spotter->GetTeamNumber() != m_team ) return true; CCSPlayer *csPlayerSpotter = NULL; bool doTrace = false; Vector eye, forward; if ( csPlayer ) { csPlayerSpotter = csPlayer; if ( csPlayer->IsBlind() ) return true; csPlayer->EyePositionAndVectors( &eye, &forward, NULL, NULL ); Vector path( m_target - eye ); float distance = path.Length(); path.NormalizeInPlace(); float dot = DotProduct( forward, path ); if ( dot < 0 ) return true; int rvm = radarvis.GetInt(); switch ( rvm ) { case 0:// original method doTrace = ( ( dot > 0.995f ) || ( dot > 0.98f && distance < 900 ) || ( dot > 0.8f && distance < 250 ) ); break; case 1: // new method method { int fov = csPlayer->GetFOVForNetworking() / 2; float cosfov = cosf( ( float )fov*3.1415f / 180.0f ); float d = distance / radardist.GetFloat(); d = clamp( powf( d, radarpow.GetFloat() ), cosfov, radarmaxdot.GetFloat() ); doTrace = ( dot > d ); } break; } } else if ( dynamic_cast( spotter ) ) { //CCSPlayer *csTargetPlayer = ToCSPlayer( m_targetEntity ); //if ( spotter->IsOtherEnemy( m_targetEntity ) ) { eye = spotter->GetAbsOrigin(); csPlayerSpotter = ToCSPlayer( dynamic_cast( spotter )->GetThrower() ); doTrace = true; /* Vector vDelta = csTargetPlayer->EyePosition() - spotter->GetAbsOrigin(); float flDistance = vDelta.Length(); float flMaxDrawDist = 1024; if ( flDistance <= flMaxDrawDist ) { trace_t tr; //if ( pCSPlayer->IsAlive() && ( flTargetIDCone > flViewCone ) && !bShowAllNamesForSpec ) { if ( TheCSBots()->IsLineBlockedBySmoke( csTargetPlayer->EyePosition(), GetAbsOrigin(), 1.0f ) == false ) { UTIL_TraceLine( csTargetPlayer->EyePosition(), GetAbsOrigin(), MASK_VISIBLE, csTargetPlayer, COLLISION_GROUP_DEBRIS, &tr ); trace_t tr2; UTIL_TraceLine( csTargetPlayer->GetAbsOrigin() + Vector( 0, 0, 16 ), GetAbsOrigin(), MASK_VISIBLE, csTargetPlayer, COLLISION_GROUP_DEBRIS, &tr2 ); if ( tr1.fraction == 1 || tr2.fraction == 1 ) { continue; } } } pPlayer->SetIsSpotted( true ); pPlayer->SetIsSpottedBy( nThrowerIndex ); } } */ } } if (doTrace && csPlayerSpotter) { trace_t tr; CTraceFilterSkipTwoEntities filter( spotter, m_targetEntity, COLLISION_GROUP_DEBRIS ); UTIL_TraceLine( eye, m_target, (CONTENTS_OPAQUE|CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_DEBRIS|MASK_OPAQUE_AND_NPCS), &filter, &tr ); if ( tr.fraction == 1.0f && TheCSBots()->IsLineBlockedBySmoke( eye, m_target, 1.0f ) == false ) { if ( !csPlayer || (csPlayer && csPlayer->GetFogObscuredRatio( m_targetEntity ) < 0.9) ) { m_spotted = true; m_spottedBy.AddToTail( csPlayerSpotter->entindex() ); //return false; // spotted already, so no reason to check for other players spotting the same thing. } } } return true; } bool IsSpotted( void ) const { return m_spotted; } int GetIsSpottedBy( int nUtlIndex ) const { if ( nUtlIndex < m_spottedBy.Count() ) return m_spottedBy[nUtlIndex]; return 0; } int GetSpottedByCount( void ) const { return m_spottedBy.Count(); } private: CBaseEntity *m_targetEntity; Vector m_target; int m_team; bool m_spotted; CUtlVector < int > m_spottedBy; }; #define BIT_SET( a, b ) ((a)[(b)>>3] & (1<<((b)&7))) extern bool WasPlayerOccluded( int fromplayer, int toplayer ); //=========================================================== // - GatherNonPVSSpottedEntitiesFunctor - // // Given a player, generate a list of spotted entities that // exist outside of that player's PVS. // Query GetSpotted for result //=========================================================== GatherNonPVSSpottedEntitiesFunctor::GatherNonPVSSpottedEntitiesFunctor( CCSPlayer * pPlayer ) : m_pPlayer( pPlayer ) { if ( pPlayer ) { m_nSourceTeam = pPlayer->GetAssociatedTeamNumber(); engine->GetPVSForCluster( engine->GetClusterForOrigin( pPlayer->EyePosition() ), sizeof( m_pSourcePVS ), m_pSourcePVS ); // spectators and OBS_ALLOW_ALL observers receive updates on all spottable entities if ( m_nSourceTeam == TEAM_SPECTATOR ) { m_bForceSpot = true; } else if ( pPlayer->GetObserverMode() != OBS_MODE_NONE ) { ConVarRef mp_forcecamera( "mp_forcecamera" ); m_bForceSpot = mp_forcecamera.GetInt() == OBS_ALLOW_ALL; } else { m_bForceSpot = false; } } } bool GatherNonPVSSpottedEntitiesFunctor::operator()( CBaseEntity * pEntity ) { if ( !pEntity->edict() || !pEntity->CanBeSpotted() ) { return true; } CBaseEntity *pParent = pEntity->GetRootMoveParent(); int iBitNumber = engine->GetClusterForOrigin( pParent->EyePosition() ); // We only care about entities who are not within this player's PVS // We include being occluded as being outside of PVS. if ( !BIT_SET( m_pSourcePVS, iBitNumber ) || ( m_pPlayer && pParent->entindex() <= MAX_PLAYERS && WasPlayerOccluded( pParent->entindex(), m_pPlayer->entindex() ) ) ) { // target outside of PVS int nSpotRules = pEntity->GetSpotRules(); bool bForceSpotted = false; if ( ( nSpotRules & CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_FRIEND ) && ( pEntity->GetTeamNumber() == m_nSourceTeam ) ) bForceSpotted = true; if ( ( nSpotRules & CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_CT ) && ( TEAM_CT == m_nSourceTeam ) ) bForceSpotted = true; if ( ( nSpotRules & CCSEntitySpotting::SPOT_RULE_ALWAYS_SEEN_BY_T ) && ( TEAM_TERRORIST == m_nSourceTeam ) ) bForceSpotted = true; CBasePlayer * pPlayer = UTIL_PlayerByIndex( pEntity->entindex() ); if ( pPlayer ) { // do not include dead players, observers, etc if ( !pPlayer->IsAlive() || pPlayer->IsObserver() || !pPlayer->IsConnected() ) { return true; } } m_EntitySpotted.Set( pEntity->entindex(), ( m_bForceSpot || pEntity->IsSpotted() || bForceSpotted ) ); } return true; } //-------------------------------------------------------------------------------------------------------- // Entity Spotting Game System //-------------------------------------------------------------------------------------------------------- CCSEntitySpotting::CCSEntitySpotting( const char * szName ) : CAutoGameSystemPerFrame( szName ), m_fLastUpdate( 0.0f ) { } void CCSEntitySpotting::FrameUpdatePostEntityThink( void ) { if ( gpGlobals->curtime > ( m_fLastUpdate + ENTITY_SPOT_FREQUENCY ) ) { UpdateSpottedEntities(); } } void CCSEntitySpotting::UpdateSpottedEntities( void ) { m_fLastUpdate = gpGlobals->curtime; CBaseEntity * pEntity = gEntList.FirstEnt(); while ( pEntity ) { if ( pEntity->CanBeSpotted() ) { int nTeamID = 0; if ( pEntity->GetSpotRules() & SPOT_RULE_ENEMY ) { nTeamID = (pEntity->GetTeamNumber( ) == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT; } else if ( pEntity->GetSpotRules() & SPOT_RULE_CT ) { nTeamID = TEAM_CT; } else if ( pEntity->GetSpotRules() & SPOT_RULE_T ) { nTeamID = TEAM_TERRORIST; } CanPlayerSeeTargetEntityFunctor canPlayerSeeTargetEntity( pEntity, nTeamID ); ForEachEntitySpotter( canPlayerSeeTargetEntity ); pEntity->SetIsSpotted( canPlayerSeeTargetEntity.IsSpotted( ) ); pEntity->ClearSpottedBy(); if ( canPlayerSeeTargetEntity.IsSpotted() ) { for ( int i = 0; i < canPlayerSeeTargetEntity.GetSpottedByCount(); i++ ) { pEntity->SetIsSpottedBy( canPlayerSeeTargetEntity.GetIsSpottedBy( i ) ); } } } pEntity = gEntList.NextEnt( pEntity ); } } bool CCSEntitySpotting::Init( void ) { return true; }