Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1662 lines
43 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include <KeyValues.h>
  7. #include "filesystem.h"
  8. #include "utldict.h"
  9. #include "interval.h"
  10. #include "engine/IEngineSound.h"
  11. #include "soundemittersystembase.h"
  12. #include "utlbuffer.h"
  13. #include "soundchars.h"
  14. #include "vstdlib/random.h"
  15. #include "checksum_crc.h"
  16. #include "SoundEmitterSystem/isoundemittersystembase.h"
  17. #include "ifilelist.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. #define MANIFEST_FILE "scripts/game_sounds_manifest.txt"
  21. #define GAME_SOUNDS_HEADER_BLOCK "scripts/game_sounds_header.txt"
  22. static IFileSystem* filesystem = 0;
  23. //-----------------------------------------------------------------------------
  24. // Purpose:
  25. //-----------------------------------------------------------------------------
  26. CSoundEmitterSystemBase::CSoundEmitterSystemBase() :
  27. m_nInitCount( 0 ),
  28. m_uManifestPlusScriptChecksum( 0 )
  29. {
  30. }
  31. //-----------------------------------------------------------------------------
  32. // Purpose:
  33. // Output : int
  34. //-----------------------------------------------------------------------------
  35. int CSoundEmitterSystemBase::First() const
  36. {
  37. return m_Sounds.FirstHandle();
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Purpose:
  41. // Input : i -
  42. // Output : int
  43. //-----------------------------------------------------------------------------
  44. int CSoundEmitterSystemBase::Next( int i ) const
  45. {
  46. return m_Sounds.NextHandle(i);
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. //-----------------------------------------------------------------------------
  51. int CSoundEmitterSystemBase::InvalidIndex() const
  52. {
  53. return m_Sounds.InvalidHandle();
  54. }
  55. //-----------------------------------------------------------------------------
  56. //
  57. // implementation of IUniformRandomStream
  58. //
  59. //-----------------------------------------------------------------------------
  60. class CSoundEmitterUniformRandomStream : public IUniformRandomStream
  61. {
  62. public:
  63. // Sets the seed of the random number generator
  64. void SetSeed( int iSeed )
  65. {
  66. // Never call this from the client or game!
  67. Assert(0);
  68. }
  69. // Generates random numbers
  70. float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f )
  71. {
  72. return ::RandomFloat( flMinVal, flMaxVal );
  73. }
  74. int RandomInt( int iMinVal, int iMaxVal )
  75. {
  76. return ::RandomInt( iMinVal, iMaxVal );
  77. }
  78. float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f )
  79. {
  80. return ::RandomFloatExp( flMinVal, flMaxVal, flExponent );
  81. }
  82. };
  83. static CSoundEmitterUniformRandomStream g_RandomStream;
  84. IUniformRandomStream *randomStream = &g_RandomStream;
  85. //-----------------------------------------------------------------------------
  86. // Connect, disconnect
  87. //-----------------------------------------------------------------------------
  88. bool CSoundEmitterSystemBase::Connect( CreateInterfaceFn factory )
  89. {
  90. // If someone already connected us up, don't redo the connection
  91. if ( NULL != filesystem )
  92. {
  93. return true;
  94. }
  95. filesystem = (IFileSystem *)factory( FILESYSTEM_INTERFACE_VERSION, NULL );
  96. if( !filesystem )
  97. {
  98. Error( "The soundemittersystem system requires the filesystem to run!\n" );
  99. return false;
  100. }
  101. return true;
  102. }
  103. void CSoundEmitterSystemBase::Disconnect()
  104. {
  105. filesystem = NULL;
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Query interface
  109. //-----------------------------------------------------------------------------
  110. void *CSoundEmitterSystemBase::QueryInterface( const char *pInterfaceName )
  111. {
  112. // Loading the engine DLL mounts *all* soundemitter interfaces
  113. CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
  114. return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Init, shutdown
  118. //-----------------------------------------------------------------------------
  119. InitReturnVal_t CSoundEmitterSystemBase::Init()
  120. {
  121. return INIT_OK;
  122. }
  123. void CSoundEmitterSystemBase::Shutdown()
  124. {
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose: Helper for checksuming script files and manifest to determine if soundname caches
  128. // need to be blown away.
  129. // Input : *crc -
  130. // *filename -
  131. // Output : static void
  132. //-----------------------------------------------------------------------------
  133. static void AccumulateFileNameAndTimestampIntoChecksum( CRC32_t *crc, char const *filename )
  134. {
  135. if ( IsX360() )
  136. {
  137. // this is an expensive i/o operation due to search path fall through
  138. // 360 doesn't need or use the checksums
  139. return;
  140. }
  141. long ft = filesystem->GetFileTime( filename, "GAME" );
  142. CRC32_ProcessBuffer( crc, &ft, sizeof( ft ) );
  143. CRC32_ProcessBuffer( crc, filename, Q_strlen( filename ) );
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose:
  147. // Output : Returns true on success, false on failure.
  148. //-----------------------------------------------------------------------------
  149. bool CSoundEmitterSystemBase::InternalModInit()
  150. {
  151. /*
  152. if ( m_SoundKeyValues.Count() > 0 )
  153. {
  154. Shutdown();
  155. }
  156. */
  157. LoadGlobalActors();
  158. m_uManifestPlusScriptChecksum = 0u;
  159. CRC32_t crc;
  160. CRC32_Init( &crc );
  161. KeyValues *manifest = new KeyValues( MANIFEST_FILE );
  162. if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDEMITTER, MANIFEST_FILE, "GAME" ) )
  163. {
  164. AccumulateFileNameAndTimestampIntoChecksum( &crc, MANIFEST_FILE );
  165. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  166. {
  167. if ( !Q_stricmp( sub->GetName(), "precache_file" ) )
  168. {
  169. AccumulateFileNameAndTimestampIntoChecksum( &crc, sub->GetString() );
  170. // Add and always precache
  171. AddSoundsFromFile( sub->GetString(), false );
  172. continue;
  173. }
  174. else if ( !Q_stricmp( sub->GetName(), "preload_file" ) )
  175. {
  176. AccumulateFileNameAndTimestampIntoChecksum( &crc, sub->GetString() );
  177. // Add and always precache
  178. AddSoundsFromFile( sub->GetString(), true );
  179. continue;
  180. }
  181. else if ( !Q_stricmp( sub->GetName(), "faceposer_file" ) )
  182. {
  183. // do nothing for these files; they're only used for faceposer
  184. continue;
  185. }
  186. Warning( "CSoundEmitterSystemBase::BaseInit: Manifest '%s' with bogus file type '%s', expecting 'declare_file' or 'precache_file'\n",
  187. MANIFEST_FILE, sub->GetName() );
  188. }
  189. }
  190. else
  191. {
  192. Error( "Unable to load manifest file '%s'\n", MANIFEST_FILE );
  193. }
  194. manifest->deleteThis();
  195. CRC32_Final( &crc );
  196. m_uManifestPlusScriptChecksum =( unsigned int )crc;
  197. // Only print total once, on server
  198. #if !defined( CLIENT_DLL ) && !defined( FACEPOSER )
  199. DevMsg( 1, "CSoundEmitterSystem: Registered %i sounds\n", m_Sounds.Count() );
  200. #endif
  201. return true;
  202. }
  203. //-----------------------------------------------------------------------------
  204. // Purpose:
  205. // Output : Returns true on success, false on failure.
  206. //-----------------------------------------------------------------------------
  207. bool CSoundEmitterSystemBase::ModInit()
  208. {
  209. ++m_nInitCount;
  210. if ( m_nInitCount > 1 )
  211. {
  212. return true;
  213. }
  214. return InternalModInit();
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose:
  218. //-----------------------------------------------------------------------------
  219. void CSoundEmitterSystemBase::InternalModShutdown()
  220. {
  221. int i;
  222. m_SoundKeyValues.RemoveAll();
  223. for ( UtlHashHandle_t nIndex = m_Sounds.FirstHandle(); nIndex != m_Sounds.InvalidHandle(); nIndex = m_Sounds.NextHandle( nIndex ) )
  224. {
  225. delete m_Sounds[ nIndex ];
  226. }
  227. m_Sounds.Purge();
  228. for ( i = 0; i < m_SavedOverrides.Count() ; ++i )
  229. {
  230. delete m_SavedOverrides[ i ];
  231. }
  232. m_SavedOverrides.Purge();
  233. m_Waves.RemoveAll();
  234. m_ActorGenders.Purge();
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose:
  238. //-----------------------------------------------------------------------------
  239. void CSoundEmitterSystemBase::ModShutdown()
  240. {
  241. if ( --m_nInitCount > 0 )
  242. return;
  243. InternalModShutdown();
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose:
  247. // Input : *pName -
  248. //-----------------------------------------------------------------------------
  249. int CSoundEmitterSystemBase::GetSoundIndex( const char *pName ) const
  250. {
  251. if ( !pName )
  252. return -1;
  253. CSoundEntry search;
  254. search.m_Name = pName;
  255. UtlHashHandle_t idx = m_Sounds.Find( pName );
  256. if ( idx == m_Sounds.InvalidHandle() )
  257. return -1;
  258. return idx;
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose:
  262. // Input : index -
  263. // Output : Returns true on success, false on failure.
  264. //-----------------------------------------------------------------------------
  265. bool CSoundEmitterSystemBase::IsValidIndex( int index )
  266. {
  267. return m_Sounds.IsValidHandle( index );
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. // Input : index -
  272. // Output : char const
  273. //-----------------------------------------------------------------------------
  274. const char *CSoundEmitterSystemBase::GetSoundName( int index )
  275. {
  276. if ( !IsValidIndex( index ) )
  277. return "";
  278. return m_Sounds[ index ]->m_Name.Get();
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Purpose:
  282. // Output : int
  283. //-----------------------------------------------------------------------------
  284. int CSoundEmitterSystemBase::GetSoundCount( void )
  285. {
  286. return m_Sounds.Count();
  287. }
  288. void CSoundEmitterSystemBase::EnsureAvailableSlotsForGender( SoundFile *pSoundnames, int c, gender_t gender )
  289. {
  290. int i;
  291. if ( c <= 0 )
  292. {
  293. return;
  294. }
  295. CUtlVector< int > slots;
  296. bool needsreset = false;
  297. for ( i = 0; i < c; i++ )
  298. {
  299. if ( pSoundnames[ i ].gender != gender )
  300. continue;
  301. // There was at least one match for the gender
  302. needsreset = true;
  303. // This sound is unavailable
  304. if ( !pSoundnames[ i ].available )
  305. continue;
  306. slots.AddToTail( i );
  307. }
  308. if ( slots.Count() == 0 && needsreset )
  309. {
  310. // Reset all slots for the specified gender!!!
  311. for ( i = 0; i < c; i++ )
  312. {
  313. if ( pSoundnames[ i ].gender != gender )
  314. continue;
  315. pSoundnames[ i ].available = true;
  316. }
  317. }
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose:
  321. // Input : gender -
  322. // soundnames -
  323. //-----------------------------------------------------------------------------
  324. int CSoundEmitterSystemBase::FindBestSoundForGender( SoundFile *pSoundnames, int c, gender_t gender )
  325. {
  326. // Check for recycling of random sounds...
  327. EnsureAvailableSlotsForGender( pSoundnames, c, gender );
  328. if ( c <= 0 )
  329. {
  330. return -1;
  331. }
  332. CUtlVector< int > slots;
  333. for ( int i = 0; i < c; i++ )
  334. {
  335. if ( pSoundnames[ i ].gender == gender &&
  336. pSoundnames[ i ].available )
  337. {
  338. slots.AddToTail( i );
  339. }
  340. }
  341. if ( slots.Count() >= 1 )
  342. {
  343. int idx = slots[ randomStream->RandomInt( 0, slots.Count() - 1 ) ];
  344. return idx;
  345. }
  346. int idx = randomStream->RandomInt( 0, c - 1 );
  347. return idx;
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose:
  351. // Input : *soundname -
  352. // params -
  353. // Output : Returns true on success, false on failure.
  354. //-----------------------------------------------------------------------------
  355. bool CSoundEmitterSystemBase::GetParametersForSound( const char *soundname, CSoundParameters& params, gender_t gender, bool isbeingemitted /*= false*/ )
  356. {
  357. HSOUNDSCRIPTHANDLE index = (HSOUNDSCRIPTHANDLE)GetSoundIndex( soundname );
  358. if ( index == SOUNDEMITTER_INVALID_HANDLE )
  359. {
  360. static CUtlSymbolTable soundWarnings;
  361. char key[ 256 ];
  362. Q_snprintf( key, sizeof( key ), "%s:%s", soundname, params.soundname );
  363. if ( UTL_INVAL_SYMBOL == soundWarnings.Find( key ) )
  364. {
  365. soundWarnings.AddString( key );
  366. DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: No such sound %s\n", soundname );
  367. }
  368. return false;
  369. }
  370. return GetParametersForSoundEx( soundname, index, params, gender, isbeingemitted );
  371. }
  372. CSoundParametersInternal *CSoundEmitterSystemBase::InternalGetParametersForSound( int index )
  373. {
  374. if ( !m_Sounds.IsValidHandle( index ) )
  375. {
  376. Assert( !"CSoundEmitterSystemBase::InternalGetParametersForSound: Bogus index" );
  377. return NULL;
  378. }
  379. return &m_Sounds[ index ]->m_SoundParams;
  380. }
  381. static void SplitName( char const *input, int splitchar, int splitlen, char *before, int beforelen, char *after, int afterlen )
  382. {
  383. char const *in = input;
  384. char *out = before;
  385. int c = 0;
  386. int l = 0;
  387. int maxl = beforelen;
  388. while ( *in )
  389. {
  390. if ( c == splitchar )
  391. {
  392. while ( --splitlen >= 0 )
  393. {
  394. in++;
  395. }
  396. *out = 0;
  397. out = after;
  398. maxl = afterlen;
  399. c++;
  400. continue;
  401. }
  402. if ( l >= maxl )
  403. {
  404. in++;
  405. c++;
  406. continue;
  407. }
  408. *out++ = *in++;
  409. l++;
  410. c++;
  411. }
  412. *out = 0;
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose:
  416. // Input : params -
  417. // *wavename -
  418. // gender -
  419. //-----------------------------------------------------------------------------
  420. void CSoundEmitterSystemBase::AddSoundName( CSoundParametersInternal& params, char const *wavename, gender_t gender )
  421. {
  422. CUtlSymbol sym = m_Waves.AddString( wavename );
  423. SoundFile e;
  424. e.symbol = sym;
  425. e.gender = gender;
  426. if ( gender != GENDER_NONE )
  427. {
  428. params.SetUsesGenderToken( true );
  429. }
  430. params.AddSoundName( e );
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose:
  434. // Input : params -
  435. // *wavename -
  436. //-----------------------------------------------------------------------------
  437. void CSoundEmitterSystemBase::ExpandSoundNameMacros( CSoundParametersInternal& params, char const *wavename )
  438. {
  439. char const *p = Q_stristr( wavename, SOUNDGENDER_MACRO );
  440. if ( !p )
  441. {
  442. AddSoundName( params, wavename, GENDER_NONE );
  443. return;
  444. }
  445. int offset = p - wavename;
  446. Assert( offset >= 0 );
  447. int duration = SOUNDGENDER_MACRO_LENGTH;
  448. // Create a "male" and "female" version of the sound
  449. char before[ 256 ], after[ 256 ];
  450. Q_memset( before, 0, sizeof( before ) );
  451. Q_memset( after, 0, sizeof( after ) );
  452. SplitName( wavename, offset, duration, before, sizeof( before ), after, sizeof( after ) );
  453. char temp[ 256 ];
  454. Q_snprintf( temp, sizeof( temp ), "%s%s%s", before, "male", after );
  455. AddSoundName( params, temp, GENDER_MALE );
  456. Q_snprintf( temp, sizeof( temp ), "%s%s%s", before, "female", after );
  457. AddSoundName( params, temp, GENDER_FEMALE );
  458. // Add the conversion entry with the gender tags still in it
  459. CUtlSymbol sym = m_Waves.AddString( wavename );
  460. SoundFile e;
  461. e.symbol = sym;
  462. e.gender = GENDER_NONE;
  463. params.AddConvertedName( e );
  464. }
  465. void CSoundEmitterSystemBase::GenderExpandString( gender_t gender, char const *in, char *out, int maxlen )
  466. {
  467. // Assume the worst
  468. Q_strncpy( out, in, maxlen );
  469. char const *p = Q_stristr( in, SOUNDGENDER_MACRO );
  470. if ( !p )
  471. {
  472. return;
  473. }
  474. // Look up actor gender
  475. if ( gender == GENDER_NONE )
  476. {
  477. return;
  478. }
  479. int offset = p - in;
  480. Assert( offset >= 0 );
  481. int duration = SOUNDGENDER_MACRO_LENGTH;
  482. // Create a "male" and "female" version of the sound
  483. char before[ 256 ], after[ 256 ];
  484. Q_memset( before, 0, sizeof( before ) );
  485. Q_memset( after, 0, sizeof( after ) );
  486. SplitName( in, offset, duration, before, sizeof( before ), after, sizeof( after ) );
  487. switch ( gender )
  488. {
  489. default:
  490. case GENDER_NONE:
  491. {
  492. Assert( !"CSoundEmitterSystemBase::GenderExpandString: expecting MALE or FEMALE!" );
  493. }
  494. break;
  495. case GENDER_MALE:
  496. {
  497. Q_snprintf( out, maxlen, "%s%s%s", before, "male", after );
  498. }
  499. break;
  500. case GENDER_FEMALE:
  501. {
  502. Q_snprintf( out, maxlen, "%s%s%s", before, "female", after );
  503. }
  504. break;
  505. }
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose:
  509. // Input : *actorname -
  510. // *in -
  511. // *out -
  512. // maxlen -
  513. //-----------------------------------------------------------------------------
  514. void CSoundEmitterSystemBase::GenderExpandString( char const *actormodel, char const *in, char *out, int maxlen )
  515. {
  516. gender_t gender = GetActorGender( actormodel );
  517. GenderExpandString( gender, in, out, maxlen );
  518. }
  519. void CSoundEmitterSystemBase::LoadGlobalActors()
  520. {
  521. // Now load the global actor list from the scripts/globalactors.txt file
  522. KeyValues *allActors = NULL;
  523. allActors = new KeyValues( "allactors" );
  524. if ( allActors->LoadFromFile( filesystem, "scripts/global_actors.txt", NULL ) )
  525. {
  526. KeyValues *pvkActor;
  527. for ( pvkActor = allActors->GetFirstSubKey(); pvkActor != NULL; pvkActor = pvkActor->GetNextKey() )
  528. {
  529. UtlHashHandle_t idx = m_ActorGenders.Find( pvkActor->GetName() );
  530. if ( idx == m_ActorGenders.InvalidHandle() )
  531. {
  532. if ( m_ActorGenders.Count() > 254 )
  533. {
  534. Warning( "Exceeded max number of actors in scripts/global_actors.txt\n" );
  535. break;
  536. }
  537. gender_t gender = GENDER_NONE;
  538. if ( !Q_stricmp( pvkActor->GetString(), "male" ) )
  539. {
  540. gender = GENDER_MALE;
  541. }
  542. else if (!Q_stricmp( pvkActor->GetString(), "female" ) )
  543. {
  544. gender = GENDER_FEMALE;
  545. }
  546. m_ActorGenders.Insert( pvkActor->GetName(), gender );
  547. }
  548. }
  549. }
  550. allActors->deleteThis();
  551. }
  552. //-----------------------------------------------------------------------------
  553. // Purpose:
  554. // Input : *actorname -
  555. // Output : gender_t
  556. //-----------------------------------------------------------------------------
  557. gender_t CSoundEmitterSystemBase::GetActorGender( char const *actormodel )
  558. {
  559. char actor[ 256 ];
  560. actor[0] = 0;
  561. if ( actormodel )
  562. {
  563. Q_FileBase( actormodel, actor, sizeof( actor ) );
  564. }
  565. UtlHashHandle_t idx = m_ActorGenders.Find( actor );
  566. if ( idx == m_ActorGenders.InvalidHandle() )
  567. return GENDER_NONE;
  568. return m_ActorGenders[ idx ];
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Purpose:
  572. // Input : *soundname -
  573. // params -
  574. // Output : Returns true on success, false on failure.
  575. //-----------------------------------------------------------------------------
  576. bool CSoundEmitterSystemBase::InitSoundInternalParameters( const char *soundname, KeyValues *kv, CSoundParametersInternal& params )
  577. {
  578. KeyValues *pKey = kv->GetFirstSubKey();
  579. while ( pKey )
  580. {
  581. if ( !Q_strcasecmp( pKey->GetName(), "channel" ) )
  582. {
  583. params.ChannelFromString( pKey->GetString() );
  584. }
  585. else if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  586. {
  587. params.VolumeFromString( pKey->GetString() );
  588. }
  589. else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) )
  590. {
  591. params.PitchFromString( pKey->GetString() );
  592. }
  593. else if ( !Q_strcasecmp( pKey->GetName(), "wave" ) )
  594. {
  595. ExpandSoundNameMacros( params, pKey->GetString() );
  596. }
  597. else if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) )
  598. {
  599. KeyValues *pWaves = pKey->GetFirstSubKey();
  600. while ( pWaves )
  601. {
  602. ExpandSoundNameMacros( params, pWaves->GetString() );
  603. pWaves = pWaves->GetNextKey();
  604. }
  605. }
  606. else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) || !Q_strcasecmp( pKey->GetName(), "CompatibilityAttenuation" ) )
  607. {
  608. if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) )
  609. {
  610. DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has \"attenuation\" with %s value!\n",
  611. soundname, pKey->GetString() );
  612. }
  613. if ( !Q_strncasecmp( pKey->GetString(), "ATTN_", strlen( "ATTN_" ) ) )
  614. {
  615. params.SetSoundLevel( ATTN_TO_SNDLVL( TranslateAttenuation( pKey->GetString() ) ) );
  616. }
  617. else
  618. {
  619. interval_t interval;
  620. interval = ReadInterval( pKey->GetString() );
  621. // Translate from attenuation to soundlevel
  622. float start = interval.start;
  623. float end = interval.start + interval.range;
  624. params.SetSoundLevel( ATTN_TO_SNDLVL( start ), ATTN_TO_SNDLVL( end ) - ATTN_TO_SNDLVL( start ) );
  625. }
  626. // Goldsrc compatibility mode.. feed the sndlevel value through the sound engine interface in such a way
  627. // that it can reconstruct the original sndlevel value and flag the sound as using Goldsrc attenuation.
  628. bool bCompatibilityAttenuation = !Q_strcasecmp( pKey->GetName(), "CompatibilityAttenuation" );
  629. if ( bCompatibilityAttenuation )
  630. {
  631. if ( params.GetSoundLevel().range != 0 )
  632. {
  633. Warning( "CompatibilityAttenuation for sound %s must have same start and end values.\n", soundname );
  634. }
  635. params.SetSoundLevel( SNDLEVEL_TO_COMPATIBILITY_MODE( params.GetSoundLevel().start ) );
  636. }
  637. }
  638. else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) )
  639. {
  640. if ( !Q_strncasecmp( pKey->GetString(), "ATTN_", strlen( "ATTN_" ) ) )
  641. {
  642. DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has \"soundlevel\" with %s value!\n",
  643. soundname, pKey->GetString() );
  644. }
  645. params.SoundLevelFromString( pKey->GetString() );
  646. }
  647. else if ( !Q_strcasecmp( pKey->GetName(), "play_to_owner_only" ) )
  648. {
  649. params.SetOnlyPlayToOwner( pKey->GetInt() ? true : false );
  650. }
  651. else if ( !Q_strcasecmp( pKey->GetName(), "delay_msec" ) )
  652. {
  653. // Don't allow negative delay
  654. params.SetDelayMsec( max( 0, pKey->GetInt() ) );
  655. }
  656. pKey = pKey->GetNextKey();
  657. }
  658. return true;
  659. }
  660. //-----------------------------------------------------------------------------
  661. // Purpose:
  662. // Input : *soundname -
  663. // Output : char const
  664. //-----------------------------------------------------------------------------
  665. const char *CSoundEmitterSystemBase::GetWavFileForSound( const char *soundname, char const *actormodel )
  666. {
  667. gender_t gender = GetActorGender( actormodel );
  668. return GetWavFileForSound( soundname, gender );
  669. }
  670. const char *CSoundEmitterSystemBase::GetWavFileForSound( const char *soundname, gender_t gender )
  671. {
  672. CSoundParameters params;
  673. if ( !GetParametersForSound( soundname, params, gender ) )
  674. {
  675. return soundname;
  676. }
  677. if ( !params.soundname[ 0 ] )
  678. {
  679. return soundname;
  680. }
  681. static char outsound[ 512 ];
  682. Q_strncpy( outsound, params.soundname, sizeof( outsound ) );
  683. return outsound;
  684. }
  685. //-----------------------------------------------------------------------------
  686. // Purpose:
  687. // Input : *soundname -
  688. // Output : soundlevel_t
  689. //-----------------------------------------------------------------------------
  690. soundlevel_t CSoundEmitterSystemBase::LookupSoundLevel( const char *soundname )
  691. {
  692. CSoundParameters params;
  693. if ( !GetParametersForSound( soundname, params, GENDER_NONE ) )
  694. {
  695. return SNDLVL_NORM;
  696. }
  697. return params.soundlevel;
  698. }
  699. //-----------------------------------------------------------------------------
  700. // Purpose:
  701. // Input : *filename -
  702. //-----------------------------------------------------------------------------
  703. void CSoundEmitterSystemBase::AddSoundsFromFile( const char *filename, bool bPreload, bool bIsOverride /*=false*/, bool bRefresh /*=false*/ )
  704. {
  705. CSoundScriptFile sf;
  706. sf.hFilename = filesystem->FindOrAddFileName( filename );
  707. sf.dirty = false;
  708. int scriptindex = m_SoundKeyValues.AddToTail( sf );
  709. int replaceCount = 0;
  710. int newOverrideCount = 0;
  711. int duplicatedReplacements = 0;
  712. // Open the soundscape data file, and abort if we can't
  713. KeyValues *kv = new KeyValues( "" );
  714. if ( filesystem->LoadKeyValues( *kv, IFileSystem::TYPE_SOUNDEMITTER, filename, "GAME" ) )
  715. {
  716. // parse out all of the top level sections and save their names
  717. KeyValues *pKeys = kv;
  718. while ( pKeys )
  719. {
  720. if ( pKeys->GetFirstSubKey() )
  721. {
  722. if ( m_Sounds.Count() >= 65534 )
  723. {
  724. Warning( "Exceeded maximum number of sound emitter entries\n" );
  725. break;
  726. }
  727. CSoundEntry *pEntry;
  728. {
  729. MEM_ALLOC_CREDIT();
  730. pEntry = new CSoundEntry;
  731. }
  732. pEntry->m_Name = pKeys->GetName();
  733. pEntry->m_bRemoved = false;
  734. pEntry->m_nScriptFileIndex = scriptindex;
  735. pEntry->m_bIsOverride = bIsOverride;
  736. if ( bIsOverride )
  737. {
  738. ++newOverrideCount;
  739. }
  740. UtlHashHandle_t lookup = m_Sounds.Insert( pEntry ); // insert returns existing item if found
  741. if ( m_Sounds[ lookup ] != pEntry )
  742. {
  743. if ( bIsOverride )
  744. {
  745. MEM_ALLOC_CREDIT();
  746. // Store off the old sound if it's not already an "override" from another file!!!
  747. // Otherwise, just whack it again!!!
  748. if ( !m_Sounds[ lookup ]->IsOverride() )
  749. {
  750. m_SavedOverrides.AddToTail( m_Sounds[ lookup ] );
  751. }
  752. else
  753. {
  754. ++duplicatedReplacements;
  755. }
  756. InitSoundInternalParameters( pKeys->GetName(), pKeys, pEntry->m_SoundParams );
  757. pEntry->m_SoundParams.SetShouldPreload( bPreload ); // this gets handled by game code after initting.
  758. m_Sounds.ReplaceKey( lookup, pEntry );
  759. ++replaceCount;
  760. }
  761. else if ( bRefresh )
  762. {
  763. InitSoundInternalParameters( pKeys->GetName(), pKeys, m_Sounds[ lookup ]->m_SoundParams );
  764. }
  765. #if 0
  766. else
  767. {
  768. DevMsg( "CSoundEmitterSystem::AddSoundsFromFile(%s): Entry %s duplicated, skipping\n", filename, pKeys->GetName() );
  769. }
  770. #endif
  771. }
  772. else
  773. {
  774. MEM_ALLOC_CREDIT();
  775. InitSoundInternalParameters( pKeys->GetName(), pKeys, pEntry->m_SoundParams );
  776. pEntry->m_SoundParams.SetShouldPreload( bPreload ); // this gets handled by game code after initting.
  777. }
  778. }
  779. pKeys = pKeys->GetNextKey();
  780. }
  781. kv->deleteThis();
  782. }
  783. else
  784. {
  785. if ( !bIsOverride )
  786. {
  787. Warning( "CSoundEmitterSystem::AddSoundsFromFile: No such file %s\n", filename );
  788. }
  789. // Discard
  790. m_SoundKeyValues.Remove( scriptindex );
  791. kv->deleteThis();
  792. return;
  793. }
  794. if ( bIsOverride )
  795. {
  796. DevMsg( "SoundEmitter: adding map sound overrides from %s [%i total, %i replacements, %i duplicated replacements]\n",
  797. filename,
  798. newOverrideCount,
  799. replaceCount,
  800. duplicatedReplacements );
  801. }
  802. Assert( scriptindex >= 0 );
  803. }
  804. //-----------------------------------------------------------------------------
  805. // Purpose: Reload a sound emitter file (used to refresh files after sv_pure is turned on)
  806. //-----------------------------------------------------------------------------
  807. void CSoundEmitterSystemBase::ReloadSoundEntriesInList( IFileList *pFilesToReload )
  808. {
  809. int i, c;
  810. c = m_SoundKeyValues.Count();
  811. CUtlVector< const char * > processed;
  812. for ( i = 0; i < c ; i++ )
  813. {
  814. const char *pszFileName = GetSoundScriptName( i );
  815. if ( pszFileName && pszFileName[0] )
  816. {
  817. if ( processed.Find( pszFileName) == processed.InvalidIndex() && pFilesToReload->IsFileInList( pszFileName ) )
  818. {
  819. Msg( "Reloading sound file '%s' due to pure settings.\n", pszFileName );
  820. AddSoundsFromFile( pszFileName, false, false, true );
  821. // Now mark this file name as being reloaded
  822. processed.AddToTail( pszFileName );
  823. }
  824. }
  825. }
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Purpose: Force ModShutdown and ModInit, skips checks for how many systems have
  829. // requested inits (for con commands).
  830. //-----------------------------------------------------------------------------
  831. void CSoundEmitterSystemBase::Flush()
  832. {
  833. InternalModShutdown();
  834. InternalModInit();
  835. }
  836. //-----------------------------------------------------------------------------
  837. // Purpose:
  838. //-----------------------------------------------------------------------------
  839. int CSoundEmitterSystemBase::CheckForMissingWavFiles( bool verbose )
  840. {
  841. int missing = 0;
  842. int c = GetSoundCount();
  843. int i;
  844. char testfile[ 512 ];
  845. for ( i = 0; i < c; i++ )
  846. {
  847. CSoundParametersInternal *internal = InternalGetParametersForSound( i );
  848. if ( !internal )
  849. {
  850. Assert( 0 );
  851. continue;
  852. }
  853. int waveCount = internal->NumSoundNames();
  854. for ( int wave = 0; wave < waveCount; wave++ )
  855. {
  856. CUtlSymbol sym = internal->GetSoundNames()[ wave ].symbol;
  857. const char *name = m_Waves.String( sym );
  858. if ( !name || !name[ 0 ] )
  859. {
  860. Assert( 0 );
  861. continue;
  862. }
  863. // Skip ! sentence stuff
  864. if ( name[0] == CHAR_SENTENCE )
  865. continue;
  866. Q_snprintf( testfile, sizeof( testfile ), "sound/%s", PSkipSoundChars( name ) );
  867. if ( filesystem->FileExists( testfile ) )
  868. continue;
  869. internal->SetHadMissingWaveFiles( true );
  870. ++missing;
  871. if ( verbose )
  872. {
  873. DevMsg( "Sound %s references missing file %s\n", GetSoundName( i ), name );
  874. }
  875. }
  876. }
  877. return missing;
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose:
  881. // Input : *key -
  882. // Output : float
  883. //-----------------------------------------------------------------------------
  884. float CSoundEmitterSystemBase::TranslateAttenuation( const char *key )
  885. {
  886. if ( !key )
  887. {
  888. Assert( 0 );
  889. return ATTN_NORM;
  890. }
  891. if ( !Q_strcasecmp( key, "ATTN_NONE" ) )
  892. return ATTN_NONE;
  893. if ( !Q_strcasecmp( key, "ATTN_NORM" ) )
  894. return ATTN_NORM;
  895. if ( !Q_strcasecmp( key, "ATTN_IDLE" ) )
  896. return ATTN_IDLE;
  897. if ( !Q_strcasecmp( key, "ATTN_STATIC" ) )
  898. return ATTN_STATIC;
  899. if ( !Q_strcasecmp( key, "ATTN_RICOCHET" ) )
  900. return ATTN_RICOCHET;
  901. if ( !Q_strcasecmp( key, "ATTN_GUNFIRE" ) )
  902. return ATTN_GUNFIRE;
  903. DevMsg( "CSoundEmitterSystem: Unknown attenuation key %s\n", key );
  904. return ATTN_NORM;
  905. }
  906. //-----------------------------------------------------------------------------
  907. // Purpose:
  908. // Input : *key -
  909. // Output : soundlevel_t
  910. //-----------------------------------------------------------------------------
  911. soundlevel_t CSoundEmitterSystemBase::TranslateSoundLevel( const char *key )
  912. {
  913. return TextToSoundLevel( key );
  914. }
  915. //-----------------------------------------------------------------------------
  916. // Purpose: Convert "chan_xxx" into integer value for channel
  917. // Input : *name -
  918. // Output : static int
  919. //-----------------------------------------------------------------------------
  920. int CSoundEmitterSystemBase::TranslateChannel( const char *name )
  921. {
  922. return TextToChannel( name );
  923. }
  924. const char *CSoundEmitterSystemBase::GetSourceFileForSound( int index ) const
  925. {
  926. if ( index < 0 || index >= (int)m_Sounds.Count() )
  927. {
  928. Assert( 0 );
  929. return "";
  930. }
  931. CSoundEntry const *entry = m_Sounds[ index ];
  932. int scriptindex = entry->m_nScriptFileIndex;
  933. if ( scriptindex < 0 || scriptindex >= m_SoundKeyValues.Count() )
  934. {
  935. Assert( 0 );
  936. return "";
  937. }
  938. static char fn[ 512 ];
  939. if ( filesystem->String( m_SoundKeyValues[ scriptindex ].hFilename, fn, sizeof( fn ) ))
  940. {
  941. return fn;
  942. }
  943. Assert( 0 );
  944. return "";
  945. }
  946. const char *CSoundEmitterSystemBase::GetWaveName( CUtlSymbol& sym )
  947. {
  948. return m_Waves.String( sym );
  949. }
  950. int CSoundEmitterSystemBase::FindSoundScript( const char *name ) const
  951. {
  952. int i, c;
  953. FileNameHandle_t hFilename = filesystem->FindFileName( name );
  954. if ( hFilename )
  955. {
  956. // First, make sure it's known
  957. c = m_SoundKeyValues.Count();
  958. for ( i = 0; i < c ; i++ )
  959. {
  960. if ( m_SoundKeyValues[ i ].hFilename == hFilename )
  961. {
  962. return i;
  963. }
  964. }
  965. }
  966. return m_SoundKeyValues.InvalidIndex();
  967. }
  968. bool CSoundEmitterSystemBase::AddSound( const char *soundname, const char *scriptfile, const CSoundParametersInternal& params )
  969. {
  970. int idx = GetSoundIndex( soundname );
  971. int i = FindSoundScript( scriptfile );
  972. if ( i == m_SoundKeyValues.InvalidIndex() )
  973. {
  974. Warning( "CSoundEmitterSystemBase::AddSound( '%s', '%s', ... ), script file not list in manifest '%s'\n",
  975. soundname, scriptfile, MANIFEST_FILE );
  976. return false;
  977. }
  978. MEM_ALLOC_CREDIT();
  979. // More like an update...
  980. if ( IsValidIndex( idx ) )
  981. {
  982. CSoundEntry *entry = m_Sounds[ idx ];
  983. entry->m_bRemoved = false;
  984. entry->m_nScriptFileIndex = i;
  985. entry->m_SoundParams.CopyFrom( params );
  986. m_SoundKeyValues[ i ].dirty = true;
  987. return true;
  988. }
  989. CSoundEntry *pEntry = new CSoundEntry;
  990. pEntry->m_Name = soundname;
  991. pEntry->m_bRemoved = false;
  992. pEntry->m_nScriptFileIndex = i;
  993. pEntry->m_SoundParams.CopyFrom( params );
  994. m_Sounds.Insert( pEntry );
  995. m_SoundKeyValues[ i ].dirty = true;
  996. return true;
  997. }
  998. void CSoundEmitterSystemBase::RemoveSound( const char *soundname )
  999. {
  1000. int idx = GetSoundIndex( soundname );
  1001. if ( !IsValidIndex( idx ) )
  1002. {
  1003. Warning( "Can't remove %s, no such sound!\n", soundname );
  1004. return;
  1005. }
  1006. m_Sounds[ idx ]->m_bRemoved = true;
  1007. // Mark script as dirty
  1008. int scriptindex = m_Sounds[ idx ]->m_nScriptFileIndex;
  1009. if ( scriptindex < 0 || scriptindex >= m_SoundKeyValues.Count() )
  1010. {
  1011. Assert( 0 );
  1012. return;
  1013. }
  1014. m_SoundKeyValues[ scriptindex ].dirty = true;
  1015. }
  1016. void CSoundEmitterSystemBase::MoveSound( const char *soundname, const char *newscript )
  1017. {
  1018. int idx = GetSoundIndex( soundname );
  1019. if ( !IsValidIndex( idx ) )
  1020. {
  1021. Warning( "Can't move '%s', no such sound!\n", soundname );
  1022. return;
  1023. }
  1024. int oldscriptindex = m_Sounds[ idx ]->m_nScriptFileIndex;
  1025. if ( oldscriptindex < 0 || oldscriptindex >= m_SoundKeyValues.Count() )
  1026. {
  1027. Assert( 0 );
  1028. return;
  1029. }
  1030. int newscriptindex = FindSoundScript( newscript );
  1031. if ( newscriptindex == m_SoundKeyValues.InvalidIndex() )
  1032. {
  1033. Warning( "CSoundEmitterSystemBase::MoveSound( '%s', '%s' ), script file not list in manifest '%s'\n",
  1034. soundname, newscript, MANIFEST_FILE );
  1035. return;
  1036. }
  1037. // No actual change
  1038. if ( oldscriptindex == newscriptindex )
  1039. {
  1040. return;
  1041. }
  1042. // Move it
  1043. m_Sounds[ idx ]->m_nScriptFileIndex = newscriptindex;
  1044. // Mark both scripts as dirty
  1045. m_SoundKeyValues[ oldscriptindex ].dirty = true;
  1046. m_SoundKeyValues[ newscriptindex ].dirty = true;
  1047. }
  1048. int CSoundEmitterSystemBase::GetNumSoundScripts() const
  1049. {
  1050. return m_SoundKeyValues.Count();
  1051. }
  1052. const char *CSoundEmitterSystemBase::GetSoundScriptName( int index ) const
  1053. {
  1054. if ( index < 0 || index >= m_SoundKeyValues.Count() )
  1055. return NULL;
  1056. static char fn[ 512 ];
  1057. if ( filesystem->String( m_SoundKeyValues[ index ].hFilename, fn, sizeof( fn ) ) )
  1058. {
  1059. return fn;
  1060. }
  1061. return "";
  1062. }
  1063. bool CSoundEmitterSystemBase::IsSoundScriptDirty( int index ) const
  1064. {
  1065. if ( index < 0 || index >= m_SoundKeyValues.Count() )
  1066. return false;
  1067. return m_SoundKeyValues[ index ].dirty;
  1068. }
  1069. void CSoundEmitterSystemBase::SaveChangesToSoundScript( int scriptindex )
  1070. {
  1071. const char *outfile = GetSoundScriptName( scriptindex );
  1072. if ( !outfile )
  1073. {
  1074. Msg( "CSoundEmitterSystemBase::SaveChangesToSoundScript: No script file for index %i\n", scriptindex );
  1075. return;
  1076. }
  1077. if ( filesystem->FileExists( outfile ) &&
  1078. !filesystem->IsFileWritable( outfile ) )
  1079. {
  1080. Warning( "%s is not writable, can't save data to file\n", outfile );
  1081. return;
  1082. }
  1083. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1084. // FIXME: Write sound script header
  1085. if ( filesystem->FileExists( GAME_SOUNDS_HEADER_BLOCK ) )
  1086. {
  1087. FileHandle_t header = filesystem->Open( GAME_SOUNDS_HEADER_BLOCK, "rb", NULL );
  1088. if ( header != FILESYSTEM_INVALID_HANDLE )
  1089. {
  1090. int len = filesystem->Size( header );
  1091. unsigned char *data = new unsigned char[ len + 1 ];
  1092. Q_memset( data, 0, len + 1 );
  1093. filesystem->Read( data, len, header );
  1094. filesystem->Close( header );
  1095. data[ len ] = 0;
  1096. char *p = (char *)data;
  1097. while ( *p )
  1098. {
  1099. if ( *p != '\r' )
  1100. {
  1101. buf.PutChar( *p );
  1102. }
  1103. ++p;
  1104. }
  1105. delete[] data;
  1106. }
  1107. buf.Printf( "\n" );
  1108. }
  1109. int c = GetSoundCount();
  1110. for ( int i = 0; i < c; i++ )
  1111. {
  1112. if ( Q_stricmp( outfile, GetSourceFileForSound( i ) ) )
  1113. continue;
  1114. // It's marked for deletion, just skip it
  1115. if ( m_Sounds[ i ]->m_bRemoved )
  1116. continue;
  1117. CSoundParametersInternal *p = InternalGetParametersForSound( i );
  1118. if ( !p )
  1119. continue;
  1120. buf.Printf( "\"%s\"\n{\n", GetSoundName( i ) );
  1121. buf.Printf( "\t\"channel\"\t\t\"%s\"\n", p->ChannelToString() );
  1122. buf.Printf( "\t\"volume\"\t\t\"%s\"\n", p->VolumeToString() );
  1123. buf.Printf( "\t\"pitch\"\t\t\t\"%s\"\n", p->PitchToString() );
  1124. buf.Printf( "\n" );
  1125. buf.Printf( "\t\"soundlevel\"\t\"%s\"\n", p->SoundLevelToString() );
  1126. if ( p->OnlyPlayToOwner() )
  1127. {
  1128. buf.Printf( "\t\"play_to_owner_only\"\t\"1\"\n" );
  1129. }
  1130. if ( p->GetDelayMsec() != 0 )
  1131. {
  1132. buf.Printf( "\t\"delay_msec\"\t\"%i\"\n", p->GetDelayMsec() );
  1133. }
  1134. int totalCount = 0;
  1135. int waveCount = p->NumSoundNames();
  1136. int convertedCount = p->NumConvertedNames();
  1137. totalCount = ( waveCount - 2 * convertedCount ) + convertedCount;
  1138. if ( totalCount > 0 )
  1139. {
  1140. buf.Printf( "\n" );
  1141. if ( waveCount == 1 )
  1142. {
  1143. Assert( p->GetSoundNames()[ 0 ].gender == GENDER_NONE );
  1144. buf.Printf( "\t\"wave\"\t\t\t\"%s\"\n", GetWaveName( p->GetSoundNames()[ 0 ].symbol ) );
  1145. }
  1146. else if ( convertedCount == 1 )
  1147. {
  1148. Assert( p->GetConvertedNames()[ 0 ].gender == GENDER_NONE );
  1149. buf.Printf( "\t\"wave\"\t\t\t\"%s\"\n", GetWaveName( p->GetConvertedNames()[ 0 ].symbol ) );
  1150. }
  1151. else
  1152. {
  1153. buf.Printf( "\t\"rndwave\"\n" );
  1154. buf.Printf( "\t{\n" );
  1155. int wave;
  1156. for ( wave = 0; wave < waveCount; wave++ )
  1157. {
  1158. // Skip macro-expanded names
  1159. if ( p->GetSoundNames()[ wave ].gender != GENDER_NONE )
  1160. continue;
  1161. buf.Printf( "\t\t\"wave\"\t\"%s\"\n", GetWaveName( p->GetSoundNames()[ wave ].symbol ) );
  1162. }
  1163. for ( wave = 0; wave < convertedCount; wave++ )
  1164. {
  1165. buf.Printf( "\t\t\"wave\"\t\"%s\"\n", GetWaveName( p->GetConvertedNames()[ wave ].symbol ) );
  1166. }
  1167. buf.Printf( "\t}\n" );
  1168. }
  1169. }
  1170. buf.Printf( "}\n" );
  1171. if ( i != c - 1 )
  1172. {
  1173. buf.Printf( "\n" );
  1174. }
  1175. }
  1176. // Write it out baby
  1177. FileHandle_t fh = filesystem->Open( outfile, "wt" );
  1178. if (fh)
  1179. {
  1180. filesystem->Write( buf.Base(), buf.TellPut(), fh );
  1181. filesystem->Close(fh);
  1182. // Changed saved successfully
  1183. m_SoundKeyValues[ scriptindex ].dirty = false;
  1184. }
  1185. else
  1186. {
  1187. Warning( "SceneManager_SaveSoundsToScriptFile: Unable to write file %s!!!\n", outfile );
  1188. }
  1189. }
  1190. //-----------------------------------------------------------------------------
  1191. // Purpose:
  1192. // Input : *name -
  1193. // Output : CUtlSymbol
  1194. //-----------------------------------------------------------------------------
  1195. CUtlSymbol CSoundEmitterSystemBase::AddWaveName( const char *name )
  1196. {
  1197. return m_Waves.AddString( name );
  1198. }
  1199. void CSoundEmitterSystemBase::RenameSound( const char *soundname, const char *newname )
  1200. {
  1201. // Same name?
  1202. if ( !Q_stricmp( soundname, newname ) )
  1203. {
  1204. return;
  1205. }
  1206. int oldindex = GetSoundIndex( soundname );
  1207. if ( !IsValidIndex( oldindex ) )
  1208. {
  1209. Msg( "Can't rename %s, no such sound\n", soundname );
  1210. return;
  1211. }
  1212. int check = GetSoundIndex( newname );
  1213. if ( IsValidIndex( check ) )
  1214. {
  1215. Msg( "Can't rename %s to %s, new name already in list\n", soundname, newname );
  1216. return;
  1217. }
  1218. MEM_ALLOC_CREDIT();
  1219. // Copy out old entry
  1220. CSoundEntry *pEntry = m_Sounds[ oldindex ];
  1221. // Remove it
  1222. m_Sounds.Remove( pEntry );
  1223. pEntry->m_Name = newname;
  1224. // Re-insert in new spot
  1225. m_Sounds.Insert( pEntry );
  1226. // Mark associated script as dirty
  1227. m_SoundKeyValues[ pEntry->m_nScriptFileIndex ].dirty = true;
  1228. }
  1229. void CSoundEmitterSystemBase::UpdateSoundParameters( const char *soundname, const CSoundParametersInternal& params )
  1230. {
  1231. int idx = GetSoundIndex( soundname );
  1232. if ( !IsValidIndex( idx ) )
  1233. {
  1234. Msg( "Can't UpdateSoundParameters %s, no such sound\n", soundname );
  1235. return;
  1236. }
  1237. CSoundEntry *entry = m_Sounds[ idx ];
  1238. if ( entry->m_SoundParams == params )
  1239. {
  1240. // No changes
  1241. return;
  1242. }
  1243. // Update parameters
  1244. entry->m_SoundParams.CopyFrom( params );
  1245. // Set dirty flag
  1246. m_SoundKeyValues[ entry->m_nScriptFileIndex ].dirty = true;
  1247. }
  1248. bool CSoundEmitterSystemBase::IsUsingGenderToken( char const *soundname )
  1249. {
  1250. int soundindex = GetSoundIndex( soundname );
  1251. if ( soundindex < 0 )
  1252. return false;
  1253. // Look up the sound level from the soundemitter system
  1254. CSoundParametersInternal *params = InternalGetParametersForSound( soundindex );
  1255. if ( !params )
  1256. return false;
  1257. return params->UsesGenderToken();
  1258. }
  1259. unsigned int CSoundEmitterSystemBase::GetManifestFileTimeChecksum()
  1260. {
  1261. return m_uManifestPlusScriptChecksum;
  1262. }
  1263. bool CSoundEmitterSystemBase::GetParametersForSoundEx( const char *soundname, HSOUNDSCRIPTHANDLE& handle, CSoundParameters& params, gender_t gender, bool isbeingemitted /*= false*/ )
  1264. {
  1265. if ( handle == SOUNDEMITTER_INVALID_HANDLE )
  1266. {
  1267. handle = GetSoundIndex( soundname );
  1268. if ( handle == SOUNDEMITTER_INVALID_HANDLE )
  1269. return false;
  1270. }
  1271. CSoundParametersInternal *internal = InternalGetParametersForSound( (int)handle );
  1272. if ( !internal )
  1273. {
  1274. Assert( 0 );
  1275. DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: No such sound %s\n", soundname );
  1276. return false;
  1277. }
  1278. params.channel = internal->GetChannel();
  1279. params.volume = internal->GetVolume().Random();
  1280. params.pitch = internal->GetPitch().Random();
  1281. params.pitchlow = internal->GetPitch().start;
  1282. params.pitchhigh = params.pitchlow + internal->GetPitch().range;
  1283. params.delay_msec = internal->GetDelayMsec();
  1284. params.count = internal->NumSoundNames();
  1285. params.soundname[ 0 ] = 0;
  1286. int bestIndex = FindBestSoundForGender( internal->GetSoundNames(), internal->NumSoundNames(), gender );
  1287. if ( bestIndex >= 0 )
  1288. {
  1289. Q_strncpy( params.soundname, GetWaveName( internal->GetSoundNames()[ bestIndex ].symbol), sizeof( params.soundname ) );
  1290. // If we are actually emitting the sound, mark it as not available...
  1291. if ( isbeingemitted )
  1292. {
  1293. internal->GetSoundNames()[ bestIndex ].available = 0;
  1294. }
  1295. }
  1296. params.soundlevel = (soundlevel_t)(int)internal->GetSoundLevel().Random();
  1297. params.play_to_owner_only = internal->OnlyPlayToOwner();
  1298. if ( !params.soundname[ 0 ] )
  1299. {
  1300. DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has no wave or rndwave key!\n", soundname );
  1301. return false;
  1302. }
  1303. if ( internal->HadMissingWaveFiles() &&
  1304. params.soundname[ 0 ] != CHAR_SENTENCE )
  1305. {
  1306. char testfile[ 256 ];
  1307. Q_snprintf( testfile, sizeof( testfile ), "sound/%s", PSkipSoundChars( params.soundname ) );
  1308. if ( !filesystem->FileExists( testfile ) )
  1309. {
  1310. // Prevent repetitive spew...
  1311. static CUtlSymbolTable soundWarnings;
  1312. char key[ 256 ];
  1313. Q_snprintf( key, sizeof( key ), "%s:%s", soundname, params.soundname );
  1314. if ( UTL_INVAL_SYMBOL == soundWarnings.Find( key ) )
  1315. {
  1316. soundWarnings.AddString( key );
  1317. DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound '%s' references wave '%s' which doesn't exist on disk!\n",
  1318. soundname,
  1319. params.soundname );
  1320. }
  1321. return false;
  1322. }
  1323. }
  1324. return true;
  1325. }
  1326. soundlevel_t CSoundEmitterSystemBase::LookupSoundLevelByHandle( char const *soundname, HSOUNDSCRIPTHANDLE& handle )
  1327. {
  1328. if ( handle == SOUNDEMITTER_INVALID_HANDLE )
  1329. {
  1330. handle = (HSOUNDSCRIPTHANDLE)GetSoundIndex( soundname );
  1331. if ( handle == SOUNDEMITTER_INVALID_HANDLE )
  1332. return SNDLVL_NORM;
  1333. }
  1334. CSoundParametersInternal *internal = InternalGetParametersForSound( (int)handle );
  1335. if ( !internal )
  1336. {
  1337. return SNDLVL_NORM;
  1338. }
  1339. return (soundlevel_t)(int)internal->GetSoundLevel().Random();
  1340. }
  1341. // 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)
  1342. // Called by LevelInitPreEntity to override sound scripts for the mod with level specific overrides based on custom mapnames, etc.
  1343. void CSoundEmitterSystemBase::AddSoundOverrides( char const *scriptfile, bool bPreload /*= false*/ )
  1344. {
  1345. FileNameHandle_t handle = filesystem->FindOrAddFileName( scriptfile );
  1346. if ( m_OverrideFiles.Find( handle ) != m_OverrideFiles.InvalidIndex() )
  1347. return;
  1348. m_OverrideFiles.AddToTail( handle );
  1349. // These are overrides
  1350. AddSoundsFromFile( scriptfile, bPreload, true );
  1351. }
  1352. // Called by either client or server in LevelShutdown to clear out custom overrides
  1353. void CSoundEmitterSystemBase::ClearSoundOverrides()
  1354. {
  1355. int i;
  1356. int removed = 0;
  1357. for ( UtlHashHandle_t i = m_Sounds.FirstHandle(); i != m_Sounds.InvalidHandle(); )
  1358. {
  1359. CSoundEntry *entry = m_Sounds[ i ];
  1360. if ( entry->IsOverride() )
  1361. {
  1362. i = m_Sounds.RemoveAndAdvance( i );
  1363. ++removed;
  1364. }
  1365. else
  1366. {
  1367. i = m_Sounds.NextHandle( i );
  1368. }
  1369. }
  1370. if (removed > 0 || m_SavedOverrides.Count() > 0 )
  1371. {
  1372. Warning( "SoundEmitter: removing map sound overrides [%i to remove, %i to restore]\n",
  1373. removed,
  1374. m_SavedOverrides.Count() );
  1375. }
  1376. // Now restore the original entries into the main dictionary.
  1377. for ( i = 0; i < m_SavedOverrides.Count(); ++i )
  1378. {
  1379. CSoundEntry *entry = m_SavedOverrides[ i ];
  1380. m_Sounds.Insert( entry );
  1381. }
  1382. m_SavedOverrides.Purge();
  1383. m_OverrideFiles.Purge();
  1384. }
  1385. CSoundEmitterSystemBase g_SoundEmitterSystemBase;
  1386. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSoundEmitterSystemBase, ISoundEmitterSystemBase,
  1387. SOUNDEMITTERSYSTEM_INTERFACE_VERSION, g_SoundEmitterSystemBase );