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.

458 lines
13 KiB

  1. //========= Copyright 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 ( IsX360() )
  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_DEVELOPMENTONLY )
  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.Size(); ++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", 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 ( IsX360() )
  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 ( IsX360() )
  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 || radius < 0 )
  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 tuned for TF. Do a max of 20 traces. That's going to happen anyway because a bunch of the maps
  265. // use radius -1 for all soundscapes. So to trace one player you'll often need that many and this code must
  266. // always trace one player's soundscapes.
  267. // If the map has been optimized, then allow more players to update per frame.
  268. int maxPlayers = gpGlobals->maxClients / 2;
  269. // maxPlayers has to be at least 1
  270. maxPlayers = MAX( 1, maxPlayers );
  271. int maxTraces = 20;
  272. if ( soundscape_debug.GetBool() )
  273. {
  274. maxTraces = 9999;
  275. maxPlayers = MAX_PLAYERS;
  276. }
  277. // load balance across server ticks a bit by limiting the numbers of players (get cluster for origin)
  278. // and traces processed in a single tick. In single player this will update the player every tick
  279. // because it always does at least one player's full load of work
  280. for ( int i = 0; i < gpGlobals->maxClients && traceCount <= maxTraces && playerCount <= maxPlayers; i++ )
  281. {
  282. m_activeIndex = (m_activeIndex+1) % gpGlobals->maxClients;
  283. CBasePlayer *pPlayer = UTIL_PlayerByIndex( m_activeIndex + 1 );
  284. if ( pPlayer && pPlayer->IsNetClient() )
  285. {
  286. // check to see if this is the sound entity that is
  287. // currently affecting this player
  288. audioparams_t &audio = pPlayer->GetAudioParams();
  289. // if we got this far, we're looking at an entity that is contending
  290. // for current player sound. the closest entity to player wins.
  291. CEnvSoundscape *pCurrent = (CEnvSoundscape *)( audio.ent.Get() );
  292. if ( pCurrent )
  293. {
  294. int nEntIndex = pCurrent->m_soundscapeEntityId - 1;
  295. NOTE_UNUSED( nEntIndex );
  296. Assert( m_soundscapeEntities[nEntIndex] == pCurrent );
  297. }
  298. ss_update_t update;
  299. update.pPlayer = pPlayer;
  300. update.pCurrentSoundscape = pCurrent;
  301. update.playerPosition = pPlayer->EarPosition();
  302. update.bInRange = false;
  303. update.currentDistance = 0;
  304. update.traceCount = 0;
  305. if ( pCurrent )
  306. {
  307. pCurrent->UpdateForPlayer(update);
  308. }
  309. int clusterIndex = engine->GetClusterForOrigin( update.playerPosition );
  310. if ( clusterIndex >= 0 && clusterIndex < m_soundscapesInCluster.Count() )
  311. {
  312. // find all soundscapes that could possibly attach to this player and update them
  313. for ( int j = 0; j < m_soundscapesInCluster[clusterIndex].soundscapeCount; j++ )
  314. {
  315. int ssIndex = m_soundscapeIndexList[m_soundscapesInCluster[clusterIndex].firstSoundscape + j];
  316. if ( m_soundscapeEntities[ssIndex] == update.pCurrentSoundscape )
  317. continue;
  318. m_soundscapeEntities[ssIndex]->UpdateForPlayer( update );
  319. }
  320. }
  321. playerCount++;
  322. traceCount += update.traceCount;
  323. }
  324. }
  325. }
  326. }
  327. void CSoundscapeSystem::AddSoundscapeSounds( KeyValues *pSoundscape, int soundscapeIndex )
  328. {
  329. if ( !IsX360() )
  330. {
  331. return;
  332. }
  333. int i = m_soundscapeSounds.AddToTail();
  334. Assert( i == soundscapeIndex );
  335. KeyValues *pKey = pSoundscape->GetFirstSubKey();
  336. while ( pKey )
  337. {
  338. if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
  339. {
  340. KeyValues *pAmbientKey = pKey->GetFirstSubKey();
  341. while ( pAmbientKey )
  342. {
  343. if ( !Q_strcasecmp( pAmbientKey->GetName(), "wave" ) )
  344. {
  345. char const *pSoundName = pAmbientKey->GetString();
  346. m_soundscapeSounds[i].AddToTail( pSoundName );
  347. }
  348. pAmbientKey = pAmbientKey->GetNextKey();
  349. }
  350. }
  351. else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
  352. {
  353. KeyValues *pRandomKey = pKey->GetFirstSubKey();
  354. while ( pRandomKey )
  355. {
  356. if ( !Q_strcasecmp( pRandomKey->GetName(), "rndwave" ) )
  357. {
  358. KeyValues *pRndWaveKey = pRandomKey->GetFirstSubKey();
  359. while ( pRndWaveKey )
  360. {
  361. if ( !Q_strcasecmp( pRndWaveKey->GetName(), "wave" ) )
  362. {
  363. char const *pSoundName = pRndWaveKey->GetString();
  364. m_soundscapeSounds[i].AddToTail( pSoundName );
  365. }
  366. pRndWaveKey = pRndWaveKey->GetNextKey();
  367. }
  368. }
  369. pRandomKey = pRandomKey->GetNextKey();
  370. }
  371. }
  372. else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) )
  373. {
  374. KeyValues *pPlayKey = pKey->GetFirstSubKey();
  375. while ( pPlayKey )
  376. {
  377. if ( !Q_strcasecmp( pPlayKey->GetName(), "name" ) )
  378. {
  379. char const *pSoundName = pPlayKey->GetString();
  380. m_soundscapeSounds[i].AddToTail( pSoundName );
  381. }
  382. pPlayKey = pPlayKey->GetNextKey();
  383. }
  384. }
  385. pKey = pKey->GetNextKey();
  386. }
  387. }
  388. void CSoundscapeSystem::PrecacheSounds( int soundscapeIndex )
  389. {
  390. if ( !IsX360() )
  391. {
  392. return;
  393. }
  394. if ( !IsValidIndex( soundscapeIndex ) )
  395. {
  396. return;
  397. }
  398. int count = m_soundscapeSounds[soundscapeIndex].Count();
  399. for ( int i=0; i<count; i++ )
  400. {
  401. const char *pSound = m_soundscapeSounds[soundscapeIndex][i];
  402. if ( Q_stristr( pSound, ".wav" ) )
  403. {
  404. CBaseEntity::PrecacheSound( pSound );
  405. }
  406. else
  407. {
  408. // recurse into new soundscape
  409. PrecacheSounds( GetSoundscapeIndex( pSound ) );
  410. }
  411. }
  412. }