You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1461 lines
40 KiB
1461 lines
40 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Entities relating to in-level sound effects. |
|
// |
|
// ambient_generic: a sound emitter used for one-shot and looping sounds. |
|
// |
|
// env_speaker: used for public address announcements over loudspeakers. |
|
// This tries not to drown out talking NPCs. |
|
// |
|
// env_soundscape: controls what sound script an area uses. |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "player.h" |
|
#include "mathlib/mathlib.h" |
|
#include "ai_speech.h" |
|
#include "stringregistry.h" |
|
#include "gamerules.h" |
|
#include "game.h" |
|
#include <ctype.h> |
|
#include "entitylist.h" |
|
#include "vstdlib/random.h" |
|
#include "engine/IEngineSound.h" |
|
#include "ndebugoverlay.h" |
|
#include "soundscape.h" |
|
#include "igamesystem.h" |
|
#include "KeyValues.h" |
|
#include "filesystem.h" |
|
|
|
#ifdef PORTAL |
|
#include "portal_gamerules.h" |
|
#endif // PORTAL |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Compute a suitable attenuation value given an audible radius |
|
// Input : radius - |
|
// playEverywhere - (disable attenuation) |
|
//----------------------------------------------------------------------------- |
|
#define REFERENCE_dB 60.0 |
|
|
|
#define AMBIENT_GENERIC_UPDATE_RATE 5 // update at 5hz |
|
#define AMBIENT_GENERIC_THINK_DELAY ( 1.0f / float( AMBIENT_GENERIC_UPDATE_RATE ) ) |
|
|
|
#ifdef HL1_DLL |
|
ConVar hl1_ref_db_distance( "hl1_ref_db_distance", "18.0" ); |
|
#define REFERENCE_dB_DISTANCE hl1_ref_db_distance.GetFloat() |
|
#else |
|
#define REFERENCE_dB_DISTANCE 36.0 |
|
#endif//HL1_DLL |
|
|
|
static soundlevel_t ComputeSoundlevel( float radius, bool playEverywhere ) |
|
{ |
|
soundlevel_t soundlevel = SNDLVL_NONE; |
|
|
|
if ( radius > 0 && !playEverywhere ) |
|
{ |
|
// attenuation is set to a distance, compute falloff |
|
|
|
float dB_loss = 20 * log10( radius / REFERENCE_dB_DISTANCE ); |
|
|
|
soundlevel = (soundlevel_t)(int)(40 + dB_loss); // sound at 40dB at reference distance |
|
} |
|
|
|
return soundlevel; |
|
} |
|
|
|
// ==================== GENERIC AMBIENT SOUND ====================================== |
|
|
|
// runtime pitch shift and volume fadein/out structure |
|
|
|
// NOTE: IF YOU CHANGE THIS STRUCT YOU MUST CHANGE THE SAVE/RESTORE VERSION NUMBER |
|
// SEE BELOW (in the typedescription for the class) |
|
typedef struct dynpitchvol |
|
{ |
|
// NOTE: do not change the order of these parameters |
|
// NOTE: unless you also change order of rgdpvpreset array elements! |
|
int preset; |
|
|
|
int pitchrun; // pitch shift % when sound is running 0 - 255 |
|
int pitchstart; // pitch shift % when sound stops or starts 0 - 255 |
|
int spinup; // spinup time 0 - 100 |
|
int spindown; // spindown time 0 - 100 |
|
|
|
int volrun; // volume change % when sound is running 0 - 10 |
|
int volstart; // volume change % when sound stops or starts 0 - 10 |
|
int fadein; // volume fade in time 0 - 100 |
|
int fadeout; // volume fade out time 0 - 100 |
|
|
|
// Low Frequency Oscillator |
|
int lfotype; // 0) off 1) square 2) triangle 3) random |
|
int lforate; // 0 - 1000, how fast lfo osciallates |
|
|
|
int lfomodpitch; // 0-100 mod of current pitch. 0 is off. |
|
int lfomodvol; // 0-100 mod of current volume. 0 is off. |
|
|
|
int cspinup; // each trigger hit increments counter and spinup pitch |
|
|
|
|
|
int cspincount; |
|
|
|
int pitch; |
|
int spinupsav; |
|
int spindownsav; |
|
int pitchfrac; |
|
|
|
int vol; |
|
int fadeinsav; |
|
int fadeoutsav; |
|
int volfrac; |
|
|
|
int lfofrac; |
|
int lfomult; |
|
|
|
|
|
} dynpitchvol_t; |
|
|
|
#define CDPVPRESETMAX 27 |
|
|
|
// presets for runtime pitch and vol modulation of ambient sounds |
|
|
|
dynpitchvol_t rgdpvpreset[CDPVPRESETMAX] = |
|
{ |
|
// pitch pstart spinup spindwn volrun volstrt fadein fadeout lfotype lforate modptch modvol cspnup |
|
{1, 255, 75, 95, 95, 10, 1, 50, 95, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{2, 255, 85, 70, 88, 10, 1, 20, 88, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{3, 255, 100, 50, 75, 10, 1, 10, 75, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{4, 100, 100, 0, 0, 10, 1, 90, 90, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{5, 100, 100, 0, 0, 10, 1, 80, 80, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{6, 100, 100, 0, 0, 10, 1, 50, 70, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{7, 100, 100, 0, 0, 5, 1, 40, 50, 1, 50, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{8, 100, 100, 0, 0, 5, 1, 40, 50, 1, 150, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{9, 100, 100, 0, 0, 5, 1, 40, 50, 1, 750, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{10,128, 100, 50, 75, 10, 1, 30, 40, 2, 8, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{11,128, 100, 50, 75, 10, 1, 30, 40, 2, 25, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{12,128, 100, 50, 75, 10, 1, 30, 40, 2, 70, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{13,50, 50, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{14,70, 70, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{15,90, 90, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{16,120, 120, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{17,180, 180, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{18,255, 255, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{19,200, 75, 90, 90, 10, 1, 50, 90, 2, 100, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{20,255, 75, 97, 90, 10, 1, 50, 90, 1, 40, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{21,100, 100, 0, 0, 10, 1, 30, 50, 3, 15, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{22,160, 160, 0, 0, 10, 1, 50, 50, 3, 500, 25, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{23,255, 75, 88, 0, 10, 1, 40, 0, 0, 0, 0, 0, 5, 0,0,0,0,0,0,0,0,0,0}, |
|
{24,200, 20, 95, 70, 10, 1, 70, 70, 3, 20, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{25,180, 100, 50, 60, 10, 1, 40, 60, 2, 90, 100, 100, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{26,60, 60, 0, 0, 10, 1, 40, 70, 3, 80, 20, 50, 0, 0,0,0,0,0,0,0,0,0,0}, |
|
{27,128, 90, 10, 10, 10, 1, 20, 40, 1, 5, 10, 20, 0, 0,0,0,0,0,0,0,0,0,0} |
|
}; |
|
|
|
class CAmbientGeneric : public CPointEntity |
|
{ |
|
public: |
|
DECLARE_CLASS( CAmbientGeneric, CPointEntity ); |
|
|
|
bool KeyValue( const char *szKeyName, const char *szValue ); |
|
void Spawn( void ); |
|
void Precache( void ); |
|
void Activate( void ); |
|
void RampThink( void ); |
|
void InitModulationParms(void); |
|
void ComputeMaxAudibleDistance( ); |
|
|
|
// Rules about which entities need to transmit along with me |
|
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ); |
|
virtual void UpdateOnRemove( void ); |
|
|
|
void ToggleSound(); |
|
void SendSound( SoundFlags_t flags ); |
|
|
|
// Input handlers |
|
void InputPlaySound( inputdata_t &inputdata ); |
|
void InputStopSound( inputdata_t &inputdata ); |
|
void InputToggleSound( inputdata_t &inputdata ); |
|
void InputPitch( inputdata_t &inputdata ); |
|
void InputVolume( inputdata_t &inputdata ); |
|
void InputFadeIn( inputdata_t &inputdata ); |
|
void InputFadeOut( inputdata_t &inputdata ); |
|
|
|
DECLARE_DATADESC(); |
|
|
|
float m_radius; |
|
float m_flMaxRadius; |
|
soundlevel_t m_iSoundLevel; // dB value |
|
dynpitchvol_t m_dpv; |
|
|
|
bool m_fActive; // only true when the entity is playing a looping sound |
|
bool m_fLooping; // true when the sound played will loop |
|
|
|
string_t m_iszSound; // Path/filename of WAV file to play. |
|
string_t m_sSourceEntName; |
|
EHANDLE m_hSoundSource; // entity from which the sound comes |
|
int m_nSoundSourceEntIndex; // In case the entity goes away before we finish stopping the sound... |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( ambient_generic, CAmbientGeneric ); |
|
|
|
BEGIN_DATADESC( CAmbientGeneric ) |
|
|
|
DEFINE_KEYFIELD( m_iszSound, FIELD_SOUNDNAME, "message" ), |
|
DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "radius" ), |
|
DEFINE_KEYFIELD( m_sSourceEntName, FIELD_STRING, "SourceEntityName" ), |
|
// recomputed in Activate() |
|
// DEFINE_FIELD( m_hSoundSource, EHANDLE ), |
|
// DEFINE_FIELD( m_nSoundSourceEntIndex, FIELD_INTERGER ), |
|
|
|
DEFINE_FIELD( m_flMaxRadius, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fActive, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_fLooping, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ), |
|
|
|
// HACKHACK - This is not really in the spirit of the save/restore design, but save this |
|
// out as a binary data block. If the dynpitchvol_t is changed, old saved games will NOT |
|
// load these correctly, so bump the save/restore version if you change the size of the struct |
|
// The right way to do this is to split the input parms (read in keyvalue) into members and re-init this |
|
// struct in Precache(), but it's unlikely that the struct will change, so it's not worth the time right now. |
|
DEFINE_ARRAY( m_dpv, FIELD_CHARACTER, sizeof(dynpitchvol_t) ), |
|
|
|
// Function Pointers |
|
DEFINE_FUNCTION( RampThink ), |
|
|
|
// Inputs |
|
DEFINE_INPUTFUNC(FIELD_VOID, "PlaySound", InputPlaySound ), |
|
DEFINE_INPUTFUNC(FIELD_VOID, "StopSound", InputStopSound ), |
|
DEFINE_INPUTFUNC(FIELD_VOID, "ToggleSound", InputToggleSound ), |
|
DEFINE_INPUTFUNC(FIELD_FLOAT, "Pitch", InputPitch ), |
|
DEFINE_INPUTFUNC(FIELD_FLOAT, "Volume", InputVolume ), |
|
DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeIn", InputFadeIn ), |
|
DEFINE_INPUTFUNC(FIELD_FLOAT, "FadeOut", InputFadeOut ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
#define SF_AMBIENT_SOUND_EVERYWHERE 1 |
|
#define SF_AMBIENT_SOUND_START_SILENT 16 |
|
#define SF_AMBIENT_SOUND_NOT_LOOPING 32 |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Spawn |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::Spawn( void ) |
|
{ |
|
m_iSoundLevel = ComputeSoundlevel( m_radius, FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE )?true:false ); |
|
ComputeMaxAudibleDistance( ); |
|
|
|
char *szSoundFile = (char *)STRING( m_iszSound ); |
|
if ( !m_iszSound || strlen( szSoundFile ) < 1 ) |
|
{ |
|
Warning( "Empty %s (%s) at %.2f, %.2f, %.2f\n", GetClassname(), GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); |
|
UTIL_Remove(this); |
|
return; |
|
} |
|
|
|
SetSolid( SOLID_NONE ); |
|
SetMoveType( MOVETYPE_NONE ); |
|
|
|
// Set up think function for dynamic modification |
|
// of ambient sound's pitch or volume. Don't |
|
// start thinking yet. |
|
|
|
SetThink(&CAmbientGeneric::RampThink); |
|
SetNextThink( TICK_NEVER_THINK ); |
|
|
|
m_fActive = false; |
|
|
|
if ( FBitSet ( m_spawnflags, SF_AMBIENT_SOUND_NOT_LOOPING ) ) |
|
{ |
|
m_fLooping = false; |
|
} |
|
else |
|
{ |
|
m_fLooping = true; |
|
} |
|
|
|
m_hSoundSource = NULL; |
|
m_nSoundSourceEntIndex = -1; |
|
|
|
Precache( ); |
|
|
|
// init all dynamic modulation parms |
|
InitModulationParms(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes the max audible radius for a given sound level |
|
//----------------------------------------------------------------------------- |
|
#define MIN_AUDIBLE_VOLUME 1.01e-3 |
|
|
|
void CAmbientGeneric::ComputeMaxAudibleDistance( ) |
|
{ |
|
if (( m_iSoundLevel == SNDLVL_NONE ) || ( m_radius == 0.0f )) |
|
{ |
|
m_flMaxRadius = -1.0f; |
|
return; |
|
} |
|
|
|
// Sadly, there's no direct way of getting at this. |
|
// We have to do an interative computation. |
|
float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, m_radius ); |
|
if ( flGain <= MIN_AUDIBLE_VOLUME ) |
|
{ |
|
m_flMaxRadius = m_radius; |
|
return; |
|
} |
|
|
|
float flMinRadius = m_radius; |
|
float flMaxRadius = m_radius * 2; |
|
while ( true ) |
|
{ |
|
// First, find a min + max range surrounding the desired distance gain |
|
float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, flMaxRadius ); |
|
if ( flGain <= MIN_AUDIBLE_VOLUME ) |
|
break; |
|
|
|
// Always audible. |
|
if ( flMaxRadius > 1e5 ) |
|
{ |
|
m_flMaxRadius = -1.0f; |
|
return; |
|
} |
|
|
|
flMinRadius = flMaxRadius; |
|
flMaxRadius *= 2.0f; |
|
} |
|
|
|
// Now home in a little bit |
|
int nInterations = 4; |
|
while ( --nInterations >= 0 ) |
|
{ |
|
float flTestRadius = (flMinRadius + flMaxRadius) * 0.5f; |
|
float flGain = enginesound->GetDistGainFromSoundLevel( m_iSoundLevel, flTestRadius ); |
|
if ( flGain <= MIN_AUDIBLE_VOLUME ) |
|
{ |
|
flMaxRadius = flTestRadius; |
|
} |
|
else |
|
{ |
|
flMinRadius = flTestRadius; |
|
} |
|
} |
|
|
|
m_flMaxRadius = flMaxRadius; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler for changing pitch. |
|
// Input : Float new pitch from 0 - 255 (100 = as recorded). |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputPitch( inputdata_t &inputdata ) |
|
{ |
|
m_dpv.pitch = clamp( FastFloatToSmallInt( inputdata.value.Float() ), 0, 255 ); |
|
|
|
SendSound( SND_CHANGE_PITCH ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler for changing volume. |
|
// Input : Float new volume, from 0 - 10. |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputVolume( inputdata_t &inputdata ) |
|
{ |
|
// |
|
// Multiply the input value by ten since volumes are expected to be from 0 - 100. |
|
// |
|
m_dpv.vol = clamp( RoundFloatToInt( inputdata.value.Float() * 10.f ), 0, 100 ); |
|
m_dpv.volfrac = m_dpv.vol << 8; |
|
|
|
SendSound( SND_CHANGE_VOL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler for fading in volume over time. |
|
// Input : Float volume fade in time 0 - 100 seconds |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputFadeIn( inputdata_t &inputdata ) |
|
{ |
|
// cancel any fade out that might be happening |
|
m_dpv.fadeout = 0; |
|
|
|
m_dpv.fadein = inputdata.value.Float(); |
|
if (m_dpv.fadein > 100) m_dpv.fadein = 100; |
|
if (m_dpv.fadein < 0) m_dpv.fadein = 0; |
|
|
|
if (m_dpv.fadein > 0) |
|
m_dpv.fadein = ( 100 << 8 ) / ( m_dpv.fadein * AMBIENT_GENERIC_UPDATE_RATE ); |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler for fading out volume over time. |
|
// Input : Float volume fade out time 0 - 100 seconds |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputFadeOut( inputdata_t &inputdata ) |
|
{ |
|
// cancel any fade in that might be happening |
|
m_dpv.fadein = 0; |
|
|
|
m_dpv.fadeout = inputdata.value.Float(); |
|
|
|
if (m_dpv.fadeout > 100) m_dpv.fadeout = 100; |
|
if (m_dpv.fadeout < 0) m_dpv.fadeout = 0; |
|
|
|
if (m_dpv.fadeout > 0) |
|
m_dpv.fadeout = ( 100 << 8 ) / ( m_dpv.fadeout * AMBIENT_GENERIC_UPDATE_RATE ); |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
|
|
|
|
void CAmbientGeneric::Precache( void ) |
|
{ |
|
char *szSoundFile = (char *)STRING( m_iszSound ); |
|
if ( m_iszSound != NULL_STRING && strlen( szSoundFile ) > 1 ) |
|
{ |
|
if (*szSoundFile != '!') |
|
{ |
|
PrecacheScriptSound(szSoundFile); |
|
} |
|
} |
|
|
|
if ( !FBitSet (m_spawnflags, SF_AMBIENT_SOUND_START_SILENT ) ) |
|
{ |
|
// start the sound ASAP |
|
if (m_fLooping) |
|
m_fActive = true; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAmbientGeneric::Activate( void ) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
// Initialize sound source. If no source was given, or source can't be found |
|
// then this is the source |
|
if (m_hSoundSource == NULL) |
|
{ |
|
if (m_sSourceEntName != NULL_STRING) |
|
{ |
|
m_hSoundSource = gEntList.FindEntityByName( NULL, m_sSourceEntName ); |
|
if ( m_hSoundSource != NULL ) |
|
{ |
|
m_nSoundSourceEntIndex = m_hSoundSource->entindex(); |
|
} |
|
} |
|
|
|
if (m_hSoundSource == NULL) |
|
{ |
|
m_hSoundSource = this; |
|
m_nSoundSourceEntIndex = entindex(); |
|
} |
|
else |
|
{ |
|
if ( !FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE ) ) |
|
{ |
|
AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); |
|
} |
|
} |
|
} |
|
|
|
#ifdef PORTAL |
|
// This is the only way we can silence the radio sound from the first room without touching them map -- jdw |
|
if ( PortalGameRules() && PortalGameRules()->ShouldRemoveRadio() ) |
|
{ |
|
if ( V_strcmp( STRING( gpGlobals->mapname ), "testchmb_a_00" ) == 0 || |
|
V_strcmp( STRING( gpGlobals->mapname ), "testchmb_a_11" ) == 0 || |
|
V_strcmp( STRING( gpGlobals->mapname ), "testchmb_a_14" ) == 0 ) |
|
{ |
|
if ( V_strcmp( STRING( GetEntityName() ), "radio_sound" ) == 0 ) |
|
{ |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
} |
|
} |
|
#endif // PORTAL |
|
|
|
// If active start the sound |
|
if ( m_fActive ) |
|
{ |
|
int flags = SND_SPAWNING; |
|
// If we are loading a saved game, we can't write into the init/signon buffer here, so just issue |
|
// as a regular sound message... |
|
if ( gpGlobals->eLoadType == MapLoad_Transition || |
|
gpGlobals->eLoadType == MapLoad_LoadGame || |
|
g_pGameRules->InRoundRestart() ) |
|
{ |
|
flags = SND_NOFLAGS; |
|
} |
|
|
|
// Tracker 76119: 8/12/07 ywb: |
|
// Make sure pitch and volume are set up to the correct value (especially after restoring a .sav file) |
|
flags |= ( SND_CHANGE_PITCH | SND_CHANGE_VOL ); |
|
|
|
// Don't bother sending over to client if volume is zero, though |
|
if ( m_dpv.vol > 0 ) |
|
{ |
|
SendSound( (SoundFlags_t)flags ); |
|
} |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Rules about which entities need to transmit along with me |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways ) |
|
{ |
|
// Ambient generics never transmit; this is just a way for us to ensure |
|
// the sound source gets transmitted; that's why we don't call pInfo->m_pTransmitEdict->Set |
|
if ( !m_hSoundSource || m_hSoundSource == this || !m_fActive ) |
|
return; |
|
|
|
// Don't bother sending the position of the source if we have to play everywhere |
|
if ( FBitSet( m_spawnflags, SF_AMBIENT_SOUND_EVERYWHERE ) ) |
|
return; |
|
|
|
Assert( pInfo->m_pClientEnt ); |
|
CBaseEntity *pClient = (CBaseEntity*)(pInfo->m_pClientEnt->GetUnknown()); |
|
if ( !pClient ) |
|
return; |
|
|
|
// Send the sound source if he's close enough |
|
if ( ( m_flMaxRadius < 0 ) || ( pClient->GetAbsOrigin().DistToSqr( m_hSoundSource->GetAbsOrigin() ) <= m_flMaxRadius * m_flMaxRadius ) ) |
|
{ |
|
m_hSoundSource->SetTransmit( pInfo, false ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::UpdateOnRemove( void ) |
|
{ |
|
if ( m_fActive ) |
|
{ |
|
// Stop the sound we're generating |
|
SendSound( SND_STOP ); |
|
} |
|
|
|
BaseClass::UpdateOnRemove(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Think at 5hz if we are dynamically modifying pitch or volume of the |
|
// playing sound. This function will ramp pitch and/or volume up or |
|
// down, modify pitch/volume with lfo if active. |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::RampThink( void ) |
|
{ |
|
int pitch = m_dpv.pitch; |
|
int vol = m_dpv.vol; |
|
int flags = 0; |
|
int fChanged = 0; // false if pitch and vol remain unchanged this round |
|
int prev; |
|
|
|
if (!m_dpv.spinup && !m_dpv.spindown && !m_dpv.fadein && !m_dpv.fadeout && !m_dpv.lfotype) |
|
return; // no ramps or lfo, stop thinking |
|
|
|
// ============== |
|
// pitch envelope |
|
// ============== |
|
if (m_dpv.spinup || m_dpv.spindown) |
|
{ |
|
prev = m_dpv.pitchfrac >> 8; |
|
|
|
if (m_dpv.spinup > 0) |
|
m_dpv.pitchfrac += m_dpv.spinup; |
|
else if (m_dpv.spindown > 0) |
|
m_dpv.pitchfrac -= m_dpv.spindown; |
|
|
|
pitch = m_dpv.pitchfrac >> 8; |
|
|
|
if (pitch > m_dpv.pitchrun) |
|
{ |
|
pitch = m_dpv.pitchrun; |
|
m_dpv.spinup = 0; // done with ramp up |
|
} |
|
|
|
if (pitch < m_dpv.pitchstart) |
|
{ |
|
pitch = m_dpv.pitchstart; |
|
m_dpv.spindown = 0; // done with ramp down |
|
|
|
// shut sound off |
|
SendSound( SND_STOP ); |
|
|
|
// return without setting m_flNextThink |
|
return; |
|
} |
|
|
|
if (pitch > 255) pitch = 255; |
|
if (pitch < 1) pitch = 1; |
|
|
|
m_dpv.pitch = pitch; |
|
|
|
fChanged |= (prev != pitch); |
|
flags |= SND_CHANGE_PITCH; |
|
} |
|
|
|
// ================== |
|
// amplitude envelope |
|
// ================== |
|
if (m_dpv.fadein || m_dpv.fadeout) |
|
{ |
|
prev = m_dpv.volfrac >> 8; |
|
|
|
if (m_dpv.fadein > 0) |
|
m_dpv.volfrac += m_dpv.fadein; |
|
else if (m_dpv.fadeout > 0) |
|
m_dpv.volfrac -= m_dpv.fadeout; |
|
|
|
vol = m_dpv.volfrac >> 8; |
|
|
|
if (vol > m_dpv.volrun) |
|
{ |
|
vol = m_dpv.volrun; |
|
m_dpv.volfrac = vol << 8; |
|
m_dpv.fadein = 0; // done with ramp up |
|
} |
|
|
|
if (vol < m_dpv.volstart) |
|
{ |
|
vol = m_dpv.volstart; |
|
m_dpv.vol = vol; |
|
m_dpv.volfrac = vol << 8; |
|
m_dpv.fadeout = 0; // done with ramp down |
|
|
|
// shut sound off |
|
SendSound( SND_STOP ); |
|
|
|
// return without setting m_flNextThink |
|
return; |
|
} |
|
|
|
if (vol > 100) |
|
{ |
|
vol = 100; |
|
m_dpv.volfrac = vol << 8; |
|
} |
|
if (vol < 1) |
|
{ |
|
vol = 1; |
|
m_dpv.volfrac = vol << 8; |
|
} |
|
|
|
m_dpv.vol = vol; |
|
|
|
fChanged |= (prev != vol); |
|
flags |= SND_CHANGE_VOL; |
|
} |
|
|
|
// =================== |
|
// pitch/amplitude LFO |
|
// =================== |
|
if (m_dpv.lfotype) |
|
{ |
|
int pos; |
|
|
|
if (m_dpv.lfofrac > 0x6fffffff) |
|
m_dpv.lfofrac = 0; |
|
|
|
// update lfo, lfofrac/255 makes a triangle wave 0-255 |
|
m_dpv.lfofrac += m_dpv.lforate; |
|
pos = m_dpv.lfofrac >> 8; |
|
|
|
if (m_dpv.lfofrac < 0) |
|
{ |
|
m_dpv.lfofrac = 0; |
|
m_dpv.lforate = abs(m_dpv.lforate); |
|
pos = 0; |
|
} |
|
else if (pos > 255) |
|
{ |
|
pos = 255; |
|
m_dpv.lfofrac = (255 << 8); |
|
m_dpv.lforate = -abs(m_dpv.lforate); |
|
} |
|
|
|
switch(m_dpv.lfotype) |
|
{ |
|
case LFO_SQUARE: |
|
if (pos < 128) |
|
m_dpv.lfomult = 255; |
|
else |
|
m_dpv.lfomult = 0; |
|
|
|
break; |
|
case LFO_RANDOM: |
|
if (pos == 255) |
|
m_dpv.lfomult = random->RandomInt(0, 255); |
|
break; |
|
case LFO_TRIANGLE: |
|
default: |
|
m_dpv.lfomult = pos; |
|
break; |
|
} |
|
|
|
if (m_dpv.lfomodpitch) |
|
{ |
|
prev = pitch; |
|
|
|
// pitch 0-255 |
|
pitch += ((m_dpv.lfomult - 128) * m_dpv.lfomodpitch) / 100; |
|
|
|
if (pitch > 255) pitch = 255; |
|
if (pitch < 1) pitch = 1; |
|
|
|
|
|
fChanged |= (prev != pitch); |
|
flags |= SND_CHANGE_PITCH; |
|
} |
|
|
|
if (m_dpv.lfomodvol) |
|
{ |
|
// vol 0-100 |
|
prev = vol; |
|
|
|
vol += ((m_dpv.lfomult - 128) * m_dpv.lfomodvol) / 100; |
|
|
|
if (vol > 100) vol = 100; |
|
if (vol < 0) vol = 0; |
|
|
|
fChanged |= (prev != vol); |
|
flags |= SND_CHANGE_VOL; |
|
} |
|
|
|
} |
|
|
|
// Send update to playing sound only if we actually changed |
|
// pitch or volume in this routine. |
|
|
|
if (flags && fChanged) |
|
{ |
|
if (pitch == PITCH_NORM) |
|
pitch = PITCH_NORM + 1; // don't send 'no pitch' ! |
|
|
|
CBaseEntity* pSoundSource = m_hSoundSource; |
|
if (pSoundSource) |
|
{ |
|
UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), |
|
STRING( m_iszSound ), (vol * 0.01), m_iSoundLevel, flags, pitch); |
|
} |
|
} |
|
|
|
// update ramps at 5hz |
|
SetNextThink( gpGlobals->curtime + AMBIENT_GENERIC_THINK_DELAY ); |
|
return; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Init all ramp params in preparation to play a new sound. |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InitModulationParms(void) |
|
{ |
|
int pitchinc; |
|
|
|
m_dpv.volrun = m_iHealth * 10; // 0 - 100 |
|
if (m_dpv.volrun > 100) m_dpv.volrun = 100; |
|
if (m_dpv.volrun < 0) m_dpv.volrun = 0; |
|
|
|
// get presets |
|
if (m_dpv.preset != 0 && m_dpv.preset <= CDPVPRESETMAX) |
|
{ |
|
// load preset values |
|
m_dpv = rgdpvpreset[m_dpv.preset - 1]; |
|
|
|
// fixup preset values, just like |
|
// fixups in KeyValue routine. |
|
if (m_dpv.spindown > 0) |
|
m_dpv.spindown = (101 - m_dpv.spindown) * 64; |
|
if (m_dpv.spinup > 0) |
|
m_dpv.spinup = (101 - m_dpv.spinup) * 64; |
|
|
|
m_dpv.volstart *= 10; |
|
m_dpv.volrun *= 10; |
|
|
|
if (m_dpv.fadein > 0) |
|
m_dpv.fadein = (101 - m_dpv.fadein) * 64; |
|
if (m_dpv.fadeout > 0) |
|
m_dpv.fadeout = (101 - m_dpv.fadeout) * 64; |
|
|
|
m_dpv.lforate *= 256; |
|
|
|
m_dpv.fadeinsav = m_dpv.fadein; |
|
m_dpv.fadeoutsav = m_dpv.fadeout; |
|
m_dpv.spinupsav = m_dpv.spinup; |
|
m_dpv.spindownsav = m_dpv.spindown; |
|
} |
|
|
|
m_dpv.fadein = m_dpv.fadeinsav; |
|
m_dpv.fadeout = 0; |
|
|
|
if (m_dpv.fadein) |
|
m_dpv.vol = m_dpv.volstart; |
|
else |
|
m_dpv.vol = m_dpv.volrun; |
|
|
|
m_dpv.spinup = m_dpv.spinupsav; |
|
m_dpv.spindown = 0; |
|
|
|
if (m_dpv.spinup) |
|
m_dpv.pitch = m_dpv.pitchstart; |
|
else |
|
m_dpv.pitch = m_dpv.pitchrun; |
|
|
|
if (m_dpv.pitch == 0) |
|
m_dpv.pitch = PITCH_NORM; |
|
|
|
m_dpv.pitchfrac = m_dpv.pitch << 8; |
|
m_dpv.volfrac = m_dpv.vol << 8; |
|
|
|
m_dpv.lfofrac = 0; |
|
m_dpv.lforate = abs(m_dpv.lforate); |
|
|
|
m_dpv.cspincount = 1; |
|
|
|
if (m_dpv.cspinup) |
|
{ |
|
pitchinc = (255 - m_dpv.pitchstart) / m_dpv.cspinup; |
|
|
|
m_dpv.pitchrun = m_dpv.pitchstart + pitchinc; |
|
if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255; |
|
} |
|
|
|
if ((m_dpv.spinupsav || m_dpv.spindownsav || (m_dpv.lfotype && m_dpv.lfomodpitch)) |
|
&& (m_dpv.pitch == PITCH_NORM)) |
|
m_dpv.pitch = PITCH_NORM + 1; // must never send 'no pitch' as first pitch |
|
// if we intend to pitch shift later! |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler that begins playing the sound. |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputPlaySound( inputdata_t &inputdata ) |
|
{ |
|
if (!m_fActive) |
|
{ |
|
//Adrian: Stop our current sound before starting a new one! |
|
SendSound( SND_STOP ); |
|
|
|
ToggleSound(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler that stops playing the sound. |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputStopSound( inputdata_t &inputdata ) |
|
{ |
|
if (m_fActive) |
|
{ |
|
ToggleSound(); |
|
} |
|
} |
|
|
|
void CAmbientGeneric::SendSound( SoundFlags_t flags) |
|
{ |
|
char *szSoundFile = (char *)STRING( m_iszSound ); |
|
CBaseEntity* pSoundSource = m_hSoundSource; |
|
if ( pSoundSource ) |
|
{ |
|
if ( flags == SND_STOP ) |
|
{ |
|
UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), szSoundFile, |
|
0, SNDLVL_NONE, flags, 0); |
|
} |
|
else |
|
{ |
|
UTIL_EmitAmbientSound(pSoundSource->GetSoundSourceIndex(), pSoundSource->GetAbsOrigin(), szSoundFile, |
|
(m_dpv.vol * 0.01), m_iSoundLevel, flags, m_dpv.pitch); |
|
} |
|
} |
|
else |
|
{ |
|
if ( ( flags == SND_STOP ) && |
|
( m_nSoundSourceEntIndex != -1 ) ) |
|
{ |
|
UTIL_EmitAmbientSound(m_nSoundSourceEntIndex, GetAbsOrigin(), szSoundFile, |
|
0, SNDLVL_NONE, flags, 0); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler that stops playing the sound. |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::InputToggleSound( inputdata_t &inputdata ) |
|
{ |
|
ToggleSound(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns an ambient sound on or off. If the ambient is a looping sound, |
|
// mark sound as active (m_fActive) if it's playing, innactive if not. |
|
// If the sound is not a looping sound, never mark it as active. |
|
// Input : pActivator - |
|
// pCaller - |
|
// useType - |
|
// value - |
|
//----------------------------------------------------------------------------- |
|
void CAmbientGeneric::ToggleSound() |
|
{ |
|
// m_fActive is true only if a looping sound is playing. |
|
|
|
if ( m_fActive ) |
|
{// turn sound off |
|
|
|
if (m_dpv.cspinup) |
|
{ |
|
// Don't actually shut off. Each toggle causes |
|
// incremental spinup to max pitch |
|
|
|
if (m_dpv.cspincount <= m_dpv.cspinup) |
|
{ |
|
int pitchinc; |
|
|
|
// start a new spinup |
|
m_dpv.cspincount++; |
|
|
|
pitchinc = (255 - m_dpv.pitchstart) / m_dpv.cspinup; |
|
|
|
m_dpv.spinup = m_dpv.spinupsav; |
|
m_dpv.spindown = 0; |
|
|
|
m_dpv.pitchrun = m_dpv.pitchstart + pitchinc * m_dpv.cspincount; |
|
if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255; |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
|
|
} |
|
else |
|
{ |
|
m_fActive = false; |
|
|
|
// HACKHACK - this makes the code in Precache() work properly after a save/restore |
|
m_spawnflags |= SF_AMBIENT_SOUND_START_SILENT; |
|
|
|
if (m_dpv.spindownsav || m_dpv.fadeoutsav) |
|
{ |
|
// spin it down (or fade it) before shutoff if spindown is set |
|
m_dpv.spindown = m_dpv.spindownsav; |
|
m_dpv.spinup = 0; |
|
|
|
m_dpv.fadeout = m_dpv.fadeoutsav; |
|
m_dpv.fadein = 0; |
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
} |
|
else |
|
{ |
|
SendSound( SND_STOP ); // stop sound |
|
} |
|
} |
|
} |
|
else |
|
{// turn sound on |
|
|
|
// only toggle if this is a looping sound. If not looping, each |
|
// trigger will cause the sound to play. If the sound is still |
|
// playing from a previous trigger press, it will be shut off |
|
// and then restarted. |
|
|
|
if (m_fLooping) |
|
m_fActive = true; |
|
else |
|
{ |
|
// shut sound off now - may be interrupting a long non-looping sound |
|
SendSound( SND_STOP ); // stop sound |
|
} |
|
|
|
// init all ramp params for startup |
|
|
|
InitModulationParms(); |
|
|
|
SendSound( SND_NOFLAGS ); // send sound |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
|
|
} |
|
} |
|
|
|
|
|
// KeyValue - load keyvalue pairs into member data of the |
|
// ambient generic. NOTE: called BEFORE spawn! |
|
bool CAmbientGeneric::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
// NOTE: changing any of the modifiers in this code |
|
// NOTE: also requires changing InitModulationParms code. |
|
|
|
// preset |
|
if (FStrEq(szKeyName, "preset")) |
|
{ |
|
m_dpv.preset = atoi(szValue); |
|
} |
|
// pitchrun |
|
else if (FStrEq(szKeyName, "pitch")) |
|
{ |
|
m_dpv.pitchrun = atoi(szValue); |
|
|
|
if (m_dpv.pitchrun > 255) m_dpv.pitchrun = 255; |
|
if (m_dpv.pitchrun < 0) m_dpv.pitchrun = 0; |
|
} |
|
// pitchstart |
|
else if (FStrEq(szKeyName, "pitchstart")) |
|
{ |
|
m_dpv.pitchstart = atoi(szValue); |
|
|
|
if (m_dpv.pitchstart > 255) m_dpv.pitchstart = 255; |
|
if (m_dpv.pitchstart < 0) m_dpv.pitchstart = 0; |
|
} |
|
// spinup |
|
else if (FStrEq(szKeyName, "spinup")) |
|
{ |
|
m_dpv.spinup = atoi(szValue); |
|
|
|
if (m_dpv.spinup > 100) m_dpv.spinup = 100; |
|
if (m_dpv.spinup < 0) m_dpv.spinup = 0; |
|
|
|
if (m_dpv.spinup > 0) |
|
m_dpv.spinup = (101 - m_dpv.spinup) * 64; |
|
m_dpv.spinupsav = m_dpv.spinup; |
|
} |
|
// spindown |
|
else if (FStrEq(szKeyName, "spindown")) |
|
{ |
|
m_dpv.spindown = atoi(szValue); |
|
|
|
if (m_dpv.spindown > 100) m_dpv.spindown = 100; |
|
if (m_dpv.spindown < 0) m_dpv.spindown = 0; |
|
|
|
if (m_dpv.spindown > 0) |
|
m_dpv.spindown = (101 - m_dpv.spindown) * 64; |
|
m_dpv.spindownsav = m_dpv.spindown; |
|
} |
|
// volstart |
|
else if (FStrEq(szKeyName, "volstart")) |
|
{ |
|
m_dpv.volstart = atoi(szValue); |
|
|
|
if (m_dpv.volstart > 10) m_dpv.volstart = 10; |
|
if (m_dpv.volstart < 0) m_dpv.volstart = 0; |
|
|
|
m_dpv.volstart *= 10; // 0 - 100 |
|
} |
|
// legacy fadein |
|
else if (FStrEq(szKeyName, "fadein")) |
|
{ |
|
m_dpv.fadein = atoi(szValue); |
|
|
|
if (m_dpv.fadein > 100) m_dpv.fadein = 100; |
|
if (m_dpv.fadein < 0) m_dpv.fadein = 0; |
|
|
|
if (m_dpv.fadein > 0) |
|
m_dpv.fadein = (101 - m_dpv.fadein) * 64; |
|
m_dpv.fadeinsav = m_dpv.fadein; |
|
} |
|
// legacy fadeout |
|
else if (FStrEq(szKeyName, "fadeout")) |
|
{ |
|
m_dpv.fadeout = atoi(szValue); |
|
|
|
if (m_dpv.fadeout > 100) m_dpv.fadeout = 100; |
|
if (m_dpv.fadeout < 0) m_dpv.fadeout = 0; |
|
|
|
if (m_dpv.fadeout > 0) |
|
m_dpv.fadeout = (101 - m_dpv.fadeout) * 64; |
|
m_dpv.fadeoutsav = m_dpv.fadeout; |
|
} |
|
// fadeinsecs |
|
else if (FStrEq(szKeyName, "fadeinsecs")) |
|
{ |
|
m_dpv.fadein = atoi(szValue); |
|
|
|
if (m_dpv.fadein > 100) m_dpv.fadein = 100; |
|
if (m_dpv.fadein < 0) m_dpv.fadein = 0; |
|
|
|
if (m_dpv.fadein > 0) |
|
m_dpv.fadein = ( 100 << 8 ) / ( m_dpv.fadein * AMBIENT_GENERIC_UPDATE_RATE ); |
|
m_dpv.fadeinsav = m_dpv.fadein; |
|
} |
|
// fadeoutsecs |
|
else if (FStrEq(szKeyName, "fadeoutsecs")) |
|
{ |
|
m_dpv.fadeout = atoi(szValue); |
|
|
|
if (m_dpv.fadeout > 100) m_dpv.fadeout = 100; |
|
if (m_dpv.fadeout < 0) m_dpv.fadeout = 0; |
|
|
|
if (m_dpv.fadeout > 0) |
|
m_dpv.fadeout = ( 100 << 8 ) / ( m_dpv.fadeout * AMBIENT_GENERIC_UPDATE_RATE ); |
|
m_dpv.fadeoutsav = m_dpv.fadeout; |
|
} |
|
// lfotype |
|
else if (FStrEq(szKeyName, "lfotype")) |
|
{ |
|
m_dpv.lfotype = atoi(szValue); |
|
if (m_dpv.lfotype > 4) m_dpv.lfotype = LFO_TRIANGLE; |
|
} |
|
// lforate |
|
else if (FStrEq(szKeyName, "lforate")) |
|
{ |
|
m_dpv.lforate = atoi(szValue); |
|
|
|
if (m_dpv.lforate > 1000) m_dpv.lforate = 1000; |
|
if (m_dpv.lforate < 0) m_dpv.lforate = 0; |
|
|
|
m_dpv.lforate *= 256; |
|
} |
|
// lfomodpitch |
|
else if (FStrEq(szKeyName, "lfomodpitch")) |
|
{ |
|
m_dpv.lfomodpitch = atoi(szValue); |
|
if (m_dpv.lfomodpitch > 100) m_dpv.lfomodpitch = 100; |
|
if (m_dpv.lfomodpitch < 0) m_dpv.lfomodpitch = 0; |
|
} |
|
|
|
// lfomodvol |
|
else if (FStrEq(szKeyName, "lfomodvol")) |
|
{ |
|
m_dpv.lfomodvol = atoi(szValue); |
|
if (m_dpv.lfomodvol > 100) m_dpv.lfomodvol = 100; |
|
if (m_dpv.lfomodvol < 0) m_dpv.lfomodvol = 0; |
|
} |
|
// cspinup |
|
else if (FStrEq(szKeyName, "cspinup")) |
|
{ |
|
m_dpv.cspinup = atoi(szValue); |
|
if (m_dpv.cspinup > 100) m_dpv.cspinup = 100; |
|
if (m_dpv.cspinup < 0) m_dpv.cspinup = 0; |
|
} |
|
else |
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
// =================== ROOM SOUND FX ========================================== |
|
|
|
|
|
|
|
|
|
// ==================== SENTENCE GROUPS, UTILITY FUNCTIONS ====================================== |
|
|
|
int fSentencesInit = false; |
|
|
|
// ===================== SENTENCE GROUPS, MAIN ROUTINES ======================== |
|
|
|
// given sentence group index, play random sentence for given entity. |
|
// returns sentenceIndex - which sentence was picked |
|
// Ipick is only needed if you plan on stopping the sound before playback is done (see SENTENCEG_Stop). |
|
// sentenceIndex can be used to find the name/length of the sentence |
|
|
|
int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, |
|
float volume, soundlevel_t soundlevel, int flags, int pitch) |
|
{ |
|
char name[64]; |
|
int ipick; |
|
|
|
if (!fSentencesInit) |
|
return -1; |
|
|
|
name[0] = 0; |
|
|
|
ipick = engine->SentenceGroupPick( isentenceg, name, sizeof( name ) ); |
|
if (ipick > 0 && name) |
|
{ |
|
int sentenceIndex = SENTENCEG_Lookup( name ); |
|
CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel ); |
|
CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, sentenceIndex, volume, soundlevel, flags, pitch ); |
|
return sentenceIndex; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Picks a sentence, but doesn't play it |
|
//----------------------------------------------------------------------------- |
|
int SENTENCEG_PickRndSz(const char *szgroupname) |
|
{ |
|
char name[64]; |
|
int ipick; |
|
int isentenceg; |
|
|
|
if (!fSentencesInit) |
|
return -1; |
|
|
|
name[0] = 0; |
|
|
|
isentenceg = engine->SentenceGroupIndexFromName(szgroupname); |
|
if (isentenceg < 0) |
|
{ |
|
Warning( "No such sentence group %s\n", szgroupname ); |
|
return -1; |
|
} |
|
|
|
ipick = engine->SentenceGroupPick(isentenceg, name, sizeof( name )); |
|
if (ipick >= 0 && name[0]) |
|
{ |
|
return SENTENCEG_Lookup( name ); |
|
} |
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Plays a sentence by sentence index |
|
//----------------------------------------------------------------------------- |
|
void SENTENCEG_PlaySentenceIndex( edict_t *entity, int iSentenceIndex, float volume, soundlevel_t soundlevel, int flags, int pitch ) |
|
{ |
|
if ( iSentenceIndex >= 0 ) |
|
{ |
|
CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel ); |
|
CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, iSentenceIndex, volume, soundlevel, flags, pitch ); |
|
} |
|
} |
|
|
|
|
|
int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname, |
|
float volume, soundlevel_t soundlevel, int flags, int pitch) |
|
{ |
|
char name[64]; |
|
int ipick; |
|
int isentenceg; |
|
|
|
if (!fSentencesInit) |
|
return -1; |
|
|
|
name[0] = 0; |
|
|
|
isentenceg = engine->SentenceGroupIndexFromName(szgroupname); |
|
if (isentenceg < 0) |
|
{ |
|
Warning( "No such sentence group %s\n", szgroupname ); |
|
return -1; |
|
} |
|
|
|
ipick = engine->SentenceGroupPick(isentenceg, name, sizeof( name )); |
|
if (ipick >= 0 && name[0]) |
|
{ |
|
int sentenceIndex = SENTENCEG_Lookup( name ); |
|
CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel ); |
|
CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, sentenceIndex, volume, soundlevel, flags, pitch ); |
|
return sentenceIndex; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
// play sentences in sequential order from sentence group. Reset after last sentence. |
|
|
|
int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szgroupname, |
|
float volume, soundlevel_t soundlevel, int flags, int pitch, int ipick, int freset) |
|
{ |
|
char name[64]; |
|
int ipicknext; |
|
int isentenceg; |
|
|
|
if (!fSentencesInit) |
|
return -1; |
|
|
|
name[0] = 0; |
|
|
|
isentenceg = engine->SentenceGroupIndexFromName(szgroupname); |
|
if (isentenceg < 0) |
|
return -1; |
|
|
|
ipicknext = engine->SentenceGroupPickSequential(isentenceg, name, sizeof( name ), ipick, freset); |
|
if (ipicknext >= 0 && name[0]) |
|
{ |
|
int sentenceIndex = SENTENCEG_Lookup( name ); |
|
CPASAttenuationFilter filter( GetContainingEntity( entity ), soundlevel ); |
|
CBaseEntity::EmitSentenceByIndex( filter, ENTINDEX(entity), CHAN_VOICE, sentenceIndex, volume, soundlevel, flags, pitch ); |
|
return sentenceIndex; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
#if 0 |
|
// for this entity, for the given sentence within the sentence group, stop |
|
// the sentence. |
|
|
|
void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick) |
|
{ |
|
char buffer[64]; |
|
char sznum[8]; |
|
|
|
if (!fSentencesInit) |
|
return; |
|
|
|
if (isentenceg < 0 || ipick < 0) |
|
return; |
|
|
|
Q_snprintf(buffer,sizeof(buffer),"!%s%d", engine->SentenceGroupNameFromIndex( isentenceg ), ipick ); |
|
|
|
UTIL_StopSound(entity, CHAN_VOICE, buffer); |
|
} |
|
#endif |
|
|
|
// open sentences.txt, scan for groups, build rgsentenceg |
|
// Should be called from world spawn, only works on the |
|
// first call and is ignored subsequently. |
|
void SENTENCEG_Init() |
|
{ |
|
if (fSentencesInit) |
|
return; |
|
|
|
engine->PrecacheSentenceFile( "scripts/sentences.txt" ); |
|
fSentencesInit = true; |
|
} |
|
|
|
// convert sentence (sample) name to !sentencenum, return !sentencenum |
|
|
|
int SENTENCEG_Lookup(const char *sample) |
|
{ |
|
return engine->SentenceIndexFromName( sample + 1 ); |
|
} |
|
|
|
|
|
int SENTENCEG_GetIndex(const char *szrootname) |
|
{ |
|
return engine->SentenceGroupIndexFromName( szrootname ); |
|
} |
|
|
|
void UTIL_RestartAmbientSounds( void ) |
|
{ |
|
CAmbientGeneric *pAmbient = NULL; |
|
while ( ( pAmbient = (CAmbientGeneric*) gEntList.FindEntityByClassname( pAmbient, "ambient_generic" ) ) != NULL ) |
|
{ |
|
if (pAmbient->m_fActive ) |
|
{ |
|
if ( strstr( STRING( pAmbient->m_iszSound ), "mp3" ) ) |
|
{ |
|
pAmbient->SendSound( SND_CHANGE_VOL ); // fake a change, so we don't create 2 sounds |
|
} |
|
pAmbient->SendSound( SND_CHANGE_VOL ); // fake a change, so we don't create 2 sounds |
|
} |
|
} |
|
} |
|
|
|
|
|
// play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename |
|
|
|
void UTIL_EmitSoundSuit(edict_t *entity, const char *sample) |
|
{ |
|
float fvol; |
|
int pitch = PITCH_NORM; |
|
|
|
fvol = suitvolume.GetFloat(); |
|
if (random->RandomInt(0,1)) |
|
pitch = random->RandomInt(0,6) + 98; |
|
|
|
// If friendlies are talking, reduce the volume of the suit |
|
if ( !g_AIFriendliesTalkSemaphore.IsAvailable( GetContainingEntity( entity ) ) ) |
|
{ |
|
fvol *= 0.3; |
|
} |
|
|
|
if (fvol > 0.05) |
|
{ |
|
CPASAttenuationFilter filter( GetContainingEntity( entity ) ); |
|
filter.MakeReliable(); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_STATIC; |
|
ep.m_pSoundName = sample; |
|
ep.m_flVolume = fvol; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
ep.m_nPitch = pitch; |
|
|
|
CBaseEntity::EmitSound( filter, ENTINDEX(entity), ep ); |
|
} |
|
} |
|
|
|
// play a sentence, randomly selected from the passed in group id, over the HEV suit speaker |
|
|
|
int UTIL_EmitGroupIDSuit(edict_t *entity, int isentenceg) |
|
{ |
|
float fvol; |
|
int pitch = PITCH_NORM; |
|
int sentenceIndex = -1; |
|
|
|
fvol = suitvolume.GetFloat(); |
|
if (random->RandomInt(0,1)) |
|
pitch = random->RandomInt(0,6) + 98; |
|
|
|
// If friendlies are talking, reduce the volume of the suit |
|
if ( !g_AIFriendliesTalkSemaphore.IsAvailable( GetContainingEntity( entity ) ) ) |
|
{ |
|
fvol *= 0.3; |
|
} |
|
|
|
if (fvol > 0.05) |
|
sentenceIndex = SENTENCEG_PlayRndI(entity, isentenceg, fvol, SNDLVL_NORM, 0, pitch); |
|
|
|
return sentenceIndex; |
|
} |
|
|
|
// play a sentence, randomly selected from the passed in groupname |
|
|
|
int UTIL_EmitGroupnameSuit(edict_t *entity, const char *groupname) |
|
{ |
|
float fvol; |
|
int pitch = PITCH_NORM; |
|
int sentenceIndex = -1; |
|
|
|
fvol = suitvolume.GetFloat(); |
|
if (random->RandomInt(0,1)) |
|
pitch = random->RandomInt(0,6) + 98; |
|
|
|
// If friendlies are talking, reduce the volume of the suit |
|
if ( !g_AIFriendliesTalkSemaphore.IsAvailable( GetContainingEntity( entity ) ) ) |
|
{ |
|
fvol *= 0.3; |
|
} |
|
|
|
if (fvol > 0.05) |
|
sentenceIndex = SENTENCEG_PlayRndSz(entity, groupname, fvol, SNDLVL_NORM, 0, pitch); |
|
|
|
return sentenceIndex; |
|
} |
|
|
|
// ===================== MATERIAL TYPE DETECTION, MAIN ROUTINES ======================== |
|
// |
|
// Used to detect the texture the player is standing on, map the |
|
// texture name to a material type. Play footstep sound based |
|
// on material type. |
|
|
|
char TEXTURETYPE_Find( trace_t *ptr ) |
|
{ |
|
const surfacedata_t *psurfaceData = physprops->GetSurfaceData( ptr->surface.surfaceProps ); |
|
|
|
return psurfaceData->game.material; |
|
}
|
|
|