aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/soundscape_system.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_system.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_system.cpp')
-rw-r--r--mp/src/game/server/soundscape_system.cpp916
1 files changed, 458 insertions, 458 deletions
diff --git a/mp/src/game/server/soundscape_system.cpp b/mp/src/game/server/soundscape_system.cpp
index 571c49db..29b29402 100644
--- a/mp/src/game/server/soundscape_system.cpp
+++ b/mp/src/game/server/soundscape_system.cpp
@@ -1,458 +1,458 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-
-#include "cbase.h"
-#include "soundscape_system.h"
-#include "soundscape.h"
-#include "KeyValues.h"
-#include "filesystem.h"
-#include "game.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt"
-
-CON_COMMAND(soundscape_flush, "Flushes the server & client side soundscapes")
-{
- CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
- if ( engine->IsDedicatedServer() )
- {
- // If it's a dedicated server, only the server console can run this.
- if ( pPlayer )
- return;
- }
- else
- {
- // If it's a listen server, only the listen server host can run this.
- if ( !pPlayer || pPlayer != UTIL_GetListenServerHost() )
- return;
- }
-
- g_SoundscapeSystem.FlushSoundscapes(); // don't bother forgetting about the entities
- g_SoundscapeSystem.Init();
-
-
- if ( engine->IsDedicatedServer() )
- {
- // If the ds console typed it, send it to everyone.
- for ( int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBasePlayer *pSendToPlayer = UTIL_PlayerByIndex( i );
- if ( pSendToPlayer )
- engine->ClientCommand( pSendToPlayer->edict(), "cl_soundscape_flush\n" );
- }
- }
- else
- {
- engine->ClientCommand( pPlayer->edict(), "cl_soundscape_flush\n" );
- }
-}
-
-CSoundscapeSystem g_SoundscapeSystem( "CSoundscapeSystem" );
-
-extern ConVar soundscape_debug;
-
-void CSoundscapeSystem::AddSoundscapeFile( const char *filename )
-{
- MEM_ALLOC_CREDIT();
- // Open the soundscape data file, and abort if we can't
- KeyValues *pKeyValuesData = new KeyValues( filename );
- if ( filesystem->LoadKeyValues( *pKeyValuesData, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) )
- {
- // parse out all of the top level sections and save their names
- KeyValues *pKeys = pKeyValuesData;
- while ( pKeys )
- {
- if ( pKeys->GetFirstSubKey() )
- {
- if ( g_pDeveloper->GetBool() )
- {
- if ( strstr( pKeys->GetName(), "{" ) )
- {
- Msg("Error parsing soundscape file %s after %s\n", filename, m_soundscapeCount>0 ?m_soundscapes.GetStringText( m_soundscapeCount-1 ) : "FIRST" );
- }
- }
- m_soundscapes.AddString( pKeys->GetName(), m_soundscapeCount );
-
- if ( IsX360() )
- {
- AddSoundscapeSounds( pKeys, m_soundscapeCount );
- }
- m_soundscapeCount++;
- }
- pKeys = pKeys->GetNextKey();
- }
- }
- pKeyValuesData->deleteThis();
-}
-
-CON_COMMAND_F( sv_soundscape_printdebuginfo, "print soundscapes", FCVAR_DEVELOPMENTONLY )
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- g_SoundscapeSystem.PrintDebugInfo();
-}
-
-
-void CSoundscapeSystem::PrintDebugInfo()
-{
- Msg( "\n------- SERVER SOUNDSCAPES -------\n" );
- for ( int key=m_soundscapes.First(); key != m_soundscapes.InvalidIndex(); key = m_soundscapes.Next( key ) )
- {
- int id = m_soundscapes.GetIDForKey( key );
- const char *pName = m_soundscapes.GetStringForKey( key );
-
- Msg( "- %d: %s\n", id, pName );
- }
- Msg( "-------- SOUNDSCAPE ENTITIES -----\n" );
- for( int entityIndex = 0; entityIndex < m_soundscapeEntities.Size(); ++entityIndex )
- {
- CEnvSoundscape *currentSoundscape = m_soundscapeEntities[entityIndex];
- Msg("- %d: %s x:%.4f y:%.4f z:%.4f\n",
- entityIndex,
- STRING(currentSoundscape->GetSoundscapeName()),
- currentSoundscape->GetAbsOrigin().x,
- currentSoundscape->GetAbsOrigin().y,
- currentSoundscape->GetAbsOrigin().z
- );
- }
- Msg( "----------------------------------\n\n" );
-}
-
-bool CSoundscapeSystem::Init()
-{
- m_soundscapeCount = 0;
-
- const char *mapname = STRING( gpGlobals->mapname );
- const char *mapSoundscapeFilename = NULL;
- if ( mapname && *mapname )
- {
- mapSoundscapeFilename = UTIL_VarArgs( "scripts/soundscapes_%s.txt", mapname );
- }
-
- KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE );
- if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) )
- {
- for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
- {
- if ( !Q_stricmp( sub->GetName(), "file" ) )
- {
- // Add
- AddSoundscapeFile( sub->GetString() );
- if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) )
- {
- mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape
- }
- continue;
- }
-
- Warning( "CSoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
- SOUNDSCAPE_MANIFEST_FILE, sub->GetName() );
- }
-
- if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) )
- {
- AddSoundscapeFile( mapSoundscapeFilename );
- }
- }
- else
- {
- Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE );
- }
- manifest->deleteThis();
- m_activeIndex = 0;
-
- return true;
-}
-
-void CSoundscapeSystem::FlushSoundscapes( void )
-{
- m_soundscapeCount = 0;
- m_soundscapes.ClearStrings();
-}
-
-void CSoundscapeSystem::Shutdown()
-{
- FlushSoundscapes();
- m_soundscapeEntities.RemoveAll();
- m_activeIndex = 0;
-
- if ( IsX360() )
- {
- m_soundscapeSounds.Purge();
- }
-}
-
-void CSoundscapeSystem::LevelInitPreEntity()
-{
- g_SoundscapeSystem.Shutdown();
- g_SoundscapeSystem.Init();
-}
-
-void CSoundscapeSystem::LevelInitPostEntity()
-{
- if ( IsX360() )
- {
- m_soundscapeSounds.Purge();
- }
- CUtlVector<bbox_t> clusterbounds;
- int clusterCount = engine->GetClusterCount();
- clusterbounds.SetCount( clusterCount );
- engine->GetAllClusterBounds( clusterbounds.Base(), clusterCount );
- m_soundscapesInCluster.SetCount(clusterCount);
- for ( int i = 0; i < clusterCount; i++ )
- {
- m_soundscapesInCluster[i].soundscapeCount = 0;
- m_soundscapesInCluster[i].firstSoundscape = 0;
- }
- unsigned char myPVS[16 * 1024];
- CUtlVector<short> clusterIndexList;
- CUtlVector<short> soundscapeIndexList;
-
- // find the clusters visible from each soundscape
- // add this soundscape to the list of soundscapes for that cluster, clip cluster bounds to radius
- for ( int i = 0; i < m_soundscapeEntities.Count(); i++ )
- {
- Vector position = m_soundscapeEntities[i]->GetAbsOrigin();
- float radius = m_soundscapeEntities[i]->m_flRadius;
- float radiusSq = radius * radius;
- engine->GetPVSForCluster( engine->GetClusterForOrigin( position ), sizeof( myPVS ), myPVS );
- for ( int j = 0; j < clusterCount; j++ )
- {
- if ( myPVS[ j >> 3 ] & (1<<(j&7)) )
- {
- float distSq = CalcSqrDistanceToAABB( clusterbounds[j].mins, clusterbounds[j].maxs, position );
- if ( distSq < radiusSq || radius < 0 )
- {
- m_soundscapesInCluster[j].soundscapeCount++;
- clusterIndexList.AddToTail(j);
- // UNDONE: Technically you just need a soundscape index and a count for this list.
- soundscapeIndexList.AddToTail(i);
- }
- }
- }
- }
-
- // basically this part is like a radix sort
- // this is how many entries we need in the soundscape index list
- m_soundscapeIndexList.SetCount(soundscapeIndexList.Count());
-
- // now compute the starting index of each cluster
- int firstSoundscape = 0;
- for ( int i = 0; i < clusterCount; i++ )
- {
- m_soundscapesInCluster[i].firstSoundscape = firstSoundscape;
- firstSoundscape += m_soundscapesInCluster[i].soundscapeCount;
- m_soundscapesInCluster[i].soundscapeCount = 0;
- }
- // now add each soundscape index to the appropriate cluster's list
- // The resulting list is precomputing all soundscapes that need to be checked for a player
- // in each cluster. This is used to accelerate the per-frame operations
- for ( int i = 0; i < soundscapeIndexList.Count(); i++ )
- {
- int cluster = clusterIndexList[i];
- int outIndex = m_soundscapesInCluster[cluster].soundscapeCount + m_soundscapesInCluster[cluster].firstSoundscape;
- m_soundscapesInCluster[cluster].soundscapeCount++;
- m_soundscapeIndexList[outIndex] = soundscapeIndexList[i];
- }
-}
-
-int CSoundscapeSystem::GetSoundscapeIndex( const char *pName )
-{
- return m_soundscapes.GetStringID( pName );
-}
-
-bool CSoundscapeSystem::IsValidIndex( int index )
-{
- if ( index >= 0 && index < m_soundscapeCount )
- return true;
- return false;
-}
-
-void CSoundscapeSystem::AddSoundscapeEntity( CEnvSoundscape *pSoundscape )
-{
- if ( m_soundscapeEntities.Find( pSoundscape ) == -1 )
- {
- int index = m_soundscapeEntities.AddToTail( pSoundscape );
- pSoundscape->m_soundscapeEntityId = index + 1;
- }
-}
-
-void CSoundscapeSystem::RemoveSoundscapeEntity( CEnvSoundscape *pSoundscape )
-{
- m_soundscapeEntities.FindAndRemove( pSoundscape );
- pSoundscape->m_soundscapeEntityId = -1;
-}
-
-void CSoundscapeSystem::FrameUpdatePostEntityThink()
-{
- int total = m_soundscapeEntities.Count();
- if ( total > 0 )
- {
- int traceCount = 0;
- int playerCount = 0;
- // budget tuned for TF. Do a max of 20 traces. That's going to happen anyway because a bunch of the maps
- // use radius -1 for all soundscapes. So to trace one player you'll often need that many and this code must
- // always trace one player's soundscapes.
- // If the map has been optimized, then allow more players to update per frame.
- int maxPlayers = gpGlobals->maxClients / 2;
- // maxPlayers has to be at least 1
- maxPlayers = MAX( 1, maxPlayers );
- int maxTraces = 20;
- if ( soundscape_debug.GetBool() )
- {
- maxTraces = 9999;
- maxPlayers = MAX_PLAYERS;
- }
-
- // load balance across server ticks a bit by limiting the numbers of players (get cluster for origin)
- // and traces processed in a single tick. In single player this will update the player every tick
- // because it always does at least one player's full load of work
- for ( int i = 0; i < gpGlobals->maxClients && traceCount <= maxTraces && playerCount <= maxPlayers; i++ )
- {
- m_activeIndex = (m_activeIndex+1) % gpGlobals->maxClients;
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( m_activeIndex + 1 );
- if ( pPlayer && pPlayer->IsNetClient() )
- {
- // check to see if this is the sound entity that is
- // currently affecting this player
- audioparams_t &audio = pPlayer->GetAudioParams();
-
- // if we got this far, we're looking at an entity that is contending
- // for current player sound. the closest entity to player wins.
- CEnvSoundscape *pCurrent = (CEnvSoundscape *)( audio.ent.Get() );
- if ( pCurrent )
- {
- int nEntIndex = pCurrent->m_soundscapeEntityId - 1;
- NOTE_UNUSED( nEntIndex );
- Assert( m_soundscapeEntities[nEntIndex] == pCurrent );
- }
- ss_update_t update;
- update.pPlayer = pPlayer;
- update.pCurrentSoundscape = pCurrent;
- update.playerPosition = pPlayer->EarPosition();
- update.bInRange = false;
- update.currentDistance = 0;
- update.traceCount = 0;
- if ( pCurrent )
- {
- pCurrent->UpdateForPlayer(update);
- }
-
- int clusterIndex = engine->GetClusterForOrigin( update.playerPosition );
-
- if ( clusterIndex >= 0 && clusterIndex < m_soundscapesInCluster.Count() )
- {
- // find all soundscapes that could possibly attach to this player and update them
- for ( int j = 0; j < m_soundscapesInCluster[clusterIndex].soundscapeCount; j++ )
- {
- int ssIndex = m_soundscapeIndexList[m_soundscapesInCluster[clusterIndex].firstSoundscape + j];
- if ( m_soundscapeEntities[ssIndex] == update.pCurrentSoundscape )
- continue;
- m_soundscapeEntities[ssIndex]->UpdateForPlayer( update );
- }
- }
- playerCount++;
- traceCount += update.traceCount;
- }
- }
- }
-}
-
-void CSoundscapeSystem::AddSoundscapeSounds( KeyValues *pSoundscape, int soundscapeIndex )
-{
- if ( !IsX360() )
- {
- return;
- }
-
- int i = m_soundscapeSounds.AddToTail();
- Assert( i == soundscapeIndex );
-
- KeyValues *pKey = pSoundscape->GetFirstSubKey();
- while ( pKey )
- {
- if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
- {
- KeyValues *pAmbientKey = pKey->GetFirstSubKey();
- while ( pAmbientKey )
- {
- if ( !Q_strcasecmp( pAmbientKey->GetName(), "wave" ) )
- {
- char const *pSoundName = pAmbientKey->GetString();
- m_soundscapeSounds[i].AddToTail( pSoundName );
- }
- pAmbientKey = pAmbientKey->GetNextKey();
- }
- }
- else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
- {
- KeyValues *pRandomKey = pKey->GetFirstSubKey();
- while ( pRandomKey )
- {
- if ( !Q_strcasecmp( pRandomKey->GetName(), "rndwave" ) )
- {
- KeyValues *pRndWaveKey = pRandomKey->GetFirstSubKey();
- while ( pRndWaveKey )
- {
- if ( !Q_strcasecmp( pRndWaveKey->GetName(), "wave" ) )
- {
- char const *pSoundName = pRndWaveKey->GetString();
- m_soundscapeSounds[i].AddToTail( pSoundName );
- }
- pRndWaveKey = pRndWaveKey->GetNextKey();
- }
- }
- pRandomKey = pRandomKey->GetNextKey();
- }
- }
- else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) )
- {
- KeyValues *pPlayKey = pKey->GetFirstSubKey();
- while ( pPlayKey )
- {
- if ( !Q_strcasecmp( pPlayKey->GetName(), "name" ) )
- {
- char const *pSoundName = pPlayKey->GetString();
- m_soundscapeSounds[i].AddToTail( pSoundName );
- }
- pPlayKey = pPlayKey->GetNextKey();
- }
- }
- pKey = pKey->GetNextKey();
- }
-}
-
-void CSoundscapeSystem::PrecacheSounds( int soundscapeIndex )
-{
- if ( !IsX360() )
- {
- return;
- }
-
- if ( !IsValidIndex( soundscapeIndex ) )
- {
- return;
- }
-
- int count = m_soundscapeSounds[soundscapeIndex].Count();
- for ( int i=0; i<count; i++ )
- {
- const char *pSound = m_soundscapeSounds[soundscapeIndex][i];
- if ( Q_stristr( pSound, ".wav" ) )
- {
- CBaseEntity::PrecacheSound( pSound );
- }
- else
- {
- // recurse into new soundscape
- PrecacheSounds( GetSoundscapeIndex( pSound ) );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+#include "soundscape_system.h"
+#include "soundscape.h"
+#include "KeyValues.h"
+#include "filesystem.h"
+#include "game.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt"
+
+CON_COMMAND(soundscape_flush, "Flushes the server & client side soundscapes")
+{
+ CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
+ if ( engine->IsDedicatedServer() )
+ {
+ // If it's a dedicated server, only the server console can run this.
+ if ( pPlayer )
+ return;
+ }
+ else
+ {
+ // If it's a listen server, only the listen server host can run this.
+ if ( !pPlayer || pPlayer != UTIL_GetListenServerHost() )
+ return;
+ }
+
+ g_SoundscapeSystem.FlushSoundscapes(); // don't bother forgetting about the entities
+ g_SoundscapeSystem.Init();
+
+
+ if ( engine->IsDedicatedServer() )
+ {
+ // If the ds console typed it, send it to everyone.
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pSendToPlayer = UTIL_PlayerByIndex( i );
+ if ( pSendToPlayer )
+ engine->ClientCommand( pSendToPlayer->edict(), "cl_soundscape_flush\n" );
+ }
+ }
+ else
+ {
+ engine->ClientCommand( pPlayer->edict(), "cl_soundscape_flush\n" );
+ }
+}
+
+CSoundscapeSystem g_SoundscapeSystem( "CSoundscapeSystem" );
+
+extern ConVar soundscape_debug;
+
+void CSoundscapeSystem::AddSoundscapeFile( const char *filename )
+{
+ MEM_ALLOC_CREDIT();
+ // Open the soundscape data file, and abort if we can't
+ KeyValues *pKeyValuesData = new KeyValues( filename );
+ if ( filesystem->LoadKeyValues( *pKeyValuesData, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) )
+ {
+ // parse out all of the top level sections and save their names
+ KeyValues *pKeys = pKeyValuesData;
+ while ( pKeys )
+ {
+ if ( pKeys->GetFirstSubKey() )
+ {
+ if ( g_pDeveloper->GetBool() )
+ {
+ if ( strstr( pKeys->GetName(), "{" ) )
+ {
+ Msg("Error parsing soundscape file %s after %s\n", filename, m_soundscapeCount>0 ?m_soundscapes.GetStringText( m_soundscapeCount-1 ) : "FIRST" );
+ }
+ }
+ m_soundscapes.AddString( pKeys->GetName(), m_soundscapeCount );
+
+ if ( IsX360() )
+ {
+ AddSoundscapeSounds( pKeys, m_soundscapeCount );
+ }
+ m_soundscapeCount++;
+ }
+ pKeys = pKeys->GetNextKey();
+ }
+ }
+ pKeyValuesData->deleteThis();
+}
+
+CON_COMMAND_F( sv_soundscape_printdebuginfo, "print soundscapes", FCVAR_DEVELOPMENTONLY )
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ g_SoundscapeSystem.PrintDebugInfo();
+}
+
+
+void CSoundscapeSystem::PrintDebugInfo()
+{
+ Msg( "\n------- SERVER SOUNDSCAPES -------\n" );
+ for ( int key=m_soundscapes.First(); key != m_soundscapes.InvalidIndex(); key = m_soundscapes.Next( key ) )
+ {
+ int id = m_soundscapes.GetIDForKey( key );
+ const char *pName = m_soundscapes.GetStringForKey( key );
+
+ Msg( "- %d: %s\n", id, pName );
+ }
+ Msg( "-------- SOUNDSCAPE ENTITIES -----\n" );
+ for( int entityIndex = 0; entityIndex < m_soundscapeEntities.Size(); ++entityIndex )
+ {
+ CEnvSoundscape *currentSoundscape = m_soundscapeEntities[entityIndex];
+ Msg("- %d: %s x:%.4f y:%.4f z:%.4f\n",
+ entityIndex,
+ STRING(currentSoundscape->GetSoundscapeName()),
+ currentSoundscape->GetAbsOrigin().x,
+ currentSoundscape->GetAbsOrigin().y,
+ currentSoundscape->GetAbsOrigin().z
+ );
+ }
+ Msg( "----------------------------------\n\n" );
+}
+
+bool CSoundscapeSystem::Init()
+{
+ m_soundscapeCount = 0;
+
+ const char *mapname = STRING( gpGlobals->mapname );
+ const char *mapSoundscapeFilename = NULL;
+ if ( mapname && *mapname )
+ {
+ mapSoundscapeFilename = UTIL_VarArgs( "scripts/soundscapes_%s.txt", mapname );
+ }
+
+ KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE );
+ if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) )
+ {
+ for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
+ {
+ if ( !Q_stricmp( sub->GetName(), "file" ) )
+ {
+ // Add
+ AddSoundscapeFile( sub->GetString() );
+ if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) )
+ {
+ mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape
+ }
+ continue;
+ }
+
+ Warning( "CSoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
+ SOUNDSCAPE_MANIFEST_FILE, sub->GetName() );
+ }
+
+ if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) )
+ {
+ AddSoundscapeFile( mapSoundscapeFilename );
+ }
+ }
+ else
+ {
+ Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE );
+ }
+ manifest->deleteThis();
+ m_activeIndex = 0;
+
+ return true;
+}
+
+void CSoundscapeSystem::FlushSoundscapes( void )
+{
+ m_soundscapeCount = 0;
+ m_soundscapes.ClearStrings();
+}
+
+void CSoundscapeSystem::Shutdown()
+{
+ FlushSoundscapes();
+ m_soundscapeEntities.RemoveAll();
+ m_activeIndex = 0;
+
+ if ( IsX360() )
+ {
+ m_soundscapeSounds.Purge();
+ }
+}
+
+void CSoundscapeSystem::LevelInitPreEntity()
+{
+ g_SoundscapeSystem.Shutdown();
+ g_SoundscapeSystem.Init();
+}
+
+void CSoundscapeSystem::LevelInitPostEntity()
+{
+ if ( IsX360() )
+ {
+ m_soundscapeSounds.Purge();
+ }
+ CUtlVector<bbox_t> clusterbounds;
+ int clusterCount = engine->GetClusterCount();
+ clusterbounds.SetCount( clusterCount );
+ engine->GetAllClusterBounds( clusterbounds.Base(), clusterCount );
+ m_soundscapesInCluster.SetCount(clusterCount);
+ for ( int i = 0; i < clusterCount; i++ )
+ {
+ m_soundscapesInCluster[i].soundscapeCount = 0;
+ m_soundscapesInCluster[i].firstSoundscape = 0;
+ }
+ unsigned char myPVS[16 * 1024];
+ CUtlVector<short> clusterIndexList;
+ CUtlVector<short> soundscapeIndexList;
+
+ // find the clusters visible from each soundscape
+ // add this soundscape to the list of soundscapes for that cluster, clip cluster bounds to radius
+ for ( int i = 0; i < m_soundscapeEntities.Count(); i++ )
+ {
+ Vector position = m_soundscapeEntities[i]->GetAbsOrigin();
+ float radius = m_soundscapeEntities[i]->m_flRadius;
+ float radiusSq = radius * radius;
+ engine->GetPVSForCluster( engine->GetClusterForOrigin( position ), sizeof( myPVS ), myPVS );
+ for ( int j = 0; j < clusterCount; j++ )
+ {
+ if ( myPVS[ j >> 3 ] & (1<<(j&7)) )
+ {
+ float distSq = CalcSqrDistanceToAABB( clusterbounds[j].mins, clusterbounds[j].maxs, position );
+ if ( distSq < radiusSq || radius < 0 )
+ {
+ m_soundscapesInCluster[j].soundscapeCount++;
+ clusterIndexList.AddToTail(j);
+ // UNDONE: Technically you just need a soundscape index and a count for this list.
+ soundscapeIndexList.AddToTail(i);
+ }
+ }
+ }
+ }
+
+ // basically this part is like a radix sort
+ // this is how many entries we need in the soundscape index list
+ m_soundscapeIndexList.SetCount(soundscapeIndexList.Count());
+
+ // now compute the starting index of each cluster
+ int firstSoundscape = 0;
+ for ( int i = 0; i < clusterCount; i++ )
+ {
+ m_soundscapesInCluster[i].firstSoundscape = firstSoundscape;
+ firstSoundscape += m_soundscapesInCluster[i].soundscapeCount;
+ m_soundscapesInCluster[i].soundscapeCount = 0;
+ }
+ // now add each soundscape index to the appropriate cluster's list
+ // The resulting list is precomputing all soundscapes that need to be checked for a player
+ // in each cluster. This is used to accelerate the per-frame operations
+ for ( int i = 0; i < soundscapeIndexList.Count(); i++ )
+ {
+ int cluster = clusterIndexList[i];
+ int outIndex = m_soundscapesInCluster[cluster].soundscapeCount + m_soundscapesInCluster[cluster].firstSoundscape;
+ m_soundscapesInCluster[cluster].soundscapeCount++;
+ m_soundscapeIndexList[outIndex] = soundscapeIndexList[i];
+ }
+}
+
+int CSoundscapeSystem::GetSoundscapeIndex( const char *pName )
+{
+ return m_soundscapes.GetStringID( pName );
+}
+
+bool CSoundscapeSystem::IsValidIndex( int index )
+{
+ if ( index >= 0 && index < m_soundscapeCount )
+ return true;
+ return false;
+}
+
+void CSoundscapeSystem::AddSoundscapeEntity( CEnvSoundscape *pSoundscape )
+{
+ if ( m_soundscapeEntities.Find( pSoundscape ) == -1 )
+ {
+ int index = m_soundscapeEntities.AddToTail( pSoundscape );
+ pSoundscape->m_soundscapeEntityId = index + 1;
+ }
+}
+
+void CSoundscapeSystem::RemoveSoundscapeEntity( CEnvSoundscape *pSoundscape )
+{
+ m_soundscapeEntities.FindAndRemove( pSoundscape );
+ pSoundscape->m_soundscapeEntityId = -1;
+}
+
+void CSoundscapeSystem::FrameUpdatePostEntityThink()
+{
+ int total = m_soundscapeEntities.Count();
+ if ( total > 0 )
+ {
+ int traceCount = 0;
+ int playerCount = 0;
+ // budget tuned for TF. Do a max of 20 traces. That's going to happen anyway because a bunch of the maps
+ // use radius -1 for all soundscapes. So to trace one player you'll often need that many and this code must
+ // always trace one player's soundscapes.
+ // If the map has been optimized, then allow more players to update per frame.
+ int maxPlayers = gpGlobals->maxClients / 2;
+ // maxPlayers has to be at least 1
+ maxPlayers = MAX( 1, maxPlayers );
+ int maxTraces = 20;
+ if ( soundscape_debug.GetBool() )
+ {
+ maxTraces = 9999;
+ maxPlayers = MAX_PLAYERS;
+ }
+
+ // load balance across server ticks a bit by limiting the numbers of players (get cluster for origin)
+ // and traces processed in a single tick. In single player this will update the player every tick
+ // because it always does at least one player's full load of work
+ for ( int i = 0; i < gpGlobals->maxClients && traceCount <= maxTraces && playerCount <= maxPlayers; i++ )
+ {
+ m_activeIndex = (m_activeIndex+1) % gpGlobals->maxClients;
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( m_activeIndex + 1 );
+ if ( pPlayer && pPlayer->IsNetClient() )
+ {
+ // check to see if this is the sound entity that is
+ // currently affecting this player
+ audioparams_t &audio = pPlayer->GetAudioParams();
+
+ // if we got this far, we're looking at an entity that is contending
+ // for current player sound. the closest entity to player wins.
+ CEnvSoundscape *pCurrent = (CEnvSoundscape *)( audio.ent.Get() );
+ if ( pCurrent )
+ {
+ int nEntIndex = pCurrent->m_soundscapeEntityId - 1;
+ NOTE_UNUSED( nEntIndex );
+ Assert( m_soundscapeEntities[nEntIndex] == pCurrent );
+ }
+ ss_update_t update;
+ update.pPlayer = pPlayer;
+ update.pCurrentSoundscape = pCurrent;
+ update.playerPosition = pPlayer->EarPosition();
+ update.bInRange = false;
+ update.currentDistance = 0;
+ update.traceCount = 0;
+ if ( pCurrent )
+ {
+ pCurrent->UpdateForPlayer(update);
+ }
+
+ int clusterIndex = engine->GetClusterForOrigin( update.playerPosition );
+
+ if ( clusterIndex >= 0 && clusterIndex < m_soundscapesInCluster.Count() )
+ {
+ // find all soundscapes that could possibly attach to this player and update them
+ for ( int j = 0; j < m_soundscapesInCluster[clusterIndex].soundscapeCount; j++ )
+ {
+ int ssIndex = m_soundscapeIndexList[m_soundscapesInCluster[clusterIndex].firstSoundscape + j];
+ if ( m_soundscapeEntities[ssIndex] == update.pCurrentSoundscape )
+ continue;
+ m_soundscapeEntities[ssIndex]->UpdateForPlayer( update );
+ }
+ }
+ playerCount++;
+ traceCount += update.traceCount;
+ }
+ }
+ }
+}
+
+void CSoundscapeSystem::AddSoundscapeSounds( KeyValues *pSoundscape, int soundscapeIndex )
+{
+ if ( !IsX360() )
+ {
+ return;
+ }
+
+ int i = m_soundscapeSounds.AddToTail();
+ Assert( i == soundscapeIndex );
+
+ KeyValues *pKey = pSoundscape->GetFirstSubKey();
+ while ( pKey )
+ {
+ if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
+ {
+ KeyValues *pAmbientKey = pKey->GetFirstSubKey();
+ while ( pAmbientKey )
+ {
+ if ( !Q_strcasecmp( pAmbientKey->GetName(), "wave" ) )
+ {
+ char const *pSoundName = pAmbientKey->GetString();
+ m_soundscapeSounds[i].AddToTail( pSoundName );
+ }
+ pAmbientKey = pAmbientKey->GetNextKey();
+ }
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
+ {
+ KeyValues *pRandomKey = pKey->GetFirstSubKey();
+ while ( pRandomKey )
+ {
+ if ( !Q_strcasecmp( pRandomKey->GetName(), "rndwave" ) )
+ {
+ KeyValues *pRndWaveKey = pRandomKey->GetFirstSubKey();
+ while ( pRndWaveKey )
+ {
+ if ( !Q_strcasecmp( pRndWaveKey->GetName(), "wave" ) )
+ {
+ char const *pSoundName = pRndWaveKey->GetString();
+ m_soundscapeSounds[i].AddToTail( pSoundName );
+ }
+ pRndWaveKey = pRndWaveKey->GetNextKey();
+ }
+ }
+ pRandomKey = pRandomKey->GetNextKey();
+ }
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) )
+ {
+ KeyValues *pPlayKey = pKey->GetFirstSubKey();
+ while ( pPlayKey )
+ {
+ if ( !Q_strcasecmp( pPlayKey->GetName(), "name" ) )
+ {
+ char const *pSoundName = pPlayKey->GetString();
+ m_soundscapeSounds[i].AddToTail( pSoundName );
+ }
+ pPlayKey = pPlayKey->GetNextKey();
+ }
+ }
+ pKey = pKey->GetNextKey();
+ }
+}
+
+void CSoundscapeSystem::PrecacheSounds( int soundscapeIndex )
+{
+ if ( !IsX360() )
+ {
+ return;
+ }
+
+ if ( !IsValidIndex( soundscapeIndex ) )
+ {
+ return;
+ }
+
+ int count = m_soundscapeSounds[soundscapeIndex].Count();
+ for ( int i=0; i<count; i++ )
+ {
+ const char *pSound = m_soundscapeSounds[soundscapeIndex][i];
+ if ( Q_stristr( pSound, ".wav" ) )
+ {
+ CBaseEntity::PrecacheSound( pSound );
+ }
+ else
+ {
+ // recurse into new soundscape
+ PrecacheSounds( GetSoundscapeIndex( pSound ) );
+ }
+ }
+}