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.

808 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "soundent.h"
  10. #include "game.h"
  11. #include "world.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. // Some enumerations needed by CSoundEnt
  16. //-----------------------------------------------------------------------------
  17. // identifiers passed to functions that can operate on either list, to indicate which list to operate on.
  18. #define SOUNDLISTTYPE_FREE 1
  19. #define SOUNDLISTTYPE_ACTIVE 2
  20. LINK_ENTITY_TO_CLASS( soundent, CSoundEnt );
  21. static CSoundEnt *g_pSoundEnt = NULL;
  22. BEGIN_SIMPLE_DATADESC( CSound )
  23. DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
  24. DEFINE_FIELD( m_iVolume, FIELD_INTEGER ),
  25. DEFINE_FIELD( m_flOcclusionScale, FIELD_FLOAT ),
  26. DEFINE_FIELD( m_iType, FIELD_INTEGER ),
  27. // DEFINE_FIELD( m_iNextAudible, FIELD_INTEGER ),
  28. DEFINE_FIELD( m_bNoExpirationTime, FIELD_BOOLEAN ),
  29. DEFINE_FIELD( m_flExpireTime, FIELD_TIME ),
  30. DEFINE_FIELD( m_iNext, FIELD_SHORT ),
  31. DEFINE_FIELD( m_ownerChannelIndex, FIELD_INTEGER ),
  32. DEFINE_FIELD( m_vecOrigin, FIELD_POSITION_VECTOR ),
  33. DEFINE_FIELD( m_bHasOwner, FIELD_BOOLEAN ),
  34. // DEFINE_FIELD( m_iMyIndex, FIELD_INTEGER ),
  35. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  36. END_DATADESC()
  37. //=========================================================
  38. // CSound - Clear - zeros all fields for a sound
  39. //=========================================================
  40. void CSound::Clear ( void )
  41. {
  42. m_vecOrigin = vec3_origin;
  43. m_iType = 0;
  44. m_iVolume = 0;
  45. m_flOcclusionScale = 0;
  46. m_flExpireTime = 0;
  47. m_bNoExpirationTime = false;
  48. m_iNext = SOUNDLIST_EMPTY;
  49. m_iNextAudible = 0;
  50. }
  51. //=========================================================
  52. // Reset - clears the volume, origin, and type for a sound,
  53. // but doesn't expire or unlink it.
  54. //=========================================================
  55. void CSound::Reset ( void )
  56. {
  57. m_vecOrigin = vec3_origin;
  58. m_iType = 0;
  59. m_iVolume = 0;
  60. m_iNext = SOUNDLIST_EMPTY;
  61. }
  62. //=========================================================
  63. // FIsSound - returns true if the sound is an Audible sound
  64. //=========================================================
  65. bool CSound::FIsSound ( void )
  66. {
  67. switch( SoundTypeNoContext() )
  68. {
  69. case SOUND_COMBAT:
  70. case SOUND_WORLD:
  71. case SOUND_PLAYER:
  72. case SOUND_DANGER:
  73. case SOUND_DANGER_SNIPERONLY:
  74. case SOUND_THUMPER:
  75. case SOUND_BULLET_IMPACT:
  76. case SOUND_BUGBAIT:
  77. case SOUND_PHYSICS_DANGER:
  78. case SOUND_MOVE_AWAY:
  79. case SOUND_PLAYER_VEHICLE:
  80. return true;
  81. default:
  82. return false;
  83. }
  84. }
  85. //=========================================================
  86. // FIsScent - returns true if the sound is actually a scent
  87. // do we really need this function? If a sound isn't a sound,
  88. // it must be a scent. (sjb)
  89. //=========================================================
  90. bool CSound::FIsScent ( void )
  91. {
  92. switch( m_iType )
  93. {
  94. case SOUND_CARCASS:
  95. case SOUND_MEAT:
  96. case SOUND_GARBAGE:
  97. return true;
  98. default:
  99. return false;
  100. }
  101. }
  102. //---------------------------------------------------------
  103. // This function returns the spot the listener should be
  104. // interested in if he hears the sound. MOST of the time,
  105. // this spot is the same as the sound's origin. But sometimes
  106. // (like with bullet impacts) the entity that owns the
  107. // sound is more interesting than the actual location of the
  108. // sound effect.
  109. //---------------------------------------------------------
  110. const Vector &CSound::GetSoundReactOrigin( void )
  111. {
  112. // Check pure types.
  113. switch( m_iType )
  114. {
  115. case SOUND_BULLET_IMPACT:
  116. case SOUND_PHYSICS_DANGER:
  117. if( m_hOwner.Get() != NULL )
  118. {
  119. // We really want the origin of this sound's
  120. // owner.
  121. return m_hOwner->GetAbsOrigin();
  122. }
  123. else
  124. {
  125. // If the owner is somehow invalid, we'll settle
  126. // for the sound's origin rather than a crash.
  127. return GetSoundOrigin();
  128. }
  129. break;
  130. }
  131. if( m_iType & SOUND_CONTEXT_REACT_TO_SOURCE )
  132. {
  133. if( m_hOwner.Get() != NULL )
  134. {
  135. return m_hOwner->GetAbsOrigin();
  136. }
  137. }
  138. // Check for types with additional context.
  139. if( m_iType & SOUND_DANGER )
  140. {
  141. if( (m_iType & SOUND_CONTEXT_FROM_SNIPER) )
  142. {
  143. if( m_hOwner.Get() != NULL )
  144. {
  145. // Be afraid of the sniper's location, not where the bullet will hit.
  146. return m_hOwner->GetAbsOrigin();
  147. }
  148. else
  149. {
  150. return GetSoundOrigin();
  151. }
  152. }
  153. }
  154. return GetSoundOrigin();
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Save/load
  158. //-----------------------------------------------------------------------------
  159. BEGIN_DATADESC( CSoundEnt )
  160. DEFINE_FIELD( m_iFreeSound, FIELD_INTEGER ),
  161. DEFINE_FIELD( m_iActiveSound, FIELD_INTEGER ),
  162. DEFINE_FIELD( m_cLastActiveSounds, FIELD_INTEGER ),
  163. DEFINE_EMBEDDED_ARRAY( m_SoundPool, MAX_WORLD_SOUNDS_SP ),
  164. END_DATADESC()
  165. //-----------------------------------------------------------------------------
  166. // Class factory methods
  167. //-----------------------------------------------------------------------------
  168. bool CSoundEnt::InitSoundEnt()
  169. {
  170. ///!!!LATER - do we want a sound ent in deathmatch? (sjb)
  171. g_pSoundEnt = (CSoundEnt*)CBaseEntity::Create( "soundent", vec3_origin, vec3_angle, GetWorldEntity() );
  172. if ( !g_pSoundEnt )
  173. {
  174. Warning( "**COULD NOT CREATE SOUNDENT**\n" );
  175. return false;
  176. }
  177. g_pSoundEnt->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );
  178. return true;
  179. }
  180. void CSoundEnt::ShutdownSoundEnt()
  181. {
  182. if ( g_pSoundEnt )
  183. {
  184. g_pSoundEnt->FreeList();
  185. g_pSoundEnt = NULL;
  186. }
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Construction, destruction
  190. //-----------------------------------------------------------------------------
  191. CSoundEnt::CSoundEnt()
  192. {
  193. }
  194. CSoundEnt::~CSoundEnt()
  195. {
  196. }
  197. //=========================================================
  198. // Spawn
  199. //=========================================================
  200. void CSoundEnt::Spawn( void )
  201. {
  202. SetSolid( SOLID_NONE );
  203. Initialize();
  204. SetNextThink( gpGlobals->curtime + 1 );
  205. }
  206. void CSoundEnt::OnRestore()
  207. {
  208. BaseClass::OnRestore();
  209. // Make sure the singleton points to the restored version of this.
  210. if ( g_pSoundEnt )
  211. {
  212. Assert( g_pSoundEnt != this );
  213. UTIL_Remove( g_pSoundEnt );
  214. }
  215. g_pSoundEnt = this;
  216. }
  217. //=========================================================
  218. // Think - at interval, the entire active sound list is checked
  219. // for sounds that have ExpireTimes less than or equal
  220. // to the current world time, and these sounds are deallocated.
  221. //=========================================================
  222. void CSoundEnt::Think ( void )
  223. {
  224. int iSound;
  225. int iPreviousSound;
  226. SetNextThink( gpGlobals->curtime + 0.1 );// how often to check the sound list.
  227. iPreviousSound = SOUNDLIST_EMPTY;
  228. iSound = m_iActiveSound;
  229. while ( iSound != SOUNDLIST_EMPTY )
  230. {
  231. if ( (m_SoundPool[ iSound ].m_flExpireTime <= gpGlobals->curtime && (!m_SoundPool[ iSound ].m_bNoExpirationTime)) || !m_SoundPool[iSound].ValidateOwner() )
  232. {
  233. int iNext = m_SoundPool[ iSound ].m_iNext;
  234. if( displaysoundlist.GetInt() == 1 )
  235. {
  236. Msg(" Removed Sound: %d (Time:%f)\n", m_SoundPool[ iSound ].SoundType(), gpGlobals->curtime );
  237. }
  238. if( displaysoundlist.GetInt() == 2 && m_SoundPool[ iSound ].IsSoundType( SOUND_DANGER ) )
  239. {
  240. Msg(" Removed Danger Sound: %d (time:%f)\n", m_SoundPool[ iSound ].SoundType(), gpGlobals->curtime );
  241. }
  242. // move this sound back into the free list
  243. FreeSound( iSound, iPreviousSound );
  244. iSound = iNext;
  245. }
  246. else
  247. {
  248. if( displaysoundlist.GetBool() )
  249. {
  250. Vector forward, right, up;
  251. GetVectors( &forward, &right, &up );
  252. byte r, g, b;
  253. // Default to yellow.
  254. r = 255;
  255. g = 255;
  256. b = 0;
  257. CSound *pSound = &m_SoundPool[ iSound ];
  258. if( pSound->IsSoundType( SOUND_DANGER ) )
  259. {
  260. r = 255;
  261. g = 0;
  262. b = 0;
  263. }
  264. if( displaysoundlist.GetInt() == 1 || (displaysoundlist.GetInt() == 2 && pSound->IsSoundType( SOUND_DANGER ) ) )
  265. {
  266. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + forward * pSound->Volume(), r,g,b, false, 0.1 );
  267. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - forward * pSound->Volume(), r,g,b, false, 0.1 );
  268. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + right * pSound->Volume(), r,g,b, false, 0.1 );
  269. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - right * pSound->Volume(), r,g,b, false, 0.1 );
  270. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + up * pSound->Volume(), r,g,b, false, 0.1 );
  271. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - up * pSound->Volume(), r,g,b, false, 0.1 );
  272. if( pSound->m_flOcclusionScale != 1.0 )
  273. {
  274. // Draw the occluded radius, too.
  275. r = 0; g = 150; b = 255;
  276. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + forward * pSound->OccludedVolume(), r,g,b, false, 0.1 );
  277. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - forward * pSound->OccludedVolume(), r,g,b, false, 0.1 );
  278. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + right * pSound->OccludedVolume(), r,g,b, false, 0.1 );
  279. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - right * pSound->OccludedVolume(), r,g,b, false, 0.1 );
  280. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + up * pSound->OccludedVolume(), r,g,b, false, 0.1 );
  281. NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - up * pSound->OccludedVolume(), r,g,b, false, 0.1 );
  282. }
  283. }
  284. DevMsg( 2, "Soundlist: %d / %d (%d)\n", ISoundsInList( SOUNDLISTTYPE_ACTIVE ),ISoundsInList( SOUNDLISTTYPE_FREE ), ISoundsInList( SOUNDLISTTYPE_ACTIVE ) - m_cLastActiveSounds );
  285. m_cLastActiveSounds = ISoundsInList ( SOUNDLISTTYPE_ACTIVE );
  286. }
  287. iPreviousSound = iSound;
  288. iSound = m_SoundPool[ iSound ].m_iNext;
  289. }
  290. }
  291. }
  292. //=========================================================
  293. // Precache - dummy function
  294. //=========================================================
  295. void CSoundEnt::Precache ( void )
  296. {
  297. }
  298. //=========================================================
  299. // FreeSound - clears the passed active sound and moves it
  300. // to the top of the free list. TAKE CARE to only call this
  301. // function for sounds in the Active list!!
  302. //=========================================================
  303. void CSoundEnt::FreeSound ( int iSound, int iPrevious )
  304. {
  305. if ( !g_pSoundEnt )
  306. {
  307. // no sound ent!
  308. return;
  309. }
  310. if ( iPrevious != SOUNDLIST_EMPTY )
  311. {
  312. // iSound is not the head of the active list, so
  313. // must fix the index for the Previous sound
  314. g_pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = g_pSoundEnt->m_SoundPool[ iSound ].m_iNext;
  315. }
  316. else
  317. {
  318. // the sound we're freeing IS the head of the active list.
  319. g_pSoundEnt->m_iActiveSound = g_pSoundEnt->m_SoundPool [ iSound ].m_iNext;
  320. }
  321. // make iSound the head of the Free list.
  322. g_pSoundEnt->m_SoundPool[ iSound ].m_iNext = g_pSoundEnt->m_iFreeSound;
  323. g_pSoundEnt->m_iFreeSound = iSound;
  324. }
  325. //=========================================================
  326. // IAllocSound - moves a sound from the Free list to the
  327. // Active list returns the index of the alloc'd sound
  328. //=========================================================
  329. int CSoundEnt::IAllocSound( void )
  330. {
  331. int iNewSound;
  332. if ( m_iFreeSound == SOUNDLIST_EMPTY )
  333. {
  334. // no free sound!
  335. if ( developer.GetInt() >= 2 )
  336. Msg( "Free Sound List is full!\n" );
  337. return SOUNDLIST_EMPTY;
  338. }
  339. // there is at least one sound available, so move it to the
  340. // Active sound list, and return its SoundPool index.
  341. iNewSound = m_iFreeSound;// copy the index of the next free sound
  342. m_iFreeSound = m_SoundPool[ m_iFreeSound ].m_iNext;// move the index down into the free list.
  343. m_SoundPool[ iNewSound ].m_iNext = m_iActiveSound;// point the new sound at the top of the active list.
  344. m_iActiveSound = iNewSound;// now make the new sound the top of the active list. You're done.
  345. #ifdef DEBUG
  346. m_SoundPool[ iNewSound ].m_iMyIndex = iNewSound;
  347. #endif // DEBUG
  348. return iNewSound;
  349. }
  350. //=========================================================
  351. // InsertSound - Allocates a free sound and fills it with
  352. // sound info.
  353. //=========================================================
  354. void CSoundEnt::InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration, CBaseEntity *pOwner, int soundChannelIndex, CBaseEntity *pSoundTarget )
  355. {
  356. int iThisSound;
  357. if ( !g_pSoundEnt )
  358. return;
  359. if( soundChannelIndex == SOUNDENT_CHANNEL_UNSPECIFIED )
  360. {
  361. // No sound channel specified. So just make a new sound.
  362. iThisSound = g_pSoundEnt->IAllocSound();
  363. }
  364. else
  365. {
  366. // If this entity has already got a sound in the soundlist that's on this
  367. // channel, update that sound. Otherwise add a new one.
  368. iThisSound = g_pSoundEnt->FindOrAllocateSound( pOwner, soundChannelIndex );
  369. }
  370. if ( iThisSound == SOUNDLIST_EMPTY )
  371. {
  372. DevMsg( "Could not AllocSound() for InsertSound() (Game DLL)\n" );
  373. return;
  374. }
  375. CSound *pSound;
  376. pSound = &g_pSoundEnt->m_SoundPool[ iThisSound ];
  377. pSound->SetSoundOrigin( vecOrigin );
  378. pSound->m_iType = iType;
  379. pSound->m_iVolume = iVolume;
  380. pSound->m_flOcclusionScale = 0.5;
  381. pSound->m_flExpireTime = gpGlobals->curtime + flDuration;
  382. pSound->m_bNoExpirationTime = false;
  383. pSound->m_hOwner.Set( pOwner );
  384. pSound->m_hTarget.Set( pSoundTarget );
  385. pSound->m_ownerChannelIndex = soundChannelIndex;
  386. // Keep track of whether this sound had an owner when it was made. If the sound has a long duration,
  387. // the owner could disappear by the time someone hears this sound, so we have to look at this boolean
  388. // and throw out sounds who have a NULL owner but this field set to true. (sjb) 12/2/2005
  389. if( pOwner )
  390. {
  391. pSound->m_bHasOwner = true;
  392. }
  393. else
  394. {
  395. pSound->m_bHasOwner = false;
  396. }
  397. if( displaysoundlist.GetInt() == 1 )
  398. {
  399. Msg(" Added Sound! Type:%d Duration:%f (Time:%f)\n", pSound->SoundType(), flDuration, gpGlobals->curtime );
  400. }
  401. if( displaysoundlist.GetInt() == 2 && (iType & SOUND_DANGER) )
  402. {
  403. Msg(" Added Danger Sound! Duration:%f (Time:%f)\n", flDuration, gpGlobals->curtime );
  404. }
  405. }
  406. //---------------------------------------------------------
  407. //---------------------------------------------------------
  408. int CSoundEnt::FindOrAllocateSound( CBaseEntity *pOwner, int soundChannelIndex )
  409. {
  410. int iSound = m_iActiveSound;
  411. while ( iSound != SOUNDLIST_EMPTY )
  412. {
  413. CSound &sound = m_SoundPool[iSound];
  414. if ( sound.m_ownerChannelIndex == soundChannelIndex && sound.m_hOwner == pOwner )
  415. {
  416. return iSound;
  417. }
  418. iSound = sound.m_iNext;
  419. }
  420. return IAllocSound();
  421. }
  422. //=========================================================
  423. // Initialize - clears all sounds and moves them into the
  424. // free sound list.
  425. //=========================================================
  426. void CSoundEnt::Initialize ( void )
  427. {
  428. int i;
  429. int iSound;
  430. m_cLastActiveSounds;
  431. m_iFreeSound = 0;
  432. m_iActiveSound = SOUNDLIST_EMPTY;
  433. // In SP, we should only use the first 64 slots so save/load works right.
  434. // In MP, have one for each player and 32 extras.
  435. int nTotalSoundsInPool = MAX_WORLD_SOUNDS_SP;
  436. if ( gpGlobals->maxClients > 1 )
  437. nTotalSoundsInPool = MIN( MAX_WORLD_SOUNDS_MP, gpGlobals->maxClients + 32 );
  438. if ( gpGlobals->maxClients+16 > nTotalSoundsInPool )
  439. {
  440. Warning( "CSoundEnt pool is low on sounds due to high number of clients.\n" );
  441. }
  442. for ( i = 0 ; i < nTotalSoundsInPool ; i++ )
  443. {
  444. // clear all sounds, and link them into the free sound list.
  445. m_SoundPool[ i ].Clear();
  446. m_SoundPool[ i ].m_iNext = i + 1;
  447. }
  448. m_SoundPool[ i - 1 ].m_iNext = SOUNDLIST_EMPTY;// terminate the list here.
  449. // now reserve enough sounds for each client
  450. for ( i = 0 ; i < gpGlobals->maxClients ; i++ )
  451. {
  452. iSound = IAllocSound();
  453. if ( iSound == SOUNDLIST_EMPTY )
  454. {
  455. DevMsg( "Could not AllocSound() for Client Reserve! (DLL)\n" );
  456. return;
  457. }
  458. m_SoundPool[ iSound ].m_bNoExpirationTime = true;
  459. }
  460. }
  461. //=========================================================
  462. // ISoundsInList - returns the number of sounds in the desired
  463. // sound list.
  464. //=========================================================
  465. int CSoundEnt::ISoundsInList ( int iListType )
  466. {
  467. int i;
  468. int iThisSound = SOUNDLIST_EMPTY;
  469. if ( iListType == SOUNDLISTTYPE_FREE )
  470. {
  471. iThisSound = m_iFreeSound;
  472. }
  473. else if ( iListType == SOUNDLISTTYPE_ACTIVE )
  474. {
  475. iThisSound = m_iActiveSound;
  476. }
  477. else
  478. {
  479. Msg( "Unknown Sound List Type!\n" );
  480. }
  481. if ( iThisSound == SOUNDLIST_EMPTY )
  482. {
  483. return 0;
  484. }
  485. i = 0;
  486. while ( iThisSound != SOUNDLIST_EMPTY )
  487. {
  488. i++;
  489. iThisSound = m_SoundPool[ iThisSound ].m_iNext;
  490. }
  491. return i;
  492. }
  493. //=========================================================
  494. // ActiveList - returns the head of the active sound list
  495. //=========================================================
  496. int CSoundEnt::ActiveList ( void )
  497. {
  498. if ( !g_pSoundEnt )
  499. {
  500. return SOUNDLIST_EMPTY;
  501. }
  502. return g_pSoundEnt->m_iActiveSound;
  503. }
  504. //=========================================================
  505. // FreeList - returns the head of the free sound list
  506. //=========================================================
  507. int CSoundEnt::FreeList ( void )
  508. {
  509. if ( !g_pSoundEnt )
  510. {
  511. return SOUNDLIST_EMPTY;
  512. }
  513. return g_pSoundEnt->m_iFreeSound;
  514. }
  515. //=========================================================
  516. // SoundPointerForIndex - returns a pointer to the instance
  517. // of CSound at index's position in the sound pool.
  518. //=========================================================
  519. CSound* CSoundEnt::SoundPointerForIndex( int iIndex )
  520. {
  521. if ( !g_pSoundEnt )
  522. {
  523. return NULL;
  524. }
  525. if ( iIndex > ( MAX_WORLD_SOUNDS_MP - 1 ) )
  526. {
  527. Msg( "SoundPointerForIndex() - Index too large!\n" );
  528. return NULL;
  529. }
  530. if ( iIndex < 0 )
  531. {
  532. Msg( "SoundPointerForIndex() - Index < 0!\n" );
  533. return NULL;
  534. }
  535. return &g_pSoundEnt->m_SoundPool[ iIndex ];
  536. }
  537. //=========================================================
  538. // Clients are numbered from 1 to MAXCLIENTS, but the client
  539. // reserved sounds in the soundlist are from 0 to MAXCLIENTS - 1,
  540. // so this function ensures that a client gets the proper index
  541. // to his reserved sound in the soundlist.
  542. //=========================================================
  543. int CSoundEnt::ClientSoundIndex ( edict_t *pClient )
  544. {
  545. int iReturn = ENTINDEX( pClient ) - 1;
  546. #ifdef _DEBUG
  547. if ( iReturn < 0 || iReturn >= gpGlobals->maxClients )
  548. {
  549. Msg( "** ClientSoundIndex returning a bogus value! **\n" );
  550. }
  551. #endif // _DEBUG
  552. return iReturn;
  553. }
  554. //-----------------------------------------------------------------------------
  555. // Purpose: Return the loudest sound of the specified type at "earposition"
  556. //-----------------------------------------------------------------------------
  557. CSound* CSoundEnt::GetLoudestSoundOfType( int iType, const Vector &vecEarPosition )
  558. {
  559. CSound *pLoudestSound = NULL;
  560. int iThisSound;
  561. int iBestSound = SOUNDLIST_EMPTY;
  562. float flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far.
  563. float flDist;
  564. CSound *pSound;
  565. iThisSound = ActiveList();
  566. while ( iThisSound != SOUNDLIST_EMPTY )
  567. {
  568. pSound = SoundPointerForIndex( iThisSound );
  569. if ( pSound && pSound->m_iType == iType && pSound->ValidateOwner() )
  570. {
  571. flDist = ( pSound->GetSoundOrigin() - vecEarPosition ).Length();
  572. //FIXME: This doesn't match what's in Listen()
  573. //flDist = UTIL_DistApprox( pSound->GetSoundOrigin(), vecEarPosition );
  574. if ( flDist <= pSound->m_iVolume && flDist < flBestDist )
  575. {
  576. pLoudestSound = pSound;
  577. iBestSound = iThisSound;
  578. flBestDist = flDist;
  579. }
  580. }
  581. iThisSound = pSound->m_iNext;
  582. }
  583. return pLoudestSound;
  584. }
  585. //-----------------------------------------------------------------------------
  586. // Purpose: Inserts an AI sound into the world sound list.
  587. //-----------------------------------------------------------------------------
  588. class CAISound : public CPointEntity
  589. {
  590. public:
  591. CAISound()
  592. {
  593. // Initialize these new keyvalues appropriately
  594. // in order to support legacy instances of ai_sound.
  595. m_iSoundContext = 0x00000000;
  596. m_iVolume = 0;
  597. m_flDuration = 0.3;
  598. }
  599. DECLARE_CLASS( CAISound, CPointEntity );
  600. DECLARE_DATADESC();
  601. // data
  602. int m_iSoundType;
  603. int m_iSoundContext;
  604. int m_iVolume;
  605. float m_flDuration;
  606. string_t m_iszProxyEntityName;
  607. // Input handlers
  608. void InputInsertSound( inputdata_t &inputdata );
  609. void InputEmitAISound( inputdata_t &inputdata );
  610. };
  611. LINK_ENTITY_TO_CLASS( ai_sound, CAISound );
  612. BEGIN_DATADESC( CAISound )
  613. DEFINE_KEYFIELD( m_iSoundType, FIELD_INTEGER, "soundtype" ),
  614. DEFINE_KEYFIELD( m_iSoundContext, FIELD_INTEGER, "soundcontext" ),
  615. DEFINE_KEYFIELD( m_iVolume, FIELD_INTEGER, "volume" ),
  616. DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
  617. DEFINE_KEYFIELD( m_iszProxyEntityName, FIELD_STRING, "locationproxy" ),
  618. DEFINE_INPUTFUNC( FIELD_INTEGER, "InsertSound", InputInsertSound ),
  619. DEFINE_INPUTFUNC( FIELD_VOID, "EmitAISound", InputEmitAISound ),
  620. END_DATADESC()
  621. //-----------------------------------------------------------------------------
  622. // Purpose: *** OBSOLETE **** Here for legacy support only!
  623. //-----------------------------------------------------------------------------
  624. void CAISound::InputInsertSound( inputdata_t &inputdata )
  625. {
  626. int iVolume;
  627. iVolume = inputdata.value.Int();
  628. Vector vecLocation = GetAbsOrigin();
  629. if( m_iszProxyEntityName != NULL_STRING )
  630. {
  631. CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName );
  632. if( pProxy )
  633. {
  634. vecLocation = pProxy->GetAbsOrigin();
  635. }
  636. else
  637. {
  638. DevWarning("Warning- ai_sound cannot find proxy entity named '%s'. Using self.\n", STRING(m_iszProxyEntityName) );
  639. }
  640. }
  641. g_pSoundEnt->InsertSound( m_iSoundType, vecLocation, iVolume, m_flDuration, this );
  642. }
  643. void CAISound::InputEmitAISound( inputdata_t &inputdata )
  644. {
  645. Vector vecLocation = GetAbsOrigin();
  646. if( m_iszProxyEntityName != NULL_STRING )
  647. {
  648. CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName );
  649. if( pProxy )
  650. {
  651. vecLocation = pProxy->GetAbsOrigin();
  652. }
  653. else
  654. {
  655. DevWarning("Warning- ai_sound cannot find proxy entity named '%s'. Using self.\n", STRING(m_iszProxyEntityName) );
  656. }
  657. }
  658. g_pSoundEnt->InsertSound( m_iSoundType | m_iSoundContext, vecLocation, m_iVolume, m_flDuration, this );
  659. }