diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/soundent.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/soundent.cpp')
| -rw-r--r-- | mp/src/game/server/soundent.cpp | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/mp/src/game/server/soundent.cpp b/mp/src/game/server/soundent.cpp new file mode 100644 index 00000000..c8dbb4c9 --- /dev/null +++ b/mp/src/game/server/soundent.cpp @@ -0,0 +1,808 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "soundent.h"
+#include "game.h"
+#include "world.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Some enumerations needed by CSoundEnt
+//-----------------------------------------------------------------------------
+
+// identifiers passed to functions that can operate on either list, to indicate which list to operate on.
+#define SOUNDLISTTYPE_FREE 1
+#define SOUNDLISTTYPE_ACTIVE 2
+
+
+
+LINK_ENTITY_TO_CLASS( soundent, CSoundEnt );
+
+static CSoundEnt *g_pSoundEnt = NULL;
+
+BEGIN_SIMPLE_DATADESC( CSound )
+
+ DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_iVolume, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flOcclusionScale, FIELD_FLOAT ),
+ DEFINE_FIELD( m_iType, FIELD_INTEGER ),
+// DEFINE_FIELD( m_iNextAudible, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bNoExpirationTime, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flExpireTime, FIELD_TIME ),
+ DEFINE_FIELD( m_iNext, FIELD_SHORT ),
+ DEFINE_FIELD( m_ownerChannelIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( m_vecOrigin, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_bHasOwner, FIELD_BOOLEAN ),
+// DEFINE_FIELD( m_iMyIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
+
+END_DATADESC()
+
+
+//=========================================================
+// CSound - Clear - zeros all fields for a sound
+//=========================================================
+void CSound::Clear ( void )
+{
+ m_vecOrigin = vec3_origin;
+ m_iType = 0;
+ m_iVolume = 0;
+ m_flOcclusionScale = 0;
+ m_flExpireTime = 0;
+ m_bNoExpirationTime = false;
+ m_iNext = SOUNDLIST_EMPTY;
+ m_iNextAudible = 0;
+}
+
+//=========================================================
+// Reset - clears the volume, origin, and type for a sound,
+// but doesn't expire or unlink it.
+//=========================================================
+void CSound::Reset ( void )
+{
+ m_vecOrigin = vec3_origin;
+ m_iType = 0;
+ m_iVolume = 0;
+ m_iNext = SOUNDLIST_EMPTY;
+}
+
+//=========================================================
+// FIsSound - returns true if the sound is an Audible sound
+//=========================================================
+bool CSound::FIsSound ( void )
+{
+ switch( SoundTypeNoContext() )
+ {
+ case SOUND_COMBAT:
+ case SOUND_WORLD:
+ case SOUND_PLAYER:
+ case SOUND_DANGER:
+ case SOUND_DANGER_SNIPERONLY:
+ case SOUND_THUMPER:
+ case SOUND_BULLET_IMPACT:
+ case SOUND_BUGBAIT:
+ case SOUND_PHYSICS_DANGER:
+ case SOUND_MOVE_AWAY:
+ case SOUND_PLAYER_VEHICLE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+//=========================================================
+// FIsScent - returns true if the sound is actually a scent
+// do we really need this function? If a sound isn't a sound,
+// it must be a scent. (sjb)
+//=========================================================
+bool CSound::FIsScent ( void )
+{
+ switch( m_iType )
+ {
+ case SOUND_CARCASS:
+ case SOUND_MEAT:
+ case SOUND_GARBAGE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+//---------------------------------------------------------
+// This function returns the spot the listener should be
+// interested in if he hears the sound. MOST of the time,
+// this spot is the same as the sound's origin. But sometimes
+// (like with bullet impacts) the entity that owns the
+// sound is more interesting than the actual location of the
+// sound effect.
+//---------------------------------------------------------
+const Vector &CSound::GetSoundReactOrigin( void )
+{
+
+ // Check pure types.
+ switch( m_iType )
+ {
+ case SOUND_BULLET_IMPACT:
+ case SOUND_PHYSICS_DANGER:
+ if( m_hOwner.Get() != NULL )
+ {
+ // We really want the origin of this sound's
+ // owner.
+ return m_hOwner->GetAbsOrigin();
+ }
+ else
+ {
+ // If the owner is somehow invalid, we'll settle
+ // for the sound's origin rather than a crash.
+ return GetSoundOrigin();
+ }
+ break;
+ }
+
+ if( m_iType & SOUND_CONTEXT_REACT_TO_SOURCE )
+ {
+ if( m_hOwner.Get() != NULL )
+ {
+ return m_hOwner->GetAbsOrigin();
+ }
+ }
+
+ // Check for types with additional context.
+ if( m_iType & SOUND_DANGER )
+ {
+ if( (m_iType & SOUND_CONTEXT_FROM_SNIPER) )
+ {
+ if( m_hOwner.Get() != NULL )
+ {
+ // Be afraid of the sniper's location, not where the bullet will hit.
+ return m_hOwner->GetAbsOrigin();
+ }
+ else
+ {
+ return GetSoundOrigin();
+ }
+ }
+ }
+
+
+ return GetSoundOrigin();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Save/load
+//-----------------------------------------------------------------------------
+BEGIN_DATADESC( CSoundEnt )
+
+ DEFINE_FIELD( m_iFreeSound, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iActiveSound, FIELD_INTEGER ),
+ DEFINE_FIELD( m_cLastActiveSounds, FIELD_INTEGER ),
+ DEFINE_EMBEDDED_ARRAY( m_SoundPool, MAX_WORLD_SOUNDS_SP ),
+
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Class factory methods
+//-----------------------------------------------------------------------------
+bool CSoundEnt::InitSoundEnt()
+{
+ ///!!!LATER - do we want a sound ent in deathmatch? (sjb)
+ g_pSoundEnt = (CSoundEnt*)CBaseEntity::Create( "soundent", vec3_origin, vec3_angle, GetWorldEntity() );
+ if ( !g_pSoundEnt )
+ {
+ Warning( "**COULD NOT CREATE SOUNDENT**\n" );
+ return false;
+ }
+ g_pSoundEnt->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );
+ return true;
+}
+
+void CSoundEnt::ShutdownSoundEnt()
+{
+ if ( g_pSoundEnt )
+ {
+ g_pSoundEnt->FreeList();
+ g_pSoundEnt = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Construction, destruction
+//-----------------------------------------------------------------------------
+CSoundEnt::CSoundEnt()
+{
+}
+
+CSoundEnt::~CSoundEnt()
+{
+}
+
+
+//=========================================================
+// Spawn
+//=========================================================
+void CSoundEnt::Spawn( void )
+{
+ SetSolid( SOLID_NONE );
+ Initialize();
+
+ SetNextThink( gpGlobals->curtime + 1 );
+}
+
+void CSoundEnt::OnRestore()
+{
+ BaseClass::OnRestore();
+
+ // Make sure the singleton points to the restored version of this.
+ if ( g_pSoundEnt )
+ {
+ Assert( g_pSoundEnt != this );
+ UTIL_Remove( g_pSoundEnt );
+ }
+ g_pSoundEnt = this;
+}
+
+
+//=========================================================
+// Think - at interval, the entire active sound list is checked
+// for sounds that have ExpireTimes less than or equal
+// to the current world time, and these sounds are deallocated.
+//=========================================================
+void CSoundEnt::Think ( void )
+{
+ int iSound;
+ int iPreviousSound;
+
+ SetNextThink( gpGlobals->curtime + 0.1 );// how often to check the sound list.
+
+ iPreviousSound = SOUNDLIST_EMPTY;
+ iSound = m_iActiveSound;
+
+ while ( iSound != SOUNDLIST_EMPTY )
+ {
+ if ( (m_SoundPool[ iSound ].m_flExpireTime <= gpGlobals->curtime && (!m_SoundPool[ iSound ].m_bNoExpirationTime)) || !m_SoundPool[iSound].ValidateOwner() )
+ {
+ int iNext = m_SoundPool[ iSound ].m_iNext;
+
+ if( displaysoundlist.GetInt() == 1 )
+ {
+ Msg(" Removed Sound: %d (Time:%f)\n", m_SoundPool[ iSound ].SoundType(), gpGlobals->curtime );
+ }
+ if( displaysoundlist.GetInt() == 2 && m_SoundPool[ iSound ].IsSoundType( SOUND_DANGER ) )
+ {
+ Msg(" Removed Danger Sound: %d (time:%f)\n", m_SoundPool[ iSound ].SoundType(), gpGlobals->curtime );
+ }
+
+ // move this sound back into the free list
+ FreeSound( iSound, iPreviousSound );
+
+ iSound = iNext;
+ }
+ else
+ {
+ if( displaysoundlist.GetBool() )
+ {
+ Vector forward, right, up;
+ GetVectors( &forward, &right, &up );
+ byte r, g, b;
+
+ // Default to yellow.
+ r = 255;
+ g = 255;
+ b = 0;
+
+ CSound *pSound = &m_SoundPool[ iSound ];
+
+ if( pSound->IsSoundType( SOUND_DANGER ) )
+ {
+ r = 255;
+ g = 0;
+ b = 0;
+ }
+
+ if( displaysoundlist.GetInt() == 1 || (displaysoundlist.GetInt() == 2 && pSound->IsSoundType( SOUND_DANGER ) ) )
+ {
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + forward * pSound->Volume(), r,g,b, false, 0.1 );
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - forward * pSound->Volume(), r,g,b, false, 0.1 );
+
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + right * pSound->Volume(), r,g,b, false, 0.1 );
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - right * pSound->Volume(), r,g,b, false, 0.1 );
+
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + up * pSound->Volume(), r,g,b, false, 0.1 );
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - up * pSound->Volume(), r,g,b, false, 0.1 );
+
+ if( pSound->m_flOcclusionScale != 1.0 )
+ {
+ // Draw the occluded radius, too.
+ r = 0; g = 150; b = 255;
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + forward * pSound->OccludedVolume(), r,g,b, false, 0.1 );
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - forward * pSound->OccludedVolume(), r,g,b, false, 0.1 );
+
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + right * pSound->OccludedVolume(), r,g,b, false, 0.1 );
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - right * pSound->OccludedVolume(), r,g,b, false, 0.1 );
+
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + up * pSound->OccludedVolume(), r,g,b, false, 0.1 );
+ NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - up * pSound->OccludedVolume(), r,g,b, false, 0.1 );
+ }
+ }
+
+ DevMsg( 2, "Soundlist: %d / %d (%d)\n", ISoundsInList( SOUNDLISTTYPE_ACTIVE ),ISoundsInList( SOUNDLISTTYPE_FREE ), ISoundsInList( SOUNDLISTTYPE_ACTIVE ) - m_cLastActiveSounds );
+ m_cLastActiveSounds = ISoundsInList ( SOUNDLISTTYPE_ACTIVE );
+ }
+
+ iPreviousSound = iSound;
+ iSound = m_SoundPool[ iSound ].m_iNext;
+ }
+ }
+
+}
+
+//=========================================================
+// Precache - dummy function
+//=========================================================
+void CSoundEnt::Precache ( void )
+{
+}
+
+//=========================================================
+// FreeSound - clears the passed active sound and moves it
+// to the top of the free list. TAKE CARE to only call this
+// function for sounds in the Active list!!
+//=========================================================
+void CSoundEnt::FreeSound ( int iSound, int iPrevious )
+{
+ if ( !g_pSoundEnt )
+ {
+ // no sound ent!
+ return;
+ }
+
+ if ( iPrevious != SOUNDLIST_EMPTY )
+ {
+ // iSound is not the head of the active list, so
+ // must fix the index for the Previous sound
+ g_pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = g_pSoundEnt->m_SoundPool[ iSound ].m_iNext;
+ }
+ else
+ {
+ // the sound we're freeing IS the head of the active list.
+ g_pSoundEnt->m_iActiveSound = g_pSoundEnt->m_SoundPool [ iSound ].m_iNext;
+ }
+
+ // make iSound the head of the Free list.
+ g_pSoundEnt->m_SoundPool[ iSound ].m_iNext = g_pSoundEnt->m_iFreeSound;
+ g_pSoundEnt->m_iFreeSound = iSound;
+}
+
+//=========================================================
+// IAllocSound - moves a sound from the Free list to the
+// Active list returns the index of the alloc'd sound
+//=========================================================
+int CSoundEnt::IAllocSound( void )
+{
+ int iNewSound;
+
+ if ( m_iFreeSound == SOUNDLIST_EMPTY )
+ {
+ // no free sound!
+ if ( developer.GetInt() >= 2 )
+ Msg( "Free Sound List is full!\n" );
+
+ return SOUNDLIST_EMPTY;
+ }
+
+ // there is at least one sound available, so move it to the
+ // Active sound list, and return its SoundPool index.
+
+ iNewSound = m_iFreeSound;// copy the index of the next free sound
+
+ m_iFreeSound = m_SoundPool[ m_iFreeSound ].m_iNext;// move the index down into the free list.
+
+ m_SoundPool[ iNewSound ].m_iNext = m_iActiveSound;// point the new sound at the top of the active list.
+
+ m_iActiveSound = iNewSound;// now make the new sound the top of the active list. You're done.
+
+#ifdef DEBUG
+ m_SoundPool[ iNewSound ].m_iMyIndex = iNewSound;
+#endif // DEBUG
+
+ return iNewSound;
+}
+
+//=========================================================
+// InsertSound - Allocates a free sound and fills it with
+// sound info.
+//=========================================================
+void CSoundEnt::InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration, CBaseEntity *pOwner, int soundChannelIndex, CBaseEntity *pSoundTarget )
+{
+ int iThisSound;
+
+ if ( !g_pSoundEnt )
+ return;
+
+ if( soundChannelIndex == SOUNDENT_CHANNEL_UNSPECIFIED )
+ {
+ // No sound channel specified. So just make a new sound.
+ iThisSound = g_pSoundEnt->IAllocSound();
+ }
+ else
+ {
+ // If this entity has already got a sound in the soundlist that's on this
+ // channel, update that sound. Otherwise add a new one.
+ iThisSound = g_pSoundEnt->FindOrAllocateSound( pOwner, soundChannelIndex );
+ }
+
+ if ( iThisSound == SOUNDLIST_EMPTY )
+ {
+ DevMsg( "Could not AllocSound() for InsertSound() (Game DLL)\n" );
+ return;
+ }
+
+ CSound *pSound;
+
+ pSound = &g_pSoundEnt->m_SoundPool[ iThisSound ];
+
+ pSound->SetSoundOrigin( vecOrigin );
+ pSound->m_iType = iType;
+ pSound->m_iVolume = iVolume;
+ pSound->m_flOcclusionScale = 0.5;
+ pSound->m_flExpireTime = gpGlobals->curtime + flDuration;
+ pSound->m_bNoExpirationTime = false;
+ pSound->m_hOwner.Set( pOwner );
+ pSound->m_hTarget.Set( pSoundTarget );
+ pSound->m_ownerChannelIndex = soundChannelIndex;
+
+ // Keep track of whether this sound had an owner when it was made. If the sound has a long duration,
+ // the owner could disappear by the time someone hears this sound, so we have to look at this boolean
+ // and throw out sounds who have a NULL owner but this field set to true. (sjb) 12/2/2005
+ if( pOwner )
+ {
+ pSound->m_bHasOwner = true;
+ }
+ else
+ {
+ pSound->m_bHasOwner = false;
+ }
+
+ if( displaysoundlist.GetInt() == 1 )
+ {
+ Msg(" Added Sound! Type:%d Duration:%f (Time:%f)\n", pSound->SoundType(), flDuration, gpGlobals->curtime );
+ }
+ if( displaysoundlist.GetInt() == 2 && (iType & SOUND_DANGER) )
+ {
+ Msg(" Added Danger Sound! Duration:%f (Time:%f)\n", flDuration, gpGlobals->curtime );
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+int CSoundEnt::FindOrAllocateSound( CBaseEntity *pOwner, int soundChannelIndex )
+{
+ int iSound = m_iActiveSound;
+
+ while ( iSound != SOUNDLIST_EMPTY )
+ {
+ CSound &sound = m_SoundPool[iSound];
+
+ if ( sound.m_ownerChannelIndex == soundChannelIndex && sound.m_hOwner == pOwner )
+ {
+ return iSound;
+ }
+
+ iSound = sound.m_iNext;
+ }
+
+ return IAllocSound();
+}
+
+//=========================================================
+// Initialize - clears all sounds and moves them into the
+// free sound list.
+//=========================================================
+void CSoundEnt::Initialize ( void )
+{
+ int i;
+ int iSound;
+
+ m_cLastActiveSounds;
+ m_iFreeSound = 0;
+ m_iActiveSound = SOUNDLIST_EMPTY;
+
+ // In SP, we should only use the first 64 slots so save/load works right.
+ // In MP, have one for each player and 32 extras.
+ int nTotalSoundsInPool = MAX_WORLD_SOUNDS_SP;
+ if ( gpGlobals->maxClients > 1 )
+ nTotalSoundsInPool = MIN( MAX_WORLD_SOUNDS_MP, gpGlobals->maxClients + 32 );
+
+ if ( gpGlobals->maxClients+16 > nTotalSoundsInPool )
+ {
+ Warning( "CSoundEnt pool is low on sounds due to high number of clients.\n" );
+ }
+
+ for ( i = 0 ; i < nTotalSoundsInPool ; i++ )
+ {
+ // clear all sounds, and link them into the free sound list.
+ m_SoundPool[ i ].Clear();
+ m_SoundPool[ i ].m_iNext = i + 1;
+ }
+
+ m_SoundPool[ i - 1 ].m_iNext = SOUNDLIST_EMPTY;// terminate the list here.
+
+
+ // now reserve enough sounds for each client
+ for ( i = 0 ; i < gpGlobals->maxClients ; i++ )
+ {
+ iSound = IAllocSound();
+
+ if ( iSound == SOUNDLIST_EMPTY )
+ {
+ DevMsg( "Could not AllocSound() for Client Reserve! (DLL)\n" );
+ return;
+ }
+
+ m_SoundPool[ iSound ].m_bNoExpirationTime = true;
+ }
+}
+
+//=========================================================
+// ISoundsInList - returns the number of sounds in the desired
+// sound list.
+//=========================================================
+int CSoundEnt::ISoundsInList ( int iListType )
+{
+ int i;
+ int iThisSound = SOUNDLIST_EMPTY;
+
+ if ( iListType == SOUNDLISTTYPE_FREE )
+ {
+ iThisSound = m_iFreeSound;
+ }
+ else if ( iListType == SOUNDLISTTYPE_ACTIVE )
+ {
+ iThisSound = m_iActiveSound;
+ }
+ else
+ {
+ Msg( "Unknown Sound List Type!\n" );
+ }
+
+ if ( iThisSound == SOUNDLIST_EMPTY )
+ {
+ return 0;
+ }
+
+ i = 0;
+
+ while ( iThisSound != SOUNDLIST_EMPTY )
+ {
+ i++;
+
+ iThisSound = m_SoundPool[ iThisSound ].m_iNext;
+ }
+
+ return i;
+}
+
+//=========================================================
+// ActiveList - returns the head of the active sound list
+//=========================================================
+int CSoundEnt::ActiveList ( void )
+{
+ if ( !g_pSoundEnt )
+ {
+ return SOUNDLIST_EMPTY;
+ }
+
+ return g_pSoundEnt->m_iActiveSound;
+}
+
+//=========================================================
+// FreeList - returns the head of the free sound list
+//=========================================================
+int CSoundEnt::FreeList ( void )
+{
+ if ( !g_pSoundEnt )
+ {
+ return SOUNDLIST_EMPTY;
+ }
+
+ return g_pSoundEnt->m_iFreeSound;
+}
+
+//=========================================================
+// SoundPointerForIndex - returns a pointer to the instance
+// of CSound at index's position in the sound pool.
+//=========================================================
+CSound* CSoundEnt::SoundPointerForIndex( int iIndex )
+{
+ if ( !g_pSoundEnt )
+ {
+ return NULL;
+ }
+
+ if ( iIndex > ( MAX_WORLD_SOUNDS_MP - 1 ) )
+ {
+ Msg( "SoundPointerForIndex() - Index too large!\n" );
+ return NULL;
+ }
+
+ if ( iIndex < 0 )
+ {
+ Msg( "SoundPointerForIndex() - Index < 0!\n" );
+ return NULL;
+ }
+
+ return &g_pSoundEnt->m_SoundPool[ iIndex ];
+}
+
+//=========================================================
+// Clients are numbered from 1 to MAXCLIENTS, but the client
+// reserved sounds in the soundlist are from 0 to MAXCLIENTS - 1,
+// so this function ensures that a client gets the proper index
+// to his reserved sound in the soundlist.
+//=========================================================
+int CSoundEnt::ClientSoundIndex ( edict_t *pClient )
+{
+ int iReturn = ENTINDEX( pClient ) - 1;
+
+#ifdef _DEBUG
+ if ( iReturn < 0 || iReturn >= gpGlobals->maxClients )
+ {
+ Msg( "** ClientSoundIndex returning a bogus value! **\n" );
+ }
+#endif // _DEBUG
+
+ return iReturn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the loudest sound of the specified type at "earposition"
+//-----------------------------------------------------------------------------
+CSound* CSoundEnt::GetLoudestSoundOfType( int iType, const Vector &vecEarPosition )
+{
+ CSound *pLoudestSound = NULL;
+
+ int iThisSound;
+ int iBestSound = SOUNDLIST_EMPTY;
+ float flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far.
+ float flDist;
+ CSound *pSound;
+
+ iThisSound = ActiveList();
+
+ while ( iThisSound != SOUNDLIST_EMPTY )
+ {
+ pSound = SoundPointerForIndex( iThisSound );
+
+ if ( pSound && pSound->m_iType == iType && pSound->ValidateOwner() )
+ {
+ flDist = ( pSound->GetSoundOrigin() - vecEarPosition ).Length();
+
+ //FIXME: This doesn't match what's in Listen()
+ //flDist = UTIL_DistApprox( pSound->GetSoundOrigin(), vecEarPosition );
+
+ if ( flDist <= pSound->m_iVolume && flDist < flBestDist )
+ {
+ pLoudestSound = pSound;
+
+ iBestSound = iThisSound;
+ flBestDist = flDist;
+ }
+ }
+
+ iThisSound = pSound->m_iNext;
+ }
+
+ return pLoudestSound;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Inserts an AI sound into the world sound list.
+//-----------------------------------------------------------------------------
+class CAISound : public CPointEntity
+{
+public:
+ CAISound()
+ {
+ // Initialize these new keyvalues appropriately
+ // in order to support legacy instances of ai_sound.
+ m_iSoundContext = 0x00000000;
+ m_iVolume = 0;
+ m_flDuration = 0.3;
+ }
+
+ DECLARE_CLASS( CAISound, CPointEntity );
+
+ DECLARE_DATADESC();
+
+ // data
+ int m_iSoundType;
+ int m_iSoundContext;
+ int m_iVolume;
+ float m_flDuration;
+ string_t m_iszProxyEntityName;
+
+ // Input handlers
+ void InputInsertSound( inputdata_t &inputdata );
+ void InputEmitAISound( inputdata_t &inputdata );
+};
+
+LINK_ENTITY_TO_CLASS( ai_sound, CAISound );
+
+BEGIN_DATADESC( CAISound )
+
+ DEFINE_KEYFIELD( m_iSoundType, FIELD_INTEGER, "soundtype" ),
+ DEFINE_KEYFIELD( m_iSoundContext, FIELD_INTEGER, "soundcontext" ),
+ DEFINE_KEYFIELD( m_iVolume, FIELD_INTEGER, "volume" ),
+ DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
+ DEFINE_KEYFIELD( m_iszProxyEntityName, FIELD_STRING, "locationproxy" ),
+
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "InsertSound", InputInsertSound ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "EmitAISound", InputEmitAISound ),
+
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose: *** OBSOLETE **** Here for legacy support only!
+//-----------------------------------------------------------------------------
+void CAISound::InputInsertSound( inputdata_t &inputdata )
+{
+ int iVolume;
+
+ iVolume = inputdata.value.Int();
+
+ Vector vecLocation = GetAbsOrigin();
+
+ if( m_iszProxyEntityName != NULL_STRING )
+ {
+ CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName );
+
+ if( pProxy )
+ {
+ vecLocation = pProxy->GetAbsOrigin();
+ }
+ else
+ {
+ DevWarning("Warning- ai_sound cannot find proxy entity named '%s'. Using self.\n", STRING(m_iszProxyEntityName) );
+ }
+ }
+
+ g_pSoundEnt->InsertSound( m_iSoundType, vecLocation, iVolume, m_flDuration, this );
+}
+
+void CAISound::InputEmitAISound( inputdata_t &inputdata )
+{
+ Vector vecLocation = GetAbsOrigin();
+
+ if( m_iszProxyEntityName != NULL_STRING )
+ {
+ CBaseEntity *pProxy = gEntList.FindEntityByName( NULL, m_iszProxyEntityName );
+
+ if( pProxy )
+ {
+ vecLocation = pProxy->GetAbsOrigin();
+ }
+ else
+ {
+ DevWarning("Warning- ai_sound cannot find proxy entity named '%s'. Using self.\n", STRING(m_iszProxyEntityName) );
+ }
+ }
+
+ g_pSoundEnt->InsertSound( m_iSoundType | m_iSoundContext, vecLocation, m_iVolume, m_flDuration, this );
+}
+
+
|