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.
454 lines
14 KiB
454 lines
14 KiB
//========= 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 ); |
|
}
|
|
|