diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/soundent.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/soundent.cpp')
| -rw-r--r-- | mp/src/game/server/soundent.cpp | 1616 |
1 files changed, 808 insertions, 808 deletions
diff --git a/mp/src/game/server/soundent.cpp b/mp/src/game/server/soundent.cpp index c8dbb4c9..4a735637 100644 --- a/mp/src/game/server/soundent.cpp +++ b/mp/src/game/server/soundent.cpp @@ -1,808 +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 );
-}
-
-
+//========= 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 ); +} + + |