Counter Strike : Global Offensive Source Code
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.

2280 lines
63 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include <ctype.h>
  8. #include <keyvalues.h>
  9. #include "engine/IEngineSound.h"
  10. #include "SoundEmitterSystem/isoundemittersystembase.h"
  11. #include "igamesystem.h"
  12. #include "soundchars.h"
  13. #include "filesystem.h"
  14. #include "tier0/vprof.h"
  15. #include "checksum_crc.h"
  16. #include "tier0/icommandline.h"
  17. #ifndef CLIENT_DLL
  18. #include "envmicrophone.h"
  19. #include "sceneentity.h"
  20. #include "closedcaptions.h"
  21. #include "usermessages.h"
  22. #else
  23. #include <vgui_controls/Controls.h>
  24. #include <vgui/IVGui.h>
  25. #include "hud_closecaption.h"
  26. #ifdef GAMEUI_UISYSTEM2_ENABLED
  27. #include "gameui.h"
  28. #endif
  29. #define CRecipientFilter C_RecipientFilter
  30. #endif
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. BEGIN_DEFINE_LOGGING_CHANNEL( LOG_SND_EMITTERSYSTEM, "SndEmitterSystem", LCF_CONSOLE_ONLY, LS_MESSAGE );
  34. ADD_LOGGING_CHANNEL_TAG( "SndEmitterSystem" );
  35. END_DEFINE_LOGGING_CHANNEL();
  36. ConVar sv_soundemitter_version( "sv_soundemitter_version", "2", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "specfies what version of soundemitter system to use\n" );
  37. #ifdef PORTAL2
  38. // THIS FUNCTION IS SUFFICIENT FOR PORTAL2 SPECIFIC CIRCUMSTANCES
  39. // AND MAY OR MAY NOT FUNCTION AS EXPECTED WHEN USED WITH MULTIPLE
  40. // SPLITSCREEN CLIENTS NETWORKED TOGETHER, ETC.
  41. ConVar snd_prevent_ss_duplicates( "snd_prevent_ss_duplicates", "1", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "switch to en/disable the prevention of splitscreen audio file duplicates\n" );
  42. #else
  43. ConVar snd_prevent_ss_duplicates( "snd_prevent_ss_duplicates", "0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "switch to en/disable the prevention of splitscreen audio file duplicates\n" );
  44. #endif
  45. #if defined( CLIENT_DLL )
  46. ConVar snd_sos_show_client_xmit( "snd_sos_show_client_xmit", "0", FCVAR_CHEAT );
  47. #else
  48. ConVar snd_sos_show_server_xmit( "snd_sos_show_server_xmit", "0", FCVAR_CHEAT );
  49. #endif
  50. ConVar sv_soundemitter_trace( "sv_soundemitter_trace", "-1", FCVAR_REPLICATED, "Show all EmitSound calls including their symbolic name and the actual wave file they resolved to. (-1 = for nobody, 0 = for everybody, n = for one entity)\n" );
  51. ConVar cc_showmissing( "cc_showmissing", "0", FCVAR_REPLICATED, "Show missing closecaption entries." );
  52. extern ISoundEmitterSystemBase *soundemitterbase;
  53. static ConVar *g_pClosecaption = NULL;
  54. static bool g_bPermitDirectSoundPrecache = false;
  55. #if !defined( CLIENT_DLL )
  56. static ConVar cc_norepeat( "cc_norepeat", "5", 0, "In multiplayer games, don't repeat captions more often than this many seconds." );
  57. class CCaptionRepeatMgr
  58. {
  59. public:
  60. CCaptionRepeatMgr() :
  61. m_rbCaptionHistory( 0, 0, DefLessFunc( unsigned int ) )
  62. {
  63. }
  64. bool CanEmitCaption( unsigned int hash );
  65. void Clear();
  66. private:
  67. void RemoveCaptionsBefore( float t );
  68. struct CaptionItem_t
  69. {
  70. unsigned int hash;
  71. float realtime;
  72. static bool Less( const CaptionItem_t &lhs, const CaptionItem_t &rhs )
  73. {
  74. return lhs.hash < rhs.hash;
  75. }
  76. };
  77. CUtlMap< unsigned int, float > m_rbCaptionHistory;
  78. };
  79. static CCaptionRepeatMgr g_CaptionRepeats;
  80. void CCaptionRepeatMgr::Clear()
  81. {
  82. m_rbCaptionHistory.Purge();
  83. }
  84. bool CCaptionRepeatMgr::CanEmitCaption( unsigned int hash )
  85. {
  86. // Don't cull in single player
  87. if ( gpGlobals->maxClients == 1 )
  88. return true;
  89. float realtime = gpGlobals->realtime;
  90. RemoveCaptionsBefore( realtime - cc_norepeat.GetFloat() );
  91. int idx = m_rbCaptionHistory.Find( hash );
  92. if ( idx == m_rbCaptionHistory.InvalidIndex() )
  93. {
  94. m_rbCaptionHistory.Insert( hash, realtime );
  95. return true;
  96. }
  97. float flLastEmitted = m_rbCaptionHistory[ idx ];
  98. if ( realtime - flLastEmitted > cc_norepeat.GetFloat() )
  99. {
  100. m_rbCaptionHistory[ idx ] = realtime;
  101. return true;
  102. }
  103. return false;
  104. }
  105. void CCaptionRepeatMgr::RemoveCaptionsBefore( float t )
  106. {
  107. CUtlVector< unsigned int > toRemove;
  108. FOR_EACH_MAP( m_rbCaptionHistory, i )
  109. {
  110. if ( m_rbCaptionHistory[ i ] < t )
  111. {
  112. toRemove.AddToTail( m_rbCaptionHistory.Key( i ) );
  113. }
  114. }
  115. for ( int i = 0; i < toRemove.Count(); ++i )
  116. {
  117. m_rbCaptionHistory.Remove( toRemove[ i ] );
  118. }
  119. }
  120. void ClearModelSoundsCache();
  121. #endif // !CLIENT_DLL
  122. void WaveTrace( char const *wavname, char const *funcname )
  123. {
  124. if ( IsGameConsole() && !IsDebug() )
  125. {
  126. return;
  127. }
  128. static CUtlSymbolTable s_WaveTrace;
  129. // Make sure we only show the message once
  130. if ( UTL_INVAL_SYMBOL == s_WaveTrace.Find( wavname ) )
  131. {
  132. DevMsg( "%s directly referenced wave %s (should use game_sounds.txt system instead)\n",
  133. funcname, wavname );
  134. s_WaveTrace.AddString( wavname );
  135. }
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Purpose:
  139. // Input : &src -
  140. //-----------------------------------------------------------------------------
  141. EmitSound_t::EmitSound_t( const CSoundParameters &src )
  142. {
  143. m_nChannel = src.channel;
  144. m_pSoundName = src.soundname;
  145. m_flVolume = src.volume;
  146. m_SoundLevel = src.soundlevel;
  147. m_nFlags = 0;
  148. m_nPitch = src.pitch;
  149. m_pOrigin = 0;
  150. m_flSoundTime = ( src.delay_msec == 0 ) ? 0.0f : gpGlobals->curtime + ( (float)src.delay_msec / 1000.0f );
  151. m_pflSoundDuration = 0;
  152. m_bEmitCloseCaption = true;
  153. m_bWarnOnMissingCloseCaption = false;
  154. m_bWarnOnDirectWaveReference = false;
  155. m_nSpeakerEntity = -1;
  156. // if sound is tagged as version 2 or higher this will be treated as a soundentry!
  157. m_hSoundScriptHash = src.m_hSoundScriptHash;
  158. m_nSoundEntryVersion = src.m_nSoundEntryVersion;
  159. }
  160. void Hack_FixEscapeChars( char *str )
  161. {
  162. int len = Q_strlen( str ) + 1;
  163. char *i = str;
  164. char *o = (char *)stackalloc( len );
  165. char *osave = o;
  166. while ( *i )
  167. {
  168. if ( *i == '\\' )
  169. {
  170. switch ( *( i + 1 ) )
  171. {
  172. case 'n':
  173. *o = '\n';
  174. ++i;
  175. break;
  176. default:
  177. *o = *i;
  178. break;
  179. }
  180. }
  181. else
  182. {
  183. *o = *i;
  184. }
  185. ++i;
  186. ++o;
  187. }
  188. *o = 0;
  189. Q_strncpy( str, osave, len );
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose:
  193. //-----------------------------------------------------------------------------
  194. class CSoundEmitterSystem : public CBaseGameSystem
  195. {
  196. public:
  197. virtual char const *Name() { return "CSoundEmitterSystem"; }
  198. #if !defined( CLIENT_DLL )
  199. bool m_bLogPrecache;
  200. FileHandle_t m_hPrecacheLogFile;
  201. CUtlSymbolTable m_PrecachedScriptSounds;
  202. CUtlVector< AsyncCaption_t > m_ServerCaptions;
  203. public:
  204. CSoundEmitterSystem( char const *pszName ) :
  205. m_bLogPrecache( false ),
  206. m_hPrecacheLogFile( FILESYSTEM_INVALID_HANDLE )
  207. {
  208. }
  209. void LogPrecache( char const *soundname )
  210. {
  211. if ( !m_bLogPrecache )
  212. return;
  213. // Make sure we only show the message once
  214. if ( UTL_INVAL_SYMBOL != m_PrecachedScriptSounds.Find( soundname ) )
  215. return;
  216. if (m_hPrecacheLogFile == FILESYSTEM_INVALID_HANDLE)
  217. {
  218. StartLog();
  219. }
  220. m_PrecachedScriptSounds.AddString( soundname );
  221. if (m_hPrecacheLogFile != FILESYSTEM_INVALID_HANDLE)
  222. {
  223. filesystem->Write("\"", 1, m_hPrecacheLogFile);
  224. filesystem->Write(soundname, Q_strlen(soundname), m_hPrecacheLogFile);
  225. filesystem->Write("\"\n", 2, m_hPrecacheLogFile);
  226. }
  227. else
  228. {
  229. Warning( "Disabling precache logging due to file i/o problem!!!\n" );
  230. m_bLogPrecache = false;
  231. }
  232. }
  233. void StartLog()
  234. {
  235. m_PrecachedScriptSounds.RemoveAll();
  236. if ( !m_bLogPrecache )
  237. return;
  238. if ( FILESYSTEM_INVALID_HANDLE != m_hPrecacheLogFile )
  239. {
  240. return;
  241. }
  242. filesystem->CreateDirHierarchy("reslists", "DEFAULT_WRITE_PATH");
  243. // open the new level reslist
  244. char path[_MAX_PATH];
  245. Q_snprintf(path, sizeof(path), "reslists\\%s.snd", gpGlobals->mapname.ToCStr() );
  246. m_hPrecacheLogFile = filesystem->Open(path, "wt", "MOD");
  247. if (m_hPrecacheLogFile == FILESYSTEM_INVALID_HANDLE)
  248. {
  249. Warning( "Unable to open %s for precache logging\n", path );
  250. }
  251. }
  252. void FinishLog()
  253. {
  254. if ( FILESYSTEM_INVALID_HANDLE != m_hPrecacheLogFile )
  255. {
  256. filesystem->Close( m_hPrecacheLogFile );
  257. m_hPrecacheLogFile = FILESYSTEM_INVALID_HANDLE;
  258. }
  259. m_PrecachedScriptSounds.RemoveAll();
  260. }
  261. #else
  262. CSoundEmitterSystem( char const *name )
  263. {
  264. }
  265. #endif
  266. #if !defined( CLIENT_DLL )
  267. void LoadServerCaptions()
  268. {
  269. m_ServerCaptions.Purge();
  270. // Server keys off of english file!!!
  271. AddCaptionFile( "resource/closecaption_english.dat" );
  272. AddCaptionFile( "resource/subtitles_english.dat" );
  273. }
  274. #endif // !defined( CLIENT_DLL )
  275. // IServerSystem stuff
  276. virtual bool Init()
  277. {
  278. Assert( soundemitterbase );
  279. #if !defined( CLIENT_DLL )
  280. m_bLogPrecache = CommandLine()->CheckParm( "-makereslists" ) ? true : false;
  281. #endif
  282. g_pClosecaption = cvar->FindVar("closecaption");
  283. Assert(g_pClosecaption);
  284. #if !defined( CLIENT_DLL )
  285. LoadServerCaptions();
  286. #endif // !defined( CLIENT_DLL )
  287. return true;
  288. }
  289. #if !defined( CLIENT_DLL )
  290. void AddCaptionFile( const char *filename )
  291. {
  292. int searchPathLen = filesystem->GetSearchPath( "GAME", true, NULL, 0 );
  293. char *searchPaths = (char *)stackalloc( searchPathLen + 1 );
  294. filesystem->GetSearchPath( "GAME", true, searchPaths, searchPathLen );
  295. for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) )
  296. {
  297. if ( IsGameConsole() && ( filesystem->GetDVDMode() == DVDMODE_STRICT ) && !V_stristr( path, ".zip" ) )
  298. {
  299. // only want zip paths
  300. continue;
  301. }
  302. char fullpath[MAX_PATH];
  303. Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", path, filename );
  304. Q_FixSlashes( fullpath );
  305. Q_strlower( fullpath );
  306. if ( IsGameConsole() )
  307. {
  308. char fullpath360[MAX_PATH];
  309. UpdateOrCreateCaptionFile( fullpath, fullpath360, sizeof( fullpath360 ) );
  310. Q_strncpy( fullpath, fullpath360, sizeof( fullpath ) );
  311. }
  312. int idx = m_ServerCaptions.AddToTail();
  313. AsyncCaption_t& entry = m_ServerCaptions[ idx ];
  314. if ( !entry.LoadFromFile( fullpath ) )
  315. {
  316. m_ServerCaptions.Remove( idx );
  317. }
  318. else
  319. {
  320. DevMsg( "Server: added caption file: %s\n", fullpath );
  321. }
  322. }
  323. }
  324. #endif
  325. virtual void Shutdown()
  326. {
  327. Assert( soundemitterbase );
  328. #if !defined( CLIENT_DLL )
  329. FinishLog();
  330. #endif
  331. }
  332. void Flush()
  333. {
  334. Shutdown();
  335. soundemitterbase->Flush();
  336. #ifdef CLIENT_DLL
  337. #ifdef GAMEUI_UISYSTEM2_ENABLED
  338. g_pGameUIGameSystem->ReloadSounds();
  339. #endif
  340. #endif
  341. Init();
  342. }
  343. virtual void TraceEmitSound( int originEnt, char const *fmt, ... )
  344. {
  345. if ( sv_soundemitter_trace.GetInt() == -1 )
  346. return;
  347. if ( sv_soundemitter_trace.GetInt() != 0 && sv_soundemitter_trace.GetInt() != originEnt )
  348. return;
  349. va_list argptr;
  350. char string[256];
  351. va_start (argptr, fmt);
  352. Q_vsnprintf( string, sizeof( string ), fmt, argptr );
  353. va_end (argptr);
  354. // Spew to console
  355. Msg( "%s %s", CBaseEntity::IsServer() ? "(sv)" : "(cl)", string );
  356. }
  357. // Precache all wave files referenced in wave or rndwave keys
  358. virtual void LevelInitPreEntity()
  359. {
  360. #if !defined( CLIENT_DLL )
  361. PreloadSounds();
  362. g_CaptionRepeats.Clear();
  363. #endif
  364. }
  365. void PreloadSounds( void )
  366. {
  367. for ( int i=soundemitterbase->First(); i != soundemitterbase->InvalidIndex(); i=soundemitterbase->Next( i ) )
  368. {
  369. CSoundParametersInternal *pParams = soundemitterbase->InternalGetParametersForSound( i );
  370. if ( pParams->ShouldPreload() )
  371. {
  372. InternalPrecacheWaves( i );
  373. }
  374. if ( pParams->ShouldAutoCache() )
  375. {
  376. PrecacheScriptSound( soundemitterbase->GetSoundName( i ) );
  377. }
  378. }
  379. #if !defined( CLIENT_DLL )
  380. g_CaptionRepeats.Clear();
  381. #endif
  382. }
  383. virtual void LevelInitPostEntity()
  384. {
  385. }
  386. virtual void LevelShutdownPostEntity()
  387. {
  388. #if !defined( CLIENT_DLL )
  389. FinishLog();
  390. g_CaptionRepeats.Clear();
  391. #endif
  392. }
  393. void InternalPrecacheWaves( int soundIndex )
  394. {
  395. CSoundParametersInternal *internal = soundemitterbase->InternalGetParametersForSound( soundIndex );
  396. if ( !internal )
  397. return;
  398. int waveCount = internal->NumSoundNames();
  399. if ( !waveCount )
  400. {
  401. DevMsg( "CSoundEmitterSystem: sounds.txt entry '%s' has no waves listed under 'wave' or 'rndwave' key!!!\n",
  402. soundemitterbase->GetSoundName( soundIndex ) );
  403. }
  404. else
  405. {
  406. g_bPermitDirectSoundPrecache = true;
  407. for( int wave = 0; wave < waveCount; wave++ )
  408. {
  409. CBaseEntity::PrecacheSound( soundemitterbase->GetWaveName( internal->GetSoundNames()[ wave ].symbol ) );
  410. }
  411. g_bPermitDirectSoundPrecache = false;
  412. }
  413. }
  414. void InternalPrefetchWaves( int soundIndex )
  415. {
  416. CSoundParametersInternal *internal = soundemitterbase->InternalGetParametersForSound( soundIndex );
  417. if ( !internal )
  418. return;
  419. int waveCount = internal->NumSoundNames();
  420. if ( !waveCount )
  421. {
  422. DevMsg( "CSoundEmitterSystem: sounds.txt entry '%s' has no waves listed under 'wave' or 'rndwave' key!!!\n",
  423. soundemitterbase->GetSoundName( soundIndex ) );
  424. }
  425. else
  426. {
  427. for( int wave = 0; wave < waveCount; wave++ )
  428. {
  429. CBaseEntity::PrefetchSound( soundemitterbase->GetWaveName( internal->GetSoundNames()[ wave ].symbol ) );
  430. }
  431. }
  432. }
  433. void PrecacheSOSScriptSounds( KeyValues *pRootKV )
  434. {
  435. if ( !pRootKV )
  436. return;
  437. // iterate through all values
  438. for ( KeyValues *pValue = pRootKV->GetFirstValue(); pValue; pValue = pValue->GetNextValue() )
  439. {
  440. const char *pName = pValue->GetName();
  441. if ( pName && !V_stricmp( pName, "entry_name" ) )
  442. {
  443. const char *pScriptName = pValue->GetString();
  444. if ( pScriptName && pScriptName[0] )
  445. {
  446. PrecacheScriptSound( pScriptName );
  447. }
  448. }
  449. }
  450. // iterate and recurse into each true subkey
  451. for ( KeyValues *pSubKey = pRootKV->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
  452. {
  453. PrecacheSOSScriptSounds( pSubKey );
  454. }
  455. }
  456. HSOUNDSCRIPTHASH PrecacheScriptSound( const char *soundname )
  457. {
  458. HSOUNDSCRIPTHASH hash = soundemitterbase->HashSoundName( soundname );
  459. int soundIndex = soundemitterbase->GetSoundIndexForHash( hash );
  460. if ( !soundemitterbase->IsValidIndex( soundIndex ) )
  461. {
  462. if ( Q_stristr( soundname, ".wav" ) || Q_strstr( soundname, ".mp3" ) )
  463. {
  464. g_bPermitDirectSoundPrecache = true;
  465. CBaseEntity::PrecacheSound( soundname );
  466. g_bPermitDirectSoundPrecache = false;
  467. return SOUNDEMITTER_INVALID_HASH;
  468. }
  469. #if !defined( CLIENT_DLL )
  470. if ( soundname[ 0 ] )
  471. {
  472. static CUtlSymbolTable s_PrecacheScriptSoundFailures;
  473. // Make sure we only show the message once
  474. if ( UTL_INVAL_SYMBOL == s_PrecacheScriptSoundFailures.Find( soundname ) )
  475. {
  476. Warning( "PrecacheScriptSound '%s' failed, no such sound script entry\n", soundname );
  477. s_PrecacheScriptSoundFailures.AddString( soundname );
  478. }
  479. }
  480. #endif
  481. return SOUNDEMITTER_INVALID_HASH;
  482. }
  483. #if !defined( CLIENT_DLL )
  484. LogPrecache( soundname );
  485. #endif
  486. // recursively descend into possible operator stacks to precache all their script sounds
  487. CSoundParametersInternal *pInternal = soundemitterbase->InternalGetParametersForSound( soundIndex );
  488. if ( pInternal && !pInternal->HasCached() )
  489. {
  490. pInternal->SetCached( true );
  491. PrecacheSOSScriptSounds( pInternal->GetOperatorsKV() );
  492. }
  493. InternalPrecacheWaves( soundIndex );
  494. return hash;
  495. }
  496. void PrefetchScriptSound( const char *soundname )
  497. {
  498. int soundIndex = soundemitterbase->GetSoundIndex( soundname );
  499. if ( !soundemitterbase->IsValidIndex( soundIndex ) )
  500. {
  501. if ( Q_stristr( soundname, ".wav" ) || Q_strstr( soundname, ".mp3" ) )
  502. {
  503. CBaseEntity::PrefetchSound( soundname );
  504. }
  505. return;
  506. }
  507. InternalPrefetchWaves( soundIndex );
  508. }
  509. public:
  510. // utility for cracking parameters
  511. bool GetSoundEntryParameters( int entindex, const EmitSound_t & ep, CSoundParameters & params, HSOUNDSCRIPTHASH& handle )
  512. {
  513. // Try to deduce the actor's gender
  514. gender_t gender = GENDER_NONE;
  515. CBaseEntity *ent = CBaseEntity::Instance( entindex );
  516. if ( ent )
  517. {
  518. char const *actorModel = STRING( ent->GetModelName() );
  519. gender = soundemitterbase->GetActorGender( actorModel );
  520. }
  521. if ( !soundemitterbase->GetParametersForSoundEx( ep.m_pSoundName, handle, params, gender, true ) )
  522. {
  523. return false;
  524. }
  525. if ( !params.soundname[0] )
  526. return false;
  527. if ( !Q_strncasecmp( params.soundname, "vo", 2 ) &&
  528. !( params.channel == CHAN_STREAM ||
  529. params.channel == CHAN_VOICE ) &&
  530. params.m_nSoundEntryVersion < 2 )
  531. {
  532. DevMsg( "EmitSound: Voice wave file %s doesn't specify CHAN_VOICE or CHAN_STREAM for sound %s\n",
  533. params.soundname, ep.m_pSoundName );
  534. }
  535. // handle SND_CHANGEPITCH/SND_CHANGEVOL and other sound flags.etc.
  536. if( ( ep.m_nFlags & SND_CHANGE_PITCH ) || ( ep.m_nFlags & SND_OVERRIDE_PITCH ) )
  537. {
  538. params.pitch = ep.m_nPitch;
  539. }
  540. if( ep.m_nFlags & SND_CHANGE_VOL )
  541. {
  542. params.volume = ep.m_flVolume;
  543. }
  544. #if !defined( CLIENT_DLL )
  545. bool bSwallowed = CEnvMicrophone::OnSoundPlayed(
  546. entindex,
  547. params.soundname,
  548. params.soundlevel,
  549. params.volume,
  550. ep.m_nFlags,
  551. params.pitch,
  552. ep.m_pOrigin,
  553. ep.m_flSoundTime,
  554. ep.m_UtlVecSoundOrigin );
  555. if ( bSwallowed )
  556. return false;
  557. #endif
  558. return true;
  559. }
  560. // spew utility
  561. void TraceEmitSoundEntry( int handle, const char *pSoundEntryName, CSoundParameters &pSoundParams, int nSeed )
  562. {
  563. #if defined( CLIENT_DLL )
  564. if( !snd_sos_show_client_xmit.GetInt() )
  565. {
  566. return;
  567. }
  568. Log_Msg( LOG_SND_EMITTERSYSTEM, Color( 180, 256, 180, 255 ), "Client: Emitting SoundEntry: %i : %s : %s : operators: %s : seed: %i\n", handle, pSoundEntryName, pSoundParams.soundname, pSoundParams.m_pOperatorsKV ? "true" : "false", nSeed );
  569. #else
  570. if( !snd_sos_show_server_xmit.GetInt() )
  571. {
  572. return;
  573. }
  574. Log_Msg( LOG_SND_EMITTERSYSTEM, Color( 180, 180, 256, 255 ), "Server: Emitting SoundEntry: %i : %s : %s : operators: %s : seed: %i\n", handle, pSoundEntryName, pSoundParams.soundname, pSoundParams.m_pOperatorsKV ? "true" : "false", nSeed );
  575. #endif
  576. }
  577. // spew utility
  578. void TraceEmitSoundEntry( HSOUNDSCRIPTHASH handle, const char *pSoundEntryName, const char *pSoundFileName )
  579. {
  580. #if defined( CLIENT_DLL )
  581. if( !snd_sos_show_client_xmit.GetInt() )
  582. {
  583. return;
  584. }
  585. Log_Msg( LOG_SND_EMITTERSYSTEM, Color( 180, 256, 180, 255 ), "Client: Emitting SoundEntry: %i : %s : %s\n", handle, pSoundEntryName, pSoundFileName );
  586. #else
  587. if( !snd_sos_show_server_xmit.GetInt() )
  588. {
  589. return;
  590. }
  591. Log_Msg( LOG_SND_EMITTERSYSTEM, Color( 180, 180, 256, 255 ), "Server: Emitting SoundEntry: %i : %s : %s\n", handle, pSoundEntryName, pSoundFileName );
  592. #endif
  593. }
  594. //
  595. // emitting via a "SoundEntry" as opposed to the actual "SoundFile"
  596. // ep.m_pSoundName = a "SoundEntry" string
  597. //
  598. int EmitSoundByHandle( IRecipientFilter& filter, int entindex, const EmitSound_t & ep, HSOUNDSCRIPTHASH& handle )
  599. {
  600. // Whether the important params have come from code, defaults or the
  601. // script, we emit using them to stay backwards compatible.
  602. // It is possible, however, to override these values via the script
  603. // entries.
  604. CSoundParameters params;
  605. if( !GetSoundEntryParameters( entindex, ep, params, handle ) )
  606. {
  607. return 0;
  608. }
  609. // NOTE: This is probably
  610. // sound precaching?
  611. // NOTE: do something about this, should be irrelevant here
  612. // because we'll select our actual sound on the other side
  613. #if defined( _DEBUG ) && !defined( CLIENT_DLL )
  614. if ( !enginesound->IsSoundPrecached( params.soundname ) )
  615. {
  616. Msg( "Sound %s:%s was not precached\n", ep.m_pSoundName, params.soundname );
  617. }
  618. #endif
  619. // calculating start time from param.delay_msec
  620. // does this get moved or just replicated on client?
  621. float st = ep.m_flSoundTime;
  622. if ( !st &&
  623. params.delay_msec != 0 )
  624. {
  625. st = gpGlobals->curtime + (float)params.delay_msec / 1000.f;
  626. }
  627. // TERROR:
  628. double startTime = Plat_FloatTime();
  629. // are we actually treating as a "SoundEntry"?
  630. int nFlags = ep.m_nFlags;
  631. if(sv_soundemitter_version.GetInt() > 1 && params.m_nSoundEntryVersion > 1)
  632. {
  633. nFlags |= SND_IS_SCRIPTHANDLE;
  634. TraceEmitSoundEntry( handle, ep.m_pSoundName, params, params.m_nRandomSeed );
  635. }
  636. // Emit via server or client engine call
  637. // NOTE: We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing
  638. // all players from it!!!!
  639. CRecipientFilter filterCopy;
  640. filterCopy.CopyFrom( (CRecipientFilter &)filter );
  641. #ifdef PORTAL2
  642. if( snd_prevent_ss_duplicates.GetBool() )
  643. {
  644. // THIS FUNCTION IS SUFFICIENT FOR PORTAL2 SPECIFIC CIRCUMSTANCES
  645. // AND MAY OR MAY NOT FUNCTION AS EXPECTED WHEN USED WITH MULTIPLE
  646. // SPLITSCREEN CLIENTS NETWORKED TOGETHER, ETC.
  647. filterCopy.ReplaceSplitScreenPlayersWithOwners();
  648. }
  649. #endif
  650. // use the cracked script params
  651. int guid = enginesound->EmitSound(
  652. filterCopy,
  653. entindex,
  654. params.channel,
  655. ep.m_pSoundName, // gamesound
  656. handle, // gamesound handle
  657. params.soundname, // soundfile
  658. params.volume,
  659. (soundlevel_t)params.soundlevel,
  660. params.m_nRandomSeed,
  661. nFlags,
  662. params.pitch,
  663. ep.m_pOrigin,
  664. NULL,
  665. &ep.m_UtlVecSoundOrigin,
  666. true,
  667. st,
  668. ep.m_nSpeakerEntity );
  669. // handle duration query
  670. // NOTE: This needs to be addressed for soundentry emission
  671. //
  672. if ( ep.m_pflSoundDuration )
  673. {
  674. #ifdef GAME_DLL
  675. double startTime = Plat_FloatTime();
  676. #endif
  677. *ep.m_pflSoundDuration = enginesound->GetSoundDuration( params.soundname );
  678. #ifdef GAME_DLL
  679. float timeSpent = ( Plat_FloatTime() - startTime ) * 1000.0f;
  680. const float thinkLimit = 10.0f;
  681. if ( timeSpent > thinkLimit )
  682. {
  683. UTIL_LogPrintf( "getting sound duration for %s took %f milliseconds\n", params.soundname, timeSpent );
  684. }
  685. #endif
  686. }
  687. //// --------------------------------------------------------
  688. // MattC?
  689. // TERROR:
  690. float timeSpent = ( Plat_FloatTime() - startTime ) * 1000.0f;
  691. const float thinkLimit = 50.0f;
  692. if ( timeSpent > thinkLimit )
  693. {
  694. #ifdef GAME_DLL
  695. UTIL_LogPrintf( "EmitSoundByHandle(%s) took %f milliseconds (server)\n",
  696. ep.m_pSoundName, timeSpent );
  697. #else
  698. DevMsg( "EmitSoundByHandle(%s) took %f milliseconds (client)\n",
  699. ep.m_pSoundName, timeSpent );
  700. #endif
  701. }
  702. // Debug spew
  703. TraceEmitSound( entindex, "EmitSound: '%s' emitted as '%s' (ent %i)\n",
  704. ep.m_pSoundName, params.soundname, entindex );
  705. // Don't caption modulations to the sound
  706. if ( !( ep.m_nFlags & ( SND_CHANGE_PITCH | SND_CHANGE_VOL ) ) )
  707. {
  708. EmitCloseCaption( filter, entindex, params, ep );
  709. }
  710. return guid;
  711. }
  712. //---------------------------------------------------------------------
  713. // Emits sound via a direct sound file reference,
  714. //---------------------------------------------------------------------
  715. int EmitSoundBySoundFile( IRecipientFilter& filter, int entindex, const EmitSound_t & ep )
  716. {
  717. #if !defined( CLIENT_DLL )
  718. bool bSwallowed = CEnvMicrophone::OnSoundPlayed(
  719. entindex,
  720. ep.m_pSoundName,
  721. ep.m_SoundLevel,
  722. ep.m_flVolume,
  723. ep.m_nFlags,
  724. ep.m_nPitch,
  725. ep.m_pOrigin,
  726. ep.m_flSoundTime,
  727. ep.m_UtlVecSoundOrigin );
  728. if ( bSwallowed )
  729. return 0;
  730. #endif
  731. // Emission by soundfile is typically because the calling code has
  732. // already cracked the soundscript, loaded it's parameters and altered some.
  733. // However, we want to retain BOTH the calling code's parameters
  734. // AND the soundscript handle so that we have ALL the data for processing.
  735. //
  736. // if this has been updated to include soundscript handle, we can tell
  737. // by the soundentry version and a valid soundscript handle. We flag it
  738. // and add the data to transmission.
  739. //
  740. int nFlags = ep.m_nFlags;
  741. const char *pSoundEntryName = ep.m_pSoundName;
  742. if( ep.m_hSoundScriptHash != SOUNDEMITTER_INVALID_HASH &&
  743. ep.m_nSoundEntryVersion > 1 &&
  744. sv_soundemitter_version.GetInt() > 1 )
  745. {
  746. // reget original soundentry name
  747. pSoundEntryName = soundemitterbase->GetSoundName( ep.m_hSoundScriptHash );
  748. nFlags |= SND_IS_SCRIPTHANDLE;
  749. TraceEmitSoundEntry( ep.m_hSoundScriptHash, pSoundEntryName, ep.m_pSoundName );
  750. }
  751. // TERROR:
  752. double startTime = Plat_FloatTime();
  753. if ( ep.m_bWarnOnDirectWaveReference &&
  754. Q_stristr( ep.m_pSoundName, ".wav" ) )
  755. {
  756. WaveTrace( ep.m_pSoundName, "Emitsound" );
  757. }
  758. #if defined( _DEBUG ) && !defined( CLIENT_DLL )
  759. if ( !enginesound->IsSoundPrecached( ep.m_pSoundName ) )
  760. {
  761. Msg( "Sound %s was not precached\n", ep.m_pSoundName );
  762. }
  763. #endif
  764. // NOTE: We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing
  765. // all players from it!!!!
  766. CRecipientFilter filterCopy;
  767. filterCopy.CopyFrom( (CRecipientFilter &)filter );
  768. // THIS FUNCTION IS SUFFICIENT FOR PORTAL2 SPECIFIC CIRCUMSTANCES
  769. // AND MAY OR MAY NOT FUNCTION AS EXPECTED WHEN USED WITH MULTIPLE
  770. // SPLITSCREEN CLIENTS NETWORKED TOGETHER, ETC.
  771. #ifdef PORTAL2
  772. if( snd_prevent_ss_duplicates.GetBool() )
  773. {
  774. filterCopy.ReplaceSplitScreenPlayersWithOwners();
  775. }
  776. #endif
  777. // Emit sound via direct soundfile reference, unless tagged as a soundentry
  778. int nGuid = enginesound->EmitSound(
  779. filterCopy,
  780. entindex,
  781. ep.m_nChannel,
  782. pSoundEntryName,
  783. ep.m_hSoundScriptHash,
  784. ep.m_pSoundName,
  785. ep.m_flVolume,
  786. ep.m_SoundLevel,
  787. 0 ,
  788. nFlags,
  789. ep.m_nPitch,
  790. ep.m_pOrigin,
  791. NULL,
  792. &ep.m_UtlVecSoundOrigin,
  793. true,
  794. ep.m_flSoundTime,
  795. ep.m_nSpeakerEntity );
  796. //// -------------------------------------------------------------------
  797. if ( ep.m_pflSoundDuration )
  798. {
  799. // TERROR:
  800. #ifdef GAME_DLL
  801. UTIL_LogPrintf( "getting wav duration for %s\n", ep.m_pSoundName );
  802. #endif
  803. VPROF( "CSoundEmitterSystem::EmitSound GetSoundDuration (calls engine)" );
  804. *ep.m_pflSoundDuration = enginesound->GetSoundDuration( ep.m_pSoundName );
  805. }
  806. TraceEmitSound( entindex, "%f EmitSound: Raw wave emitted '%s' (ent %i) (vol %f)\n",
  807. gpGlobals->curtime, ep.m_pSoundName, entindex, ep.m_flVolume );
  808. // TERROR:
  809. float timeSpent = ( Plat_FloatTime() - startTime ) * 1000.0f;
  810. const float thinkLimit = 50.0f;
  811. if ( timeSpent > thinkLimit )
  812. {
  813. #ifdef GAME_DLL
  814. UTIL_LogPrintf( "CSoundEmitterSystem::EmitSound(%s) took %f milliseconds (server)\n",
  815. ep.m_pSoundName, timeSpent );
  816. #else
  817. DevMsg( "CSoundEmitterSystem::EmitSound(%s) took %f milliseconds (client)\n",
  818. ep.m_pSoundName, timeSpent );
  819. #endif
  820. }
  821. return nGuid;
  822. }
  823. //
  824. // Checks for direct soundfile reference and splits to either gamesound handle
  825. // based emission or direct soundfile emission
  826. //
  827. int EmitSound( IRecipientFilter& filter, int entindex, const EmitSound_t & ep )
  828. {
  829. VPROF( "CSoundEmitterSystem::EmitSound (calls engine)" );
  830. // Is this a direct soundfile reference or pre-parameterized call?
  831. if ( ep.m_pSoundName &&
  832. ( Q_stristr( ep.m_pSoundName, ".wav" ) ||
  833. Q_stristr( ep.m_pSoundName, ".mp3" ) ||
  834. ep.m_pSoundName[0] == '!' ))
  835. {
  836. return EmitSoundBySoundFile(filter, entindex, ep);
  837. }
  838. // handle as a script sound entry
  839. if ( ep.m_hSoundScriptHash == SOUNDEMITTER_INVALID_HASH )
  840. {
  841. ep.m_hSoundScriptHash = soundemitterbase->HashSoundName( ep.m_pSoundName );
  842. }
  843. return EmitSoundByHandle( filter, entindex, ep, ep.m_hSoundScriptHash );
  844. }
  845. void EmitCloseCaption( IRecipientFilter& filter, int entindex, bool fromplayer, char const *token, CUtlVector< Vector >& originlist, float duration, bool warnifmissing /*= false*/, bool bForceSubtitle = false )
  846. {
  847. // Don't use dedicated closecaption ConVar since it will prevent remote clients from getting captions.
  848. // Okay to use it in SP, since it's the same ConVar, not the FCVAR_USERINFO one
  849. if ( gpGlobals->maxClients == 1 &&
  850. !g_pClosecaption->GetBool())
  851. {
  852. return;
  853. }
  854. // A negative duration means fill it in from the wav file if possible
  855. if ( duration < 0.0f )
  856. {
  857. char const *wav = soundemitterbase->GetWavFileForSound( token, GENDER_NONE );
  858. if ( wav )
  859. {
  860. duration = enginesound->GetSoundDuration( wav );
  861. }
  862. else
  863. {
  864. duration = 2.0f;
  865. }
  866. }
  867. char lowercase[ 256 ];
  868. Q_strncpy( lowercase, token, sizeof( lowercase ) );
  869. Q_strlower( lowercase );
  870. if ( Q_strstr( lowercase, "\\" ) )
  871. {
  872. Hack_FixEscapeChars( lowercase );
  873. }
  874. // NOTE: We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing
  875. // all players from it!!!!
  876. CRecipientFilter filterCopy;
  877. filterCopy.CopyFrom( (CRecipientFilter &)filter );
  878. // Captions only route to host player (there is only one closecaptioning HUD)
  879. filterCopy.RemoveSplitScreenPlayers();
  880. if ( !bForceSubtitle )
  881. {
  882. // Remove any players who don't want close captions
  883. CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( (CRecipientFilter &)filterCopy );
  884. }
  885. #if !defined( CLIENT_DLL )
  886. {
  887. // Defined in sceneentity.cpp
  888. bool AttenuateCaption( const char *token, const Vector& listener, CUtlVector< Vector >& soundorigins );
  889. if ( filterCopy.GetRecipientCount() > 0 )
  890. {
  891. int c = filterCopy.GetRecipientCount();
  892. for ( int i = c - 1 ; i >= 0; --i )
  893. {
  894. CBasePlayer *player = UTIL_PlayerByIndex( filterCopy.GetRecipientIndex( i ) );
  895. if ( !player )
  896. continue;
  897. Vector playerOrigin = player->GetAbsOrigin();
  898. soundlevel_t iSoundlevel = soundemitterbase->LookupSoundLevel( lowercase );
  899. if ( !bForceSubtitle && ( iSoundlevel != SNDLVL_NONE ) && AttenuateCaption( lowercase, playerOrigin, originlist ) )
  900. {
  901. filterCopy.RemoveRecipient( player );
  902. }
  903. }
  904. }
  905. }
  906. #endif
  907. // Anyone left?
  908. if ( filterCopy.GetRecipientCount() > 0 )
  909. {
  910. #if !defined( CLIENT_DLL )
  911. char lowercase_nogender[ 256 ];
  912. Q_strncpy( lowercase_nogender, lowercase, sizeof( lowercase_nogender ) );
  913. bool bTriedGender = false;
  914. CBaseEntity *pActor = CBaseEntity::Instance( entindex );
  915. if ( pActor )
  916. {
  917. char const *pszActorModel = STRING( pActor->GetModelName() );
  918. gender_t gender = soundemitterbase->GetActorGender( pszActorModel );
  919. if ( gender == GENDER_MALE )
  920. {
  921. Q_strncat( lowercase, "_male", sizeof( lowercase ), COPY_ALL_CHARACTERS );
  922. bTriedGender = true;
  923. }
  924. else if ( gender == GENDER_FEMALE )
  925. {
  926. Q_strncat( lowercase, "_female", sizeof( lowercase ), COPY_ALL_CHARACTERS );
  927. bTriedGender = true;
  928. }
  929. }
  930. unsigned int hash = 0u;
  931. bool bFound = GetCaptionHash( lowercase, true, hash );
  932. // if not found, try the no-gender version
  933. if ( !bFound && bTriedGender )
  934. {
  935. bFound = GetCaptionHash( lowercase_nogender, true, hash );
  936. }
  937. if ( bFound )
  938. {
  939. if ( g_CaptionRepeats.CanEmitCaption( hash ) )
  940. {
  941. if ( bForceSubtitle )
  942. {
  943. CCSUsrMsg_CloseCaptionDirect msg;
  944. msg.set_hash( hash );
  945. msg.set_duration( clamp( (int)( duration * 10.0f ), 0, 65535 ) );
  946. msg.set_from_player( fromplayer ? 1 : 0 );
  947. // Send forced caption and duration hint down to client
  948. SendUserMessage( filterCopy, CS_UM_CloseCaptionDirect, msg );
  949. }
  950. else
  951. {
  952. CCSUsrMsg_CloseCaption msg;
  953. msg.set_hash( hash );
  954. msg.set_duration( clamp( (int)( duration * 10.0f ), 0, 65535 ) );
  955. msg.set_from_player( fromplayer ? 1 : 0 );
  956. // Send caption and duration hint down to client
  957. SendUserMessage( filterCopy, CS_UM_CloseCaption, msg );
  958. }
  959. }
  960. }
  961. #else
  962. // Direct dispatch
  963. CHudCloseCaption *cchud = GET_FULLSCREEN_HUDELEMENT( CHudCloseCaption );
  964. if ( cchud )
  965. {
  966. cchud->ProcessCaption( lowercase, duration, fromplayer );
  967. }
  968. #endif
  969. }
  970. }
  971. void EmitCloseCaption( IRecipientFilter& filter, int entindex, const CSoundParameters & params, const EmitSound_t & ep )
  972. {
  973. // Don't use dedicated closecaption ConVar since it will prevent remote clients from getting captions.
  974. // Okay to use it in SP, since it's the same ConVar, not the FCVAR_USERINFO one
  975. if ( gpGlobals->maxClients == 1 &&
  976. !g_pClosecaption->GetBool())
  977. {
  978. return;
  979. }
  980. bool bForceSubtitle = false;
  981. if ( TestSoundChar( params.soundname, CHAR_SUBTITLED ) )
  982. {
  983. bForceSubtitle = true;
  984. }
  985. if ( !bForceSubtitle && !ep.m_bEmitCloseCaption )
  986. {
  987. return;
  988. }
  989. // NOTE: We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing
  990. // all players from it!!!!
  991. CRecipientFilter filterCopy;
  992. filterCopy.CopyFrom( (CRecipientFilter &)filter );
  993. if ( !bForceSubtitle )
  994. {
  995. // Remove any players who don't want close captions
  996. CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( (CRecipientFilter &)filterCopy );
  997. }
  998. // Anyone left?
  999. if ( filterCopy.GetRecipientCount() <= 0 )
  1000. {
  1001. return;
  1002. }
  1003. float duration = 0.0f;
  1004. if ( ep.m_pflSoundDuration )
  1005. {
  1006. duration = *ep.m_pflSoundDuration;
  1007. }
  1008. else
  1009. {
  1010. duration = enginesound->GetSoundDuration( params.soundname );
  1011. }
  1012. bool fromplayer = false;
  1013. CBaseEntity *ent = CBaseEntity::Instance( entindex );
  1014. if ( ent )
  1015. {
  1016. while ( ent )
  1017. {
  1018. if ( ent->IsPlayer() )
  1019. {
  1020. fromplayer = true;
  1021. break;
  1022. }
  1023. ent = ent->GetOwnerEntity();
  1024. }
  1025. }
  1026. EmitCloseCaption( filter, entindex, fromplayer, ep.m_pSoundName, ep.m_UtlVecSoundOrigin, duration, ep.m_bWarnOnMissingCloseCaption, bForceSubtitle );
  1027. }
  1028. void EmitAmbientSoundAsEntry(CSoundParameters &params, int entindex, const Vector& origin, const char *soundname, float flVolume, int iFlags, int iPitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1029. {
  1030. EmitSound_t ep ;
  1031. ep.m_nChannel = CHAN_STATIC;
  1032. ep.m_pSoundName = soundname;
  1033. ep.m_flVolume = flVolume;
  1034. ep.m_SoundLevel = params.soundlevel;
  1035. ep.m_nFlags = iFlags;
  1036. ep.m_nPitch = iPitch;
  1037. ep.m_pOrigin = &origin;
  1038. ep.m_flSoundTime = soundtime;
  1039. ep.m_pflSoundDuration = duration;
  1040. // ep. m_bEmitCloseCaption = true;
  1041. // m_bWarnOnMissingCloseCaption = false;
  1042. // m_bWarnOnDirectWaveReference = false;
  1043. // m_nSpeakerEntity = -1;
  1044. // if sound is tagged as version 2 or higher this will be treated as a soundentry!
  1045. ep.m_hSoundScriptHash = params.m_hSoundScriptHash;
  1046. ep.m_nSoundEntryVersion = params.m_nSoundEntryVersion;
  1047. // send sound to all active players
  1048. CReliableBroadcastRecipientFilter filter;
  1049. EmitSoundByHandle( filter, entindex, ep, params.m_hSoundScriptHash );
  1050. }
  1051. void EmitAmbientSound( int entindex, const Vector& origin, const char *soundname, float flVolume, int iFlags, int iPitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1052. {
  1053. // Pull data from parameters
  1054. CSoundParameters params;
  1055. if ( !soundemitterbase->GetParametersForSound( soundname, params, GENDER_NONE ) )
  1056. {
  1057. return;
  1058. }
  1059. // hijack if it's a new style sound
  1060. if( params.m_hSoundScriptHash != SOUNDEMITTER_INVALID_HASH && params.m_nSoundEntryVersion > 1 )
  1061. {
  1062. EmitAmbientSoundAsEntry( params, entindex, origin, soundname, flVolume, iFlags, iPitch, soundtime, duration );
  1063. return;
  1064. }
  1065. if( iFlags & SND_CHANGE_PITCH )
  1066. {
  1067. params.pitch = iPitch;
  1068. }
  1069. if( iFlags & SND_CHANGE_VOL )
  1070. {
  1071. params.volume = flVolume;
  1072. }
  1073. #if defined( CLIENT_DLL )
  1074. enginesound->EmitAmbientSound( params.soundname, params.volume, params.pitch, iFlags, soundtime );
  1075. #else
  1076. engine->EmitAmbientSound(entindex, origin, params.soundname, params.volume, params.soundlevel, iFlags, params.pitch, soundtime );
  1077. #endif
  1078. bool needsCC = !( iFlags & ( SND_STOP | SND_CHANGE_VOL | SND_CHANGE_PITCH ) );
  1079. float soundduration = 0.0f;
  1080. if ( duration
  1081. #if defined( CLIENT_DLL )
  1082. || needsCC
  1083. #endif
  1084. )
  1085. {
  1086. soundduration = enginesound->GetSoundDuration( params.soundname );
  1087. if ( duration )
  1088. {
  1089. *duration = soundduration;
  1090. }
  1091. }
  1092. TraceEmitSound( entindex, "EmitAmbientSound: '%s' emitted as '%s' (ent %i)\n",
  1093. soundname, params.soundname, entindex );
  1094. // We only want to trigger the CC on the start of the sound, not on any changes or halting of the sound
  1095. if ( needsCC )
  1096. {
  1097. CRecipientFilter filter;
  1098. filter.AddAllPlayers();
  1099. filter.MakeReliable();
  1100. CUtlVector< Vector > dummy;
  1101. EmitCloseCaption( filter, entindex, false, soundname, dummy, soundduration, false );
  1102. }
  1103. }
  1104. void StopSoundByHandle( int entindex, const char *soundname, HSOUNDSCRIPTHASH& handle, bool bIsStoppingSpeakerSound = false )
  1105. {
  1106. if ( handle == SOUNDEMITTER_INVALID_HASH )
  1107. {
  1108. handle = soundemitterbase->HashSoundName( soundname );
  1109. }
  1110. int index = soundemitterbase->GetSoundIndexForHash( handle );
  1111. CSoundParametersInternal *params = soundemitterbase->InternalGetParametersForSound( index );
  1112. if ( !params )
  1113. {
  1114. return;
  1115. }
  1116. const char *pSoundEntryName = NULL;
  1117. if( params->GetSoundEntryVersion() > 1 &&
  1118. sv_soundemitter_version.GetInt() > 1 )
  1119. {
  1120. // reget original soundentry name
  1121. pSoundEntryName = soundemitterbase->GetSoundName( index );
  1122. enginesound->StopSound(
  1123. entindex,
  1124. params->GetChannel(),
  1125. pSoundEntryName,
  1126. handle );
  1127. TraceEmitSoundEntry( handle, pSoundEntryName, soundname );
  1128. }
  1129. // HACK: we have to stop all sounds if there are > 1 in the rndwave section...
  1130. int c = params->NumSoundNames();
  1131. for ( int i = 0; i < c; ++i )
  1132. {
  1133. char const *wavename = soundemitterbase->GetWaveName( params->GetSoundNames()[ i ].symbol );
  1134. Assert( wavename );
  1135. enginesound->StopSound(
  1136. entindex,
  1137. params->GetChannel(),
  1138. wavename );
  1139. TraceEmitSound( entindex, "StopSound: '%s' stopped as '%s' (ent %i)\n",
  1140. soundname, wavename, entindex );
  1141. #if !defined ( CLIENT_DLL )
  1142. if ( bIsStoppingSpeakerSound == false )
  1143. {
  1144. StopSpeakerSounds( wavename );
  1145. }
  1146. #endif // !CLIENT_DLL
  1147. }
  1148. }
  1149. void StopSound( int entindex, const char *soundname )
  1150. {
  1151. HSOUNDSCRIPTHASH hash = SOUNDEMITTER_INVALID_HASH;
  1152. StopSoundByHandle( entindex, soundname, hash );
  1153. }
  1154. void StopSound( int iEntIndex, int iChannel, const char *pSample, bool bIsStoppingSpeakerSound = false )
  1155. {
  1156. if ( pSample && ( Q_stristr( pSample, ".wav" ) || Q_stristr( pSample, ".mp3" ) || pSample[0] == '!' ) )
  1157. {
  1158. enginesound->StopSound( iEntIndex, iChannel, pSample );
  1159. TraceEmitSound( iEntIndex, "StopSound: Raw wave stopped '%s' (ent %i)\n",
  1160. pSample, iEntIndex );
  1161. #if !defined ( CLIENT_DLL )
  1162. if ( bIsStoppingSpeakerSound == false )
  1163. {
  1164. StopSpeakerSounds( pSample );
  1165. }
  1166. #endif // !CLIENT_DLL
  1167. }
  1168. else
  1169. {
  1170. // Look it up in sounds.txt and ignore other parameters
  1171. StopSound( iEntIndex, pSample );
  1172. }
  1173. }
  1174. void EmitAmbientSound( int entindex, const Vector &origin, const char *pSample, float volume, soundlevel_t soundlevel, int flags, int pitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1175. {
  1176. #if !defined( CLIENT_DLL )
  1177. CUtlVector< Vector > dummyorigins;
  1178. // Loop through all registered microphones and tell them the sound was just played
  1179. // NOTE: This means that pitch shifts/sound changes on the original ambient will not be reflected in the re-broadcasted sound
  1180. bool bSwallowed = CEnvMicrophone::OnSoundPlayed(
  1181. entindex,
  1182. pSample,
  1183. soundlevel,
  1184. volume,
  1185. flags,
  1186. pitch,
  1187. &origin,
  1188. soundtime,
  1189. dummyorigins );
  1190. if ( bSwallowed )
  1191. return;
  1192. #endif
  1193. if ( pSample && ( Q_stristr( pSample, ".wav" ) || Q_stristr( pSample, ".mp3" )) )
  1194. {
  1195. #if defined( CLIENT_DLL )
  1196. enginesound->EmitAmbientSound( pSample, volume, pitch, flags, soundtime );
  1197. #else
  1198. engine->EmitAmbientSound( entindex, origin, pSample, volume, soundlevel, flags, pitch, soundtime );
  1199. #endif
  1200. if ( duration )
  1201. {
  1202. *duration = enginesound->GetSoundDuration( pSample );
  1203. }
  1204. TraceEmitSound( entindex, "EmitAmbientSound: Raw wave emitted '%s' (ent %i)\n",
  1205. pSample, entindex );
  1206. }
  1207. else
  1208. {
  1209. EmitAmbientSound( entindex, origin, pSample, volume, flags, pitch, soundtime, duration );
  1210. }
  1211. }
  1212. #if !defined( CLIENT_DLL )
  1213. bool GetCaptionHash( char const *pchStringName, bool bWarnIfMissing, unsigned int &hash )
  1214. {
  1215. // hash the string, find in dictionary or return 0u if not there!!!
  1216. CUtlVector< AsyncCaption_t >& directories = m_ServerCaptions;
  1217. CaptionLookup_t search;
  1218. search.SetHash( pchStringName );
  1219. hash = search.hash;
  1220. int idx = -1;
  1221. int i;
  1222. int dc = directories.Count();
  1223. for ( i = 0; i < dc; ++i )
  1224. {
  1225. idx = directories[ i ].m_CaptionDirectory.Find( search );
  1226. if ( idx == directories[ i ].m_CaptionDirectory.InvalidIndex() )
  1227. continue;
  1228. break;
  1229. }
  1230. if ( i >= dc || idx == -1 )
  1231. {
  1232. if ( bWarnIfMissing && cc_showmissing.GetBool() )
  1233. {
  1234. static CUtlRBTree< unsigned int > s_MissingHashes( 0, 0, DefLessFunc( unsigned int ) );
  1235. if ( s_MissingHashes.Find( hash ) == s_MissingHashes.InvalidIndex() )
  1236. {
  1237. s_MissingHashes.Insert( hash );
  1238. Msg( "Missing caption for %s\n", pchStringName );
  1239. }
  1240. }
  1241. return false;
  1242. }
  1243. // Anything marked as L"" by content folks doesn't need to transmit either!!!
  1244. CaptionLookup_t &entry = directories[ i ].m_CaptionDirectory[ idx ];
  1245. if ( entry.length <= sizeof( wchar_t ) )
  1246. {
  1247. return false;
  1248. }
  1249. return true;
  1250. }
  1251. void StopSpeakerSounds( const char *wavename )
  1252. {
  1253. // Stop sound on any speakers playing this wav name
  1254. // but don't recurse in if this stopsound is happening on a speaker
  1255. CEnvMicrophone::OnSoundStopped( wavename );
  1256. }
  1257. #endif
  1258. };
  1259. static CSoundEmitterSystem g_SoundEmitterSystem( "CSoundEmitterSystem" );
  1260. IGameSystem *SoundEmitterSystem()
  1261. {
  1262. return &g_SoundEmitterSystem;
  1263. }
  1264. void SoundSystemPreloadSounds( void )
  1265. {
  1266. g_SoundEmitterSystem.PreloadSounds();
  1267. }
  1268. #if !defined( CLIENT_DLL )
  1269. #if defined( CLIENT_DLL )
  1270. CON_COMMAND_F( cl_soundemitter_flush, "Flushes the sounds.txt system (client only)", FCVAR_CHEAT )
  1271. #else
  1272. CON_COMMAND_F( sv_soundemitter_flush, "Flushes the sounds.txt system (server only)", FCVAR_DEVELOPMENTONLY )
  1273. #endif
  1274. {
  1275. #if !defined( CLIENT_DLL )
  1276. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1277. return;
  1278. #endif
  1279. // save the current soundscape
  1280. // kill the system
  1281. g_SoundEmitterSystem.Flush();
  1282. // Redo precache all wave files... (this should work now that we have dynamic string tables)
  1283. g_SoundEmitterSystem.LevelInitPreEntity();
  1284. // These store raw sound indices for faster precaching, blow them away.
  1285. ClearModelSoundsCache();
  1286. // TODO: when we go to a handle system, we'll need to invalidate handles somehow
  1287. }
  1288. CON_COMMAND_F( sv_soundemitter_filecheck, "Report missing wave files for sounds and game_sounds files.", FCVAR_DEVELOPMENTONLY )
  1289. {
  1290. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1291. return;
  1292. int missing = soundemitterbase->CheckForMissingWavFiles( true );
  1293. DevMsg( "---------------------------\nTotal missing files %i\n", missing );
  1294. }
  1295. //!!!HACK- Zoid 8/9/2009
  1296. //This hack is for L4D DLC2. We need to reload the soundemitter, but its reference counted by the
  1297. //client and the server, so we have to Shutdown() and Init() twice.
  1298. CON_COMMAND( sv_soundemitter_reload, "Flushes the sounds.txt system" )
  1299. {
  1300. // Reload server side captions
  1301. g_SoundEmitterSystem.LoadServerCaptions();
  1302. }
  1303. CON_COMMAND_F( sv_findsoundname, "Find sound names which reference the specified wave files.", FCVAR_DEVELOPMENTONLY )
  1304. {
  1305. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1306. return;
  1307. if ( args.ArgC() != 2 )
  1308. return;
  1309. int c = soundemitterbase->GetSoundCount();
  1310. int i;
  1311. char const *search = args[ 1 ];
  1312. if ( !search )
  1313. return;
  1314. for ( i = 0; i < c; i++ )
  1315. {
  1316. CSoundParametersInternal *internal = soundemitterbase->InternalGetParametersForSound( i );
  1317. if ( !internal )
  1318. continue;
  1319. int waveCount = internal->NumSoundNames();
  1320. if ( waveCount > 0 )
  1321. {
  1322. for( int wave = 0; wave < waveCount; wave++ )
  1323. {
  1324. char const *wavefilename = soundemitterbase->GetWaveName( internal->GetSoundNames()[ wave ].symbol );
  1325. if ( Q_stristr( wavefilename, search ) )
  1326. {
  1327. char const *soundname = soundemitterbase->GetSoundName( i );
  1328. char const *scriptname = soundemitterbase->GetSourceFileForSound( i );
  1329. Msg( "Referenced by '%s:%s' -- %s\n", scriptname, soundname, wavefilename );
  1330. }
  1331. }
  1332. }
  1333. }
  1334. }
  1335. CON_COMMAND_F( sv_soundemitter_spew, "Print details about a sound.", FCVAR_DEVELOPMENTONLY )
  1336. {
  1337. if ( args.ArgC() != 2 )
  1338. {
  1339. Msg( "Usage: soundemitter_spew < sndname >\n" );
  1340. return;
  1341. }
  1342. soundemitterbase->DescribeSound( args.Arg( 1 ) );
  1343. }
  1344. #else
  1345. //!!!HACK- Zoid 8/9/2009
  1346. //This hack is for L4D DLC2. We need to reload the soundemitter, but its reference counted by the
  1347. //client and the server, so we have to Shutdown() and Init() twice.
  1348. CON_COMMAND( cl_soundemitter_reload, "Flushes the sounds.txt system" )
  1349. {
  1350. // kill the system
  1351. g_SoundEmitterSystem.Shutdown();
  1352. // restart the system
  1353. g_SoundEmitterSystem.Init();
  1354. }
  1355. CON_COMMAND( cl_soundemitter_flush, "Flushes the sounds.txt system (server only)" )
  1356. {
  1357. // save the current soundscape
  1358. // kill the system
  1359. g_SoundEmitterSystem.Flush();
  1360. // Redo precache all wave files... (this should work now that we have dynamic string tables)
  1361. g_SoundEmitterSystem.LevelInitPreEntity();
  1362. // These store raw sound indices for faster precaching, blow them away.
  1363. // ClearModelSoundsCache();
  1364. // TODO: when we go to a handle system, we'll need to invalidate handles somehow
  1365. }
  1366. void Playgamesound_f( const CCommand &args )
  1367. {
  1368. CBasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1369. if ( pPlayer )
  1370. {
  1371. if ( args.ArgC() > 2 )
  1372. {
  1373. EmitSound_t params;
  1374. if ( !V_strcmp( args[2], "stop" ) )
  1375. {
  1376. pPlayer->StopSound( args[1] );
  1377. return;
  1378. }
  1379. ABS_QUERY_GUARD( true );
  1380. CBroadcastRecipientFilter filter;
  1381. Vector position = pPlayer->EyePosition();
  1382. Vector forward;
  1383. pPlayer->GetVectors( &forward, NULL, NULL );
  1384. position += atof( args[2] ) * forward;
  1385. params.m_pOrigin = &position;
  1386. params.m_pSoundName = args[1];
  1387. params.m_flVolume = 0.0f;
  1388. params.m_nPitch = 0;
  1389. g_SoundEmitterSystem.EmitSound( filter, 0, params );
  1390. }
  1391. else
  1392. {
  1393. pPlayer->EmitSound( args[1] );
  1394. }
  1395. }
  1396. else
  1397. {
  1398. Msg("Can't play until a game is started.\n");
  1399. // UNDONE: Make something like this work?
  1400. //CBroadcastRecipientFilter filter;
  1401. //g_SoundEmitterSystem.EmitSound( filter, 1, args[1], 0.0, 0, 0, &vec3_origin, 0, NULL );
  1402. }
  1403. }
  1404. static int GamesoundCompletion( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  1405. {
  1406. int current = 0;
  1407. const char *cmdname = "playgamesound";
  1408. char *substring = NULL;
  1409. int substringLen = 0;
  1410. if ( Q_strstr( partial, cmdname ) && strlen(partial) > strlen(cmdname) + 1 )
  1411. {
  1412. substring = (char *)partial + strlen( cmdname ) + 1;
  1413. substringLen = strlen(substring);
  1414. }
  1415. for ( int i = soundemitterbase->GetSoundCount()-1; i >= 0 && current < COMMAND_COMPLETION_MAXITEMS; i-- )
  1416. {
  1417. const char *pSoundName = soundemitterbase->GetSoundName( i );
  1418. if ( pSoundName )
  1419. {
  1420. if ( !substring || !Q_strncasecmp( pSoundName, substring, substringLen ) )
  1421. {
  1422. Q_snprintf( commands[ current ], sizeof( commands[ current ] ), "%s %s", cmdname, pSoundName );
  1423. current++;
  1424. }
  1425. }
  1426. }
  1427. return current;
  1428. }
  1429. static ConCommand Command_Playgamesound( "playgamesound", Playgamesound_f, "Play a sound from the game sounds txt file", FCVAR_CLIENTCMD_CAN_EXECUTE | FCVAR_SERVER_CAN_EXECUTE, GamesoundCompletion );
  1430. // --------------------------------------------------------------------
  1431. // snd_playsounds
  1432. //
  1433. // This a utility for testing sound values
  1434. // --------------------------------------------------------------------
  1435. static int GamesoundCompletion2( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  1436. {
  1437. int current = 0;
  1438. const char *cmdname = "snd_playsounds";
  1439. char *substring = NULL;
  1440. int substringLen = 0;
  1441. if ( Q_strstr( partial, cmdname ) && strlen(partial) > strlen(cmdname) + 1 )
  1442. {
  1443. substring = (char *)partial + strlen( cmdname ) + 1;
  1444. substringLen = strlen(substring);
  1445. }
  1446. for ( int i = soundemitterbase->GetSoundCount()-1; i >= 0 && current < COMMAND_COMPLETION_MAXITEMS; i-- )
  1447. {
  1448. const char *pSoundName = soundemitterbase->GetSoundName( i );
  1449. if ( pSoundName )
  1450. {
  1451. if ( !substring || !Q_strncasecmp( pSoundName, substring, substringLen ) )
  1452. {
  1453. Q_snprintf( commands[ current ], sizeof( commands[ current ] ), "%s %s", cmdname, pSoundName );
  1454. current++;
  1455. }
  1456. }
  1457. }
  1458. return current;
  1459. }
  1460. void S_PlaySounds( const CCommand &args )
  1461. {
  1462. CBasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1463. if ( pPlayer )
  1464. {
  1465. if ( args.ArgC() > 4 )
  1466. {
  1467. // Vector position = pPlayer->EyePosition();
  1468. Vector position;
  1469. // Vector forward;
  1470. // pPlayer->GetVectors( &forward, NULL, NULL );
  1471. // position += atof( args[2] ) * forward;
  1472. position[0] = atof( args[2] );
  1473. position[1] = atof( args[3] );
  1474. position[2] = atof( args[4] );
  1475. ABS_QUERY_GUARD( true );
  1476. CBroadcastRecipientFilter filter;
  1477. EmitSound_t params;
  1478. params.m_pSoundName = args[1];
  1479. params.m_pOrigin = &position;
  1480. params.m_flVolume = 0.0f;
  1481. params.m_nPitch = 0;
  1482. g_SoundEmitterSystem.EmitSound( filter, 0, params );
  1483. }
  1484. else
  1485. {
  1486. pPlayer->EmitSound( args[1] );
  1487. }
  1488. }
  1489. else
  1490. {
  1491. Msg("Can't play until a game is started.\n");
  1492. // UNDONE: Make something like this work?
  1493. //CBroadcastRecipientFilter filter;
  1494. //g_SoundEmitterSystem.EmitSound( filter, 1, args[1], 0.0, 0, 0, &vec3_origin, 0, NULL );
  1495. }
  1496. }
  1497. static ConCommand SND_PlaySounds( "snd_playsounds", S_PlaySounds, "Play sounds from the game sounds txt file at a given location", FCVAR_CHEAT | FCVAR_CLIENTCMD_CAN_EXECUTE | FCVAR_SERVER_CAN_EXECUTE, GamesoundCompletion2 );
  1498. static int GamesoundCompletion3( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  1499. {
  1500. int current = 0;
  1501. const char *cmdname = "snd_setsoundparam";
  1502. char *substring = NULL;
  1503. int substringLen = 0;
  1504. if ( Q_strstr( partial, cmdname ) && strlen(partial) > strlen(cmdname) + 1 )
  1505. {
  1506. substring = (char *)partial + strlen( cmdname ) + 1;
  1507. substringLen = strlen(substring);
  1508. }
  1509. for ( int i = soundemitterbase->GetSoundCount()-1; i >= 0 && current < COMMAND_COMPLETION_MAXITEMS; i-- )
  1510. {
  1511. const char *pSoundName = soundemitterbase->GetSoundName( i );
  1512. if ( pSoundName )
  1513. {
  1514. if ( !substring || !Q_strncasecmp( pSoundName, substring, substringLen ) )
  1515. {
  1516. Q_snprintf( commands[ current ], sizeof( commands[ current ] ), "%s %s", cmdname, pSoundName );
  1517. current++;
  1518. }
  1519. }
  1520. }
  1521. return current;
  1522. }
  1523. static void S_SetSoundParam( const CCommand &args )
  1524. {
  1525. if ( args.ArgC() != 4 )
  1526. {
  1527. DevMsg("Parameters: mix group name, [vol, mute, solo], value");
  1528. return;
  1529. }
  1530. const char *szSoundName = args[1];
  1531. const char *szparam = args[2];
  1532. const char *szValue = args[3];
  1533. // get the sound we're working on
  1534. int soundindex = soundemitterbase->GetSoundIndex( szSoundName);
  1535. if ( !soundemitterbase->IsValidIndex(soundindex) )
  1536. return;
  1537. // Look up the sound level from the soundemitter system
  1538. CSoundParametersInternal *soundparams = soundemitterbase->InternalGetParametersForSound( soundindex );
  1539. if ( !soundparams )
  1540. {
  1541. return;
  1542. }
  1543. // // See if it's writable, if not then bail
  1544. // char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
  1545. // if ( !scriptfile ||
  1546. // !filesystem->FileExists( scriptfile ) ||
  1547. // !filesystem->IsFileWritable( scriptfile ) )
  1548. // {
  1549. // return;
  1550. // }
  1551. // Copy the parameters
  1552. CSoundParametersInternal newparams;
  1553. newparams.CopyFrom( *soundparams );
  1554. if(!Q_stricmp("volume", szparam))
  1555. newparams.VolumeFromString( szValue);
  1556. else if(!Q_stricmp("level", szparam))
  1557. newparams.SoundLevelFromString( szValue );
  1558. // No change
  1559. if ( newparams == *soundparams )
  1560. {
  1561. return;
  1562. }
  1563. soundemitterbase->UpdateSoundParameters( szSoundName , newparams );
  1564. }
  1565. static ConCommand SND_SetSoundParam( "snd_setsoundparam", S_SetSoundParam, "Set a sound paramater", FCVAR_CLIENTCMD_CAN_EXECUTE | FCVAR_SERVER_CAN_EXECUTE, GamesoundCompletion3 );
  1566. #endif // CLIENT_DLL
  1567. //-----------------------------------------------------------------------------
  1568. // Purpose: Non-static override for doing the general case of CBroadcastRecipientFilter, and EmitSound( filter, entindex(), etc. );
  1569. // Input : *soundname -
  1570. //-----------------------------------------------------------------------------
  1571. int CBaseEntity::EmitSound( const char *soundname, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1572. {
  1573. //VPROF( "CBaseEntity::EmitSound" );
  1574. VPROF_BUDGET( "CBaseEntity::EmitSound", _T( "CBaseEntity::EmitSound" ) );
  1575. ABS_QUERY_GUARD( true );
  1576. CBroadcastRecipientFilter filter;
  1577. EmitSound_t params;
  1578. params.m_pSoundName = soundname;
  1579. params.m_flSoundTime = soundtime;
  1580. params.m_pflSoundDuration = duration;
  1581. params.m_bWarnOnDirectWaveReference = true;
  1582. return EmitSound( filter, entindex(), params );
  1583. }
  1584. //-----------------------------------------------------------------------------
  1585. // Purpose: Non-static override for doing the general case of CBroadcastRecipientFilter, and EmitSound( filter, entindex(), etc. );
  1586. // Input : *soundname -
  1587. //-----------------------------------------------------------------------------
  1588. int CBaseEntity::EmitSound( const char *soundname, HSOUNDSCRIPTHASH& handle, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1589. {
  1590. VPROF_BUDGET( "CBaseEntity::EmitSound", _T( "CBaseEntity::EmitSound" ) );
  1591. // VPROF( "CBaseEntity::EmitSound" );
  1592. ABS_QUERY_GUARD( true );
  1593. CBroadcastRecipientFilter filter;
  1594. EmitSound_t params;
  1595. params.m_pSoundName = soundname;
  1596. params.m_flSoundTime = soundtime;
  1597. params.m_pflSoundDuration = duration;
  1598. params.m_bWarnOnDirectWaveReference = true;
  1599. return EmitSound( filter, entindex(), params, handle );
  1600. }
  1601. #if !defined ( CLIENT_DLL )
  1602. void CBaseEntity::ScriptEmitSound( const char *soundname )
  1603. {
  1604. EmitSound( soundname );
  1605. }
  1606. void CBaseEntity::ScriptStopSound( const char *soundname )
  1607. {
  1608. StopSound( soundname );
  1609. }
  1610. float CBaseEntity::ScriptSoundDuration( const char *soundname, const char *actormodel )
  1611. {
  1612. float duration = CBaseEntity::GetSoundDuration( soundname, actormodel );
  1613. return duration;
  1614. }
  1615. #endif // !CLIENT
  1616. //-----------------------------------------------------------------------------
  1617. // Purpose:
  1618. // Input : filter -
  1619. // iEntIndex -
  1620. // *soundname -
  1621. // *pOrigin -
  1622. //-----------------------------------------------------------------------------
  1623. int CBaseEntity::EmitSound( IRecipientFilter& filter, int iEntIndex, const char *soundname, const Vector *pOrigin /*= NULL*/, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1624. {
  1625. VPROF_BUDGET( "CBaseEntity::EmitSound", _T( "CBaseEntity::EmitSound" ) );
  1626. // VPROF( "CBaseEntity::EmitSound" );
  1627. EmitSound_t params;
  1628. params.m_pSoundName = soundname;
  1629. params.m_flSoundTime = soundtime;
  1630. params.m_pOrigin = pOrigin;
  1631. params.m_pflSoundDuration = duration;
  1632. params.m_bWarnOnDirectWaveReference = true;
  1633. return EmitSound( filter, iEntIndex, params );
  1634. }
  1635. //-----------------------------------------------------------------------------
  1636. // Purpose:
  1637. // Input : filter -
  1638. // iEntIndex -
  1639. // *soundname -
  1640. // *pOrigin -
  1641. //-----------------------------------------------------------------------------
  1642. int CBaseEntity::EmitSound( IRecipientFilter& filter, int iEntIndex, const char *soundname, HSOUNDSCRIPTHASH& handle, const Vector *pOrigin /*= NULL*/, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1643. {
  1644. VPROF_BUDGET( "CBaseEntity::EmitSound", _T( "CBaseEntity::EmitSound" ) );
  1645. //VPROF( "CBaseEntity::EmitSound" );
  1646. EmitSound_t params;
  1647. params.m_pSoundName = soundname;
  1648. params.m_flSoundTime = soundtime;
  1649. params.m_pOrigin = pOrigin;
  1650. params.m_pflSoundDuration = duration;
  1651. params.m_bWarnOnDirectWaveReference = true;
  1652. return EmitSound( filter, iEntIndex, params, handle );
  1653. }
  1654. static void Helper_UpdateLastMadeNoiseTime( const IRecipientFilter &filter, int iEntIndex, const EmitSound_t &params )
  1655. {
  1656. CBaseEntity * pEnt = NULL;
  1657. #ifdef GAME_DLL
  1658. if ( ( filter.GetRecipientCount() > 1 ) ||
  1659. ( filter.GetRecipientCount() == 1 && filter.GetRecipientIndex( 0 ) != iEntIndex ) )
  1660. {
  1661. pEnt = UTIL_EntityByIndex( iEntIndex );
  1662. }
  1663. #else
  1664. pEnt = ClientEntityList().GetEnt( iEntIndex );
  1665. #endif
  1666. if ( pEnt )
  1667. pEnt->UpdateLastMadeNoiseTime( params.m_pSoundName );
  1668. }
  1669. //-----------------------------------------------------------------------------
  1670. // Purpose:
  1671. // Input : filter -
  1672. // iEntIndex -
  1673. // params -
  1674. //-----------------------------------------------------------------------------
  1675. int CBaseEntity::EmitSound( IRecipientFilter& filter, int iEntIndex, const EmitSound_t & params )
  1676. {
  1677. VPROF_BUDGET( "CBaseEntity::EmitSound", _T( "CBaseEntity::EmitSound" ) );
  1678. Helper_UpdateLastMadeNoiseTime( filter, iEntIndex, params );
  1679. // VPROF( "CBaseEntity::EmitSound" );
  1680. // Call into the sound emitter system...
  1681. return g_SoundEmitterSystem.EmitSound( filter, iEntIndex, params );
  1682. }
  1683. //-----------------------------------------------------------------------------
  1684. // Purpose:
  1685. // Input : filter -
  1686. // iEntIndex -
  1687. // params -
  1688. //-----------------------------------------------------------------------------
  1689. int CBaseEntity::EmitSound( IRecipientFilter& filter, int iEntIndex, const EmitSound_t & params, HSOUNDSCRIPTHASH& handle )
  1690. {
  1691. VPROF_BUDGET( "CBaseEntity::EmitSound", _T( "CBaseEntity::EmitSound" ) );
  1692. Helper_UpdateLastMadeNoiseTime( filter, iEntIndex, params );
  1693. // VPROF( "CBaseEntity::EmitSound" );
  1694. // Call into the sound emitter system...
  1695. return g_SoundEmitterSystem.EmitSoundByHandle( filter, iEntIndex, params, handle );
  1696. }
  1697. //-----------------------------------------------------------------------------
  1698. // Purpose:
  1699. // Input : *soundname -
  1700. //-----------------------------------------------------------------------------
  1701. void CBaseEntity::StopSound( const char *soundname )
  1702. {
  1703. #if defined( CLIENT_DLL )
  1704. if ( entindex() == -1 )
  1705. {
  1706. // If we're a clientside entity, we need to use the soundsourceindex instead of the entindex
  1707. StopSound( GetSoundSourceIndex(), soundname );
  1708. return;
  1709. }
  1710. #endif
  1711. StopSound( entindex(), soundname );
  1712. }
  1713. //-----------------------------------------------------------------------------
  1714. // Purpose:
  1715. // Input : *soundname -
  1716. //-----------------------------------------------------------------------------
  1717. void CBaseEntity::StopSound( const char *soundname, HSOUNDSCRIPTHASH& handle )
  1718. {
  1719. #if defined( CLIENT_DLL )
  1720. if ( entindex() == -1 )
  1721. {
  1722. // If we're a clientside entity, we need to use the soundsourceindex instead of the entindex
  1723. StopSound( GetSoundSourceIndex(), soundname );
  1724. return;
  1725. }
  1726. #endif
  1727. g_SoundEmitterSystem.StopSoundByHandle( entindex(), soundname, handle );
  1728. }
  1729. //-----------------------------------------------------------------------------
  1730. // Purpose:
  1731. // Input : iEntIndex -
  1732. // *soundname -
  1733. //-----------------------------------------------------------------------------
  1734. void CBaseEntity::StopSound( int iEntIndex, const char *soundname )
  1735. {
  1736. g_SoundEmitterSystem.StopSound( iEntIndex, soundname );
  1737. }
  1738. void CBaseEntity::StopSound( int iEntIndex, int iChannel, const char *pSample, bool bIsStoppingSpeakerSound )
  1739. {
  1740. g_SoundEmitterSystem.StopSound( iEntIndex, iChannel, pSample, bIsStoppingSpeakerSound );
  1741. }
  1742. soundlevel_t CBaseEntity::LookupSoundLevel( const char *soundname )
  1743. {
  1744. return soundemitterbase->LookupSoundLevel( soundname );
  1745. }
  1746. soundlevel_t CBaseEntity::LookupSoundLevel( const char *soundname, HSOUNDSCRIPTHASH& handle )
  1747. {
  1748. return soundemitterbase->LookupSoundLevelByHandle( soundname, handle );
  1749. }
  1750. //-----------------------------------------------------------------------------
  1751. // Purpose:
  1752. // Input : *entity -
  1753. // origin -
  1754. // flags -
  1755. // *soundname -
  1756. //-----------------------------------------------------------------------------
  1757. void CBaseEntity::EmitAmbientSound( int entindex, const Vector& origin, const char *soundname, int flags, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1758. {
  1759. g_SoundEmitterSystem.EmitAmbientSound( entindex, origin, soundname, 0.0, flags, 0, soundtime, duration );
  1760. }
  1761. // HACK HACK: Do we need to pull the entire SENTENCEG_* wrapper over to the client .dll?
  1762. #if defined( CLIENT_DLL )
  1763. int SENTENCEG_Lookup(const char *sample)
  1764. {
  1765. return engine->SentenceIndexFromName( sample + 1 );
  1766. }
  1767. #endif
  1768. void UTIL_EmitAmbientSound( int entindex, const Vector &vecOrigin, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float soundtime /*= 0.0f*/, float *duration /*=NULL*/ )
  1769. {
  1770. if (samp && *samp == '!')
  1771. {
  1772. int sentenceIndex = SENTENCEG_Lookup(samp);
  1773. if (sentenceIndex >= 0)
  1774. {
  1775. char name[32];
  1776. Q_snprintf( name, sizeof(name), "!%d", sentenceIndex );
  1777. #if !defined( CLIENT_DLL )
  1778. engine->EmitAmbientSound( entindex, vecOrigin, name, vol, soundlevel, fFlags, pitch, soundtime );
  1779. #else
  1780. enginesound->EmitAmbientSound( name, vol, pitch, fFlags, soundtime );
  1781. #endif
  1782. if ( duration )
  1783. {
  1784. *duration = enginesound->GetSoundDuration( name );
  1785. }
  1786. g_SoundEmitterSystem.TraceEmitSound( entindex, "UTIL_EmitAmbientSound: Sentence emitted '%s' (ent %i)\n",
  1787. name, entindex );
  1788. }
  1789. }
  1790. else
  1791. {
  1792. g_SoundEmitterSystem.EmitAmbientSound( entindex, vecOrigin, samp, vol, soundlevel, fFlags, pitch, soundtime, duration );
  1793. }
  1794. }
  1795. static const char *UTIL_TranslateSoundName( const char *soundname, const char *actormodel )
  1796. {
  1797. Assert( soundname );
  1798. if ( Q_stristr( soundname, ".wav" ) || Q_stristr( soundname, ".mp3" ) )
  1799. {
  1800. if ( Q_stristr( soundname, ".wav" ) )
  1801. {
  1802. WaveTrace( soundname, "UTIL_TranslateSoundName" );
  1803. }
  1804. return soundname;
  1805. }
  1806. return soundemitterbase->GetWavFileForSound( soundname, actormodel );
  1807. }
  1808. void CBaseEntity::GenderExpandString( char const *in, char *out, int maxlen )
  1809. {
  1810. soundemitterbase->GenderExpandString( STRING( GetModelName() ), in, out, maxlen );
  1811. }
  1812. bool CBaseEntity::GetParametersForSound( const char *soundname, CSoundParameters &params, const char *actormodel )
  1813. {
  1814. gender_t gender = soundemitterbase->GetActorGender( actormodel );
  1815. return soundemitterbase->GetParametersForSound( soundname, params, gender );
  1816. }
  1817. bool CBaseEntity::GetParametersForSound( const char *soundname, HSOUNDSCRIPTHASH& handle, CSoundParameters &params, const char *actormodel )
  1818. {
  1819. gender_t gender = soundemitterbase->GetActorGender( actormodel );
  1820. return soundemitterbase->GetParametersForSoundEx( soundname, handle, params, gender );
  1821. }
  1822. HSOUNDSCRIPTHASH CBaseEntity::PrecacheScriptSound( const char *soundname )
  1823. {
  1824. #if !defined( CLIENT_DLL )
  1825. return g_SoundEmitterSystem.PrecacheScriptSound( soundname );
  1826. #else
  1827. HSOUNDSCRIPTHASH hash = soundemitterbase->HashSoundName( soundname );
  1828. int soundIndex = soundemitterbase->GetSoundIndexForHash( hash );
  1829. if ( soundemitterbase->IsValidIndex( soundIndex ) )
  1830. return hash;
  1831. return SOUNDEMITTER_INVALID_HASH;
  1832. #endif
  1833. }
  1834. #if !defined ( CLIENT_DLL )
  1835. // Same as server version of above, but signiture changed so it can be deduced by the macros
  1836. void CBaseEntity::VScriptPrecacheScriptSound( const char *soundname )
  1837. {
  1838. g_SoundEmitterSystem.PrecacheScriptSound( soundname );
  1839. }
  1840. #endif // !CLIENT_DLL
  1841. void CBaseEntity::PrefetchScriptSound( const char *soundname )
  1842. {
  1843. g_SoundEmitterSystem.PrefetchScriptSound( soundname );
  1844. }
  1845. //-----------------------------------------------------------------------------
  1846. // Purpose:
  1847. // Input : *soundname -
  1848. // Output : float
  1849. //-----------------------------------------------------------------------------
  1850. float CBaseEntity::GetSoundDuration( const char *soundname, char const *actormodel )
  1851. {
  1852. return enginesound->GetSoundDuration( PSkipSoundChars( UTIL_TranslateSoundName( soundname, actormodel ) ) );
  1853. }
  1854. //-----------------------------------------------------------------------------
  1855. // Purpose:
  1856. // Input : filter -
  1857. // *token -
  1858. // duration -
  1859. // warnifmissing -
  1860. //-----------------------------------------------------------------------------
  1861. void CBaseEntity::EmitCloseCaption( IRecipientFilter& filter, int entindex, char const *token, CUtlVector< Vector >& soundorigin, float duration, bool warnifmissing /*= false*/ )
  1862. {
  1863. bool fromplayer = false;
  1864. CBaseEntity *ent = CBaseEntity::Instance( entindex );
  1865. while ( ent )
  1866. {
  1867. if ( ent->IsPlayer() )
  1868. {
  1869. fromplayer = true;
  1870. break;
  1871. }
  1872. ent = ent->GetOwnerEntity();
  1873. }
  1874. g_SoundEmitterSystem.EmitCloseCaption( filter, entindex, fromplayer, token, soundorigin, duration, warnifmissing );
  1875. }
  1876. //-----------------------------------------------------------------------------
  1877. // Purpose:
  1878. // Input : *name -
  1879. // preload -
  1880. // Output : Returns true on success, false on failure.
  1881. //-----------------------------------------------------------------------------
  1882. bool CBaseEntity::PrecacheSound( const char *name )
  1883. {
  1884. if ( IsPC() && !g_bPermitDirectSoundPrecache )
  1885. {
  1886. Warning( "Direct precache of %s\n", name );
  1887. }
  1888. // If this is out of order, warn
  1889. if ( !CBaseEntity::IsPrecacheAllowed() )
  1890. {
  1891. if ( !enginesound->IsSoundPrecached( name ) )
  1892. {
  1893. Assert( !"CBaseEntity::PrecacheSound: too late" );
  1894. Warning( "Late precache of %s\n", name );
  1895. }
  1896. }
  1897. bool bret = enginesound->PrecacheSound( name, true );
  1898. return bret;
  1899. }
  1900. //-----------------------------------------------------------------------------
  1901. // Purpose:
  1902. // Input : *name -
  1903. //-----------------------------------------------------------------------------
  1904. void CBaseEntity::PrefetchSound( const char *name )
  1905. {
  1906. enginesound->PrefetchSound( name );
  1907. }
  1908. #if !defined( CLIENT_DLL )
  1909. bool GetCaptionHash( char const *pchStringName, bool bWarnIfMissing, unsigned int &hash )
  1910. {
  1911. return g_SoundEmitterSystem.GetCaptionHash( pchStringName, bWarnIfMissing, hash );
  1912. }
  1913. bool CanEmitCaption( unsigned int hash )
  1914. {
  1915. return g_CaptionRepeats.CanEmitCaption( hash );
  1916. }
  1917. #endif