From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/soundent.cpp | 1616 +++++++++++++++++++-------------------- 1 file changed, 808 insertions(+), 808 deletions(-) (limited to 'mp/src/game/server/soundent.cpp') 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 ); +} + + -- cgit v1.2.3