summaryrefslogtreecommitdiff
path: root/soundemittersystem/soundemittersystembase.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /soundemittersystem/soundemittersystembase.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'soundemittersystem/soundemittersystembase.cpp')
-rw-r--r--soundemittersystem/soundemittersystembase.cpp1663
1 files changed, 1663 insertions, 0 deletions
diff --git a/soundemittersystem/soundemittersystembase.cpp b/soundemittersystem/soundemittersystembase.cpp
new file mode 100644
index 0000000..41d01e3
--- /dev/null
+++ b/soundemittersystem/soundemittersystembase.cpp
@@ -0,0 +1,1663 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+
+#include <KeyValues.h>
+#include "filesystem.h"
+#include "utldict.h"
+#include "interval.h"
+#include "engine/IEngineSound.h"
+#include "soundemittersystembase.h"
+#include "utlbuffer.h"
+#include "soundchars.h"
+#include "vstdlib/random.h"
+#include "checksum_crc.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "ifilelist.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define MANIFEST_FILE "scripts/game_sounds_manifest.txt"
+#define GAME_SOUNDS_HEADER_BLOCK "scripts/game_sounds_header.txt"
+
+static IFileSystem* filesystem = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CSoundEmitterSystemBase::CSoundEmitterSystemBase() :
+ m_nInitCount( 0 ),
+ m_uManifestPlusScriptChecksum( 0 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::First() const
+{
+ return m_Sounds.FirstHandle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : i -
+// Output : int
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::Next( int i ) const
+{
+ return m_Sounds.NextHandle(i);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::InvalidIndex() const
+{
+ return m_Sounds.InvalidHandle();
+}
+
+//-----------------------------------------------------------------------------
+//
+// implementation of IUniformRandomStream
+//
+//-----------------------------------------------------------------------------
+class CSoundEmitterUniformRandomStream : public IUniformRandomStream
+{
+public:
+ // Sets the seed of the random number generator
+ void SetSeed( int iSeed )
+ {
+ // Never call this from the client or game!
+ Assert(0);
+ }
+
+ // Generates random numbers
+ float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f )
+ {
+ return ::RandomFloat( flMinVal, flMaxVal );
+ }
+
+ int RandomInt( int iMinVal, int iMaxVal )
+ {
+ return ::RandomInt( iMinVal, iMaxVal );
+ }
+
+ float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f )
+ {
+ return ::RandomFloatExp( flMinVal, flMaxVal, flExponent );
+ }
+
+};
+
+static CSoundEmitterUniformRandomStream g_RandomStream;
+IUniformRandomStream *randomStream = &g_RandomStream;
+
+
+//-----------------------------------------------------------------------------
+// Connect, disconnect
+//-----------------------------------------------------------------------------
+bool CSoundEmitterSystemBase::Connect( CreateInterfaceFn factory )
+{
+ // If someone already connected us up, don't redo the connection
+ if ( NULL != filesystem )
+ {
+ return true;
+ }
+
+ filesystem = (IFileSystem *)factory( FILESYSTEM_INTERFACE_VERSION, NULL );
+ if( !filesystem )
+ {
+ Error( "The soundemittersystem system requires the filesystem to run!\n" );
+ return false;
+ }
+
+ return true;
+}
+
+
+void CSoundEmitterSystemBase::Disconnect()
+{
+ filesystem = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Query interface
+//-----------------------------------------------------------------------------
+void *CSoundEmitterSystemBase::QueryInterface( const char *pInterfaceName )
+{
+ // Loading the engine DLL mounts *all* soundemitter interfaces
+ CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
+ return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+InitReturnVal_t CSoundEmitterSystemBase::Init()
+{
+ return INIT_OK;
+}
+
+void CSoundEmitterSystemBase::Shutdown()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for checksuming script files and manifest to determine if soundname caches
+// need to be blown away.
+// Input : *crc -
+// *filename -
+// Output : static void
+//-----------------------------------------------------------------------------
+static void AccumulateFileNameAndTimestampIntoChecksum( CRC32_t *crc, char const *filename )
+{
+ if ( IsX360() )
+ {
+ // this is an expensive i/o operation due to search path fall through
+ // 360 doesn't need or use the checksums
+ return;
+ }
+
+ long ft = filesystem->GetFileTime( filename, "GAME" );
+ CRC32_ProcessBuffer( crc, &ft, sizeof( ft ) );
+ CRC32_ProcessBuffer( crc, filename, Q_strlen( filename ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSoundEmitterSystemBase::InternalModInit()
+{
+ /*
+ if ( m_SoundKeyValues.Count() > 0 )
+ {
+ Shutdown();
+ }
+ */
+
+ LoadGlobalActors();
+
+ m_uManifestPlusScriptChecksum = 0u;
+
+ CRC32_t crc;
+ CRC32_Init( &crc );
+
+ KeyValues *manifest = new KeyValues( MANIFEST_FILE );
+ if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDEMITTER, MANIFEST_FILE, "GAME" ) )
+ {
+ AccumulateFileNameAndTimestampIntoChecksum( &crc, MANIFEST_FILE );
+
+ for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
+ {
+ if ( !Q_stricmp( sub->GetName(), "precache_file" ) )
+ {
+ AccumulateFileNameAndTimestampIntoChecksum( &crc, sub->GetString() );
+
+ // Add and always precache
+ AddSoundsFromFile( sub->GetString(), false );
+ continue;
+ }
+ else if ( !Q_stricmp( sub->GetName(), "preload_file" ) )
+ {
+ AccumulateFileNameAndTimestampIntoChecksum( &crc, sub->GetString() );
+
+ // Add and always precache
+ AddSoundsFromFile( sub->GetString(), true );
+ continue;
+ }
+ else if ( !Q_stricmp( sub->GetName(), "faceposer_file" ) )
+ {
+ // do nothing for these files; they're only used for faceposer
+ continue;
+ }
+
+ Warning( "CSoundEmitterSystemBase::BaseInit: Manifest '%s' with bogus file type '%s', expecting 'declare_file' or 'precache_file'\n",
+ MANIFEST_FILE, sub->GetName() );
+ }
+ }
+ else
+ {
+ Error( "Unable to load manifest file '%s'\n", MANIFEST_FILE );
+ }
+ manifest->deleteThis();
+
+ CRC32_Final( &crc );
+
+ m_uManifestPlusScriptChecksum =( unsigned int )crc;
+
+// Only print total once, on server
+#if !defined( CLIENT_DLL ) && !defined( FACEPOSER )
+ DevMsg( 1, "CSoundEmitterSystem: Registered %i sounds\n", m_Sounds.Count() );
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSoundEmitterSystemBase::ModInit()
+{
+ ++m_nInitCount;
+
+ if ( m_nInitCount > 1 )
+ {
+ return true;
+ }
+
+ return InternalModInit();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::InternalModShutdown()
+{
+ int i;
+ m_SoundKeyValues.RemoveAll();
+
+ for ( UtlHashHandle_t nIndex = m_Sounds.FirstHandle(); nIndex != m_Sounds.InvalidHandle(); nIndex = m_Sounds.NextHandle( nIndex ) )
+ {
+ delete m_Sounds[ nIndex ];
+ }
+
+ m_Sounds.Purge();
+
+ for ( i = 0; i < m_SavedOverrides.Count() ; ++i )
+ {
+ delete m_SavedOverrides[ i ];
+ }
+ m_SavedOverrides.Purge();
+ m_Waves.RemoveAll();
+ m_ActorGenders.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::ModShutdown()
+{
+ if ( --m_nInitCount > 0 )
+ return;
+
+ InternalModShutdown();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pName -
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::GetSoundIndex( const char *pName ) const
+{
+ if ( !pName )
+ return -1;
+
+ CSoundEntry search;
+ search.m_Name = pName;
+ UtlHashHandle_t idx = m_Sounds.Find( pName );
+ if ( idx == m_Sounds.InvalidHandle() )
+ return -1;
+
+ return idx;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSoundEmitterSystemBase::IsValidIndex( int index )
+{
+ return m_Sounds.IsValidHandle( index );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+// Output : char const
+//-----------------------------------------------------------------------------
+const char *CSoundEmitterSystemBase::GetSoundName( int index )
+{
+ if ( !IsValidIndex( index ) )
+ return "";
+
+ return m_Sounds[ index ]->m_Name.Get();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::GetSoundCount( void )
+{
+ return m_Sounds.Count();
+}
+
+void CSoundEmitterSystemBase::EnsureAvailableSlotsForGender( SoundFile *pSoundnames, int c, gender_t gender )
+{
+ int i;
+ if ( c <= 0 )
+ {
+ return;
+ }
+
+ CUtlVector< int > slots;
+
+ bool needsreset = false;
+ for ( i = 0; i < c; i++ )
+ {
+ if ( pSoundnames[ i ].gender != gender )
+ continue;
+
+ // There was at least one match for the gender
+ needsreset = true;
+
+ // This sound is unavailable
+ if ( !pSoundnames[ i ].available )
+ continue;
+
+ slots.AddToTail( i );
+ }
+
+ if ( slots.Count() == 0 && needsreset )
+ {
+ // Reset all slots for the specified gender!!!
+ for ( i = 0; i < c; i++ )
+ {
+ if ( pSoundnames[ i ].gender != gender )
+ continue;
+
+ pSoundnames[ i ].available = true;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : gender -
+// soundnames -
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::FindBestSoundForGender( SoundFile *pSoundnames, int c, gender_t gender )
+{
+ // Check for recycling of random sounds...
+ EnsureAvailableSlotsForGender( pSoundnames, c, gender );
+
+ if ( c <= 0 )
+ {
+ return -1;
+ }
+
+ CUtlVector< int > slots;
+
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( pSoundnames[ i ].gender == gender &&
+ pSoundnames[ i ].available )
+ {
+ slots.AddToTail( i );
+ }
+ }
+
+ if ( slots.Count() >= 1 )
+ {
+ int idx = slots[ randomStream->RandomInt( 0, slots.Count() - 1 ) ];
+ return idx;
+ }
+
+ int idx = randomStream->RandomInt( 0, c - 1 );
+ return idx;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *soundname -
+// params -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSoundEmitterSystemBase::GetParametersForSound( const char *soundname, CSoundParameters& params, gender_t gender, bool isbeingemitted /*= false*/ )
+{
+ HSOUNDSCRIPTHANDLE index = (HSOUNDSCRIPTHANDLE)GetSoundIndex( soundname );
+ if ( index == SOUNDEMITTER_INVALID_HANDLE )
+ {
+ static CUtlSymbolTable soundWarnings;
+ char key[ 256 ];
+ Q_snprintf( key, sizeof( key ), "%s:%s", soundname, params.soundname );
+ if ( UTL_INVAL_SYMBOL == soundWarnings.Find( key ) )
+ {
+ soundWarnings.AddString( key );
+
+ DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: No such sound %s\n", soundname );
+ }
+ return false;
+ }
+
+ return GetParametersForSoundEx( soundname, index, params, gender, isbeingemitted );
+}
+
+CSoundParametersInternal *CSoundEmitterSystemBase::InternalGetParametersForSound( int index )
+{
+ if ( !m_Sounds.IsValidHandle( index ) )
+ {
+ Assert( !"CSoundEmitterSystemBase::InternalGetParametersForSound: Bogus index" );
+ return NULL;
+ }
+
+ return &m_Sounds[ index ]->m_SoundParams;
+}
+
+static void SplitName( char const *input, int splitchar, int splitlen, char *before, int beforelen, char *after, int afterlen )
+{
+ char const *in = input;
+ char *out = before;
+
+ int c = 0;
+ int l = 0;
+ int maxl = beforelen;
+ while ( *in )
+ {
+ if ( c == splitchar )
+ {
+ while ( --splitlen >= 0 )
+ {
+ in++;
+ }
+
+ *out = 0;
+ out = after;
+ maxl = afterlen;
+ c++;
+ continue;
+ }
+
+ if ( l >= maxl )
+ {
+ in++;
+ c++;
+ continue;
+ }
+
+ *out++ = *in++;
+ l++;
+ c++;
+ }
+
+ *out = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : params -
+// *wavename -
+// gender -
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::AddSoundName( CSoundParametersInternal& params, char const *wavename, gender_t gender )
+{
+ CUtlSymbol sym = m_Waves.AddString( wavename );
+ SoundFile e;
+ e.symbol = sym;
+ e.gender = gender;
+ if ( gender != GENDER_NONE )
+ {
+ params.SetUsesGenderToken( true );
+ }
+ params.AddSoundName( e );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : params -
+// *wavename -
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::ExpandSoundNameMacros( CSoundParametersInternal& params, char const *wavename )
+{
+ char const *p = Q_stristr( wavename, SOUNDGENDER_MACRO );
+
+ if ( !p )
+ {
+ AddSoundName( params, wavename, GENDER_NONE );
+ return;
+ }
+
+ int offset = p - wavename;
+ Assert( offset >= 0 );
+ int duration = SOUNDGENDER_MACRO_LENGTH;
+
+ // Create a "male" and "female" version of the sound
+ char before[ 256 ], after[ 256 ];
+ Q_memset( before, 0, sizeof( before ) );
+ Q_memset( after, 0, sizeof( after ) );
+
+ SplitName( wavename, offset, duration, before, sizeof( before ), after, sizeof( after ) );
+
+ char temp[ 256 ];
+ Q_snprintf( temp, sizeof( temp ), "%s%s%s", before, "male", after );
+ AddSoundName( params, temp, GENDER_MALE );
+ Q_snprintf( temp, sizeof( temp ), "%s%s%s", before, "female", after );
+ AddSoundName( params, temp, GENDER_FEMALE );
+
+ // Add the conversion entry with the gender tags still in it
+ CUtlSymbol sym = m_Waves.AddString( wavename );
+ SoundFile e;
+ e.symbol = sym;
+ e.gender = GENDER_NONE;
+ params.AddConvertedName( e );
+}
+
+void CSoundEmitterSystemBase::GenderExpandString( gender_t gender, char const *in, char *out, int maxlen )
+{
+ // Assume the worst
+ Q_strncpy( out, in, maxlen );
+
+ char const *p = Q_stristr( in, SOUNDGENDER_MACRO );
+ if ( !p )
+ {
+ return;
+ }
+
+ // Look up actor gender
+ if ( gender == GENDER_NONE )
+ {
+ return;
+ }
+
+ int offset = p - in;
+ Assert( offset >= 0 );
+ int duration = SOUNDGENDER_MACRO_LENGTH;
+
+ // Create a "male" and "female" version of the sound
+ char before[ 256 ], after[ 256 ];
+ Q_memset( before, 0, sizeof( before ) );
+ Q_memset( after, 0, sizeof( after ) );
+
+ SplitName( in, offset, duration, before, sizeof( before ), after, sizeof( after ) );
+
+ switch ( gender )
+ {
+ default:
+ case GENDER_NONE:
+ {
+ Assert( !"CSoundEmitterSystemBase::GenderExpandString: expecting MALE or FEMALE!" );
+ }
+ break;
+ case GENDER_MALE:
+ {
+ Q_snprintf( out, maxlen, "%s%s%s", before, "male", after );
+ }
+ break;
+ case GENDER_FEMALE:
+ {
+ Q_snprintf( out, maxlen, "%s%s%s", before, "female", after );
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actorname -
+// *in -
+// *out -
+// maxlen -
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::GenderExpandString( char const *actormodel, char const *in, char *out, int maxlen )
+{
+ gender_t gender = GetActorGender( actormodel );
+ GenderExpandString( gender, in, out, maxlen );
+}
+
+void CSoundEmitterSystemBase::LoadGlobalActors()
+{
+ // Now load the global actor list from the scripts/globalactors.txt file
+ KeyValues *allActors = NULL;
+
+ allActors = new KeyValues( "allactors" );
+ if ( allActors->LoadFromFile( filesystem, "scripts/global_actors.txt", NULL ) )
+ {
+ KeyValues *pvkActor;
+ for ( pvkActor = allActors->GetFirstSubKey(); pvkActor != NULL; pvkActor = pvkActor->GetNextKey() )
+ {
+ UtlHashHandle_t idx = m_ActorGenders.Find( pvkActor->GetName() );
+ if ( idx == m_ActorGenders.InvalidHandle() )
+ {
+ if ( m_ActorGenders.Count() > 254 )
+ {
+ Warning( "Exceeded max number of actors in scripts/global_actors.txt\n" );
+ break;
+ }
+
+ gender_t gender = GENDER_NONE;
+ if ( !Q_stricmp( pvkActor->GetString(), "male" ) )
+ {
+ gender = GENDER_MALE;
+ }
+ else if (!Q_stricmp( pvkActor->GetString(), "female" ) )
+ {
+ gender = GENDER_FEMALE;
+ }
+ m_ActorGenders.Insert( pvkActor->GetName(), gender );
+ }
+ }
+ }
+ allActors->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actorname -
+// Output : gender_t
+//-----------------------------------------------------------------------------
+gender_t CSoundEmitterSystemBase::GetActorGender( char const *actormodel )
+{
+ char actor[ 256 ];
+ actor[0] = 0;
+ if ( actormodel )
+ {
+ Q_FileBase( actormodel, actor, sizeof( actor ) );
+ }
+
+ UtlHashHandle_t idx = m_ActorGenders.Find( actor );
+ if ( idx == m_ActorGenders.InvalidHandle() )
+ return GENDER_NONE;
+
+ return m_ActorGenders[ idx ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *soundname -
+// params -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSoundEmitterSystemBase::InitSoundInternalParameters( const char *soundname, KeyValues *kv, CSoundParametersInternal& params )
+{
+ KeyValues *pKey = kv->GetFirstSubKey();
+ while ( pKey )
+ {
+ if ( !Q_strcasecmp( pKey->GetName(), "channel" ) )
+ {
+ params.ChannelFromString( pKey->GetString() );
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
+ {
+ params.VolumeFromString( pKey->GetString() );
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) )
+ {
+ params.PitchFromString( pKey->GetString() );
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "wave" ) )
+ {
+ ExpandSoundNameMacros( params, pKey->GetString() );
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) )
+ {
+ KeyValues *pWaves = pKey->GetFirstSubKey();
+ while ( pWaves )
+ {
+ ExpandSoundNameMacros( params, pWaves->GetString() );
+
+ pWaves = pWaves->GetNextKey();
+ }
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) || !Q_strcasecmp( pKey->GetName(), "CompatibilityAttenuation" ) )
+ {
+ if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) )
+ {
+ DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has \"attenuation\" with %s value!\n",
+ soundname, pKey->GetString() );
+ }
+
+ if ( !Q_strncasecmp( pKey->GetString(), "ATTN_", strlen( "ATTN_" ) ) )
+ {
+ params.SetSoundLevel( ATTN_TO_SNDLVL( TranslateAttenuation( pKey->GetString() ) ) );
+ }
+ else
+ {
+ interval_t interval;
+ interval = ReadInterval( pKey->GetString() );
+
+ // Translate from attenuation to soundlevel
+ float start = interval.start;
+ float end = interval.start + interval.range;
+
+ params.SetSoundLevel( ATTN_TO_SNDLVL( start ), ATTN_TO_SNDLVL( end ) - ATTN_TO_SNDLVL( start ) );
+ }
+
+ // Goldsrc compatibility mode.. feed the sndlevel value through the sound engine interface in such a way
+ // that it can reconstruct the original sndlevel value and flag the sound as using Goldsrc attenuation.
+ bool bCompatibilityAttenuation = !Q_strcasecmp( pKey->GetName(), "CompatibilityAttenuation" );
+ if ( bCompatibilityAttenuation )
+ {
+ if ( params.GetSoundLevel().range != 0 )
+ {
+ Warning( "CompatibilityAttenuation for sound %s must have same start and end values.\n", soundname );
+ }
+
+ params.SetSoundLevel( SNDLEVEL_TO_COMPATIBILITY_MODE( params.GetSoundLevel().start ) );
+ }
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) )
+ {
+ if ( !Q_strncasecmp( pKey->GetString(), "ATTN_", strlen( "ATTN_" ) ) )
+ {
+ DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has \"soundlevel\" with %s value!\n",
+ soundname, pKey->GetString() );
+ }
+
+ params.SoundLevelFromString( pKey->GetString() );
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "play_to_owner_only" ) )
+ {
+ params.SetOnlyPlayToOwner( pKey->GetInt() ? true : false );
+ }
+ else if ( !Q_strcasecmp( pKey->GetName(), "delay_msec" ) )
+ {
+ // Don't allow negative delay
+ params.SetDelayMsec( max( 0, pKey->GetInt() ) );
+
+ }
+
+ pKey = pKey->GetNextKey();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *soundname -
+// Output : char const
+//-----------------------------------------------------------------------------
+const char *CSoundEmitterSystemBase::GetWavFileForSound( const char *soundname, char const *actormodel )
+{
+ gender_t gender = GetActorGender( actormodel );
+ return GetWavFileForSound( soundname, gender );
+}
+
+const char *CSoundEmitterSystemBase::GetWavFileForSound( const char *soundname, gender_t gender )
+{
+ CSoundParameters params;
+ if ( !GetParametersForSound( soundname, params, gender ) )
+ {
+ return soundname;
+ }
+
+ if ( !params.soundname[ 0 ] )
+ {
+ return soundname;
+ }
+
+ static char outsound[ 512 ];
+ Q_strncpy( outsound, params.soundname, sizeof( outsound ) );
+ return outsound;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *soundname -
+// Output : soundlevel_t
+//-----------------------------------------------------------------------------
+soundlevel_t CSoundEmitterSystemBase::LookupSoundLevel( const char *soundname )
+{
+ CSoundParameters params;
+ if ( !GetParametersForSound( soundname, params, GENDER_NONE ) )
+ {
+ return SNDLVL_NORM;
+ }
+
+ return params.soundlevel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::AddSoundsFromFile( const char *filename, bool bPreload, bool bIsOverride /*=false*/, bool bRefresh /*=false*/ )
+{
+ CSoundScriptFile sf;
+ sf.hFilename = filesystem->FindOrAddFileName( filename );
+ sf.dirty = false;
+
+ int scriptindex = m_SoundKeyValues.AddToTail( sf );
+
+ int replaceCount = 0;
+ int newOverrideCount = 0;
+ int duplicatedReplacements = 0;
+
+ // Open the soundscape data file, and abort if we can't
+ KeyValues *kv = new KeyValues( "" );
+ if ( filesystem->LoadKeyValues( *kv, IFileSystem::TYPE_SOUNDEMITTER, filename, "GAME" ) )
+ {
+ // parse out all of the top level sections and save their names
+ KeyValues *pKeys = kv;
+ while ( pKeys )
+ {
+ if ( pKeys->GetFirstSubKey() )
+ {
+ if ( m_Sounds.Count() >= 65534 )
+ {
+ Warning( "Exceeded maximum number of sound emitter entries\n" );
+ break;
+ }
+
+ CSoundEntry *pEntry;
+
+ {
+ MEM_ALLOC_CREDIT();
+ pEntry = new CSoundEntry;
+ }
+
+ pEntry->m_Name = pKeys->GetName();
+ pEntry->m_bRemoved = false;
+ pEntry->m_nScriptFileIndex = scriptindex;
+ pEntry->m_bIsOverride = bIsOverride;
+
+ if ( bIsOverride )
+ {
+ ++newOverrideCount;
+ }
+
+ UtlHashHandle_t lookup = m_Sounds.Insert( pEntry ); // insert returns existing item if found
+ if ( m_Sounds[ lookup ] != pEntry )
+ {
+ if ( bIsOverride )
+ {
+ MEM_ALLOC_CREDIT();
+
+ // Store off the old sound if it's not already an "override" from another file!!!
+ // Otherwise, just whack it again!!!
+ if ( !m_Sounds[ lookup ]->IsOverride() )
+ {
+ m_SavedOverrides.AddToTail( m_Sounds[ lookup ] );
+ }
+ else
+ {
+ ++duplicatedReplacements;
+ }
+
+ InitSoundInternalParameters( pKeys->GetName(), pKeys, pEntry->m_SoundParams );
+ pEntry->m_SoundParams.SetShouldPreload( bPreload ); // this gets handled by game code after initting.
+
+ m_Sounds.ReplaceKey( lookup, pEntry );
+
+ ++replaceCount;
+ }
+ else if ( bRefresh )
+ {
+ InitSoundInternalParameters( pKeys->GetName(), pKeys, m_Sounds[ lookup ]->m_SoundParams );
+ }
+#if 0
+ else
+ {
+ DevMsg( "CSoundEmitterSystem::AddSoundsFromFile(%s): Entry %s duplicated, skipping\n", filename, pKeys->GetName() );
+ }
+#endif
+ }
+ else
+ {
+ MEM_ALLOC_CREDIT();
+
+ InitSoundInternalParameters( pKeys->GetName(), pKeys, pEntry->m_SoundParams );
+ pEntry->m_SoundParams.SetShouldPreload( bPreload ); // this gets handled by game code after initting.
+ }
+ }
+ pKeys = pKeys->GetNextKey();
+ }
+
+ kv->deleteThis();
+ }
+ else
+ {
+ if ( !bIsOverride )
+ {
+ Warning( "CSoundEmitterSystem::AddSoundsFromFile: No such file %s\n", filename );
+ }
+
+ // Discard
+ m_SoundKeyValues.Remove( scriptindex );
+
+ kv->deleteThis();
+
+ return;
+ }
+
+
+ if ( bIsOverride )
+ {
+ DevMsg( "SoundEmitter: adding map sound overrides from %s [%i total, %i replacements, %i duplicated replacements]\n",
+ filename,
+ newOverrideCount,
+ replaceCount,
+ duplicatedReplacements );
+ }
+
+ Assert( scriptindex >= 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reload a sound emitter file (used to refresh files after sv_pure is turned on)
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::ReloadSoundEntriesInList( IFileList *pFilesToReload )
+{
+ int i, c;
+ c = m_SoundKeyValues.Count();
+ CUtlVector< const char * > processed;
+ for ( i = 0; i < c ; i++ )
+ {
+ const char *pszFileName = GetSoundScriptName( i );
+ if ( pszFileName && pszFileName[0] )
+ {
+ if ( processed.Find( pszFileName) == processed.InvalidIndex() && pFilesToReload->IsFileInList( pszFileName ) )
+ {
+ Msg( "Reloading sound file '%s' due to pure settings.\n", pszFileName );
+
+ AddSoundsFromFile( pszFileName, false, false, true );
+
+ // Now mark this file name as being reloaded
+ processed.AddToTail( pszFileName );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Force ModShutdown and ModInit, skips checks for how many systems have
+// requested inits (for con commands).
+//-----------------------------------------------------------------------------
+void CSoundEmitterSystemBase::Flush()
+{
+ InternalModShutdown();
+ InternalModInit();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::CheckForMissingWavFiles( bool verbose )
+{
+ int missing = 0;
+
+ int c = GetSoundCount();
+ int i;
+ char testfile[ 512 ];
+
+ for ( i = 0; i < c; i++ )
+ {
+ CSoundParametersInternal *internal = InternalGetParametersForSound( i );
+ if ( !internal )
+ {
+ Assert( 0 );
+ continue;
+ }
+
+ int waveCount = internal->NumSoundNames();
+ for ( int wave = 0; wave < waveCount; wave++ )
+ {
+ CUtlSymbol sym = internal->GetSoundNames()[ wave ].symbol;
+ const char *name = m_Waves.String( sym );
+ if ( !name || !name[ 0 ] )
+ {
+ Assert( 0 );
+ continue;
+ }
+
+ // Skip ! sentence stuff
+ if ( name[0] == CHAR_SENTENCE )
+ continue;
+ Q_snprintf( testfile, sizeof( testfile ), "sound/%s", PSkipSoundChars( name ) );
+ if ( filesystem->FileExists( testfile ) )
+ continue;
+
+ internal->SetHadMissingWaveFiles( true );
+
+ ++missing;
+
+ if ( verbose )
+ {
+ DevMsg( "Sound %s references missing file %s\n", GetSoundName( i ), name );
+ }
+ }
+ }
+
+ return missing;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *key -
+// Output : float
+//-----------------------------------------------------------------------------
+float CSoundEmitterSystemBase::TranslateAttenuation( const char *key )
+{
+ if ( !key )
+ {
+ Assert( 0 );
+ return ATTN_NORM;
+ }
+
+ if ( !Q_strcasecmp( key, "ATTN_NONE" ) )
+ return ATTN_NONE;
+
+ if ( !Q_strcasecmp( key, "ATTN_NORM" ) )
+ return ATTN_NORM;
+
+ if ( !Q_strcasecmp( key, "ATTN_IDLE" ) )
+ return ATTN_IDLE;
+
+ if ( !Q_strcasecmp( key, "ATTN_STATIC" ) )
+ return ATTN_STATIC;
+
+ if ( !Q_strcasecmp( key, "ATTN_RICOCHET" ) )
+ return ATTN_RICOCHET;
+
+ if ( !Q_strcasecmp( key, "ATTN_GUNFIRE" ) )
+ return ATTN_GUNFIRE;
+
+ DevMsg( "CSoundEmitterSystem: Unknown attenuation key %s\n", key );
+
+ return ATTN_NORM;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *key -
+// Output : soundlevel_t
+//-----------------------------------------------------------------------------
+soundlevel_t CSoundEmitterSystemBase::TranslateSoundLevel( const char *key )
+{
+ return TextToSoundLevel( key );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Convert "chan_xxx" into integer value for channel
+// Input : *name -
+// Output : static int
+//-----------------------------------------------------------------------------
+int CSoundEmitterSystemBase::TranslateChannel( const char *name )
+{
+ return TextToChannel( name );
+}
+
+const char *CSoundEmitterSystemBase::GetSourceFileForSound( int index ) const
+{
+ if ( index < 0 || index >= (int)m_Sounds.Count() )
+ {
+ Assert( 0 );
+ return "";
+ }
+
+ CSoundEntry const *entry = m_Sounds[ index ];
+ int scriptindex = entry->m_nScriptFileIndex;
+ if ( scriptindex < 0 || scriptindex >= m_SoundKeyValues.Count() )
+ {
+ Assert( 0 );
+ return "";
+ }
+ static char fn[ 512 ];
+ if ( filesystem->String( m_SoundKeyValues[ scriptindex ].hFilename, fn, sizeof( fn ) ))
+ {
+ return fn;
+ }
+ Assert( 0 );
+ return "";
+}
+
+const char *CSoundEmitterSystemBase::GetWaveName( CUtlSymbol& sym )
+{
+ return m_Waves.String( sym );
+}
+
+int CSoundEmitterSystemBase::FindSoundScript( const char *name ) const
+{
+ int i, c;
+
+ FileNameHandle_t hFilename = filesystem->FindFileName( name );
+ if ( hFilename )
+ {
+ // First, make sure it's known
+ c = m_SoundKeyValues.Count();
+ for ( i = 0; i < c ; i++ )
+ {
+ if ( m_SoundKeyValues[ i ].hFilename == hFilename )
+ {
+ return i;
+ }
+ }
+ }
+
+ return m_SoundKeyValues.InvalidIndex();
+}
+
+bool CSoundEmitterSystemBase::AddSound( const char *soundname, const char *scriptfile, const CSoundParametersInternal& params )
+{
+ int idx = GetSoundIndex( soundname );
+
+
+ int i = FindSoundScript( scriptfile );
+ if ( i == m_SoundKeyValues.InvalidIndex() )
+ {
+ Warning( "CSoundEmitterSystemBase::AddSound( '%s', '%s', ... ), script file not list in manifest '%s'\n",
+ soundname, scriptfile, MANIFEST_FILE );
+ return false;
+ }
+
+ MEM_ALLOC_CREDIT();
+
+ // More like an update...
+ if ( IsValidIndex( idx ) )
+ {
+ CSoundEntry *entry = m_Sounds[ idx ];
+
+ entry->m_bRemoved = false;
+ entry->m_nScriptFileIndex = i;
+ entry->m_SoundParams.CopyFrom( params );
+
+ m_SoundKeyValues[ i ].dirty = true;
+
+ return true;
+ }
+
+ CSoundEntry *pEntry = new CSoundEntry;
+ pEntry->m_Name = soundname;
+ pEntry->m_bRemoved = false;
+ pEntry->m_nScriptFileIndex = i;
+ pEntry->m_SoundParams.CopyFrom( params );
+
+ m_Sounds.Insert( pEntry );
+
+ m_SoundKeyValues[ i ].dirty = true;
+
+ return true;
+}
+
+void CSoundEmitterSystemBase::RemoveSound( const char *soundname )
+{
+ int idx = GetSoundIndex( soundname );
+ if ( !IsValidIndex( idx ) )
+ {
+ Warning( "Can't remove %s, no such sound!\n", soundname );
+ return;
+ }
+
+ m_Sounds[ idx ]->m_bRemoved = true;
+
+ // Mark script as dirty
+ int scriptindex = m_Sounds[ idx ]->m_nScriptFileIndex;
+ if ( scriptindex < 0 || scriptindex >= m_SoundKeyValues.Count() )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ m_SoundKeyValues[ scriptindex ].dirty = true;
+}
+
+void CSoundEmitterSystemBase::MoveSound( const char *soundname, const char *newscript )
+{
+ int idx = GetSoundIndex( soundname );
+ if ( !IsValidIndex( idx ) )
+ {
+ Warning( "Can't move '%s', no such sound!\n", soundname );
+ return;
+ }
+
+ int oldscriptindex = m_Sounds[ idx ]->m_nScriptFileIndex;
+ if ( oldscriptindex < 0 || oldscriptindex >= m_SoundKeyValues.Count() )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ int newscriptindex = FindSoundScript( newscript );
+ if ( newscriptindex == m_SoundKeyValues.InvalidIndex() )
+ {
+ Warning( "CSoundEmitterSystemBase::MoveSound( '%s', '%s' ), script file not list in manifest '%s'\n",
+ soundname, newscript, MANIFEST_FILE );
+ return;
+ }
+
+ // No actual change
+ if ( oldscriptindex == newscriptindex )
+ {
+ return;
+ }
+
+ // Move it
+ m_Sounds[ idx ]->m_nScriptFileIndex = newscriptindex;
+
+ // Mark both scripts as dirty
+ m_SoundKeyValues[ oldscriptindex ].dirty = true;
+ m_SoundKeyValues[ newscriptindex ].dirty = true;
+}
+
+int CSoundEmitterSystemBase::GetNumSoundScripts() const
+{
+ return m_SoundKeyValues.Count();
+}
+
+const char *CSoundEmitterSystemBase::GetSoundScriptName( int index ) const
+{
+ if ( index < 0 || index >= m_SoundKeyValues.Count() )
+ return NULL;
+
+ static char fn[ 512 ];
+ if ( filesystem->String( m_SoundKeyValues[ index ].hFilename, fn, sizeof( fn ) ) )
+ {
+ return fn;
+ }
+ return "";
+}
+
+bool CSoundEmitterSystemBase::IsSoundScriptDirty( int index ) const
+{
+ if ( index < 0 || index >= m_SoundKeyValues.Count() )
+ return false;
+
+ return m_SoundKeyValues[ index ].dirty;
+}
+
+void CSoundEmitterSystemBase::SaveChangesToSoundScript( int scriptindex )
+{
+ const char *outfile = GetSoundScriptName( scriptindex );
+ if ( !outfile )
+ {
+ Msg( "CSoundEmitterSystemBase::SaveChangesToSoundScript: No script file for index %i\n", scriptindex );
+ return;
+ }
+
+ if ( filesystem->FileExists( outfile ) &&
+ !filesystem->IsFileWritable( outfile ) )
+ {
+ Warning( "%s is not writable, can't save data to file\n", outfile );
+ return;
+ }
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ // FIXME: Write sound script header
+ if ( filesystem->FileExists( GAME_SOUNDS_HEADER_BLOCK ) )
+ {
+ FileHandle_t header = filesystem->Open( GAME_SOUNDS_HEADER_BLOCK, "rb", NULL );
+ if ( header != FILESYSTEM_INVALID_HANDLE )
+ {
+ int len = filesystem->Size( header );
+
+ unsigned char *data = new unsigned char[ len + 1 ];
+ Q_memset( data, 0, len + 1 );
+
+ filesystem->Read( data, len, header );
+ filesystem->Close( header );
+
+ data[ len ] = 0;
+
+ char *p = (char *)data;
+ while ( *p )
+ {
+ if ( *p != '\r' )
+ {
+ buf.PutChar( *p );
+ }
+ ++p;
+ }
+
+ delete[] data;
+ }
+
+ buf.Printf( "\n" );
+ }
+
+
+ int c = GetSoundCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( Q_stricmp( outfile, GetSourceFileForSound( i ) ) )
+ continue;
+
+ // It's marked for deletion, just skip it
+ if ( m_Sounds[ i ]->m_bRemoved )
+ continue;
+
+ CSoundParametersInternal *p = InternalGetParametersForSound( i );
+ if ( !p )
+ continue;
+
+ buf.Printf( "\"%s\"\n{\n", GetSoundName( i ) );
+
+ buf.Printf( "\t\"channel\"\t\t\"%s\"\n", p->ChannelToString() );
+ buf.Printf( "\t\"volume\"\t\t\"%s\"\n", p->VolumeToString() );
+ buf.Printf( "\t\"pitch\"\t\t\t\"%s\"\n", p->PitchToString() );
+ buf.Printf( "\n" );
+ buf.Printf( "\t\"soundlevel\"\t\"%s\"\n", p->SoundLevelToString() );
+
+ if ( p->OnlyPlayToOwner() )
+ {
+ buf.Printf( "\t\"play_to_owner_only\"\t\"1\"\n" );
+ }
+
+ if ( p->GetDelayMsec() != 0 )
+ {
+ buf.Printf( "\t\"delay_msec\"\t\"%i\"\n", p->GetDelayMsec() );
+ }
+
+ int totalCount = 0;
+
+ int waveCount = p->NumSoundNames();
+ int convertedCount = p->NumConvertedNames();
+
+ totalCount = ( waveCount - 2 * convertedCount ) + convertedCount;
+
+ if ( totalCount > 0 )
+ {
+ buf.Printf( "\n" );
+
+ if ( waveCount == 1 )
+ {
+ Assert( p->GetSoundNames()[ 0 ].gender == GENDER_NONE );
+ buf.Printf( "\t\"wave\"\t\t\t\"%s\"\n", GetWaveName( p->GetSoundNames()[ 0 ].symbol ) );
+ }
+ else if ( convertedCount == 1 )
+ {
+ Assert( p->GetConvertedNames()[ 0 ].gender == GENDER_NONE );
+ buf.Printf( "\t\"wave\"\t\t\t\"%s\"\n", GetWaveName( p->GetConvertedNames()[ 0 ].symbol ) );
+ }
+ else
+ {
+ buf.Printf( "\t\"rndwave\"\n" );
+ buf.Printf( "\t{\n" );
+
+ int wave;
+ for ( wave = 0; wave < waveCount; wave++ )
+ {
+ // Skip macro-expanded names
+ if ( p->GetSoundNames()[ wave ].gender != GENDER_NONE )
+ continue;
+
+ buf.Printf( "\t\t\"wave\"\t\"%s\"\n", GetWaveName( p->GetSoundNames()[ wave ].symbol ) );
+ }
+ for ( wave = 0; wave < convertedCount; wave++ )
+ {
+ buf.Printf( "\t\t\"wave\"\t\"%s\"\n", GetWaveName( p->GetConvertedNames()[ wave ].symbol ) );
+ }
+
+ buf.Printf( "\t}\n" );
+ }
+
+ }
+
+ buf.Printf( "}\n" );
+
+ if ( i != c - 1 )
+ {
+ buf.Printf( "\n" );
+ }
+ }
+
+ // Write it out baby
+ FileHandle_t fh = filesystem->Open( outfile, "wt" );
+ if (fh)
+ {
+ filesystem->Write( buf.Base(), buf.TellPut(), fh );
+ filesystem->Close(fh);
+
+ // Changed saved successfully
+ m_SoundKeyValues[ scriptindex ].dirty = false;
+ }
+ else
+ {
+ Warning( "SceneManager_SaveSoundsToScriptFile: Unable to write file %s!!!\n", outfile );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// Output : CUtlSymbol
+//-----------------------------------------------------------------------------
+CUtlSymbol CSoundEmitterSystemBase::AddWaveName( const char *name )
+{
+ return m_Waves.AddString( name );
+}
+
+void CSoundEmitterSystemBase::RenameSound( const char *soundname, const char *newname )
+{
+ // Same name?
+ if ( !Q_stricmp( soundname, newname ) )
+ {
+ return;
+ }
+
+ int oldindex = GetSoundIndex( soundname );
+ if ( !IsValidIndex( oldindex ) )
+ {
+ Msg( "Can't rename %s, no such sound\n", soundname );
+ return;
+ }
+
+ int check = GetSoundIndex( newname );
+ if ( IsValidIndex( check ) )
+ {
+ Msg( "Can't rename %s to %s, new name already in list\n", soundname, newname );
+ return;
+ }
+
+ MEM_ALLOC_CREDIT();
+
+ // Copy out old entry
+ CSoundEntry *pEntry = m_Sounds[ oldindex ];
+ // Remove it
+ m_Sounds.Remove( pEntry );
+ pEntry->m_Name = newname;
+ // Re-insert in new spot
+ m_Sounds.Insert( pEntry );
+
+ // Mark associated script as dirty
+ m_SoundKeyValues[ pEntry->m_nScriptFileIndex ].dirty = true;
+}
+
+void CSoundEmitterSystemBase::UpdateSoundParameters( const char *soundname, const CSoundParametersInternal& params )
+{
+ int idx = GetSoundIndex( soundname );
+ if ( !IsValidIndex( idx ) )
+ {
+ Msg( "Can't UpdateSoundParameters %s, no such sound\n", soundname );
+ return;
+ }
+
+ CSoundEntry *entry = m_Sounds[ idx ];
+
+ if ( entry->m_SoundParams == params )
+ {
+ // No changes
+ return;
+ }
+
+ // Update parameters
+ entry->m_SoundParams.CopyFrom( params );
+ // Set dirty flag
+ m_SoundKeyValues[ entry->m_nScriptFileIndex ].dirty = true;
+}
+
+bool CSoundEmitterSystemBase::IsUsingGenderToken( char const *soundname )
+{
+ int soundindex = GetSoundIndex( soundname );
+ if ( soundindex < 0 )
+ return false;
+
+ // Look up the sound level from the soundemitter system
+ CSoundParametersInternal *params = InternalGetParametersForSound( soundindex );
+ if ( !params )
+ return false;
+
+ return params->UsesGenderToken();
+}
+unsigned int CSoundEmitterSystemBase::GetManifestFileTimeChecksum()
+{
+ return m_uManifestPlusScriptChecksum;
+}
+
+bool CSoundEmitterSystemBase::GetParametersForSoundEx( const char *soundname, HSOUNDSCRIPTHANDLE& handle, CSoundParameters& params, gender_t gender, bool isbeingemitted /*= false*/ )
+{
+ if ( handle == SOUNDEMITTER_INVALID_HANDLE )
+ {
+ handle = GetSoundIndex( soundname );
+ if ( handle == SOUNDEMITTER_INVALID_HANDLE )
+ return false;
+ }
+
+ CSoundParametersInternal *internal = InternalGetParametersForSound( (int)handle );
+ if ( !internal )
+ {
+ Assert( 0 );
+ DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: No such sound %s\n", soundname );
+ return false;
+ }
+
+ params.channel = internal->GetChannel();
+ params.volume = internal->GetVolume().Random();
+ params.pitch = internal->GetPitch().Random();
+ params.pitchlow = internal->GetPitch().start;
+ params.pitchhigh = params.pitchlow + internal->GetPitch().range;
+ params.delay_msec = internal->GetDelayMsec();
+ params.count = internal->NumSoundNames();
+ params.soundname[ 0 ] = 0;
+
+ int bestIndex = FindBestSoundForGender( internal->GetSoundNames(), internal->NumSoundNames(), gender );
+
+ if ( bestIndex >= 0 )
+ {
+ Q_strncpy( params.soundname, GetWaveName( internal->GetSoundNames()[ bestIndex ].symbol), sizeof( params.soundname ) );
+
+ // If we are actually emitting the sound, mark it as not available...
+ if ( isbeingemitted )
+ {
+ internal->GetSoundNames()[ bestIndex ].available = 0;
+ }
+ }
+ params.soundlevel = (soundlevel_t)(int)internal->GetSoundLevel().Random();
+ params.play_to_owner_only = internal->OnlyPlayToOwner();
+
+ if ( !params.soundname[ 0 ] )
+ {
+ DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has no wave or rndwave key!\n", soundname );
+ return false;
+ }
+
+ if ( internal->HadMissingWaveFiles() &&
+ params.soundname[ 0 ] != CHAR_SENTENCE )
+ {
+ char testfile[ 256 ];
+ Q_snprintf( testfile, sizeof( testfile ), "sound/%s", PSkipSoundChars( params.soundname ) );
+ if ( !filesystem->FileExists( testfile ) )
+ {
+ // Prevent repetitive spew...
+ static CUtlSymbolTable soundWarnings;
+ char key[ 256 ];
+ Q_snprintf( key, sizeof( key ), "%s:%s", soundname, params.soundname );
+ if ( UTL_INVAL_SYMBOL == soundWarnings.Find( key ) )
+ {
+ soundWarnings.AddString( key );
+
+ DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound '%s' references wave '%s' which doesn't exist on disk!\n",
+ soundname,
+ params.soundname );
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+soundlevel_t CSoundEmitterSystemBase::LookupSoundLevelByHandle( char const *soundname, HSOUNDSCRIPTHANDLE& handle )
+{
+ if ( handle == SOUNDEMITTER_INVALID_HANDLE )
+ {
+ handle = (HSOUNDSCRIPTHANDLE)GetSoundIndex( soundname );
+ if ( handle == SOUNDEMITTER_INVALID_HANDLE )
+ return SNDLVL_NORM;
+ }
+
+ CSoundParametersInternal *internal = InternalGetParametersForSound( (int)handle );
+ if ( !internal )
+ {
+ return SNDLVL_NORM;
+ }
+
+ return (soundlevel_t)(int)internal->GetSoundLevel().Random();
+}
+
+
+// Called from both client and server (single player) or just one (server only in dedicated server and client only if connected to a remote server)
+// Called by LevelInitPreEntity to override sound scripts for the mod with level specific overrides based on custom mapnames, etc.
+void CSoundEmitterSystemBase::AddSoundOverrides( char const *scriptfile, bool bPreload /*= false*/ )
+{
+ FileNameHandle_t handle = filesystem->FindOrAddFileName( scriptfile );
+ if ( m_OverrideFiles.Find( handle ) != m_OverrideFiles.InvalidIndex() )
+ return;
+
+ m_OverrideFiles.AddToTail( handle );
+ // These are overrides
+ AddSoundsFromFile( scriptfile, bPreload, true );
+}
+
+// Called by either client or server in LevelShutdown to clear out custom overrides
+void CSoundEmitterSystemBase::ClearSoundOverrides()
+{
+ int i;
+ int removed = 0;
+
+ for ( UtlHashHandle_t i = m_Sounds.FirstHandle(); i != m_Sounds.InvalidHandle(); )
+ {
+ CSoundEntry *entry = m_Sounds[ i ];
+ if ( entry->IsOverride() )
+ {
+ i = m_Sounds.RemoveAndAdvance( i );
+ ++removed;
+ }
+ else
+ {
+ i = m_Sounds.NextHandle( i );
+ }
+ }
+
+ if (removed > 0 || m_SavedOverrides.Count() > 0 )
+ {
+ Warning( "SoundEmitter: removing map sound overrides [%i to remove, %i to restore]\n",
+ removed,
+ m_SavedOverrides.Count() );
+ }
+
+ // Now restore the original entries into the main dictionary.
+ for ( i = 0; i < m_SavedOverrides.Count(); ++i )
+ {
+ CSoundEntry *entry = m_SavedOverrides[ i ];
+ m_Sounds.Insert( entry );
+ }
+
+ m_SavedOverrides.Purge();
+ m_OverrideFiles.Purge();
+}
+
+CSoundEmitterSystemBase g_SoundEmitterSystemBase;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSoundEmitterSystemBase, ISoundEmitterSystemBase,
+ SOUNDEMITTERSYSTEM_INTERFACE_VERSION, g_SoundEmitterSystemBase ); \ No newline at end of file