source-engine/game/server/hl2/info_darknessmode_lightsource.cpp

455 lines
14 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= 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 );
}