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.

853 lines
23 KiB

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