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.
455 lines
14 KiB
455 lines
14 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=============================================================================
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "info_darknessmode_lightsource.h"
|
||
|
#include "ai_debug_shared.h"
|
||
|
|
||
|
void CV_Debug_Darkness( IConVar *var, const char *pOldString, float flOldValue );
|
||
|
ConVar g_debug_darkness( "g_debug_darkness", "0", FCVAR_NONE, "Show darkness mode lightsources.", CV_Debug_Darkness );
|
||
|
ConVar darkness_ignore_LOS_to_sources( "darkness_ignore_LOS_to_sources", "1", FCVAR_NONE );
|
||
|
|
||
|
class CInfoDarknessLightSource;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Manages entities that provide light while in darkness mode
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CDarknessLightSourcesSystem : public CAutoGameSystem
|
||
|
{
|
||
|
public:
|
||
|
CDarknessLightSourcesSystem() : CAutoGameSystem( "CDarknessLightSourcesSystem" )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void LevelInitPreEntity();
|
||
|
|
||
|
void AddLightSource( CInfoDarknessLightSource *pEntity, float flRadius );
|
||
|
void RemoveLightSource( CInfoDarknessLightSource *pEntity );
|
||
|
bool IsEntityVisibleToTarget( CBaseEntity *pLooker, CBaseEntity *pTarget );
|
||
|
bool AreThereLightSourcesWithinRadius( CBaseEntity *pLooker, float flRadius );
|
||
|
void SetDebug( bool bDebug );
|
||
|
|
||
|
private:
|
||
|
struct lightsource_t
|
||
|
{
|
||
|
float flLightRadiusSqr;
|
||
|
CHandle<CInfoDarknessLightSource> hEntity;
|
||
|
};
|
||
|
|
||
|
CUtlVector<lightsource_t> m_LightSources;
|
||
|
};
|
||
|
|
||
|
CDarknessLightSourcesSystem *DarknessLightSourcesSystem();
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Darkness mode light source entity
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CInfoDarknessLightSource : public CBaseEntity
|
||
|
{
|
||
|
DECLARE_CLASS( CInfoDarknessLightSource, CBaseEntity );
|
||
|
public:
|
||
|
DECLARE_DATADESC();
|
||
|
|
||
|
virtual void Activate()
|
||
|
{
|
||
|
if ( m_bDisabled == false )
|
||
|
{
|
||
|
DarknessLightSourcesSystem()->AddLightSource( this, m_flLightRadius );
|
||
|
|
||
|
if ( g_debug_darkness.GetBool() )
|
||
|
{
|
||
|
SetThink( &CInfoDarknessLightSource::DebugThink );
|
||
|
SetNextThink( gpGlobals->curtime );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BaseClass::Activate();
|
||
|
}
|
||
|
virtual void UpdateOnRemove()
|
||
|
{
|
||
|
DarknessLightSourcesSystem()->RemoveLightSource( this );
|
||
|
BaseClass::UpdateOnRemove();
|
||
|
}
|
||
|
void SetLightRadius( float flRadius )
|
||
|
{
|
||
|
m_flLightRadius = flRadius;
|
||
|
}
|
||
|
|
||
|
void InputEnable( inputdata_t &inputdata )
|
||
|
{
|
||
|
DarknessLightSourcesSystem()->AddLightSource( this, m_flLightRadius );
|
||
|
m_bDisabled = false;
|
||
|
}
|
||
|
|
||
|
void InputDisable( inputdata_t &inputdata )
|
||
|
{
|
||
|
DarknessLightSourcesSystem()->RemoveLightSource( this );
|
||
|
m_bDisabled = true;
|
||
|
}
|
||
|
|
||
|
void DebugThink( void )
|
||
|
{
|
||
|
Vector vecRadius( m_flLightRadius, m_flLightRadius, m_flLightRadius );
|
||
|
NDebugOverlay::Box( GetAbsOrigin(), -vecRadius, vecRadius, 255,255,255, 8, 0.1 );
|
||
|
NDebugOverlay::Box( GetAbsOrigin(), -Vector(5,5,5), Vector(5,5,5), 255,0,0, 8, 0.1 );
|
||
|
SetNextThink( gpGlobals->curtime + 0.1 );
|
||
|
|
||
|
int textoffset = 0;
|
||
|
EntityText( textoffset, UTIL_VarArgs("Org: %.2f %.2f %.2f", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ), 0.1 );
|
||
|
textoffset++;
|
||
|
EntityText( textoffset, UTIL_VarArgs("Radius %.2f", m_flLightRadius), 0.1 );
|
||
|
textoffset++;
|
||
|
if ( m_bIgnoreLOS )
|
||
|
{
|
||
|
EntityText( textoffset, "Ignoring LOS", 0.1 );
|
||
|
textoffset++;
|
||
|
}
|
||
|
if ( m_bDisabled )
|
||
|
{
|
||
|
EntityText( textoffset, "DISABLED", 0.1 );
|
||
|
textoffset++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IgnoreLOS( void )
|
||
|
{
|
||
|
m_bIgnoreLOS = true;
|
||
|
}
|
||
|
|
||
|
bool ShouldIgnoreLOS( void )
|
||
|
{
|
||
|
return m_bIgnoreLOS;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
float m_flLightRadius;
|
||
|
bool m_bDisabled;
|
||
|
bool m_bIgnoreLOS;
|
||
|
};
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( info_darknessmode_lightsource, CInfoDarknessLightSource );
|
||
|
|
||
|
BEGIN_DATADESC( CInfoDarknessLightSource )
|
||
|
DEFINE_KEYFIELD( m_flLightRadius, FIELD_FLOAT, "LightRadius" ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||
|
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
|
||
|
DEFINE_FIELD( m_bIgnoreLOS, FIELD_BOOLEAN ),
|
||
|
|
||
|
DEFINE_THINKFUNC( DebugThink ),
|
||
|
END_DATADESC()
|
||
|
|
||
|
CDarknessLightSourcesSystem g_DarknessLightSourcesSystem;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CDarknessLightSourcesSystem *DarknessLightSourcesSystem()
|
||
|
{
|
||
|
return &g_DarknessLightSourcesSystem;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CDarknessLightSourcesSystem::LevelInitPreEntity()
|
||
|
{
|
||
|
m_LightSources.Purge();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CDarknessLightSourcesSystem::AddLightSource( CInfoDarknessLightSource *pEntity, float flRadius )
|
||
|
{
|
||
|
lightsource_t sNewSource;
|
||
|
sNewSource.hEntity = pEntity;
|
||
|
sNewSource.flLightRadiusSqr = flRadius * flRadius;
|
||
|
m_LightSources.AddToTail( sNewSource );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CDarknessLightSourcesSystem::RemoveLightSource( CInfoDarknessLightSource *pEntity )
|
||
|
{
|
||
|
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
if ( m_LightSources[i].hEntity == pEntity )
|
||
|
{
|
||
|
m_LightSources.Remove(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CDarknessLightSourcesSystem::IsEntityVisibleToTarget( CBaseEntity *pLooker, CBaseEntity *pTarget )
|
||
|
{
|
||
|
if ( pTarget->IsEffectActive( EF_BRIGHTLIGHT ) || pTarget->IsEffectActive( EF_DIMLIGHT ) )
|
||
|
return true;
|
||
|
|
||
|
bool bDebug = g_debug_darkness.GetBool();
|
||
|
if ( bDebug && pLooker )
|
||
|
{
|
||
|
bDebug = (pLooker->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) != 0;
|
||
|
}
|
||
|
|
||
|
trace_t tr;
|
||
|
|
||
|
// Loop through all the light sources. Do it backwards, so we can remove dead ones.
|
||
|
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
// Removed?
|
||
|
if ( m_LightSources[i].hEntity == NULL || m_LightSources[i].hEntity->IsMarkedForDeletion() )
|
||
|
{
|
||
|
m_LightSources.FastRemove( i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
CInfoDarknessLightSource *pLightSource = m_LightSources[i].hEntity;
|
||
|
|
||
|
// Close enough to a light source?
|
||
|
float flDistanceSqr = (pTarget->WorldSpaceCenter() - pLightSource->GetAbsOrigin()).LengthSqr();
|
||
|
if ( flDistanceSqr < m_LightSources[i].flLightRadiusSqr )
|
||
|
{
|
||
|
if ( pLightSource->ShouldIgnoreLOS() )
|
||
|
{
|
||
|
if ( bDebug )
|
||
|
{
|
||
|
NDebugOverlay::Line( pTarget->WorldSpaceCenter(), pLightSource->GetAbsOrigin(), 0,255,0,true, 0.1);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check LOS from the light to the target
|
||
|
CTraceFilterSkipTwoEntities filter( pTarget, pLooker, COLLISION_GROUP_NONE );
|
||
|
AI_TraceLine( pTarget->WorldSpaceCenter(), pLightSource->GetAbsOrigin(), MASK_BLOCKLOS, &filter, &tr );
|
||
|
if ( tr.fraction == 1.0 )
|
||
|
{
|
||
|
if ( bDebug )
|
||
|
{
|
||
|
NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0,true, 0.1);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( bDebug )
|
||
|
{
|
||
|
NDebugOverlay::Line( tr.startpos, tr.endpos, 255,0,0,true, 0.1);
|
||
|
NDebugOverlay::Line( tr.endpos, pLightSource->GetAbsOrigin(), 128,0,0,true, 0.1);
|
||
|
}
|
||
|
|
||
|
// If the target is within the radius of the light, don't do sillhouette checks
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !pLooker )
|
||
|
continue;
|
||
|
|
||
|
// Between a light source and the looker?
|
||
|
Vector vecLookerToLight = (pLightSource->GetAbsOrigin() - pLooker->WorldSpaceCenter());
|
||
|
Vector vecLookerToTarget = (pTarget->WorldSpaceCenter() - pLooker->WorldSpaceCenter());
|
||
|
float flDistToSource = VectorNormalize( vecLookerToLight );
|
||
|
float flDistToTarget = VectorNormalize( vecLookerToTarget );
|
||
|
float flDot = DotProduct( vecLookerToLight, vecLookerToTarget );
|
||
|
if ( flDot > 0 )
|
||
|
{
|
||
|
// Make sure the target is in front of the lightsource
|
||
|
if ( flDistToTarget < flDistToSource )
|
||
|
{
|
||
|
if ( bDebug )
|
||
|
{
|
||
|
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), pLooker->WorldSpaceCenter() + (vecLookerToLight * 128), 255,255,255,true, 0.1);
|
||
|
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), pLooker->WorldSpaceCenter() + (vecLookerToTarget * 128), 255,0,0,true, 0.1);
|
||
|
}
|
||
|
|
||
|
// Now, we need to find out if the light source is obscured by anything.
|
||
|
// To do this, we want to calculate the point of intersection between the light source
|
||
|
// sphere and the line from the looker through the target.
|
||
|
float flASqr = (flDistToSource * flDistToSource);
|
||
|
float flB = -2 * flDistToSource * flDot;
|
||
|
float flCSqr = m_LightSources[i].flLightRadiusSqr;
|
||
|
float flDesc = (flB * flB) - (4 * (flASqr - flCSqr));
|
||
|
if ( flDesc >= 0 )
|
||
|
{
|
||
|
float flLength = (-flB - sqrt(flDesc)) / 2;
|
||
|
Vector vecSpherePoint = pLooker->WorldSpaceCenter() + (vecLookerToTarget * flLength);
|
||
|
|
||
|
// We've got the point of intersection. See if we can see it.
|
||
|
CTraceFilterSkipTwoEntities filter( pTarget, pLooker, COLLISION_GROUP_NONE );
|
||
|
AI_TraceLine( pLooker->EyePosition(), vecSpherePoint, MASK_SOLID_BRUSHONLY, &filter, &tr );
|
||
|
|
||
|
if ( bDebug )
|
||
|
{
|
||
|
if (tr.fraction != 1.0)
|
||
|
{
|
||
|
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), vecSpherePoint, 255,0,0,true, 0.1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), vecSpherePoint, 0,255,0,true, 0.1);
|
||
|
NDebugOverlay::Line( pLightSource->GetAbsOrigin(), vecSpherePoint, 255,0,0,true, 0.1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( tr.fraction == 1.0 )
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CDarknessLightSourcesSystem::AreThereLightSourcesWithinRadius( CBaseEntity *pLooker, float flRadius )
|
||
|
{
|
||
|
float flRadiusSqr = (flRadius * flRadius);
|
||
|
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
// Removed?
|
||
|
if ( m_LightSources[i].hEntity == NULL || m_LightSources[i].hEntity->IsMarkedForDeletion() )
|
||
|
{
|
||
|
m_LightSources.FastRemove( i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
CBaseEntity *pLightSource = m_LightSources[i].hEntity;
|
||
|
|
||
|
// Close enough to a light source?
|
||
|
float flDistanceSqr = (pLooker->WorldSpaceCenter() - pLightSource->GetAbsOrigin()).LengthSqr();
|
||
|
if ( flDistanceSqr < flRadiusSqr )
|
||
|
{
|
||
|
trace_t tr;
|
||
|
AI_TraceLine( pLooker->EyePosition(), pLightSource->GetAbsOrigin(), MASK_SOLID_BRUSHONLY, pLooker, COLLISION_GROUP_NONE, &tr );
|
||
|
|
||
|
if ( g_debug_darkness.GetBool() )
|
||
|
{
|
||
|
if (tr.fraction != 1.0)
|
||
|
{
|
||
|
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), tr.endpos, 255,0,0,true, 0.1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), tr.endpos, 0,255,0,true, 0.1);
|
||
|
NDebugOverlay::Line( pLightSource->GetAbsOrigin(), tr.endpos, 255,0,0,true, 0.1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( tr.fraction == 1.0 )
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CDarknessLightSourcesSystem::SetDebug( bool bDebug )
|
||
|
{
|
||
|
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
CInfoDarknessLightSource *pLightSource = dynamic_cast<CInfoDarknessLightSource*>(m_LightSources[i].hEntity.Get());
|
||
|
if ( pLightSource )
|
||
|
{
|
||
|
if ( bDebug )
|
||
|
{
|
||
|
pLightSource->SetThink( &CInfoDarknessLightSource::DebugThink );
|
||
|
pLightSource->SetNextThink( gpGlobals->curtime );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pLightSource->SetThink( NULL );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CV_Debug_Darkness( IConVar *pConVar, const char *pOldString, float flOldValue )
|
||
|
{
|
||
|
ConVarRef var( pConVar );
|
||
|
DarknessLightSourcesSystem()->SetDebug( var.GetBool() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pEntity -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void AddEntityToDarknessCheck( CBaseEntity *pEntity, float flLightRadius /*=DARKNESS_LIGHTSOURCE_SIZE*/ )
|
||
|
{
|
||
|
// Create a light source, and attach it to the entity
|
||
|
CInfoDarknessLightSource *pLightSource = (CInfoDarknessLightSource *) CreateEntityByName( "info_darknessmode_lightsource" );
|
||
|
if ( pLightSource )
|
||
|
{
|
||
|
pLightSource->SetLightRadius( flLightRadius );
|
||
|
DispatchSpawn( pLightSource );
|
||
|
pLightSource->SetAbsOrigin( pEntity->WorldSpaceCenter() );
|
||
|
pLightSource->SetParent( pEntity );
|
||
|
pLightSource->Activate();
|
||
|
|
||
|
// Dynamically created darkness sources can ignore LOS
|
||
|
// to match the (broken) visual representation of our dynamic lights.
|
||
|
if ( darkness_ignore_LOS_to_sources.GetBool() )
|
||
|
{
|
||
|
pLightSource->IgnoreLOS();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pEntity -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void RemoveEntityFromDarknessCheck( CBaseEntity *pEntity )
|
||
|
{
|
||
|
// Find any light sources parented to this entity, and remove them
|
||
|
CBaseEntity *pChild = pEntity->FirstMoveChild();
|
||
|
while ( pChild )
|
||
|
{
|
||
|
CBaseEntity *pPrevChild = pChild;
|
||
|
pChild = pChild->NextMovePeer();
|
||
|
|
||
|
if ( dynamic_cast<CInfoDarknessLightSource*>(pPrevChild) )
|
||
|
{
|
||
|
UTIL_Remove( pPrevChild );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : *pEntity -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool LookerCouldSeeTargetInDarkness( CBaseEntity *pLooker, CBaseEntity *pTarget )
|
||
|
{
|
||
|
if ( DarknessLightSourcesSystem()->IsEntityVisibleToTarget( pLooker, pTarget ) )
|
||
|
{
|
||
|
//NDebugOverlay::Line( pTarget->WorldSpaceCenter(), pLooker->WorldSpaceCenter(), 0,255,0,true, 0.1);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Return true if there is at least 1 darkness light source within
|
||
|
// the specified radius of the looker.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool DarknessLightSourceWithinRadius( CBaseEntity *pLooker, float flRadius )
|
||
|
{
|
||
|
return DarknessLightSourcesSystem()->AreThereLightSourcesWithinRadius( pLooker, flRadius );
|
||
|
}
|