aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/soundscape.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/soundscape.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/soundscape.cpp')
-rw-r--r--mp/src/game/server/soundscape.cpp1196
1 files changed, 598 insertions, 598 deletions
diff --git a/mp/src/game/server/soundscape.cpp b/mp/src/game/server/soundscape.cpp
index fcf4d742..3c37cc59 100644
--- a/mp/src/game/server/soundscape.cpp
+++ b/mp/src/game/server/soundscape.cpp
@@ -1,598 +1,598 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//===========================================================================//
-
-#include "cbase.h"
-#include "soundscape.h"
-#include "datamap.h"
-#include "soundscape_system.h"
-#include "triggers.h"
-#include "saverestore_utlvector.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-ConVar soundscape_debug( "soundscape_debug", "0", FCVAR_CHEAT, "When on, draws lines to all env_soundscape entities. Green lines show the active soundscape, red lines show soundscapes that aren't in range, and white lines show soundscapes that are in range, but not the active soundscape." );
-
-// ----------------------------------------------------------------------------- //
-// CEnvSoundscapeProxy stuff.
-// ----------------------------------------------------------------------------- //
-
-LINK_ENTITY_TO_CLASS( env_soundscape_proxy, CEnvSoundscapeProxy );
-
-BEGIN_DATADESC( CEnvSoundscapeProxy )
-
- DEFINE_KEYFIELD( m_MainSoundscapeName, FIELD_STRING, "MainSoundscapeName" )
-
-END_DATADESC()
-
-
-CEnvSoundscapeProxy::CEnvSoundscapeProxy()
-{
- m_MainSoundscapeName = NULL_STRING;
-}
-
-
-void CEnvSoundscapeProxy::Activate()
-{
- if ( m_MainSoundscapeName != NULL_STRING )
- {
- CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_MainSoundscapeName );
- if ( pEntity )
- {
- m_hProxySoundscape = dynamic_cast< CEnvSoundscape* >( pEntity );
- }
- }
-
- if ( m_hProxySoundscape )
- {
- // Copy the relevant parameters from our main soundscape.
- m_soundscapeIndex = m_hProxySoundscape->m_soundscapeIndex;
- for ( int i=0; i < ARRAYSIZE( m_positionNames ); i++ )
- m_positionNames[i] = m_hProxySoundscape->m_positionNames[i];
- }
- else
- {
- Warning( "env_soundscape_proxy can't find target soundscape: '%s'\n", STRING( m_MainSoundscapeName ) );
- }
-
- BaseClass::Activate();
-}
-
-
-// ----------------------------------------------------------------------------- //
-// CEnvSoundscape stuff.
-// ----------------------------------------------------------------------------- //
-
-LINK_ENTITY_TO_CLASS( env_soundscape, CEnvSoundscape );
-
-BEGIN_DATADESC( CEnvSoundscape )
-
- DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
- // don't save, recomputed on load
- //DEFINE_FIELD( m_soundscapeIndex, FIELD_INTEGER ),
- DEFINE_FIELD( m_soundscapeName, FIELD_STRING ),
- DEFINE_FIELD( m_hProxySoundscape, FIELD_EHANDLE ),
-
-// Silence, Classcheck!
-// DEFINE_ARRAY( m_positionNames, FIELD_STRING, 4 ),
-
- DEFINE_KEYFIELD( m_positionNames[0], FIELD_STRING, "position0" ),
- DEFINE_KEYFIELD( m_positionNames[1], FIELD_STRING, "position1" ),
- DEFINE_KEYFIELD( m_positionNames[2], FIELD_STRING, "position2" ),
- DEFINE_KEYFIELD( m_positionNames[3], FIELD_STRING, "position3" ),
- DEFINE_KEYFIELD( m_positionNames[4], FIELD_STRING, "position4" ),
- DEFINE_KEYFIELD( m_positionNames[5], FIELD_STRING, "position5" ),
- DEFINE_KEYFIELD( m_positionNames[6], FIELD_STRING, "position6" ),
- DEFINE_KEYFIELD( m_positionNames[7], FIELD_STRING, "position7" ),
-
- DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "ToggleEnabled", InputToggleEnabled ),
-
- DEFINE_OUTPUT( m_OnPlay, "OnPlay" ),
-
-
-END_DATADESC()
-
-CEnvSoundscape::CEnvSoundscape()
-{
- m_soundscapeName = NULL_STRING;
- m_soundscapeIndex = -1;
- m_soundscapeEntityId = -1;
- m_bDisabled = false;
- g_SoundscapeSystem.AddSoundscapeEntity( this );
-}
-
-CEnvSoundscape::~CEnvSoundscape()
-{
- g_SoundscapeSystem.RemoveSoundscapeEntity( this );
-}
-
-void CEnvSoundscape::InputEnable( inputdata_t &inputdata )
-{
- if (!IsEnabled())
- {
- Enable();
- }
-}
-
-void CEnvSoundscape::InputDisable( inputdata_t &inputdata )
-{
- if (IsEnabled())
- {
- Disable();
- }
-}
-
-void CEnvSoundscape::InputToggleEnabled( inputdata_t &inputdata )
-{
- if ( IsEnabled() )
- {
- Disable();
- }
- else
- {
- Enable();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns whether the laser is currently active.
-//-----------------------------------------------------------------------------
-bool CEnvSoundscape::IsEnabled( void ) const
-{
- return !m_bDisabled;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CEnvSoundscape::Disable( void )
-{
- m_bDisabled = true;
-
- // Reset if we are the currently active soundscape
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CEnvSoundscape::Enable( void )
-{
- m_bDisabled = false;
-
- // Force the player to recheck soundscapes
-}
-
-bool CEnvSoundscape::KeyValue( const char *szKeyName, const char *szValue )
-{
- if (FStrEq(szKeyName, "soundscape"))
- {
- m_soundscapeName = AllocPooledString( szValue );
- }
- else
- return BaseClass::KeyValue( szKeyName, szValue );
-
- return true;
-}
-
-// returns true if the given sound entity is in range
-// and can see the given player entity (pTarget)
-
-bool CEnvSoundscape::InRangeOfPlayer( CBasePlayer *pTarget )
-{
- Vector vecSpot1 = EarPosition();
- Vector vecSpot2 = pTarget->EarPosition();
-
- // calc range from sound entity to player
- Vector vecRange = vecSpot2 - vecSpot1;
- float range = vecRange.Length();
- if ( m_flRadius > range || m_flRadius == -1 )
- {
- trace_t tr;
-
- UTIL_TraceLine( vecSpot1, vecSpot2, MASK_SOLID_BRUSHONLY|MASK_WATER, pTarget, COLLISION_GROUP_NONE, &tr );
-
- if ( tr.fraction == 1 && !tr.startsolid )
- {
- return true;
- }
- }
-
- return false;
-}
-
-int CEnvSoundscape::UpdateTransmitState()
-{
- // Always transmit all soundscapes to the player.
- return SetTransmitState( FL_EDICT_ALWAYS );
-}
-
-void CEnvSoundscape::WriteAudioParamsTo( audioparams_t &audio )
-{
- audio.ent.Set( this );
- audio.soundscapeIndex = m_soundscapeIndex;
- audio.localBits = 0;
- for ( int i = 0; i < ARRAYSIZE(m_positionNames); i++ )
- {
- if ( m_positionNames[i] != NULL_STRING )
- {
- // We are a valid entity for a sound position
- CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_positionNames[i], this, this );
- if ( pEntity )
- {
- audio.localBits |= 1<<i;
- audio.localSound.Set( i, pEntity->GetAbsOrigin() );
- }
- }
- }
-
- m_OnPlay.FireOutput( this, this );
-}
-
-
-//
-// A client that is visible and in range of a sound entity will
-// have its soundscape set by that sound entity. If two or more
-// sound entities are contending for a client, then the nearest
-// sound entity to the client will set the client's soundscape.
-// A client's soundscape will remain set to its prior value until
-// a new in-range, visible sound entity resets a new soundscape.
-//
-
-// CONSIDER: if player in water state, autoset and underwater soundscape?
-void CEnvSoundscape::UpdateForPlayer( ss_update_t &update )
-{
- if ( !IsEnabled() )
- {
- if ( update.pCurrentSoundscape == this )
- {
- update.pCurrentSoundscape = NULL;
- update.currentDistance = 0;
- update.bInRange = false;
- }
- return;
- }
-
- // calc range from sound entity to player
- Vector target = EarPosition();
- float range = (update.playerPosition - target).Length();
-
- if ( update.pCurrentSoundscape == this )
- {
- update.currentDistance = range;
- update.bInRange = false;
- if ( m_flRadius > range || m_flRadius == -1 )
- {
- trace_t tr;
-
- update.traceCount++;
- UTIL_TraceLine( target, update.playerPosition, MASK_SOLID_BRUSHONLY|MASK_WATER, update.pPlayer, COLLISION_GROUP_NONE, &tr );
- if ( tr.fraction == 1 && !tr.startsolid )
- {
- update.bInRange = true;
- }
- }
- }
- else
- {
- if ( (!update.bInRange || range < update.currentDistance ) && (m_flRadius > range || m_flRadius == -1) )
- {
- trace_t tr;
-
- update.traceCount++;
- UTIL_TraceLine( target, update.playerPosition, MASK_SOLID_BRUSHONLY|MASK_WATER, update.pPlayer, COLLISION_GROUP_NONE, &tr );
-
- if ( tr.fraction == 1 && !tr.startsolid )
- {
- audioparams_t &audio = update.pPlayer->GetAudioParams();
- WriteAudioParamsTo( audio );
- update.pCurrentSoundscape = this;
- update.bInRange = true;
- update.currentDistance = range;
- }
- }
- }
-
-
- if ( soundscape_debug.GetBool() )
- {
- // draw myself
- NDebugOverlay::Box(GetAbsOrigin(), Vector(-10,-10,-10), Vector(10,10,10), 255, 0, 255, 64, NDEBUG_PERSIST_TILL_NEXT_SERVER );
-
- if ( update.pPlayer )
- {
- audioparams_t &audio = update.pPlayer->GetAudioParams();
- if ( audio.ent.Get() != this )
- {
- if ( InRangeOfPlayer( update.pPlayer ) )
- {
- NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 255, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- else
- {
- NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 0, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- }
- else
- {
- if ( InRangeOfPlayer( update.pPlayer ) )
- {
- NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- else
- {
- NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 170, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
-
- // also draw lines to each sound position.
- // we don't store the number of local sound positions, just a bitvector of which ones are on.
- unsigned int soundbits = audio.localBits.Get();
- float periodic = 2.0f * sin((fmod(gpGlobals->curtime,2.0f) - 1.0f) * M_PI); // = -4f .. 4f
- for (int ii = 0 ; ii < NUM_AUDIO_LOCAL_SOUNDS ; ++ii )
- {
- if ( soundbits & (1 << ii) )
- {
- const Vector &soundLoc = audio.localSound.Get(ii);
- NDebugOverlay::Line( GetAbsOrigin(), soundLoc, 0, 32 , 255 , false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- NDebugOverlay::Cross3D( soundLoc, 16.0f + periodic, 0, 0, 255, false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
- }
- }
- }
-
- NDebugOverlay::EntityTextAtPosition( GetAbsOrigin(), 0, STRING(m_soundscapeName), NDEBUG_PERSIST_TILL_NEXT_SERVER );
- }
-}
-
-//
-// env_soundscape - spawn a sound entity that will set player soundscape
-// when player moves in range and sight.
-//
-//
-void CEnvSoundscape::Spawn( )
-{
- Precache();
- // Because the soundscape has no model, need to make sure it doesn't get culled from the PVS for this reason and therefore
- // never exist on the client, etc.
- AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
-
-}
-
-void CEnvSoundscape::Precache()
-{
- if ( m_soundscapeName == NULL_STRING )
- {
- DevMsg("Found soundscape entity with no soundscape name.\n" );
- return;
- }
-
- m_soundscapeIndex = g_SoundscapeSystem.GetSoundscapeIndex( STRING(m_soundscapeName) );
- if ( IsX360())
- {
- g_SoundscapeSystem.PrecacheSounds( m_soundscapeIndex );
- }
- if ( !g_SoundscapeSystem.IsValidIndex( m_soundscapeIndex ) )
- {
- DevWarning("Can't find soundscape: %s\n", STRING(m_soundscapeName) );
- }
-}
-
-void CEnvSoundscape::DrawDebugGeometryOverlays( void )
-{
- if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
- if ( pPlayer )
- {
- audioparams_t &audio = pPlayer->GetAudioParams();
- if ( audio.ent.Get() != this )
- {
- CBaseEntity *pEnt = pPlayer; // ->GetSoundscapeListener();
- if ( pEnt )
- {
- NDebugOverlay::Line(GetAbsOrigin(), pEnt->WorldSpaceCenter(), 255, 0, 255, false, 0 );
- }
- }
- }
- }
-
- BaseClass::DrawDebugGeometryOverlays();
-}
-
-
-// ---------------------------------------------------------------------------------------------------- //
-// CEnvSoundscapeTriggerable
-// ---------------------------------------------------------------------------------------------------- //
-
-LINK_ENTITY_TO_CLASS( env_soundscape_triggerable, CEnvSoundscapeTriggerable );
-
-BEGIN_DATADESC( CEnvSoundscapeTriggerable )
-END_DATADESC()
-
-
-CEnvSoundscapeTriggerable::CEnvSoundscapeTriggerable()
-{
-}
-
-
-void CEnvSoundscapeTriggerable::DelegateStartTouch( CBaseEntity *pEnt )
-{
- CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( pEnt );
- if ( !pPlayer )
- return;
-
- // Just in case.. we shouldn't already be in the player's list because it should have
- // called DelegateEndTouch, but this seems to happen when they're noclipping.
- pPlayer->m_hTriggerSoundscapeList.FindAndRemove( this );
-
- // Add us to the player's list of soundscapes and
- pPlayer->m_hTriggerSoundscapeList.AddToHead( this );
- WriteAudioParamsTo( pPlayer->GetAudioParams() );
-}
-
-
-void CEnvSoundscapeTriggerable::DelegateEndTouch( CBaseEntity *pEnt )
-{
- CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( pEnt );
- if ( !pPlayer )
- return;
-
- // Remove us from the ent's list of soundscapes.
- pPlayer->m_hTriggerSoundscapeList.FindAndRemove( this );
- while ( pPlayer->m_hTriggerSoundscapeList.Count() > 0 )
- {
- CEnvSoundscapeTriggerable *pSS = dynamic_cast< CEnvSoundscapeTriggerable* >( pPlayer->m_hTriggerSoundscapeList[0].Get() );
- if ( pSS )
- {
- // Make this one current.
- pSS->WriteAudioParamsTo( pPlayer->GetAudioParams() );
- return;
- }
- else
- {
- pPlayer->m_hTriggerSoundscapeList.Remove( 0 );
- }
- }
-
- // No soundscapes left.
- pPlayer->GetAudioParams().ent = NULL;
-}
-
-
-void CEnvSoundscapeTriggerable::Think()
-{
- // Overrides the base class's think and prevents it from running at all.
-}
-
-
-// ---------------------------------------------------------------------------------------------------- //
-// CTriggerSoundscape
-// ---------------------------------------------------------------------------------------------------- //
-
-class CTriggerSoundscape : public CBaseTrigger
-{
-public:
- DECLARE_CLASS( CTriggerSoundscape, CBaseTrigger );
- DECLARE_DATADESC();
-
- CTriggerSoundscape();
-
- virtual void StartTouch( CBaseEntity *pOther );
- virtual void EndTouch( CBaseEntity *pOther );
-
- virtual void Spawn();
- virtual void Activate();
-
- void PlayerUpdateThink();
-
-private:
- CHandle<CEnvSoundscapeTriggerable> m_hSoundscape;
- string_t m_SoundscapeName;
-
- CUtlVector<CBasePlayerHandle> m_spectators; // spectators in our volume
-};
-
-
-LINK_ENTITY_TO_CLASS( trigger_soundscape, CTriggerSoundscape );
-
-BEGIN_DATADESC( CTriggerSoundscape )
- DEFINE_THINKFUNC( PlayerUpdateThink ),
- DEFINE_KEYFIELD( m_SoundscapeName, FIELD_STRING, "soundscape" ),
- DEFINE_FIELD( m_hSoundscape, FIELD_EHANDLE ),
- DEFINE_UTLVECTOR( m_spectators, FIELD_EHANDLE ),
-END_DATADESC()
-
-
-CTriggerSoundscape::CTriggerSoundscape()
-{
-}
-
-
-void CTriggerSoundscape::StartTouch( CBaseEntity *pOther )
-{
- if ( m_hSoundscape )
- m_hSoundscape->DelegateStartTouch( pOther );
-
- BaseClass::StartTouch( pOther );
-}
-
-
-void CTriggerSoundscape::EndTouch( CBaseEntity *pOther )
-{
- if ( m_hSoundscape )
- m_hSoundscape->DelegateEndTouch( pOther );
-
- BaseClass::EndTouch( pOther );
-}
-
-
-void CTriggerSoundscape::Spawn()
-{
- BaseClass::Spawn();
- InitTrigger();
-
- SetThink( &CTriggerSoundscape::PlayerUpdateThink );
- SetNextThink( gpGlobals->curtime + 0.2 );
-}
-
-
-void CTriggerSoundscape::Activate()
-{
- m_hSoundscape = dynamic_cast< CEnvSoundscapeTriggerable* >( gEntList.FindEntityByName( NULL, m_SoundscapeName ) );
- BaseClass::Activate();
-}
-
-
-// look for dead/spectating players in our volume, to call touch on
-void CTriggerSoundscape::PlayerUpdateThink()
-{
- int i;
- SetNextThink( gpGlobals->curtime + 0.2 );
-
- CUtlVector<CBasePlayerHandle> oldSpectators;
- oldSpectators = m_spectators;
- m_spectators.RemoveAll();
-
- for ( i=1; i <= gpGlobals->maxClients; ++i )
- {
- CBasePlayer *player = UTIL_PlayerByIndex( i );
-
- if ( !player )
- continue;
-
- if ( player->IsAlive() )
- continue;
-
- // if the spectator is intersecting the trigger, track it, and start a touch if it is just starting to touch
- if ( Intersects( player ) )
- {
- if ( !oldSpectators.HasElement( player ) )
- {
- StartTouch( player );
- }
- m_spectators.AddToTail( player );
- }
- }
-
- // check for spectators who are no longer intersecting
- for ( i=0; i<oldSpectators.Count(); ++i )
- {
- CBasePlayer *player = oldSpectators[i];
-
- if ( !player )
- continue;
-
- if ( !m_spectators.HasElement( player ) )
- {
- EndTouch( player );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "cbase.h"
+#include "soundscape.h"
+#include "datamap.h"
+#include "soundscape_system.h"
+#include "triggers.h"
+#include "saverestore_utlvector.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar soundscape_debug( "soundscape_debug", "0", FCVAR_CHEAT, "When on, draws lines to all env_soundscape entities. Green lines show the active soundscape, red lines show soundscapes that aren't in range, and white lines show soundscapes that are in range, but not the active soundscape." );
+
+// ----------------------------------------------------------------------------- //
+// CEnvSoundscapeProxy stuff.
+// ----------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( env_soundscape_proxy, CEnvSoundscapeProxy );
+
+BEGIN_DATADESC( CEnvSoundscapeProxy )
+
+ DEFINE_KEYFIELD( m_MainSoundscapeName, FIELD_STRING, "MainSoundscapeName" )
+
+END_DATADESC()
+
+
+CEnvSoundscapeProxy::CEnvSoundscapeProxy()
+{
+ m_MainSoundscapeName = NULL_STRING;
+}
+
+
+void CEnvSoundscapeProxy::Activate()
+{
+ if ( m_MainSoundscapeName != NULL_STRING )
+ {
+ CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_MainSoundscapeName );
+ if ( pEntity )
+ {
+ m_hProxySoundscape = dynamic_cast< CEnvSoundscape* >( pEntity );
+ }
+ }
+
+ if ( m_hProxySoundscape )
+ {
+ // Copy the relevant parameters from our main soundscape.
+ m_soundscapeIndex = m_hProxySoundscape->m_soundscapeIndex;
+ for ( int i=0; i < ARRAYSIZE( m_positionNames ); i++ )
+ m_positionNames[i] = m_hProxySoundscape->m_positionNames[i];
+ }
+ else
+ {
+ Warning( "env_soundscape_proxy can't find target soundscape: '%s'\n", STRING( m_MainSoundscapeName ) );
+ }
+
+ BaseClass::Activate();
+}
+
+
+// ----------------------------------------------------------------------------- //
+// CEnvSoundscape stuff.
+// ----------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( env_soundscape, CEnvSoundscape );
+
+BEGIN_DATADESC( CEnvSoundscape )
+
+ DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
+ // don't save, recomputed on load
+ //DEFINE_FIELD( m_soundscapeIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( m_soundscapeName, FIELD_STRING ),
+ DEFINE_FIELD( m_hProxySoundscape, FIELD_EHANDLE ),
+
+// Silence, Classcheck!
+// DEFINE_ARRAY( m_positionNames, FIELD_STRING, 4 ),
+
+ DEFINE_KEYFIELD( m_positionNames[0], FIELD_STRING, "position0" ),
+ DEFINE_KEYFIELD( m_positionNames[1], FIELD_STRING, "position1" ),
+ DEFINE_KEYFIELD( m_positionNames[2], FIELD_STRING, "position2" ),
+ DEFINE_KEYFIELD( m_positionNames[3], FIELD_STRING, "position3" ),
+ DEFINE_KEYFIELD( m_positionNames[4], FIELD_STRING, "position4" ),
+ DEFINE_KEYFIELD( m_positionNames[5], FIELD_STRING, "position5" ),
+ DEFINE_KEYFIELD( m_positionNames[6], FIELD_STRING, "position6" ),
+ DEFINE_KEYFIELD( m_positionNames[7], FIELD_STRING, "position7" ),
+
+ DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ToggleEnabled", InputToggleEnabled ),
+
+ DEFINE_OUTPUT( m_OnPlay, "OnPlay" ),
+
+
+END_DATADESC()
+
+CEnvSoundscape::CEnvSoundscape()
+{
+ m_soundscapeName = NULL_STRING;
+ m_soundscapeIndex = -1;
+ m_soundscapeEntityId = -1;
+ m_bDisabled = false;
+ g_SoundscapeSystem.AddSoundscapeEntity( this );
+}
+
+CEnvSoundscape::~CEnvSoundscape()
+{
+ g_SoundscapeSystem.RemoveSoundscapeEntity( this );
+}
+
+void CEnvSoundscape::InputEnable( inputdata_t &inputdata )
+{
+ if (!IsEnabled())
+ {
+ Enable();
+ }
+}
+
+void CEnvSoundscape::InputDisable( inputdata_t &inputdata )
+{
+ if (IsEnabled())
+ {
+ Disable();
+ }
+}
+
+void CEnvSoundscape::InputToggleEnabled( inputdata_t &inputdata )
+{
+ if ( IsEnabled() )
+ {
+ Disable();
+ }
+ else
+ {
+ Enable();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether the laser is currently active.
+//-----------------------------------------------------------------------------
+bool CEnvSoundscape::IsEnabled( void ) const
+{
+ return !m_bDisabled;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEnvSoundscape::Disable( void )
+{
+ m_bDisabled = true;
+
+ // Reset if we are the currently active soundscape
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEnvSoundscape::Enable( void )
+{
+ m_bDisabled = false;
+
+ // Force the player to recheck soundscapes
+}
+
+bool CEnvSoundscape::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "soundscape"))
+ {
+ m_soundscapeName = AllocPooledString( szValue );
+ }
+ else
+ return BaseClass::KeyValue( szKeyName, szValue );
+
+ return true;
+}
+
+// returns true if the given sound entity is in range
+// and can see the given player entity (pTarget)
+
+bool CEnvSoundscape::InRangeOfPlayer( CBasePlayer *pTarget )
+{
+ Vector vecSpot1 = EarPosition();
+ Vector vecSpot2 = pTarget->EarPosition();
+
+ // calc range from sound entity to player
+ Vector vecRange = vecSpot2 - vecSpot1;
+ float range = vecRange.Length();
+ if ( m_flRadius > range || m_flRadius == -1 )
+ {
+ trace_t tr;
+
+ UTIL_TraceLine( vecSpot1, vecSpot2, MASK_SOLID_BRUSHONLY|MASK_WATER, pTarget, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction == 1 && !tr.startsolid )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int CEnvSoundscape::UpdateTransmitState()
+{
+ // Always transmit all soundscapes to the player.
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+void CEnvSoundscape::WriteAudioParamsTo( audioparams_t &audio )
+{
+ audio.ent.Set( this );
+ audio.soundscapeIndex = m_soundscapeIndex;
+ audio.localBits = 0;
+ for ( int i = 0; i < ARRAYSIZE(m_positionNames); i++ )
+ {
+ if ( m_positionNames[i] != NULL_STRING )
+ {
+ // We are a valid entity for a sound position
+ CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_positionNames[i], this, this );
+ if ( pEntity )
+ {
+ audio.localBits |= 1<<i;
+ audio.localSound.Set( i, pEntity->GetAbsOrigin() );
+ }
+ }
+ }
+
+ m_OnPlay.FireOutput( this, this );
+}
+
+
+//
+// A client that is visible and in range of a sound entity will
+// have its soundscape set by that sound entity. If two or more
+// sound entities are contending for a client, then the nearest
+// sound entity to the client will set the client's soundscape.
+// A client's soundscape will remain set to its prior value until
+// a new in-range, visible sound entity resets a new soundscape.
+//
+
+// CONSIDER: if player in water state, autoset and underwater soundscape?
+void CEnvSoundscape::UpdateForPlayer( ss_update_t &update )
+{
+ if ( !IsEnabled() )
+ {
+ if ( update.pCurrentSoundscape == this )
+ {
+ update.pCurrentSoundscape = NULL;
+ update.currentDistance = 0;
+ update.bInRange = false;
+ }
+ return;
+ }
+
+ // calc range from sound entity to player
+ Vector target = EarPosition();
+ float range = (update.playerPosition - target).Length();
+
+ if ( update.pCurrentSoundscape == this )
+ {
+ update.currentDistance = range;
+ update.bInRange = false;
+ if ( m_flRadius > range || m_flRadius == -1 )
+ {
+ trace_t tr;
+
+ update.traceCount++;
+ UTIL_TraceLine( target, update.playerPosition, MASK_SOLID_BRUSHONLY|MASK_WATER, update.pPlayer, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1 && !tr.startsolid )
+ {
+ update.bInRange = true;
+ }
+ }
+ }
+ else
+ {
+ if ( (!update.bInRange || range < update.currentDistance ) && (m_flRadius > range || m_flRadius == -1) )
+ {
+ trace_t tr;
+
+ update.traceCount++;
+ UTIL_TraceLine( target, update.playerPosition, MASK_SOLID_BRUSHONLY|MASK_WATER, update.pPlayer, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction == 1 && !tr.startsolid )
+ {
+ audioparams_t &audio = update.pPlayer->GetAudioParams();
+ WriteAudioParamsTo( audio );
+ update.pCurrentSoundscape = this;
+ update.bInRange = true;
+ update.currentDistance = range;
+ }
+ }
+ }
+
+
+ if ( soundscape_debug.GetBool() )
+ {
+ // draw myself
+ NDebugOverlay::Box(GetAbsOrigin(), Vector(-10,-10,-10), Vector(10,10,10), 255, 0, 255, 64, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+
+ if ( update.pPlayer )
+ {
+ audioparams_t &audio = update.pPlayer->GetAudioParams();
+ if ( audio.ent.Get() != this )
+ {
+ if ( InRangeOfPlayer( update.pPlayer ) )
+ {
+ NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 255, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ }
+ else
+ {
+ NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 0, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ }
+ }
+ else
+ {
+ if ( InRangeOfPlayer( update.pPlayer ) )
+ {
+ NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ }
+ else
+ {
+ NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 170, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ }
+
+ // also draw lines to each sound position.
+ // we don't store the number of local sound positions, just a bitvector of which ones are on.
+ unsigned int soundbits = audio.localBits.Get();
+ float periodic = 2.0f * sin((fmod(gpGlobals->curtime,2.0f) - 1.0f) * M_PI); // = -4f .. 4f
+ for (int ii = 0 ; ii < NUM_AUDIO_LOCAL_SOUNDS ; ++ii )
+ {
+ if ( soundbits & (1 << ii) )
+ {
+ const Vector &soundLoc = audio.localSound.Get(ii);
+ NDebugOverlay::Line( GetAbsOrigin(), soundLoc, 0, 32 , 255 , false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ NDebugOverlay::Cross3D( soundLoc, 16.0f + periodic, 0, 0, 255, false, NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ }
+ }
+ }
+ }
+
+ NDebugOverlay::EntityTextAtPosition( GetAbsOrigin(), 0, STRING(m_soundscapeName), NDEBUG_PERSIST_TILL_NEXT_SERVER );
+ }
+}
+
+//
+// env_soundscape - spawn a sound entity that will set player soundscape
+// when player moves in range and sight.
+//
+//
+void CEnvSoundscape::Spawn( )
+{
+ Precache();
+ // Because the soundscape has no model, need to make sure it doesn't get culled from the PVS for this reason and therefore
+ // never exist on the client, etc.
+ AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
+
+}
+
+void CEnvSoundscape::Precache()
+{
+ if ( m_soundscapeName == NULL_STRING )
+ {
+ DevMsg("Found soundscape entity with no soundscape name.\n" );
+ return;
+ }
+
+ m_soundscapeIndex = g_SoundscapeSystem.GetSoundscapeIndex( STRING(m_soundscapeName) );
+ if ( IsX360())
+ {
+ g_SoundscapeSystem.PrecacheSounds( m_soundscapeIndex );
+ }
+ if ( !g_SoundscapeSystem.IsValidIndex( m_soundscapeIndex ) )
+ {
+ DevWarning("Can't find soundscape: %s\n", STRING(m_soundscapeName) );
+ }
+}
+
+void CEnvSoundscape::DrawDebugGeometryOverlays( void )
+{
+ if ( m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_PIVOT_BIT|OVERLAY_ABSBOX_BIT) )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
+ if ( pPlayer )
+ {
+ audioparams_t &audio = pPlayer->GetAudioParams();
+ if ( audio.ent.Get() != this )
+ {
+ CBaseEntity *pEnt = pPlayer; // ->GetSoundscapeListener();
+ if ( pEnt )
+ {
+ NDebugOverlay::Line(GetAbsOrigin(), pEnt->WorldSpaceCenter(), 255, 0, 255, false, 0 );
+ }
+ }
+ }
+ }
+
+ BaseClass::DrawDebugGeometryOverlays();
+}
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// CEnvSoundscapeTriggerable
+// ---------------------------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( env_soundscape_triggerable, CEnvSoundscapeTriggerable );
+
+BEGIN_DATADESC( CEnvSoundscapeTriggerable )
+END_DATADESC()
+
+
+CEnvSoundscapeTriggerable::CEnvSoundscapeTriggerable()
+{
+}
+
+
+void CEnvSoundscapeTriggerable::DelegateStartTouch( CBaseEntity *pEnt )
+{
+ CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( pEnt );
+ if ( !pPlayer )
+ return;
+
+ // Just in case.. we shouldn't already be in the player's list because it should have
+ // called DelegateEndTouch, but this seems to happen when they're noclipping.
+ pPlayer->m_hTriggerSoundscapeList.FindAndRemove( this );
+
+ // Add us to the player's list of soundscapes and
+ pPlayer->m_hTriggerSoundscapeList.AddToHead( this );
+ WriteAudioParamsTo( pPlayer->GetAudioParams() );
+}
+
+
+void CEnvSoundscapeTriggerable::DelegateEndTouch( CBaseEntity *pEnt )
+{
+ CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( pEnt );
+ if ( !pPlayer )
+ return;
+
+ // Remove us from the ent's list of soundscapes.
+ pPlayer->m_hTriggerSoundscapeList.FindAndRemove( this );
+ while ( pPlayer->m_hTriggerSoundscapeList.Count() > 0 )
+ {
+ CEnvSoundscapeTriggerable *pSS = dynamic_cast< CEnvSoundscapeTriggerable* >( pPlayer->m_hTriggerSoundscapeList[0].Get() );
+ if ( pSS )
+ {
+ // Make this one current.
+ pSS->WriteAudioParamsTo( pPlayer->GetAudioParams() );
+ return;
+ }
+ else
+ {
+ pPlayer->m_hTriggerSoundscapeList.Remove( 0 );
+ }
+ }
+
+ // No soundscapes left.
+ pPlayer->GetAudioParams().ent = NULL;
+}
+
+
+void CEnvSoundscapeTriggerable::Think()
+{
+ // Overrides the base class's think and prevents it from running at all.
+}
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// CTriggerSoundscape
+// ---------------------------------------------------------------------------------------------------- //
+
+class CTriggerSoundscape : public CBaseTrigger
+{
+public:
+ DECLARE_CLASS( CTriggerSoundscape, CBaseTrigger );
+ DECLARE_DATADESC();
+
+ CTriggerSoundscape();
+
+ virtual void StartTouch( CBaseEntity *pOther );
+ virtual void EndTouch( CBaseEntity *pOther );
+
+ virtual void Spawn();
+ virtual void Activate();
+
+ void PlayerUpdateThink();
+
+private:
+ CHandle<CEnvSoundscapeTriggerable> m_hSoundscape;
+ string_t m_SoundscapeName;
+
+ CUtlVector<CBasePlayerHandle> m_spectators; // spectators in our volume
+};
+
+
+LINK_ENTITY_TO_CLASS( trigger_soundscape, CTriggerSoundscape );
+
+BEGIN_DATADESC( CTriggerSoundscape )
+ DEFINE_THINKFUNC( PlayerUpdateThink ),
+ DEFINE_KEYFIELD( m_SoundscapeName, FIELD_STRING, "soundscape" ),
+ DEFINE_FIELD( m_hSoundscape, FIELD_EHANDLE ),
+ DEFINE_UTLVECTOR( m_spectators, FIELD_EHANDLE ),
+END_DATADESC()
+
+
+CTriggerSoundscape::CTriggerSoundscape()
+{
+}
+
+
+void CTriggerSoundscape::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_hSoundscape )
+ m_hSoundscape->DelegateStartTouch( pOther );
+
+ BaseClass::StartTouch( pOther );
+}
+
+
+void CTriggerSoundscape::EndTouch( CBaseEntity *pOther )
+{
+ if ( m_hSoundscape )
+ m_hSoundscape->DelegateEndTouch( pOther );
+
+ BaseClass::EndTouch( pOther );
+}
+
+
+void CTriggerSoundscape::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+
+ SetThink( &CTriggerSoundscape::PlayerUpdateThink );
+ SetNextThink( gpGlobals->curtime + 0.2 );
+}
+
+
+void CTriggerSoundscape::Activate()
+{
+ m_hSoundscape = dynamic_cast< CEnvSoundscapeTriggerable* >( gEntList.FindEntityByName( NULL, m_SoundscapeName ) );
+ BaseClass::Activate();
+}
+
+
+// look for dead/spectating players in our volume, to call touch on
+void CTriggerSoundscape::PlayerUpdateThink()
+{
+ int i;
+ SetNextThink( gpGlobals->curtime + 0.2 );
+
+ CUtlVector<CBasePlayerHandle> oldSpectators;
+ oldSpectators = m_spectators;
+ m_spectators.RemoveAll();
+
+ for ( i=1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = UTIL_PlayerByIndex( i );
+
+ if ( !player )
+ continue;
+
+ if ( player->IsAlive() )
+ continue;
+
+ // if the spectator is intersecting the trigger, track it, and start a touch if it is just starting to touch
+ if ( Intersects( player ) )
+ {
+ if ( !oldSpectators.HasElement( player ) )
+ {
+ StartTouch( player );
+ }
+ m_spectators.AddToTail( player );
+ }
+ }
+
+ // check for spectators who are no longer intersecting
+ for ( i=0; i<oldSpectators.Count(); ++i )
+ {
+ CBasePlayer *player = oldSpectators[i];
+
+ if ( !player )
+ continue;
+
+ if ( !m_spectators.HasElement( player ) )
+ {
+ EndTouch( player );
+ }
+ }
+}