//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// // //=============================================================================// #include "cbase.h" #include "paint_cleanser_manager.h" #include "igameevents.h" #ifdef CLIENT_DLL #include "c_trigger_paint_cleanser.h" #include "debugoverlay_shared.h" ConVar paint_cleanser_visibility_poll_frequency( "paint_cleanser_visibility_poll_rate", "0.5", FCVAR_CHEAT ); ConVar paint_cleanser_visibility_checks_debug( "paint_cleanser_visibility_checks_debug", "0", FCVAR_DEVELOPMENTONLY ); ConVar paint_cleanser_visibility_range( "paint_cleanser_visibility_range", "1000.0f", FCVAR_CHEAT ); ConVar paint_cleanser_visibility_look_angle( "paint_cleanser_visibility_look_angle", "60.0f", FCVAR_CHEAT ); #else #include "trigger_paint_cleanser.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CPaintCleanserManager PaintCleanserManager( "PaintCleanserManager" ); CPaintCleanserManager::CPaintCleanserManager( char const *name ) #ifdef CLIENT_DLL : CAutoGameSystemPerFrame( name ), m_flNextPollTime( 0.0f ) #else : CAutoGameSystem( name ) #endif { #ifdef CLIENT_DLL memset( m_ppVisibleCleanser, 0, sizeof(m_ppVisibleCleanser) ); #endif } CPaintCleanserManager::~CPaintCleanserManager( void ) { } void CPaintCleanserManager::LevelInitPreEntity() { #ifdef CLIENT_DLL m_flNextPollTime = gpGlobals->curtime + paint_cleanser_visibility_poll_frequency.GetFloat(); #endif } void CPaintCleanserManager::LevelShutdownPreEntity() { m_PaintCleansers.RemoveAll(); } void CPaintCleanserManager::AddPaintCleanser( C_TriggerPaintCleanser *pCleanser ) { m_PaintCleansers.AddToTail( pCleanser ); } void CPaintCleanserManager::RemovePaintCleanser( C_TriggerPaintCleanser *pCleanser ) { m_PaintCleansers.FindAndRemove( pCleanser ); } void CPaintCleanserManager::GetPaintCleansers( PaintCleanserVector_t& paintCleansers ) { paintCleansers = m_PaintCleansers; } #ifdef CLIENT_DLL void CPaintCleanserManager::Update( float frametime ) { //Check if it is time to update the cleanser visibility if( gpGlobals->curtime < m_flNextPollTime ) { return; } //Set the time for the next check m_flNextPollTime = gpGlobals->curtime + paint_cleanser_visibility_poll_frequency.GetFloat(); UpdatePaintCleanserVisibility(); } void CPaintCleanserManager::UpdatePaintCleanserVisibility( void ) { FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh ) { C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer( hh ); if( !pPlayer ) continue; Vector vecPlayerForward; AngleVectors( pPlayer->EyeAngles(), &vecPlayerForward ); VectorNormalize( vecPlayerForward ); Vector vecPlayerEyePos = pPlayer->EyePosition(); //Max distance to check for visibility float flVisibilityRange = paint_cleanser_visibility_range.GetFloat() * paint_cleanser_visibility_range.GetFloat(); bool bDebugging = paint_cleanser_visibility_checks_debug.GetBool(); float flClosestCleanserDist = FLT_MAX; C_TriggerPaintCleanser *pClosestCleanser = NULL; //If there is a cleanser visible before the check bool bCleanserWasVisible = m_ppVisibleCleanser[hh] ? true : false; for( int i = 0; i < m_PaintCleansers.Count(); ++i ) { Vector vecCleanserPos = m_PaintCleansers[i]->WorldSpaceCenter(); //Don't test the cleanser if the player is too far from it float flDistSqr = vecPlayerEyePos.DistToSqr( vecCleanserPos ); if( flDistSqr > flVisibilityRange ) { if( m_ppVisibleCleanser[hh] == m_PaintCleansers[i] ) { m_ppVisibleCleanser[hh] = NULL; } continue; } //Don't test the cleanser if the player isn't looking in its direction Vector vecPlayerCleanserDir = vecCleanserPos - vecPlayerEyePos; VectorNormalize( vecPlayerCleanserDir ); float flPlayerCleanserAngle = RAD2DEG( acos( DotProduct( vecPlayerForward, vecPlayerCleanserDir ) ) ); if( flPlayerCleanserAngle >= paint_cleanser_visibility_look_angle.GetFloat() ) { if( m_ppVisibleCleanser[hh] == m_PaintCleansers[i] ) { m_ppVisibleCleanser[hh] = NULL; } continue; } //Trace from the player's eye to the paint cleanser Ray_t playerCleanserRay; playerCleanserRay.Init( vecPlayerEyePos, vecCleanserPos ); trace_t tr; int mask = MASK_BLOCKLOS_AND_NPCS & ~CONTENTS_BLOCKLOS; UTIL_TraceRay( playerCleanserRay, mask, pPlayer, COLLISION_GROUP_NONE, &tr ); //If there is nothing between the player and the cleanser if( tr.fraction == 1.0f || tr.m_pEnt == m_PaintCleansers[i] ) { if( bDebugging ) { NDebugOverlay::Line( pPlayer->WorldSpaceCenter(), vecCleanserPos, 0, 255, 255, false, paint_cleanser_visibility_poll_frequency.GetFloat() ); } Color debugColor( 255, 0, 0 ); //If this is the closest visible paint cleanser if( flDistSqr < flClosestCleanserDist ) { flClosestCleanserDist = flDistSqr; pClosestCleanser = m_PaintCleansers[i]; debugColor = Color( 0, 255, 0 ); } if( bDebugging ) { NDebugOverlay::Cross3D( vecCleanserPos, 16, debugColor.r(), debugColor.g(), debugColor.b(), false, paint_cleanser_visibility_poll_frequency.GetFloat() ); } } else { if( bDebugging ) { NDebugOverlay::Line( pPlayer->WorldSpaceCenter(), vecCleanserPos, 0, 0, 255, false, paint_cleanser_visibility_poll_frequency.GetFloat() ); } } } //For all the paint cleansers //If the player can see a different cleanser if( pClosestCleanser && pClosestCleanser != m_ppVisibleCleanser[hh] ) { m_ppVisibleCleanser[hh] = pClosestCleanser; if( pPlayer->Weapon_OwnsThisType( "weapon_paintgun" ) ) { IGameEvent *event = gameeventmanager->CreateEvent( "paint_cleanser_visible" ); if ( event ) { event->SetInt( "userid", pPlayer->GetUserID() ); event->SetInt( "subject", m_ppVisibleCleanser[hh]->entindex() ); gameeventmanager->FireEventClientSide( event ); } } } //If there is no visible cleanser else if( !m_ppVisibleCleanser[hh] && bCleanserWasVisible ) { IGameEvent *event = gameeventmanager->CreateEvent( "paint_cleanser_not_visible" ); if( event ) { event->SetInt( "userid", pPlayer->GetUserID() ); gameeventmanager->FireEventClientSide( event ); } } } //for each pPlayer } #endif