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.
463 lines
12 KiB
463 lines
12 KiB
//========= 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<CSensorGrenadeProjectile*>( 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<CSensorGrenadeProjectile*>( spotter ) )
|
|
{
|
|
//CCSPlayer *csTargetPlayer = ToCSPlayer( m_targetEntity );
|
|
//if ( spotter->IsOtherEnemy( m_targetEntity ) )
|
|
{
|
|
eye = spotter->GetAbsOrigin();
|
|
csPlayerSpotter = ToCSPlayer( dynamic_cast<CSensorGrenadeProjectile*>( 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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|