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.

453 lines
13 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "soundscape_system.h"
  8. #include "soundscape.h"
  9. #include "keyvalues.h"
  10. #include "filesystem.h"
  11. #include "game.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. #define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt"
  15. CON_COMMAND(soundscape_flush, "Flushes the server & client side soundscapes")
  16. {
  17. CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() );
  18. if ( engine->IsDedicatedServer() )
  19. {
  20. // If it's a dedicated server, only the server console can run this.
  21. if ( pPlayer )
  22. return;
  23. }
  24. else
  25. {
  26. // If it's a listen server, only the listen server host can run this.
  27. if ( !pPlayer || pPlayer != UTIL_GetListenServerHost() )
  28. return;
  29. }
  30. g_SoundscapeSystem.FlushSoundscapes(); // don't bother forgetting about the entities
  31. g_SoundscapeSystem.Init();
  32. if ( engine->IsDedicatedServer() )
  33. {
  34. // If the ds console typed it, send it to everyone.
  35. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  36. {
  37. CBasePlayer *pSendToPlayer = UTIL_PlayerByIndex( i );
  38. if ( pSendToPlayer )
  39. engine->ClientCommand( pSendToPlayer->edict(), "cl_soundscape_flush\n" );
  40. }
  41. }
  42. else
  43. {
  44. engine->ClientCommand( pPlayer->edict(), "cl_soundscape_flush\n" );
  45. }
  46. }
  47. CSoundscapeSystem g_SoundscapeSystem( "CSoundscapeSystem" );
  48. extern ConVar soundscape_debug;
  49. void CSoundscapeSystem::AddSoundscapeFile( const char *filename )
  50. {
  51. MEM_ALLOC_CREDIT();
  52. // Open the soundscape data file, and abort if we can't
  53. KeyValues *pKeyValuesData = new KeyValues( filename );
  54. if ( filesystem->LoadKeyValues( *pKeyValuesData, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) )
  55. {
  56. // parse out all of the top level sections and save their names
  57. KeyValues *pKeys = pKeyValuesData;
  58. while ( pKeys )
  59. {
  60. if ( pKeys->GetFirstSubKey() )
  61. {
  62. if ( g_pDeveloper->GetBool() )
  63. {
  64. if ( strstr( pKeys->GetName(), "{" ) )
  65. {
  66. Msg("Error parsing soundscape file %s after %s\n", filename, m_soundscapeCount>0 ?m_soundscapes.GetStringText( m_soundscapeCount-1 ) : "FIRST" );
  67. }
  68. }
  69. m_soundscapes.AddString( pKeys->GetName(), m_soundscapeCount );
  70. if ( IsGameConsole() )
  71. {
  72. AddSoundscapeSounds( pKeys, m_soundscapeCount );
  73. }
  74. m_soundscapeCount++;
  75. }
  76. pKeys = pKeys->GetNextKey();
  77. }
  78. }
  79. pKeyValuesData->deleteThis();
  80. }
  81. CON_COMMAND_F(sv_soundscape_printdebuginfo, "print soundscapes", FCVAR_CHEAT)
  82. {
  83. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  84. return;
  85. g_SoundscapeSystem.PrintDebugInfo();
  86. }
  87. void CSoundscapeSystem::PrintDebugInfo()
  88. {
  89. Msg( "\n------- SERVER SOUNDSCAPES -------\n" );
  90. for ( int key=m_soundscapes.First(); key != m_soundscapes.InvalidIndex(); key = m_soundscapes.Next( key ) )
  91. {
  92. int id = m_soundscapes.GetIDForKey( key );
  93. const char *pName = m_soundscapes.GetStringForKey( key );
  94. Msg( "- %d: %s\n", id, pName );
  95. }
  96. Msg( "-------- SOUNDSCAPE ENTITIES -----\n" );
  97. for( int entityIndex = 0; entityIndex < m_soundscapeEntities.Count(); ++entityIndex )
  98. {
  99. CEnvSoundscape *currentSoundscape = m_soundscapeEntities[entityIndex];
  100. Msg("- %d: %s x:%.4f y:%.4f z:%.4f\n",
  101. entityIndex,
  102. STRING(currentSoundscape->GetSoundscapeName()),
  103. currentSoundscape->GetAbsOrigin().x,
  104. currentSoundscape->GetAbsOrigin().y,
  105. currentSoundscape->GetAbsOrigin().z
  106. );
  107. }
  108. Msg( "----------------------------------\n\n" );
  109. }
  110. bool CSoundscapeSystem::Init()
  111. {
  112. m_soundscapeCount = 0;
  113. const char *mapname = STRING( gpGlobals->mapname );
  114. const char *mapSoundscapeFilename = NULL;
  115. if ( mapname && *mapname )
  116. {
  117. mapSoundscapeFilename = UTIL_VarArgs( "scripts/soundscapes_%s.txt", V_GetFileName( mapname ) );
  118. }
  119. KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE );
  120. if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) )
  121. {
  122. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  123. {
  124. if ( !Q_stricmp( sub->GetName(), "file" ) )
  125. {
  126. // Add
  127. AddSoundscapeFile( sub->GetString() );
  128. if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) )
  129. {
  130. mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape
  131. }
  132. continue;
  133. }
  134. Warning( "CSoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
  135. SOUNDSCAPE_MANIFEST_FILE, sub->GetName() );
  136. }
  137. if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) )
  138. {
  139. AddSoundscapeFile( mapSoundscapeFilename );
  140. }
  141. }
  142. else
  143. {
  144. Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE );
  145. }
  146. manifest->deleteThis();
  147. m_activeIndex = 0;
  148. return true;
  149. }
  150. void CSoundscapeSystem::FlushSoundscapes( void )
  151. {
  152. m_soundscapeCount = 0;
  153. m_soundscapes.ClearStrings();
  154. }
  155. void CSoundscapeSystem::Shutdown()
  156. {
  157. FlushSoundscapes();
  158. m_soundscapeEntities.RemoveAll();
  159. m_activeIndex = 0;
  160. if ( IsGameConsole() )
  161. {
  162. m_soundscapeSounds.Purge();
  163. }
  164. }
  165. void CSoundscapeSystem::LevelInitPreEntity()
  166. {
  167. g_SoundscapeSystem.Shutdown();
  168. g_SoundscapeSystem.Init();
  169. }
  170. void CSoundscapeSystem::LevelInitPostEntity()
  171. {
  172. if ( IsGameConsole() )
  173. {
  174. m_soundscapeSounds.Purge();
  175. }
  176. CUtlVector<bbox_t> clusterbounds;
  177. int clusterCount = engine->GetClusterCount();
  178. clusterbounds.SetCount( clusterCount );
  179. engine->GetAllClusterBounds( clusterbounds.Base(), clusterCount );
  180. m_soundscapesInCluster.SetCount(clusterCount);
  181. for ( int i = 0; i < clusterCount; i++ )
  182. {
  183. m_soundscapesInCluster[i].soundscapeCount = 0;
  184. m_soundscapesInCluster[i].firstSoundscape = 0;
  185. }
  186. unsigned char myPVS[16 * 1024];
  187. CUtlVector<short> clusterIndexList;
  188. CUtlVector<short> soundscapeIndexList;
  189. // find the clusters visible from each soundscape
  190. // add this soundscape to the list of soundscapes for that cluster, clip cluster bounds to radius
  191. for ( int i = 0; i < m_soundscapeEntities.Count(); i++ )
  192. {
  193. Vector position = m_soundscapeEntities[i]->GetAbsOrigin();
  194. float radius = m_soundscapeEntities[i]->m_flRadius;
  195. float radiusSq = radius * radius;
  196. engine->GetPVSForCluster( engine->GetClusterForOrigin( position ), sizeof( myPVS ), myPVS );
  197. for ( int j = 0; j < clusterCount; j++ )
  198. {
  199. if ( myPVS[ j >> 3 ] & (1<<(j&7)) )
  200. {
  201. float distSq = CalcSqrDistanceToAABB( clusterbounds[j].mins, clusterbounds[j].maxs, position );
  202. if ( distSq < radiusSq )
  203. {
  204. m_soundscapesInCluster[j].soundscapeCount++;
  205. clusterIndexList.AddToTail(j);
  206. // UNDONE: Technically you just need a soundscape index and a count for this list.
  207. soundscapeIndexList.AddToTail(i);
  208. }
  209. }
  210. }
  211. }
  212. // basically this part is like a radix sort
  213. // this is how many entries we need in the soundscape index list
  214. m_soundscapeIndexList.SetCount(soundscapeIndexList.Count());
  215. // now compute the starting index of each cluster
  216. int firstSoundscape = 0;
  217. for ( int i = 0; i < clusterCount; i++ )
  218. {
  219. m_soundscapesInCluster[i].firstSoundscape = firstSoundscape;
  220. firstSoundscape += m_soundscapesInCluster[i].soundscapeCount;
  221. m_soundscapesInCluster[i].soundscapeCount = 0;
  222. }
  223. // now add each soundscape index to the appropriate cluster's list
  224. // The resulting list is precomputing all soundscapes that need to be checked for a player
  225. // in each cluster. This is used to accelerate the per-frame operations
  226. for ( int i = 0; i < soundscapeIndexList.Count(); i++ )
  227. {
  228. int cluster = clusterIndexList[i];
  229. int outIndex = m_soundscapesInCluster[cluster].soundscapeCount + m_soundscapesInCluster[cluster].firstSoundscape;
  230. m_soundscapesInCluster[cluster].soundscapeCount++;
  231. m_soundscapeIndexList[outIndex] = soundscapeIndexList[i];
  232. }
  233. }
  234. int CSoundscapeSystem::GetSoundscapeIndex( const char *pName )
  235. {
  236. return m_soundscapes.GetStringID( pName );
  237. }
  238. bool CSoundscapeSystem::IsValidIndex( int index )
  239. {
  240. if ( index >= 0 && index < m_soundscapeCount )
  241. return true;
  242. return false;
  243. }
  244. void CSoundscapeSystem::AddSoundscapeEntity( CEnvSoundscape *pSoundscape )
  245. {
  246. if ( m_soundscapeEntities.Find( pSoundscape ) == -1 )
  247. {
  248. int index = m_soundscapeEntities.AddToTail( pSoundscape );
  249. pSoundscape->m_soundscapeEntityId = index + 1;
  250. }
  251. }
  252. void CSoundscapeSystem::RemoveSoundscapeEntity( CEnvSoundscape *pSoundscape )
  253. {
  254. m_soundscapeEntities.FindAndRemove( pSoundscape );
  255. pSoundscape->m_soundscapeEntityId = -1;
  256. }
  257. void CSoundscapeSystem::FrameUpdatePostEntityThink()
  258. {
  259. int total = m_soundscapeEntities.Count();
  260. if ( total > 0 )
  261. {
  262. int traceCount = 0;
  263. int playerCount = 0;
  264. // budget to do 2 players with 2 traces each tick. Anything more expensive than that should
  265. // get spread across multiple ticks
  266. int maxPlayers = 2;
  267. int maxTraces = maxPlayers * 2;
  268. if ( soundscape_debug.GetBool() )
  269. {
  270. maxTraces = 9999;
  271. maxPlayers = MAX_PLAYERS;
  272. }
  273. // load balance across server ticks a bit by limiting the numbers of players (get cluster for origin)
  274. // and traces processed in a single tick. In single player this will update the player every tick
  275. // because it always does at least one player's full load of work
  276. for ( int i = 0; i < gpGlobals->maxClients && traceCount <= maxTraces && playerCount <= maxPlayers; i++ )
  277. {
  278. m_activeIndex = (m_activeIndex+1) % gpGlobals->maxClients;
  279. CBasePlayer *pPlayer = UTIL_PlayerByIndex( m_activeIndex + 1 );
  280. if ( pPlayer && pPlayer->IsNetClient() )
  281. {
  282. // check to see if this is the sound entity that is
  283. // currently affecting this player
  284. audioparams_t &audio = pPlayer->GetAudioParams();
  285. // if we got this far, we're looking at an entity that is contending
  286. // for current player sound. the closest entity to player wins.
  287. CEnvSoundscape *pCurrent = NULL;
  288. if ( audio.entIndex > 0 && audio.entIndex <= m_soundscapeEntities.Count() )
  289. {
  290. int ssIndex = audio.entIndex - 1;
  291. pCurrent = m_soundscapeEntities[ssIndex];
  292. }
  293. ss_update_t update;
  294. update.pPlayer = pPlayer;
  295. update.pCurrentSoundscape = pCurrent;
  296. update.playerPosition = pPlayer->EarPosition();
  297. update.bInRange = false;
  298. update.currentDistance = 0;
  299. update.traceCount = 0;
  300. if ( pCurrent )
  301. {
  302. pCurrent->UpdateForPlayer(update);
  303. }
  304. int clusterIndex = engine->GetClusterForOrigin( update.playerPosition );
  305. if ( clusterIndex >= 0 && clusterIndex < m_soundscapesInCluster.Count() )
  306. {
  307. // find all soundscapes that could possibly attach to this player and update them
  308. for ( int j = 0; j < m_soundscapesInCluster[clusterIndex].soundscapeCount; j++ )
  309. {
  310. int ssIndex = m_soundscapeIndexList[m_soundscapesInCluster[clusterIndex].firstSoundscape + j];
  311. if ( m_soundscapeEntities[ssIndex] == update.pCurrentSoundscape )
  312. continue;
  313. m_soundscapeEntities[ssIndex]->UpdateForPlayer( update );
  314. }
  315. }
  316. playerCount++;
  317. traceCount += update.traceCount;
  318. }
  319. }
  320. }
  321. }
  322. void CSoundscapeSystem::AddSoundscapeSounds( KeyValues *pSoundscape, int soundscapeIndex )
  323. {
  324. if ( !IsGameConsole() )
  325. {
  326. return;
  327. }
  328. int i = m_soundscapeSounds.AddToTail();
  329. Assert( i == soundscapeIndex );
  330. KeyValues *pKey = pSoundscape->GetFirstSubKey();
  331. while ( pKey )
  332. {
  333. if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
  334. {
  335. KeyValues *pAmbientKey = pKey->GetFirstSubKey();
  336. while ( pAmbientKey )
  337. {
  338. if ( !Q_strcasecmp( pAmbientKey->GetName(), "wave" ) )
  339. {
  340. char const *pSoundName = pAmbientKey->GetString();
  341. m_soundscapeSounds[i].AddToTail( pSoundName );
  342. }
  343. pAmbientKey = pAmbientKey->GetNextKey();
  344. }
  345. }
  346. else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
  347. {
  348. KeyValues *pRandomKey = pKey->GetFirstSubKey();
  349. while ( pRandomKey )
  350. {
  351. if ( !Q_strcasecmp( pRandomKey->GetName(), "rndwave" ) )
  352. {
  353. KeyValues *pRndWaveKey = pRandomKey->GetFirstSubKey();
  354. while ( pRndWaveKey )
  355. {
  356. if ( !Q_strcasecmp( pRndWaveKey->GetName(), "wave" ) )
  357. {
  358. char const *pSoundName = pRndWaveKey->GetString();
  359. m_soundscapeSounds[i].AddToTail( pSoundName );
  360. }
  361. pRndWaveKey = pRndWaveKey->GetNextKey();
  362. }
  363. }
  364. pRandomKey = pRandomKey->GetNextKey();
  365. }
  366. }
  367. else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) )
  368. {
  369. KeyValues *pPlayKey = pKey->GetFirstSubKey();
  370. while ( pPlayKey )
  371. {
  372. if ( !Q_strcasecmp( pPlayKey->GetName(), "name" ) )
  373. {
  374. char const *pSoundName = pPlayKey->GetString();
  375. m_soundscapeSounds[i].AddToTail( pSoundName );
  376. }
  377. pPlayKey = pPlayKey->GetNextKey();
  378. }
  379. }
  380. pKey = pKey->GetNextKey();
  381. }
  382. }
  383. void CSoundscapeSystem::PrecacheSounds( int soundscapeIndex )
  384. {
  385. if ( !IsGameConsole() )
  386. {
  387. return;
  388. }
  389. if ( !IsValidIndex( soundscapeIndex ) )
  390. {
  391. return;
  392. }
  393. int count = m_soundscapeSounds[soundscapeIndex].Count();
  394. for ( int i=0; i<count; i++ )
  395. {
  396. const char *pSound = m_soundscapeSounds[soundscapeIndex][i];
  397. if ( Q_stristr( pSound, ".wav" ) )
  398. {
  399. CBaseEntity::PrecacheSound( pSound );
  400. }
  401. else
  402. {
  403. // recurse into new soundscape
  404. PrecacheSounds( GetSoundscapeIndex( pSound ) );
  405. }
  406. }
  407. }