Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "info_darknessmode_lightsource.h"
  8. #include "ai_debug_shared.h"
  9. void CV_Debug_Darkness( IConVar *var, const char *pOldString, float flOldValue );
  10. ConVar g_debug_darkness( "g_debug_darkness", "0", FCVAR_NONE, "Show darkness mode lightsources.", CV_Debug_Darkness );
  11. ConVar darkness_ignore_LOS_to_sources( "darkness_ignore_LOS_to_sources", "1", FCVAR_NONE );
  12. class CInfoDarknessLightSource;
  13. //-----------------------------------------------------------------------------
  14. // Purpose: Manages entities that provide light while in darkness mode
  15. //-----------------------------------------------------------------------------
  16. class CDarknessLightSourcesSystem : public CAutoGameSystem
  17. {
  18. public:
  19. CDarknessLightSourcesSystem() : CAutoGameSystem( "CDarknessLightSourcesSystem" )
  20. {
  21. }
  22. void LevelInitPreEntity();
  23. void AddLightSource( CInfoDarknessLightSource *pEntity, float flRadius );
  24. void RemoveLightSource( CInfoDarknessLightSource *pEntity );
  25. bool IsEntityVisibleToTarget( CBaseEntity *pLooker, CBaseEntity *pTarget );
  26. bool AreThereLightSourcesWithinRadius( CBaseEntity *pLooker, float flRadius );
  27. void SetDebug( bool bDebug );
  28. private:
  29. struct lightsource_t
  30. {
  31. float flLightRadiusSqr;
  32. CHandle<CInfoDarknessLightSource> hEntity;
  33. };
  34. CUtlVector<lightsource_t> m_LightSources;
  35. };
  36. CDarknessLightSourcesSystem *DarknessLightSourcesSystem();
  37. //-----------------------------------------------------------------------------
  38. // Darkness mode light source entity
  39. //-----------------------------------------------------------------------------
  40. class CInfoDarknessLightSource : public CBaseEntity
  41. {
  42. DECLARE_CLASS( CInfoDarknessLightSource, CBaseEntity );
  43. public:
  44. DECLARE_DATADESC();
  45. virtual void Activate()
  46. {
  47. if ( m_bDisabled == false )
  48. {
  49. DarknessLightSourcesSystem()->AddLightSource( this, m_flLightRadius );
  50. if ( g_debug_darkness.GetBool() )
  51. {
  52. SetThink( &CInfoDarknessLightSource::DebugThink );
  53. SetNextThink( gpGlobals->curtime );
  54. }
  55. }
  56. BaseClass::Activate();
  57. }
  58. virtual void UpdateOnRemove()
  59. {
  60. DarknessLightSourcesSystem()->RemoveLightSource( this );
  61. BaseClass::UpdateOnRemove();
  62. }
  63. void SetLightRadius( float flRadius )
  64. {
  65. m_flLightRadius = flRadius;
  66. }
  67. void InputEnable( inputdata_t &inputdata )
  68. {
  69. DarknessLightSourcesSystem()->AddLightSource( this, m_flLightRadius );
  70. m_bDisabled = false;
  71. }
  72. void InputDisable( inputdata_t &inputdata )
  73. {
  74. DarknessLightSourcesSystem()->RemoveLightSource( this );
  75. m_bDisabled = true;
  76. }
  77. void DebugThink( void )
  78. {
  79. Vector vecRadius( m_flLightRadius, m_flLightRadius, m_flLightRadius );
  80. NDebugOverlay::Box( GetAbsOrigin(), -vecRadius, vecRadius, 255,255,255, 8, 0.1 );
  81. NDebugOverlay::Box( GetAbsOrigin(), -Vector(5,5,5), Vector(5,5,5), 255,0,0, 8, 0.1 );
  82. SetNextThink( gpGlobals->curtime + 0.1 );
  83. int textoffset = 0;
  84. EntityText( textoffset, UTIL_VarArgs("Org: %.2f %.2f %.2f", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ), 0.1 );
  85. textoffset++;
  86. EntityText( textoffset, UTIL_VarArgs("Radius %.2f", m_flLightRadius), 0.1 );
  87. textoffset++;
  88. if ( m_bIgnoreLOS )
  89. {
  90. EntityText( textoffset, "Ignoring LOS", 0.1 );
  91. textoffset++;
  92. }
  93. if ( m_bDisabled )
  94. {
  95. EntityText( textoffset, "DISABLED", 0.1 );
  96. textoffset++;
  97. }
  98. }
  99. void IgnoreLOS( void )
  100. {
  101. m_bIgnoreLOS = true;
  102. }
  103. bool ShouldIgnoreLOS( void )
  104. {
  105. return m_bIgnoreLOS;
  106. }
  107. private:
  108. float m_flLightRadius;
  109. bool m_bDisabled;
  110. bool m_bIgnoreLOS;
  111. };
  112. LINK_ENTITY_TO_CLASS( info_darknessmode_lightsource, CInfoDarknessLightSource );
  113. BEGIN_DATADESC( CInfoDarknessLightSource )
  114. DEFINE_KEYFIELD( m_flLightRadius, FIELD_FLOAT, "LightRadius" ),
  115. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  116. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  117. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  118. DEFINE_FIELD( m_bIgnoreLOS, FIELD_BOOLEAN ),
  119. DEFINE_THINKFUNC( DebugThink ),
  120. END_DATADESC()
  121. CDarknessLightSourcesSystem g_DarknessLightSourcesSystem;
  122. //-----------------------------------------------------------------------------
  123. // Purpose:
  124. //-----------------------------------------------------------------------------
  125. CDarknessLightSourcesSystem *DarknessLightSourcesSystem()
  126. {
  127. return &g_DarknessLightSourcesSystem;
  128. }
  129. //-----------------------------------------------------------------------------
  130. // Purpose:
  131. //-----------------------------------------------------------------------------
  132. void CDarknessLightSourcesSystem::LevelInitPreEntity()
  133. {
  134. m_LightSources.Purge();
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose:
  138. //-----------------------------------------------------------------------------
  139. void CDarknessLightSourcesSystem::AddLightSource( CInfoDarknessLightSource *pEntity, float flRadius )
  140. {
  141. lightsource_t sNewSource;
  142. sNewSource.hEntity = pEntity;
  143. sNewSource.flLightRadiusSqr = flRadius * flRadius;
  144. m_LightSources.AddToTail( sNewSource );
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose:
  148. //-----------------------------------------------------------------------------
  149. void CDarknessLightSourcesSystem::RemoveLightSource( CInfoDarknessLightSource *pEntity )
  150. {
  151. for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
  152. {
  153. if ( m_LightSources[i].hEntity == pEntity )
  154. {
  155. m_LightSources.Remove(i);
  156. }
  157. }
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose:
  161. //-----------------------------------------------------------------------------
  162. bool CDarknessLightSourcesSystem::IsEntityVisibleToTarget( CBaseEntity *pLooker, CBaseEntity *pTarget )
  163. {
  164. if ( pTarget->IsEffectActive( EF_BRIGHTLIGHT ) || pTarget->IsEffectActive( EF_DIMLIGHT ) )
  165. return true;
  166. bool bDebug = g_debug_darkness.GetBool();
  167. if ( bDebug && pLooker )
  168. {
  169. bDebug = (pLooker->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) != 0;
  170. }
  171. trace_t tr;
  172. // Loop through all the light sources. Do it backwards, so we can remove dead ones.
  173. for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
  174. {
  175. // Removed?
  176. if ( m_LightSources[i].hEntity == NULL || m_LightSources[i].hEntity->IsMarkedForDeletion() )
  177. {
  178. m_LightSources.FastRemove( i );
  179. continue;
  180. }
  181. CInfoDarknessLightSource *pLightSource = m_LightSources[i].hEntity;
  182. // Close enough to a light source?
  183. float flDistanceSqr = (pTarget->WorldSpaceCenter() - pLightSource->GetAbsOrigin()).LengthSqr();
  184. if ( flDistanceSqr < m_LightSources[i].flLightRadiusSqr )
  185. {
  186. if ( pLightSource->ShouldIgnoreLOS() )
  187. {
  188. if ( bDebug )
  189. {
  190. NDebugOverlay::Line( pTarget->WorldSpaceCenter(), pLightSource->GetAbsOrigin(), 0,255,0,true, 0.1);
  191. }
  192. return true;
  193. }
  194. // Check LOS from the light to the target
  195. CTraceFilterSkipTwoEntities filter( pTarget, pLooker, COLLISION_GROUP_NONE );
  196. AI_TraceLine( pTarget->WorldSpaceCenter(), pLightSource->GetAbsOrigin(), MASK_BLOCKLOS, &filter, &tr );
  197. if ( tr.fraction == 1.0 )
  198. {
  199. if ( bDebug )
  200. {
  201. NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0,true, 0.1);
  202. }
  203. return true;
  204. }
  205. if ( bDebug )
  206. {
  207. NDebugOverlay::Line( tr.startpos, tr.endpos, 255,0,0,true, 0.1);
  208. NDebugOverlay::Line( tr.endpos, pLightSource->GetAbsOrigin(), 128,0,0,true, 0.1);
  209. }
  210. // If the target is within the radius of the light, don't do sillhouette checks
  211. continue;
  212. }
  213. if ( !pLooker )
  214. continue;
  215. // Between a light source and the looker?
  216. Vector vecLookerToLight = (pLightSource->GetAbsOrigin() - pLooker->WorldSpaceCenter());
  217. Vector vecLookerToTarget = (pTarget->WorldSpaceCenter() - pLooker->WorldSpaceCenter());
  218. float flDistToSource = VectorNormalize( vecLookerToLight );
  219. float flDistToTarget = VectorNormalize( vecLookerToTarget );
  220. float flDot = DotProduct( vecLookerToLight, vecLookerToTarget );
  221. if ( flDot > 0 )
  222. {
  223. // Make sure the target is in front of the lightsource
  224. if ( flDistToTarget < flDistToSource )
  225. {
  226. if ( bDebug )
  227. {
  228. NDebugOverlay::Line( pLooker->WorldSpaceCenter(), pLooker->WorldSpaceCenter() + (vecLookerToLight * 128), 255,255,255,true, 0.1);
  229. NDebugOverlay::Line( pLooker->WorldSpaceCenter(), pLooker->WorldSpaceCenter() + (vecLookerToTarget * 128), 255,0,0,true, 0.1);
  230. }
  231. // Now, we need to find out if the light source is obscured by anything.
  232. // To do this, we want to calculate the point of intersection between the light source
  233. // sphere and the line from the looker through the target.
  234. float flASqr = (flDistToSource * flDistToSource);
  235. float flB = -2 * flDistToSource * flDot;
  236. float flCSqr = m_LightSources[i].flLightRadiusSqr;
  237. float flDesc = (flB * flB) - (4 * (flASqr - flCSqr));
  238. if ( flDesc >= 0 )
  239. {
  240. float flLength = (-flB - sqrt(flDesc)) / 2;
  241. Vector vecSpherePoint = pLooker->WorldSpaceCenter() + (vecLookerToTarget * flLength);
  242. // We've got the point of intersection. See if we can see it.
  243. CTraceFilterSkipTwoEntities filter( pTarget, pLooker, COLLISION_GROUP_NONE );
  244. AI_TraceLine( pLooker->EyePosition(), vecSpherePoint, MASK_SOLID_BRUSHONLY, &filter, &tr );
  245. if ( bDebug )
  246. {
  247. if (tr.fraction != 1.0)
  248. {
  249. NDebugOverlay::Line( pLooker->WorldSpaceCenter(), vecSpherePoint, 255,0,0,true, 0.1);
  250. }
  251. else
  252. {
  253. NDebugOverlay::Line( pLooker->WorldSpaceCenter(), vecSpherePoint, 0,255,0,true, 0.1);
  254. NDebugOverlay::Line( pLightSource->GetAbsOrigin(), vecSpherePoint, 255,0,0,true, 0.1);
  255. }
  256. }
  257. if ( tr.fraction == 1.0 )
  258. return true;
  259. }
  260. }
  261. }
  262. }
  263. return false;
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose:
  267. //-----------------------------------------------------------------------------
  268. bool CDarknessLightSourcesSystem::AreThereLightSourcesWithinRadius( CBaseEntity *pLooker, float flRadius )
  269. {
  270. float flRadiusSqr = (flRadius * flRadius);
  271. for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
  272. {
  273. // Removed?
  274. if ( m_LightSources[i].hEntity == NULL || m_LightSources[i].hEntity->IsMarkedForDeletion() )
  275. {
  276. m_LightSources.FastRemove( i );
  277. continue;
  278. }
  279. CBaseEntity *pLightSource = m_LightSources[i].hEntity;
  280. // Close enough to a light source?
  281. float flDistanceSqr = (pLooker->WorldSpaceCenter() - pLightSource->GetAbsOrigin()).LengthSqr();
  282. if ( flDistanceSqr < flRadiusSqr )
  283. {
  284. trace_t tr;
  285. AI_TraceLine( pLooker->EyePosition(), pLightSource->GetAbsOrigin(), MASK_SOLID_BRUSHONLY, pLooker, COLLISION_GROUP_NONE, &tr );
  286. if ( g_debug_darkness.GetBool() )
  287. {
  288. if (tr.fraction != 1.0)
  289. {
  290. NDebugOverlay::Line( pLooker->WorldSpaceCenter(), tr.endpos, 255,0,0,true, 0.1);
  291. }
  292. else
  293. {
  294. NDebugOverlay::Line( pLooker->WorldSpaceCenter(), tr.endpos, 0,255,0,true, 0.1);
  295. NDebugOverlay::Line( pLightSource->GetAbsOrigin(), tr.endpos, 255,0,0,true, 0.1);
  296. }
  297. }
  298. if ( tr.fraction == 1.0 )
  299. return true;
  300. }
  301. }
  302. return false;
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose:
  306. //-----------------------------------------------------------------------------
  307. void CDarknessLightSourcesSystem::SetDebug( bool bDebug )
  308. {
  309. for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
  310. {
  311. CInfoDarknessLightSource *pLightSource = dynamic_cast<CInfoDarknessLightSource*>(m_LightSources[i].hEntity.Get());
  312. if ( pLightSource )
  313. {
  314. if ( bDebug )
  315. {
  316. pLightSource->SetThink( &CInfoDarknessLightSource::DebugThink );
  317. pLightSource->SetNextThink( gpGlobals->curtime );
  318. }
  319. else
  320. {
  321. pLightSource->SetThink( NULL );
  322. }
  323. }
  324. }
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose:
  328. //-----------------------------------------------------------------------------
  329. void CV_Debug_Darkness( IConVar *pConVar, const char *pOldString, float flOldValue )
  330. {
  331. ConVarRef var( pConVar );
  332. DarknessLightSourcesSystem()->SetDebug( var.GetBool() );
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. // Input : *pEntity -
  337. //-----------------------------------------------------------------------------
  338. void AddEntityToDarknessCheck( CBaseEntity *pEntity, float flLightRadius /*=DARKNESS_LIGHTSOURCE_SIZE*/ )
  339. {
  340. // Create a light source, and attach it to the entity
  341. CInfoDarknessLightSource *pLightSource = (CInfoDarknessLightSource *) CreateEntityByName( "info_darknessmode_lightsource" );
  342. if ( pLightSource )
  343. {
  344. pLightSource->SetLightRadius( flLightRadius );
  345. DispatchSpawn( pLightSource );
  346. pLightSource->SetAbsOrigin( pEntity->WorldSpaceCenter() );
  347. pLightSource->SetParent( pEntity );
  348. pLightSource->Activate();
  349. // Dynamically created darkness sources can ignore LOS
  350. // to match the (broken) visual representation of our dynamic lights.
  351. if ( darkness_ignore_LOS_to_sources.GetBool() )
  352. {
  353. pLightSource->IgnoreLOS();
  354. }
  355. }
  356. }
  357. //-----------------------------------------------------------------------------
  358. // Purpose:
  359. // Input : *pEntity -
  360. //-----------------------------------------------------------------------------
  361. void RemoveEntityFromDarknessCheck( CBaseEntity *pEntity )
  362. {
  363. // Find any light sources parented to this entity, and remove them
  364. CBaseEntity *pChild = pEntity->FirstMoveChild();
  365. while ( pChild )
  366. {
  367. CBaseEntity *pPrevChild = pChild;
  368. pChild = pChild->NextMovePeer();
  369. if ( dynamic_cast<CInfoDarknessLightSource*>(pPrevChild) )
  370. {
  371. UTIL_Remove( pPrevChild );
  372. }
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose:
  377. // Input : *pEntity -
  378. //-----------------------------------------------------------------------------
  379. bool LookerCouldSeeTargetInDarkness( CBaseEntity *pLooker, CBaseEntity *pTarget )
  380. {
  381. if ( DarknessLightSourcesSystem()->IsEntityVisibleToTarget( pLooker, pTarget ) )
  382. {
  383. //NDebugOverlay::Line( pTarget->WorldSpaceCenter(), pLooker->WorldSpaceCenter(), 0,255,0,true, 0.1);
  384. return true;
  385. }
  386. return false;
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Purpose: Return true if there is at least 1 darkness light source within
  390. // the specified radius of the looker.
  391. //-----------------------------------------------------------------------------
  392. bool DarknessLightSourceWithinRadius( CBaseEntity *pLooker, float flRadius )
  393. {
  394. return DarknessLightSourcesSystem()->AreThereLightSourcesWithinRadius( pLooker, flRadius );
  395. }