//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "item_sonarpulse.h" #ifdef CLIENT_DLL #include "view.h" #include "materialsystem/imaterialvar.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define SONARPULSE_MODEL "models/props/props_br/br_sonar/br_sonar.mdl" #define SONARPULSE_SPEED 270 // in units/sec #define SONARPULSE_PRICE 1000 // in american dollars #ifdef CLIENT_DLL CUtlVector g_SonarPulsers; CUtlVector g_SonarPulseIcons; #endif #if defined( CLIENT_DLL ) #else BEGIN_DATADESC( CSonarPulse ) END_DATADESC() #endif BEGIN_PREDICTION_DATA( CSonarPulse ) END_PREDICTION_DATA() IMPLEMENT_NETWORKCLASS_ALIASED( SonarPulse, DT_SonarPulse ) BEGIN_NETWORK_TABLE( CSonarPulse, DT_SonarPulse ) #ifdef CLIENT_DLL RecvPropBool( RECVINFO(m_bPulseInProgress) ), RecvPropFloat( RECVINFO(m_flPulseInitTime) ) #else SendPropBool( SENDINFO(m_bPulseInProgress) ), SendPropFloat( SENDINFO(m_flPulseInitTime) ) #endif END_NETWORK_TABLE() LINK_ENTITY_TO_CLASS_ALIASED( item_sonarpulse, SonarPulse ); PRECACHE_REGISTER( item_sonarpulse ); CSonarPulse::CSonarPulse() { #ifndef CLIENT_DLL m_bPulseInProgress = false; m_flPulseInitTime = 0; m_vecPlayersOutsidePulse.RemoveAll(); m_vecPingedPlayers.RemoveAll(); m_flPlaybackRate = 0; #else ListenForGameEvent( "add_player_sonar_icon" ); g_SonarPulsers.FindAndFastRemove( this ); g_SonarPulsers.AddToTail( this ); m_pIconMaterial = NULL; g_SonarPulseIcons.RemoveAll(); #endif } CSonarPulse::~CSonarPulse() { #ifdef CLIENT_DLL g_SonarPulsers.FindAndFastRemove( this ); #endif } void CSonarPulse::Spawn( void ) { BaseClass::Spawn(); SetMoveType( MOVETYPE_NONE ); SetSolid( SOLID_VPHYSICS ); SetModel( SONARPULSE_MODEL ); #ifdef CLIENT_DLL m_pIconMaterial = materials->FindMaterial( "dev/sonar_icon.vmt", TEXTURE_GROUP_OTHER, false ); SetThink( &CSonarPulse::ClientThink ); SetNextClientThink( CLIENT_THINK_ALWAYS ); #else SetPlaybackRate( 0 ); #endif } void CSonarPulse::Precache() { PrecacheModel( SONARPULSE_MODEL ); PrecacheSound( "ambient/atmosphere/cs_metalscrapeverb10.wav" ); } float CSonarPulse::GetPulseRadius( void ) { if ( !m_bPulseInProgress ) return 0; float flCurrentDuration = gpGlobals->curtime - m_flPulseInitTime; return flCurrentDuration * SONARPULSE_SPEED; } Vector CSonarPulse::GetPulseOrigin( void ) { return GetAbsOrigin(); } #ifdef CLIENT_DLL void CSonarPulse::ClientThink( void ) { StudioFrameAdvance(); SetNextClientThink( CLIENT_THINK_ALWAYS ); } void CSonarPulse::RenderIcons( void ) { if( !g_SonarPulseIcons.Count() || m_pIconMaterial == NULL ) return; C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); if ( !pLocalPlayer ) return; Vector vecEyePos = MainViewOrigin(GET_ACTIVE_SPLITSCREEN_SLOT()); CMatRenderContextPtr pRenderContext(materials); FOR_EACH_VEC_BACK( g_SonarPulseIcons, n ) { float flLifeTimeRemaining = g_SonarPulseIcons[n].GetLifeRemaining(); if ( flLifeTimeRemaining > 0 ) { Vector vecIconPos = g_SonarPulseIcons[n].m_vecPos; Vector vecOrigin; ScreenTransform( vecIconPos, vecOrigin ); float flSize = RemapValClamped( flLifeTimeRemaining, 10, 9, 1, 32 ); float flEdgeAlpha = 1.0f; float flTheta = 0.95f; if ( vecOrigin.Length() > flTheta ) { vecOrigin = vecOrigin.Normalized() * flTheta; flSize = MIN( flSize, 24 ); flEdgeAlpha = 0.7f; } ConvertNormalizedScreenSpaceToPixelScreenSpace( vecOrigin ); //scale size by distance a bit, as per Brian's request. flSize *= RemapValClamped( vecEyePos.DistToSqr( vecIconPos ), 1600*1600, 500*500, 0.5f, 1.0f ); vecOrigin.x -= flSize * 0.5f; vecOrigin.y -= flSize * 0.5f; IMaterialVar* pVar = m_pIconMaterial->FindVar( "$alpha", NULL ); if ( pVar ) { pVar->SetFloatValue( flEdgeAlpha * clamp( flLifeTimeRemaining * 0.5f, 0, 1 ) ); } pRenderContext->DrawScreenSpaceRectangle( m_pIconMaterial, vecOrigin.x, vecOrigin.y, flSize, flSize, 0, 0, 32, 32, 32, 32 ); } else { g_SonarPulseIcons.FastRemove( n ); } } } void CSonarPulse::FireGameEvent( IGameEvent *event ) { if ( Q_strcmp( event->GetName(), "add_player_sonar_icon" ) == 0 ) { // don't make an icon for the local player - it's confusing int nUserID = event->GetInt( "userid", -1 ); C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); if ( pLocalPlayer && pLocalPlayer->GetUserID() == nUserID ) return; Vector vecPos = Vector( event->GetFloat("pos_x",0), event->GetFloat("pos_y",0), event->GetFloat("pos_z",0) ); sonarpulseicon_t newIcon( vecPos ); g_SonarPulseIcons.AddToTail( newIcon ); //CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByUserId( nUserID ) ); //ConColorMsg( Color(0,255,255,255), "RECEIVE: sonar icon added at [%f, %f, %f] for player: %s\n", vecPos.x, vecPos.y, vecPos.z, pPlayer ? pPlayer->GetPlayerName() : "null" ); } } #endif #ifndef CLIENT_DLL void CSonarPulse::PulseStart( void ) { EmitSound( "ambient/atmosphere/cs_metalscrapeverb10.wav" ); m_vecPlayersOutsidePulse.RemoveAll(); m_vecPingedPlayers.RemoveAll(); m_bPulseInProgress = true; m_flPulseInitTime = gpGlobals->curtime; SetPlaybackRate( 1 ); SetThink( &CSonarPulse::PulseThink ); SetNextThink( gpGlobals->curtime ); } void CSonarPulse::PulseReset( void ) { SetThink( NULL ); SetPlaybackRate( 0 ); m_bPulseInProgress = false; m_flPulseInitTime = 0; m_vecPlayersOutsidePulse.RemoveAll(); m_vecPingedPlayers.RemoveAll(); m_flPlaybackRate = 0; } bool CSonarPulse::IsOkToPulse( CCSPlayer* pPlayer ) { return ( pPlayer && pPlayer->IsAlive() && !pPlayer->IsChickenClass() && !pPlayer->IsFlyingDroneClass() && !pPlayer->m_bIsParachuting ); } void CSonarPulse::PulseThink( void ) { if ( !m_bPulseInProgress ) { PulseReset(); return; } float flRadius = GetPulseRadius(); Vector vecPulseCenter = GetPulseOrigin(); float flRadSqrCurrent = flRadius * flRadius; // ping the players that were outside the pulse last think and are now inside FOR_EACH_VEC_BACK( m_vecPlayersOutsidePulse, n ) { CCSPlayer *pPlayer = ToCSPlayer( m_vecPlayersOutsidePulse[n] ); if ( IsOkToPulse( pPlayer ) ) { Vector vecPlayerPos = pPlayer->GetAbsOrigin() + Vector(0,0,40); float flPlayerDist = vecPlayerPos.DistToSqr( vecPulseCenter ); if ( flPlayerDist < flRadSqrCurrent && !m_vecPingedPlayers.HasElement( pPlayer ) ) { // The pulse crossed this player - ping them! pPlayer->EmitSound( "Bot.StuckSound" ); IGameEvent * sonar_ping_event = gameeventmanager->CreateEvent( "add_player_sonar_icon" ); if ( sonar_ping_event ) { sonar_ping_event->SetInt( "userid", pPlayer->GetUserID() ); sonar_ping_event->SetFloat( "pos_x", vecPlayerPos.x ); sonar_ping_event->SetFloat( "pos_y", vecPlayerPos.y ); sonar_ping_event->SetFloat( "pos_z", vecPlayerPos.z ); gameeventmanager->FireEvent( sonar_ping_event ); } m_vecPlayersOutsidePulse.FindAndRemove( pPlayer ); m_vecPingedPlayers.AddToTail( pPlayer ); //ConColorMsg( Color(0,255,0,255), "SEND: sonar icon added at [%f, %f, %f] for player: %s\n", vecPlayerPos.x, vecPlayerPos.y, vecPlayerPos.z, pPlayer->GetPlayerName() ); } } else { // player is invalid for some reason, might have left, been killed, etc m_vecPlayersOutsidePulse.FindAndRemove( pPlayer ); } } // re-gather players to add to the list that are outside the pulse for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( IsOkToPulse( pPlayer ) ) { Vector vecPlayerPos = pPlayer->GetAbsOrigin() + Vector(0,0,40); float flPlayerDist = vecPlayerPos.DistToSqr( vecPulseCenter ); if ( flPlayerDist > flRadSqrCurrent && !m_vecPlayersOutsidePulse.HasElement(pPlayer) ) { m_vecPlayersOutsidePulse.AddToTail( pPlayer ); } } } if ( m_vecPlayersOutsidePulse.Count() == 0 ) { PulseReset(); return; } SetNextThink( gpGlobals->curtime + 0.1f ); } void CSonarPulse::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( pActivator ); if ( !pPlayer ) return; int nCost = SONARPULSE_PRICE; if ( m_bPulseInProgress || pPlayer->GetAccountBalance() < nCost ) { // we dont have enough money // play an error sound Vector soundPosition = GetAbsOrigin() + Vector( 0, 0, 32 ); CPASAttenuationFilter filter( soundPosition ); EmitSound( filter, 0, "Vote.Failed", &GetAbsOrigin() ); return; } // deduct the amount pPlayer->AddAccount( -nCost, false, true, "" ); m_bPulseInProgress = true; SetThink( &CSonarPulse::PulseStart ); SetNextThink( gpGlobals->curtime + 1 ); EmitSound( "UI.Guardian.TooFarWarning" ); } #endif // #ifndef CLIENT_DLL