2016-06-04 15:24:23 +02:00
/***
*
* Copyright ( c ) 1996 - 2002 , Valve LLC . All rights reserved .
*
* This product contains software technology licensed from Id
* Software , Inc . ( " Id Technology " ) . Id Technology ( c ) 1996 Id Software , Inc .
* All Rights Reserved .
*
* Use , distribution , and modification of this source code and / or resulting
* object code is restricted to non - commercial enhancements to products from
* Valve LLC . All other use , distribution , or modification is prohibited
* without written permission from Valve LLC .
*
* * * */
//=========================================================
// sound.cpp
//=========================================================
# include "extdll.h"
# include "util.h"
# include "cbase.h"
# include "weapons.h"
# include "player.h"
# include "talkmonster.h"
# include "gamerules.h"
static char * memfgets ( byte * pMemFile , int fileSize , int & filePos , char * pBuffer , int bufferSize ) ;
// ==================== 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
2016-07-31 15:48:50 +02:00
int lfotype ; // 0) off 1) square 2) triangle 3) random
2016-06-04 15:24:23 +02:00
int lforate ; // 0 - 1000, how fast lfo osciallates
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
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
2016-07-31 15:48:50 +02:00
int cspincount ;
2016-06-04 15:24:23 +02:00
int pitch ;
int spinupsav ;
int spindownsav ;
int pitchfrac ;
int vol ;
int fadeinsav ;
int fadeoutsav ;
int volfrac ;
2016-07-31 15:48:50 +02:00
int lfofrac ;
int lfomult ;
2016-06-04 15:24:23 +02:00
} dynpitchvol_t ;
# define CDPVPRESETMAX 27
// presets for runtime pitch and vol modulation of ambient sounds
2016-06-25 18:33:39 +02:00
dynpitchvol_t rgdpvpreset [ CDPVPRESETMAX ] =
2016-06-04 15:24:23 +02:00
{
// 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 CBaseEntity
{
public :
2016-07-31 15:48:50 +02:00
void KeyValue ( KeyValueData * pkvd ) ;
2016-06-04 15:24:23 +02:00
void Spawn ( void ) ;
void Precache ( void ) ;
2016-07-31 15:48:50 +02:00
void EXPORT ToggleUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
2016-06-04 15:24:23 +02:00
void EXPORT RampThink ( void ) ;
2016-07-31 15:48:50 +02:00
void InitModulationParms ( void ) ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
virtual int ObjectCaps ( void ) { return ( CBaseEntity : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ) ; }
2016-06-04 15:24:23 +02:00
float m_flAttenuation ; // attenuation value
2016-07-31 15:48:50 +02:00
dynpitchvol_t m_dpv ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
BOOL m_fActive ; // only TRUE when the entity is playing a looping sound
BOOL m_fLooping ; // TRUE when the sound played will loop
2016-06-04 15:24:23 +02:00
} ;
2016-06-25 18:33:39 +02:00
LINK_ENTITY_TO_CLASS ( ambient_generic , CAmbientGeneric )
TYPEDESCRIPTION CAmbientGeneric : : m_SaveData [ ] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD ( CAmbientGeneric , m_flAttenuation , FIELD_FLOAT ) ,
DEFINE_FIELD ( CAmbientGeneric , m_fActive , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( CAmbientGeneric , m_fLooping , FIELD_BOOLEAN ) ,
// 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 ( CAmbientGeneric , m_dpv , FIELD_CHARACTER , sizeof ( dynpitchvol_t ) ) ,
} ;
2016-06-25 18:33:39 +02:00
IMPLEMENT_SAVERESTORE ( CAmbientGeneric , CBaseEntity )
2016-06-04 15:24:23 +02:00
//
// ambient_generic - general-purpose user-defined static sound
//
2016-07-31 15:48:50 +02:00
void CAmbientGeneric : : Spawn ( void )
2016-06-04 15:24:23 +02:00
{
/*
- 1 : " Default "
0 : " Everywhere "
200 : " Small Radius "
125 : " Medium Radius "
80 : " Large Radius "
*/
2016-07-31 15:48:50 +02:00
if ( FBitSet ( pev - > spawnflags , AMBIENT_SOUND_EVERYWHERE ) )
2016-06-04 15:24:23 +02:00
{
m_flAttenuation = ATTN_NONE ;
}
2016-07-31 15:48:50 +02:00
else if ( FBitSet ( pev - > spawnflags , AMBIENT_SOUND_SMALLRADIUS ) )
2016-06-04 15:24:23 +02:00
{
m_flAttenuation = ATTN_IDLE ;
}
2016-07-31 15:48:50 +02:00
else if ( FBitSet ( pev - > spawnflags , AMBIENT_SOUND_MEDIUMRADIUS ) )
2016-06-04 15:24:23 +02:00
{
m_flAttenuation = ATTN_STATIC ;
}
2016-07-31 15:48:50 +02:00
else if ( FBitSet ( pev - > spawnflags , AMBIENT_SOUND_LARGERADIUS ) )
2016-06-04 15:24:23 +02:00
{
m_flAttenuation = ATTN_NORM ;
}
else
2016-06-25 18:33:39 +02:00
{
// if the designer didn't set a sound attenuation, default to one.
2016-06-04 15:24:23 +02:00
m_flAttenuation = ATTN_STATIC ;
}
2016-06-25 18:33:39 +02:00
2017-07-23 23:24:55 +02:00
const char * szSoundFile = STRING ( pev - > message ) ;
2016-06-04 15:24:23 +02:00
2019-09-24 00:00:37 +02:00
if ( FStringNull ( pev - > message ) | | szSoundFile [ 0 ] = = ' \0 ' )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
ALERT ( at_error , " EMPTY AMBIENT AT: %f, %f, %f \n " , ( double ) pev - > origin . x , ( double ) pev - > origin . y , ( double ) pev - > origin . z ) ;
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
SetThink ( & CBaseEntity : : SUB_Remove ) ;
return ;
}
2016-07-31 15:48:50 +02:00
pev - > solid = SOLID_NOT ;
pev - > movetype = MOVETYPE_NONE ;
2016-06-04 15:24:23 +02:00
// Set up think function for dynamic modification
// of ambient sound's pitch or volume. Don't
// start thinking yet.
2016-07-31 15:48:50 +02:00
SetThink ( & CAmbientGeneric : : RampThink ) ;
2019-10-13 13:49:25 +02:00
pev - > nextthink = 0.0f ;
2016-06-04 15:24:23 +02:00
// allow on/off switching via 'use' function.
SetUse ( & CAmbientGeneric : : ToggleUse ) ;
m_fActive = FALSE ;
2016-07-31 15:48:50 +02:00
if ( FBitSet ( pev - > spawnflags , AMBIENT_SOUND_NOT_LOOPING ) )
2016-06-04 15:24:23 +02:00
m_fLooping = FALSE ;
else
m_fLooping = TRUE ;
2016-07-31 15:48:50 +02:00
Precache ( ) ;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
void CAmbientGeneric : : Precache ( void )
2016-06-04 15:24:23 +02:00
{
2017-07-23 23:24:55 +02:00
const char * szSoundFile = STRING ( pev - > message ) ;
2016-06-04 15:24:23 +02:00
2019-09-24 00:00:37 +02:00
if ( ! FStringNull ( pev - > message ) & & szSoundFile [ 0 ] ! = ' \0 ' )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( * szSoundFile ! = ' ! ' )
PRECACHE_SOUND ( szSoundFile ) ;
2016-06-04 15:24:23 +02:00
}
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// init all dynamic modulation parms
InitModulationParms ( ) ;
2016-07-31 15:48:50 +02:00
if ( ! FBitSet ( pev - > spawnflags , AMBIENT_SOUND_START_SILENT ) )
2016-06-04 15:24:23 +02:00
{
// start the sound ASAP
2016-07-31 15:48:50 +02:00
if ( m_fLooping )
2016-06-04 15:24:23 +02:00
m_fActive = TRUE ;
}
2016-07-31 15:48:50 +02:00
if ( m_fActive )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile ,
2019-10-13 13:49:25 +02:00
( m_dpv . vol * 0.01f ) , m_flAttenuation , SND_SPAWNING , m_dpv . pitch ) ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
}
}
// RampThink - 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.
2016-07-31 15:48:50 +02:00
void CAmbientGeneric : : RampThink ( void )
2016-06-04 15:24:23 +02:00
{
2017-07-23 23:24:55 +02:00
const char * szSoundFile = STRING ( pev - > message ) ;
2016-06-04 15:24:23 +02:00
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
2016-07-31 15:48:50 +02:00
int prev ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( ! m_dpv . spinup & & ! m_dpv . spindown & & ! m_dpv . fadein & & ! m_dpv . fadeout & & ! m_dpv . lfotype )
2016-06-04 15:24:23 +02:00
return ; // no ramps or lfo, stop thinking
// ==============
// pitch envelope
// ==============
2016-07-31 15:48:50 +02:00
if ( m_dpv . spinup | | m_dpv . spindown )
2016-06-04 15:24:23 +02:00
{
prev = m_dpv . pitchfrac > > 8 ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . spinup > 0 )
2016-06-04 15:24:23 +02:00
m_dpv . pitchfrac + = m_dpv . spinup ;
2016-07-31 15:48:50 +02:00
else if ( m_dpv . spindown > 0 )
2016-06-04 15:24:23 +02:00
m_dpv . pitchfrac - = m_dpv . spindown ;
pitch = m_dpv . pitchfrac > > 8 ;
2016-07-31 15:48:50 +02:00
if ( pitch > m_dpv . pitchrun )
2016-06-04 15:24:23 +02:00
{
pitch = m_dpv . pitchrun ;
m_dpv . spinup = 0 ; // done with ramp up
}
2016-07-31 15:48:50 +02:00
if ( pitch < m_dpv . pitchstart )
2016-06-04 15:24:23 +02:00
{
2019-11-16 09:54:30 +01:00
// pitch = m_dpv.pitchstart;
2016-06-04 15:24:23 +02:00
m_dpv . spindown = 0 ; // done with ramp down
// shut sound off
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile ,
0 , 0 , SND_STOP , 0 ) ;
2016-06-04 15:24:23 +02:00
// return without setting nextthink
return ;
}
2016-07-31 15:48:50 +02:00
if ( pitch > 255 )
pitch = 255 ;
if ( pitch < 1 )
pitch = 1 ;
2016-06-04 15:24:23 +02:00
m_dpv . pitch = pitch ;
2016-07-31 15:48:50 +02:00
fChanged | = ( prev ! = pitch ) ;
2016-06-04 15:24:23 +02:00
flags | = SND_CHANGE_PITCH ;
}
// ==================
// amplitude envelope
// ==================
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadein | | m_dpv . fadeout )
2016-06-04 15:24:23 +02:00
{
prev = m_dpv . volfrac > > 8 ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadein > 0 )
2016-06-04 15:24:23 +02:00
m_dpv . volfrac + = m_dpv . fadein ;
2016-07-31 15:48:50 +02:00
else if ( m_dpv . fadeout > 0 )
2016-06-04 15:24:23 +02:00
m_dpv . volfrac - = m_dpv . fadeout ;
vol = m_dpv . volfrac > > 8 ;
2016-07-31 15:48:50 +02:00
if ( vol > m_dpv . volrun )
2016-06-04 15:24:23 +02:00
{
vol = m_dpv . volrun ;
m_dpv . fadein = 0 ; // done with ramp up
}
2016-07-31 15:48:50 +02:00
if ( vol < m_dpv . volstart )
2016-06-04 15:24:23 +02:00
{
2019-11-16 09:54:30 +01:00
// vol = m_dpv.volstart;
2016-06-04 15:24:23 +02:00
m_dpv . fadeout = 0 ; // done with ramp down
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// shut sound off
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile ,
0 , 0 , SND_STOP , 0 ) ;
2016-06-04 15:24:23 +02:00
// return without setting nextthink
return ;
}
2016-07-31 15:48:50 +02:00
if ( vol > 100 )
vol = 100 ;
if ( vol < 1 )
vol = 1 ;
2016-06-04 15:24:23 +02:00
m_dpv . vol = vol ;
2016-07-31 15:48:50 +02:00
fChanged | = ( prev ! = vol ) ;
2016-06-04 15:24:23 +02:00
flags | = SND_CHANGE_VOL ;
}
// ===================
// pitch/amplitude LFO
// ===================
2016-07-31 15:48:50 +02:00
if ( m_dpv . lfotype )
2016-06-04 15:24:23 +02:00
{
int pos ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . lfofrac > 0x6fffffff )
2016-06-04 15:24:23 +02:00
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 ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . lfofrac < 0 )
2016-06-04 15:24:23 +02:00
{
m_dpv . lfofrac = 0 ;
2016-07-31 15:48:50 +02:00
m_dpv . lforate = abs ( m_dpv . lforate ) ;
2016-06-04 15:24:23 +02:00
pos = 0 ;
}
2016-07-31 15:48:50 +02:00
else if ( pos > 255 )
2016-06-04 15:24:23 +02:00
{
pos = 255 ;
2016-07-31 15:48:50 +02:00
m_dpv . lfofrac = ( 255 < < 8 ) ;
m_dpv . lforate = - abs ( m_dpv . lforate ) ;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
switch ( m_dpv . lfotype )
2016-06-04 15:24:23 +02:00
{
case LFO_SQUARE :
2016-07-31 15:48:50 +02:00
if ( pos < 128 )
2016-06-04 15:24:23 +02:00
m_dpv . lfomult = 255 ;
else
m_dpv . lfomult = 0 ;
break ;
case LFO_RANDOM :
2016-07-31 15:48:50 +02:00
if ( pos = = 255 )
m_dpv . lfomult = RANDOM_LONG ( 0 , 255 ) ;
2016-06-04 15:24:23 +02:00
break ;
case LFO_TRIANGLE :
default :
m_dpv . lfomult = pos ;
break ;
}
2016-07-31 15:48:50 +02:00
if ( m_dpv . lfomodpitch )
2016-06-04 15:24:23 +02:00
{
prev = pitch ;
// pitch 0-255
2016-07-31 15:48:50 +02:00
pitch + = ( ( m_dpv . lfomult - 128 ) * m_dpv . lfomodpitch ) / 100 ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( pitch > 255 )
pitch = 255 ;
if ( pitch < 1 )
pitch = 1 ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
fChanged | = ( prev ! = pitch ) ;
2016-06-04 15:24:23 +02:00
flags | = SND_CHANGE_PITCH ;
}
2016-07-31 15:48:50 +02:00
if ( m_dpv . lfomodvol )
2016-06-04 15:24:23 +02:00
{
// vol 0-100
prev = vol ;
2016-07-31 15:48:50 +02:00
vol + = ( ( m_dpv . lfomult - 128 ) * m_dpv . lfomodvol ) / 100 ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( vol > 100 )
vol = 100 ;
if ( vol < 0 )
vol = 0 ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
fChanged | = ( prev ! = vol ) ;
2016-06-04 15:24:23 +02:00
flags | = SND_CHANGE_VOL ;
}
}
// Send update to playing sound only if we actually changed
// pitch or volume in this routine.
2016-07-31 15:48:50 +02:00
if ( flags & & fChanged )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( pitch = = PITCH_NORM )
2016-06-04 15:24:23 +02:00
pitch = PITCH_NORM + 1 ; // don't send 'no pitch' !
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile ,
2019-10-13 13:49:25 +02:00
( vol * 0.01f ) , m_flAttenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
}
// update ramps at 5hz
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.2f ;
2016-06-04 15:24:23 +02:00
return ;
}
// Init all ramp params in preparation to
// play a new sound
2016-07-31 15:48:50 +02:00
void CAmbientGeneric : : InitModulationParms ( void )
2016-06-04 15:24:23 +02:00
{
int pitchinc ;
2017-06-29 15:56:03 +02:00
m_dpv . volrun = ( int ) ( pev - > health * 10 ) ; // 0 - 100
2016-07-31 15:48:50 +02:00
if ( m_dpv . volrun > 100 )
m_dpv . volrun = 100 ;
if ( m_dpv . volrun < 0 )
m_dpv . volrun = 0 ;
2016-06-04 15:24:23 +02:00
// get presets
2016-07-31 15:48:50 +02:00
if ( m_dpv . preset ! = 0 & & m_dpv . preset < = CDPVPRESETMAX )
2016-06-04 15:24:23 +02:00
{
// load preset values
m_dpv = rgdpvpreset [ m_dpv . preset - 1 ] ;
// fixup preset values, just like
// fixups in KeyValue routine.
2016-07-31 15:48:50 +02:00
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 ;
2016-06-04 15:24:23 +02:00
m_dpv . volstart * = 10 ;
m_dpv . volrun * = 10 ;
2016-07-31 15:48:50 +02:00
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 ;
2016-06-04 15:24:23 +02:00
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 ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadein )
2016-06-04 15:24:23 +02:00
m_dpv . vol = m_dpv . volstart ;
else
m_dpv . vol = m_dpv . volrun ;
m_dpv . spinup = m_dpv . spinupsav ;
m_dpv . spindown = 0 ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . spinup )
2016-06-04 15:24:23 +02:00
m_dpv . pitch = m_dpv . pitchstart ;
else
m_dpv . pitch = m_dpv . pitchrun ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . pitch = = 0 )
2016-06-04 15:24:23 +02:00
m_dpv . pitch = PITCH_NORM ;
m_dpv . pitchfrac = m_dpv . pitch < < 8 ;
m_dpv . volfrac = m_dpv . vol < < 8 ;
m_dpv . lfofrac = 0 ;
2016-07-31 15:48:50 +02:00
m_dpv . lforate = abs ( m_dpv . lforate ) ;
2016-06-04 15:24:23 +02:00
m_dpv . cspincount = 1 ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . cspinup )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
pitchinc = ( 255 - m_dpv . pitchstart ) / m_dpv . cspinup ;
2016-06-04 15:24:23 +02:00
m_dpv . pitchrun = m_dpv . pitchstart + pitchinc ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . pitchrun > 255 )
m_dpv . pitchrun = 255 ;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
if ( ( m_dpv . spinupsav | | m_dpv . spindownsav | | ( m_dpv . lfotype & & m_dpv . lfomodpitch ) )
& & ( m_dpv . pitch = = PITCH_NORM ) )
2016-06-04 15:24:23 +02:00
m_dpv . pitch = PITCH_NORM + 1 ; // must never send 'no pitch' as first pitch
2016-06-25 18:33:39 +02:00
// if we intend to pitch shift later!
2016-06-04 15:24:23 +02:00
}
//
// ToggleUse - 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.
//
2016-07-31 15:48:50 +02:00
void CAmbientGeneric : : ToggleUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
2016-06-04 15:24:23 +02:00
{
2017-07-23 23:24:55 +02:00
const char * szSoundFile = STRING ( pev - > message ) ;
2016-06-04 15:24:23 +02:00
float fraction ;
2016-07-31 15:48:50 +02:00
if ( useType ! = USE_TOGGLE )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( ( m_fActive & & useType = = USE_ON ) | | ( ! m_fActive & & useType = = USE_OFF ) )
2016-06-04 15:24:23 +02:00
return ;
}
2016-06-25 18:33:39 +02:00
// Directly change pitch if arg passed. Only works if sound is already playing.
2016-07-31 15:48:50 +02:00
if ( useType = = USE_SET & & m_fActive ) // Momentary buttons will pass down a float in here
2016-06-04 15:24:23 +02:00
{
fraction = value ;
2016-07-31 15:48:50 +02:00
2019-10-13 13:49:25 +02:00
if ( fraction > 1.0f )
fraction = 1.0f ;
if ( fraction < 0.0f )
fraction = 0.01f ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
m_dpv . pitch = ( int ) ( fraction * 255.0f ) ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile , 0 , 0 , SND_CHANGE_PITCH , m_dpv . pitch ) ;
2016-06-04 15:24:23 +02:00
return ;
}
// Toggle
// m_fActive is TRUE only if a looping sound is playing.
2016-07-31 15:48:50 +02:00
if ( m_fActive )
2016-06-25 18:33:39 +02:00
{
// turn sound off
2016-07-31 15:48:50 +02:00
if ( m_dpv . cspinup )
2016-06-04 15:24:23 +02:00
{
// Don't actually shut off. Each toggle causes
// incremental spinup to max pitch
2016-07-31 15:48:50 +02:00
if ( m_dpv . cspincount < = m_dpv . cspinup )
{
2016-06-04 15:24:23 +02:00
int pitchinc ;
// start a new spinup
m_dpv . cspincount + + ;
2016-07-31 15:48:50 +02:00
pitchinc = ( 255 - m_dpv . pitchstart ) / m_dpv . cspinup ;
2016-06-04 15:24:23 +02:00
m_dpv . spinup = m_dpv . spinupsav ;
m_dpv . spindown = 0 ;
m_dpv . pitchrun = m_dpv . pitchstart + pitchinc * m_dpv . cspincount ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . pitchrun > 255 )
m_dpv . pitchrun = 255 ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
}
}
else
{
m_fActive = FALSE ;
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// HACKHACK - this makes the code in Precache() work properly after a save/restore
pev - > spawnflags | = AMBIENT_SOUND_START_SILENT ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . spindownsav | | m_dpv . fadeoutsav )
2016-06-04 15:24:23 +02:00
{
// 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 ;
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
}
else
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile , 0 , 0 , SND_STOP , 0 ) ;
2016-06-04 15:24:23 +02:00
}
}
else
2016-06-25 18:33:39 +02:00
{
// turn sound on
2016-06-04 15:24:23 +02:00
// 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.
2016-07-31 15:48:50 +02:00
if ( m_fLooping )
2016-06-04 15:24:23 +02:00
m_fActive = TRUE ;
else
// shut sound off now - may be interrupting a long non-looping sound
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile , 0 , 0 , SND_STOP , 0 ) ;
2016-06-04 15:24:23 +02:00
2016-06-25 18:33:39 +02:00
// init all ramp params for startup
2016-06-04 15:24:23 +02:00
InitModulationParms ( ) ;
2019-10-13 13:49:25 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile , ( m_dpv . vol * 0.01f ) , m_flAttenuation , 0 , m_dpv . pitch ) ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
}
}
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// KeyValue - load keyvalue pairs into member data of the
// ambient generic. NOTE: called BEFORE spawn!
2016-07-31 15:48:50 +02:00
void CAmbientGeneric : : KeyValue ( KeyValueData * pkvd )
2016-06-04 15:24:23 +02:00
{
// NOTE: changing any of the modifiers in this code
// NOTE: also requires changing InitModulationParms code.
// preset
2016-07-31 15:48:50 +02:00
if ( FStrEq ( pkvd - > szKeyName , " preset " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . preset = atoi ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
// pitchrun
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " pitch " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . pitchrun = atoi ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
2016-07-31 15:48:50 +02:00
if ( m_dpv . pitchrun > 255 )
m_dpv . pitchrun = 255 ;
if ( m_dpv . pitchrun < 0 )
m_dpv . pitchrun = 0 ;
2016-06-25 18:33:39 +02:00
}
2016-06-04 15:24:23 +02:00
// pitchstart
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " pitchstart " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . pitchstart = atoi ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . pitchstart > 255 )
m_dpv . pitchstart = 255 ;
if ( m_dpv . pitchstart < 0 )
m_dpv . pitchstart = 0 ;
2016-06-04 15:24:23 +02:00
}
// spinup
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " spinup " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . spinup = atoi ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
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 ;
2016-06-04 15:24:23 +02:00
m_dpv . spinupsav = m_dpv . spinup ;
pkvd - > fHandled = TRUE ;
2016-06-25 18:33:39 +02:00
}
2016-06-04 15:24:23 +02:00
// spindown
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " spindown " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . spindown = atoi ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
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 ;
2016-06-04 15:24:23 +02:00
m_dpv . spindownsav = m_dpv . spindown ;
pkvd - > fHandled = TRUE ;
}
// volstart
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " volstart " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . volstart = atoi ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . volstart > 10 )
m_dpv . volstart = 10 ;
if ( m_dpv . volstart < 0 )
m_dpv . volstart = 0 ;
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
m_dpv . volstart * = 10 ; // 0 - 100
pkvd - > fHandled = TRUE ;
}
// fadein
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " fadein " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . fadein = atoi ( pkvd - > szValue ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadein > 100 )
m_dpv . fadein = 100 ;
if ( m_dpv . fadein < 0 )
m_dpv . fadein = 0 ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadein > 0 )
m_dpv . fadein = ( 101 - m_dpv . fadein ) * 64 ;
2016-06-04 15:24:23 +02:00
m_dpv . fadeinsav = m_dpv . fadein ;
pkvd - > fHandled = TRUE ;
}
// fadeout
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " fadeout " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . fadeout = atoi ( pkvd - > szValue ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadeout > 100 )
m_dpv . fadeout = 100 ;
if ( m_dpv . fadeout < 0 )
m_dpv . fadeout = 0 ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . fadeout > 0 )
m_dpv . fadeout = ( 101 - m_dpv . fadeout ) * 64 ;
2016-06-04 15:24:23 +02:00
m_dpv . fadeoutsav = m_dpv . fadeout ;
pkvd - > fHandled = TRUE ;
}
// lfotype
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " lfotype " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . lfotype = atoi ( pkvd - > szValue ) ;
if ( m_dpv . lfotype > 4 )
m_dpv . lfotype = LFO_TRIANGLE ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
// lforate
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " lforate " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . lforate = atoi ( pkvd - > szValue ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( m_dpv . lforate > 1000 )
m_dpv . lforate = 1000 ;
if ( m_dpv . lforate < 0 )
m_dpv . lforate = 0 ;
2016-06-04 15:24:23 +02:00
m_dpv . lforate * = 256 ;
pkvd - > fHandled = TRUE ;
}
// lfomodpitch
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " lfomodpitch " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . lfomodpitch = atoi ( pkvd - > szValue ) ;
if ( m_dpv . lfomodpitch > 100 )
m_dpv . lfomodpitch = 100 ;
if ( m_dpv . lfomodpitch < 0 )
m_dpv . lfomodpitch = 0 ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
// lfomodvol
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " lfomodvol " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . lfomodvol = atoi ( pkvd - > szValue ) ;
if ( m_dpv . lfomodvol > 100 )
m_dpv . lfomodvol = 100 ;
if ( m_dpv . lfomodvol < 0 )
m_dpv . lfomodvol = 0 ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
// cspinup
2016-07-31 15:48:50 +02:00
else if ( FStrEq ( pkvd - > szKeyName , " cspinup " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_dpv . cspinup = atoi ( pkvd - > szValue ) ;
if ( m_dpv . cspinup > 100 )
m_dpv . cspinup = 100 ;
if ( m_dpv . cspinup < 0 )
m_dpv . cspinup = 0 ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
else
CBaseEntity : : KeyValue ( pkvd ) ;
}
// =================== ROOM SOUND FX ==========================================
class CEnvSound : public CPointEntity
{
public :
void KeyValue ( KeyValueData * pkvd ) ;
void Spawn ( void ) ;
void Think ( void ) ;
2016-07-31 15:48:50 +02:00
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
2016-06-04 15:24:23 +02:00
float m_flRadius ;
float m_flRoomtype ;
} ;
2016-06-25 18:33:39 +02:00
LINK_ENTITY_TO_CLASS ( env_sound , CEnvSound )
TYPEDESCRIPTION CEnvSound : : m_SaveData [ ] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD ( CEnvSound , m_flRadius , FIELD_FLOAT ) ,
DEFINE_FIELD ( CEnvSound , m_flRoomtype , FIELD_FLOAT ) ,
} ;
2021-12-10 03:38:48 +01:00
IMPLEMENT_SAVERESTORE ( CEnvSound , CPointEntity )
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
void CEnvSound : : KeyValue ( KeyValueData * pkvd )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( FStrEq ( pkvd - > szKeyName , " radius " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_flRadius = atof ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
2016-07-31 15:48:50 +02:00
if ( FStrEq ( pkvd - > szKeyName , " roomtype " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_flRoomtype = atof ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
}
// returns TRUE if the given sound entity (pev) is in range
// and can see the given player entity (pevTarget)
2016-07-31 15:48:50 +02:00
BOOL FEnvSoundInRange ( entvars_t * pev , entvars_t * pevTarget , float * pflRange )
2016-06-04 15:24:23 +02:00
{
CEnvSound * pSound = GetClassPtr ( ( CEnvSound * ) pev ) ;
Vector vecSpot1 = pev - > origin + pev - > view_ofs ;
Vector vecSpot2 = pevTarget - > origin + pevTarget - > view_ofs ;
Vector vecRange ;
float flRange ;
TraceResult tr ;
2016-07-31 15:48:50 +02:00
UTIL_TraceLine ( vecSpot1 , vecSpot2 , ignore_monsters , ENT ( pev ) , & tr ) ;
2016-06-04 15:24:23 +02:00
2016-06-25 18:33:39 +02:00
// check if line of sight crosses water boundary, or is blocked
2016-07-31 15:48:50 +02:00
if ( ( tr . fInOpen & & tr . fInWater ) | | tr . flFraction ! = 1 )
2016-06-04 15:24:23 +02:00
return FALSE ;
// calc range from sound entity to player
vecRange = tr . vecEndPos - vecSpot1 ;
flRange = vecRange . Length ( ) ;
2016-07-31 15:48:50 +02:00
if ( pSound - > m_flRadius < flRange )
2016-06-04 15:24:23 +02:00
return FALSE ;
2016-07-31 15:48:50 +02:00
if ( pflRange )
2016-06-04 15:24:23 +02:00
* pflRange = flRange ;
return TRUE ;
}
//
// A client that is visible and in range of a sound entity will
// have its room_type set by that sound entity. If two or more
// sound entities are contending for a client, then the nearest
// sound entity to the client will set the client's room_type.
// A client's room_type will remain set to its prior value until
// a new in-range, visible sound entity resets a new room_type.
//
// CONSIDER: if player in water state, autoset roomtype to 14,15 or 16.
2016-07-31 15:48:50 +02:00
void CEnvSound : : Think ( void )
2016-06-04 15:24:23 +02:00
{
// get pointer to client if visible; FIND_CLIENT_IN_PVS will
// cycle through visible clients on consecutive calls.
2016-07-31 15:48:50 +02:00
edict_t * pentPlayer = FIND_CLIENT_IN_PVS ( edict ( ) ) ;
2016-06-04 15:24:23 +02:00
CBasePlayer * pPlayer = NULL ;
2016-07-31 15:48:50 +02:00
if ( FNullEnt ( pentPlayer ) )
2016-06-04 15:24:23 +02:00
goto env_sound_Think_slow ; // no player in pvs of sound entity, slow it down
2016-07-31 15:48:50 +02:00
pPlayer = GetClassPtr ( ( CBasePlayer * ) VARS ( pentPlayer ) ) ;
2016-06-04 15:24:23 +02:00
float flRange ;
// check to see if this is the sound entity that is
// currently affecting this player
2016-07-31 15:48:50 +02:00
if ( ! FNullEnt ( pPlayer - > m_pentSndLast ) & & ( pPlayer - > m_pentSndLast = = ENT ( pev ) ) )
2016-06-25 18:33:39 +02:00
{
2016-06-04 15:24:23 +02:00
// this is the entity currently affecting player, check
// for validity
2016-07-31 15:48:50 +02:00
if ( pPlayer - > m_flSndRoomtype ! = 0 & & pPlayer - > m_flSndRange ! = 0 )
2016-06-25 18:33:39 +02:00
{
2016-06-04 15:24:23 +02:00
// we're looking at a valid sound entity affecting
// player, make sure it's still valid, update range
2016-07-31 15:48:50 +02:00
if ( FEnvSoundInRange ( pev , VARS ( pentPlayer ) , & flRange ) )
2016-06-25 18:33:39 +02:00
{
2016-06-04 15:24:23 +02:00
pPlayer - > m_flSndRange = flRange ;
goto env_sound_Think_fast ;
2016-06-25 18:33:39 +02:00
}
else
{
2016-06-04 15:24:23 +02:00
// current sound entity affecting player is no longer valid,
// flag this state by clearing room_type and range.
// NOTE: we do not actually change the player's room_type
// NOTE: until we have a new valid room_type to change it to.
pPlayer - > m_flSndRange = 0 ;
pPlayer - > m_flSndRoomtype = 0 ;
goto env_sound_Think_slow ;
}
2016-06-25 18:33:39 +02:00
}
else
{
2016-06-04 15:24:23 +02:00
// entity is affecting player but is out of range,
// wait passively for another entity to usurp it...
goto env_sound_Think_slow ;
}
}
// if we got this far, we're looking at an entity that is contending
// for current player sound. the closest entity to player wins.
2016-07-31 15:48:50 +02:00
if ( FEnvSoundInRange ( pev , VARS ( pentPlayer ) , & flRange ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( flRange < pPlayer - > m_flSndRange | | pPlayer - > m_flSndRange = = 0 )
2016-06-04 15:24:23 +02:00
{
// new entity is closer to player, so it wins.
2016-07-31 15:48:50 +02:00
pPlayer - > m_pentSndLast = ENT ( pev ) ;
2016-06-04 15:24:23 +02:00
pPlayer - > m_flSndRoomtype = m_flRoomtype ;
pPlayer - > m_flSndRange = flRange ;
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// send room_type command to player's server.
// this should be a rare event - once per change of room_type
// only!
2016-07-31 15:48:50 +02:00
//CLIENT_COMMAND( pentPlayer, "room_type %f", m_flRoomtype );
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
MESSAGE_BEGIN ( MSG_ONE , SVC_ROOMTYPE , NULL , pentPlayer ) ; // use the magic #1 for "one client"
WRITE_SHORT ( ( short ) m_flRoomtype ) ; // sequence number
MESSAGE_END ( ) ;
// crank up nextthink rate for new active sound entity
// by falling through to think_fast...
}
// player is not closer to the contending sound entity,
// just fall through to think_fast. this effectively
// cranks up the think_rate of entities near the player.
}
// player is in pvs of sound entity, but either not visible or
// not in range. do nothing, fall through to think_fast...
env_sound_Think_fast :
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.25f ;
2016-06-04 15:24:23 +02:00
return ;
env_sound_Think_slow :
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.75f ;
2016-06-04 15:24:23 +02:00
return ;
}
//
// env_sound - spawn a sound entity that will set player roomtype
// when player moves in range and sight.
//
//
2016-07-31 15:48:50 +02:00
void CEnvSound : : Spawn ( )
2016-06-04 15:24:23 +02:00
{
// spread think times
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + RANDOM_FLOAT ( 0.0f , 0.5f ) ;
2016-06-04 15:24:23 +02:00
}
// ==================== SENTENCE GROUPS, UTILITY FUNCTIONS ======================================
# define CSENTENCE_LRU_MAX 32 // max number of elements per sentence group
// group of related sentences
typedef struct sentenceg
{
char szgroupname [ CBSENTENCENAME_MAX ] ;
int count ;
unsigned char rgblru [ CSENTENCE_LRU_MAX ] ;
} SENTENCEG ;
# define CSENTENCEG_MAX 200 // max number of sentence groups
// globals
SENTENCEG rgsentenceg [ CSENTENCEG_MAX ] ;
int fSentencesInit = FALSE ;
char gszallsentencenames [ CVOXFILESENTENCEMAX ] [ CBSENTENCENAME_MAX ] ;
int gcallsentences = 0 ;
// randomize list of sentence name indices
2016-07-31 15:48:50 +02:00
void USENTENCEG_InitLRU ( unsigned char * plru , int count )
2016-06-04 15:24:23 +02:00
{
int i , j , k ;
unsigned char temp ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return ;
2016-07-31 15:48:50 +02:00
if ( count > CSENTENCE_LRU_MAX )
2016-06-04 15:24:23 +02:00
count = CSENTENCE_LRU_MAX ;
2016-07-31 15:48:50 +02:00
for ( i = 0 ; i < count ; i + + )
plru [ i ] = ( unsigned char ) i ;
2016-06-04 15:24:23 +02:00
// randomize array
2016-07-31 15:48:50 +02:00
for ( i = 0 ; i < ( count * 4 ) ; i + + )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
j = RANDOM_LONG ( 0 , count - 1 ) ;
k = RANDOM_LONG ( 0 , count - 1 ) ;
2016-06-04 15:24:23 +02:00
temp = plru [ j ] ;
plru [ j ] = plru [ k ] ;
plru [ k ] = temp ;
}
}
// ignore lru. pick next sentence from sentence group. Go in order until we hit the last sentence,
// then repeat list if freset is true. If freset is false, then repeat last sentence.
// ipick is passed in as the requested sentence ordinal.
// ipick 'next' is returned.
// return of -1 indicates an error.
2016-07-31 15:48:50 +02:00
int USENTENCEG_PickSequential ( int isentenceg , char * szfound , int ipick , int freset )
2016-06-04 15:24:23 +02:00
{
2019-07-07 17:30:20 +02:00
const char * szgroupname ;
2016-06-04 15:24:23 +02:00
unsigned char count ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return - 1 ;
2016-07-31 15:48:50 +02:00
if ( isentenceg < 0 )
2016-06-04 15:24:23 +02:00
return - 1 ;
szgroupname = rgsentenceg [ isentenceg ] . szgroupname ;
count = rgsentenceg [ isentenceg ] . count ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( count = = 0 )
2016-06-04 15:24:23 +02:00
return - 1 ;
2016-07-31 15:48:50 +02:00
if ( ipick > = count )
ipick = count - 1 ;
2016-06-04 15:24:23 +02:00
2019-07-07 17:30:20 +02:00
sprintf ( szfound , " !%s%d " , szgroupname , ipick ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( ipick > = count )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( freset )
2016-06-04 15:24:23 +02:00
// reset at end of list
return 0 ;
else
return count ;
}
return ipick + 1 ;
}
// pick a random sentence from rootname0 to rootnameX.
// picks from the rgsentenceg[isentenceg] least
// recently used, modifies lru array. returns the sentencename.
// note, lru must be seeded with 0-n randomized sentence numbers, with the
// rest of the lru filled with -1. The first integer in the lru is
// actually the size of the list. Returns ipick, the ordinal
// of the picked sentence within the group.
2016-07-31 15:48:50 +02:00
int USENTENCEG_Pick ( int isentenceg , char * szfound )
2016-06-04 15:24:23 +02:00
{
2019-07-07 17:30:20 +02:00
const char * szgroupname ;
2016-06-04 15:24:23 +02:00
unsigned char * plru ;
unsigned char i ;
unsigned char count ;
unsigned char ipick ;
int ffound = FALSE ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return - 1 ;
2016-07-31 15:48:50 +02:00
if ( isentenceg < 0 )
2016-06-04 15:24:23 +02:00
return - 1 ;
szgroupname = rgsentenceg [ isentenceg ] . szgroupname ;
count = rgsentenceg [ isentenceg ] . count ;
plru = rgsentenceg [ isentenceg ] . rgblru ;
2016-07-31 15:48:50 +02:00
while ( ! ffound )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
for ( i = 0 ; i < count ; i + + )
if ( plru [ i ] ! = 0xFF )
2016-06-04 15:24:23 +02:00
{
ipick = plru [ i ] ;
plru [ i ] = 0xFF ;
ffound = TRUE ;
break ;
}
2016-07-31 15:48:50 +02:00
if ( ! ffound )
USENTENCEG_InitLRU ( plru , count ) ;
2016-06-04 15:24:23 +02:00
else
{
2019-07-07 17:30:20 +02:00
sprintf ( szfound , " !%s%d " , szgroupname , ipick ) ;
2016-06-04 15:24:23 +02:00
return ipick ;
}
}
return - 1 ;
}
// ===================== SENTENCE GROUPS, MAIN ROUTINES ========================
// Given sentence group rootname (name without number suffix),
// get sentence group index (isentenceg). Returns -1 if no such name.
2016-07-31 15:48:50 +02:00
int SENTENCEG_GetIndex ( const char * szgroupname )
2016-06-04 15:24:23 +02:00
{
int i ;
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit | | ! szgroupname )
2016-06-04 15:24:23 +02:00
return - 1 ;
// search rgsentenceg for match on szgroupname
i = 0 ;
2016-07-31 15:48:50 +02:00
while ( rgsentenceg [ i ] . count )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( ! strcmp ( szgroupname , rgsentenceg [ i ] . szgroupname ) )
2016-06-04 15:24:23 +02:00
return i ;
i + + ;
}
return - 1 ;
}
// given sentence group index, play random sentence for given entity.
// returns ipick - which sentence was picked to
// play from the group. Ipick is only needed if you plan on stopping
// the sound before playback is done (see SENTENCEG_Stop).
2016-07-31 15:48:50 +02:00
int SENTENCEG_PlayRndI ( edict_t * entity , int isentenceg , float volume , float attenuation , int flags , int pitch )
2016-06-04 15:24:23 +02:00
{
char name [ 64 ] ;
int ipick ;
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return - 1 ;
name [ 0 ] = 0 ;
2016-07-31 15:48:50 +02:00
ipick = USENTENCEG_Pick ( isentenceg , name ) ;
2016-06-13 15:48:17 +02:00
if ( ipick > 0 )
2016-07-31 15:48:50 +02:00
EMIT_SOUND_DYN ( entity , CHAN_VOICE , name , volume , attenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
return ipick ;
}
// same as above, but takes sentence group name instead of index
2016-07-31 15:48:50 +02:00
int SENTENCEG_PlayRndSz ( edict_t * entity , const char * szgroupname , float volume , float attenuation , int flags , int pitch )
2016-06-04 15:24:23 +02:00
{
char name [ 64 ] ;
int ipick ;
int isentenceg ;
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return - 1 ;
name [ 0 ] = 0 ;
2016-07-31 15:48:50 +02:00
isentenceg = SENTENCEG_GetIndex ( szgroupname ) ;
if ( isentenceg < 0 )
2016-06-04 15:24:23 +02:00
{
ALERT ( at_console , " No such sentence group %s \n " , szgroupname ) ;
return - 1 ;
}
2016-07-31 15:48:50 +02:00
ipick = USENTENCEG_Pick ( isentenceg , name ) ;
if ( ipick > = 0 & & name [ 0 ] )
EMIT_SOUND_DYN ( entity , CHAN_VOICE , name , volume , attenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
return ipick ;
}
// play sentences in sequential order from sentence group. Reset after last sentence.
2016-07-31 15:48:50 +02:00
int SENTENCEG_PlaySequentialSz ( edict_t * entity , const char * szgroupname , float volume , float attenuation , int flags , int pitch , int ipick , int freset )
2016-06-04 15:24:23 +02:00
{
char name [ 64 ] ;
int ipicknext ;
int isentenceg ;
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return - 1 ;
name [ 0 ] = 0 ;
isentenceg = SENTENCEG_GetIndex ( szgroupname ) ;
2016-07-31 15:48:50 +02:00
if ( isentenceg < 0 )
2016-06-04 15:24:23 +02:00
return - 1 ;
2016-07-31 15:48:50 +02:00
ipicknext = USENTENCEG_PickSequential ( isentenceg , name , ipick , freset ) ;
if ( ipicknext > = 0 & & name [ 0 ] )
EMIT_SOUND_DYN ( entity , CHAN_VOICE , name , volume , attenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
return ipicknext ;
}
// for this entity, for the given sentence within the sentence group, stop
// the sentence.
2016-07-31 15:48:50 +02:00
void SENTENCEG_Stop ( edict_t * entity , int isentenceg , int ipick )
2016-06-04 15:24:23 +02:00
{
char buffer [ 64 ] ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( ! fSentencesInit )
2016-06-04 15:24:23 +02:00
return ;
2016-07-31 15:48:50 +02:00
if ( isentenceg < 0 | | ipick < 0 )
2016-06-04 15:24:23 +02:00
return ;
2019-07-07 17:30:20 +02:00
sprintf ( buffer , " !%s%d " , rgsentenceg [ isentenceg ] . szgroupname , ipick ) ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
STOP_SOUND ( entity , CHAN_VOICE , buffer ) ;
2016-06-04 15:24:23 +02:00
}
// 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 ( )
{
char buffer [ 512 ] ;
char szgroup [ 64 ] ;
int i , j ;
int isentencegs ;
2016-07-31 15:48:50 +02:00
if ( fSentencesInit )
2016-06-04 15:24:23 +02:00
return ;
2016-07-31 15:48:50 +02:00
memset ( gszallsentencenames , 0 , CVOXFILESENTENCEMAX * CBSENTENCENAME_MAX ) ;
2016-06-04 15:24:23 +02:00
gcallsentences = 0 ;
2016-07-31 15:48:50 +02:00
memset ( rgsentenceg , 0 , CSENTENCEG_MAX * sizeof ( SENTENCEG ) ) ;
2016-06-04 15:24:23 +02:00
isentencegs = - 1 ;
int filePos = 0 , fileSize ;
byte * pMemFile = g_engfuncs . pfnLoadFileForMe ( " sound/sentences.txt " , & fileSize ) ;
2016-07-31 15:48:50 +02:00
if ( ! pMemFile )
2016-06-04 15:24:23 +02:00
return ;
2016-08-02 13:59:22 +02:00
memset ( buffer , 0 , 512 ) ;
2016-08-02 18:24:33 +02:00
memset ( szgroup , 0 , 64 ) ;
2016-06-04 15:24:23 +02:00
// for each line in the file...
2016-07-31 15:48:50 +02:00
while ( memfgets ( pMemFile , fileSize , filePos , buffer , 511 ) ! = NULL )
2016-06-04 15:24:23 +02:00
{
// skip whitespace
i = 0 ;
2016-07-31 15:48:50 +02:00
while ( buffer [ i ] & & buffer [ i ] = = ' ' )
2016-06-04 15:24:23 +02:00
i + + ;
2016-07-31 15:48:50 +02:00
if ( ! buffer [ i ] )
2016-06-04 15:24:23 +02:00
continue ;
2016-07-31 15:48:50 +02:00
if ( buffer [ i ] = = ' / ' | | ! isalpha ( buffer [ i ] ) )
2016-06-04 15:24:23 +02:00
continue ;
// get sentence name
j = i ;
2016-07-31 15:48:50 +02:00
while ( buffer [ j ] & & buffer [ j ] ! = ' ' )
2016-06-04 15:24:23 +02:00
j + + ;
2016-07-31 15:48:50 +02:00
if ( ! buffer [ j ] )
2016-06-04 15:24:23 +02:00
continue ;
2019-09-20 20:28:52 +02:00
if ( gcallsentences > = CVOXFILESENTENCEMAX )
2016-06-04 15:24:23 +02:00
{
2019-09-20 20:28:52 +02:00
ALERT ( at_error , " Too many sentences in sentences.txt! >%d \n " , gcallsentences ) ;
2016-06-04 15:24:23 +02:00
break ;
}
// null-terminate name and save in sentences array
buffer [ j ] = 0 ;
const char * pString = buffer + i ;
2016-07-31 15:48:50 +02:00
if ( strlen ( pString ) > = CBSENTENCENAME_MAX )
ALERT ( at_warning , " Sentence %s longer than %d letters \n " , pString , CBSENTENCENAME_MAX - 1 ) ;
2016-06-04 15:24:23 +02:00
strcpy ( gszallsentencenames [ gcallsentences + + ] , pString ) ;
j - - ;
2016-07-31 15:48:50 +02:00
if ( j < = i )
2016-06-04 15:24:23 +02:00
continue ;
2016-07-31 15:48:50 +02:00
if ( ! isdigit ( buffer [ j ] ) )
2016-06-04 15:24:23 +02:00
continue ;
// cut out suffix numbers
2016-07-31 15:48:50 +02:00
while ( j > i & & isdigit ( buffer [ j ] ) )
2016-06-04 15:24:23 +02:00
j - - ;
2016-07-31 15:48:50 +02:00
if ( j < = i )
2016-06-04 15:24:23 +02:00
continue ;
2016-07-31 15:48:50 +02:00
buffer [ j + 1 ] = 0 ;
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// if new name doesn't match previous group name,
// make a new group.
2016-07-31 15:48:50 +02:00
if ( strcmp ( szgroup , & ( buffer [ i ] ) ) )
2016-06-04 15:24:23 +02:00
{
// name doesn't match with prev name,
// copy name into group, init count to 1
isentencegs + + ;
2016-07-31 15:48:50 +02:00
if ( isentencegs > = CSENTENCEG_MAX )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ALERT ( at_error , " Too many sentence groups in sentences.txt! \n " ) ;
2016-06-04 15:24:23 +02:00
break ;
}
2016-07-31 15:48:50 +02:00
strcpy ( rgsentenceg [ isentencegs ] . szgroupname , & ( buffer [ i ] ) ) ;
2016-06-04 15:24:23 +02:00
rgsentenceg [ isentencegs ] . count = 1 ;
2016-07-31 15:48:50 +02:00
strcpy ( szgroup , & ( buffer [ i ] ) ) ;
2016-06-04 15:24:23 +02:00
continue ;
}
else
{
//name matches with previous, increment group count
2016-07-31 15:48:50 +02:00
if ( isentencegs > = 0 )
2016-06-04 15:24:23 +02:00
rgsentenceg [ isentencegs ] . count + + ;
}
}
g_engfuncs . pfnFreeFile ( pMemFile ) ;
2016-06-25 18:33:39 +02:00
2024-01-31 12:01:12 +01:00
if ( gcallsentences > CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 )
2023-12-21 03:22:12 +01:00
{
2024-01-31 12:01:12 +01:00
ALERT ( at_warning , " NOTE: this mod might not work properly under GoldSource (post-anniversary update) engine: more than %d sentences \n " , CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 ) ;
2023-12-21 03:22:12 +01:00
}
2024-01-31 12:01:12 +01:00
else if ( gcallsentences > CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY )
2023-12-21 03:22:12 +01:00
{
2024-01-31 12:01:12 +01:00
ALERT ( at_warning , " NOTE: this mod might not work properly under GoldSource (pre-anniversary update) engine: more than %d sentences \n " , CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY ) ;
2023-12-21 03:22:12 +01:00
}
2016-06-04 15:24:23 +02:00
fSentencesInit = TRUE ;
// init lru lists
i = 0 ;
2016-07-31 15:48:50 +02:00
while ( rgsentenceg [ i ] . count & & i < CSENTENCEG_MAX )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
USENTENCEG_InitLRU ( & ( rgsentenceg [ i ] . rgblru [ 0 ] ) , rgsentenceg [ i ] . count ) ;
2016-06-04 15:24:23 +02:00
i + + ;
}
}
// convert sentence (sample) name to !sentencenum, return !sentencenum
2016-07-31 15:48:50 +02:00
int SENTENCEG_Lookup ( const char * sample , char * sentencenum )
2016-06-04 15:24:23 +02:00
{
int i ;
2019-07-07 17:30:20 +02:00
2016-06-04 15:24:23 +02:00
// this is a sentence name; lookup sentence number
// and give to engine as string.
2016-07-31 15:48:50 +02:00
for ( i = 0 ; i < gcallsentences ; i + + )
if ( ! stricmp ( gszallsentencenames [ i ] , sample + 1 ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( sentencenum )
2016-06-04 15:24:23 +02:00
{
2019-07-07 17:30:20 +02:00
sprintf ( sentencenum , " !%d " , i ) ;
2016-06-04 15:24:23 +02:00
}
return i ;
}
// sentence name not found!
return - 1 ;
}
2016-07-31 15:48:50 +02:00
void EMIT_SOUND_DYN ( edict_t * entity , int channel , const char * sample , float volume , float attenuation , int flags , int pitch )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( sample & & * sample = = ' ! ' )
2016-06-04 15:24:23 +02:00
{
char name [ 32 ] ;
2016-07-31 15:48:50 +02:00
if ( SENTENCEG_Lookup ( sample , name ) > = 0 )
EMIT_SOUND_DYN2 ( entity , channel , name , volume , attenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
else
ALERT ( at_aiconsole , " Unable to find %s in sentences.txt \n " , sample ) ;
}
else
2016-07-31 15:48:50 +02:00
EMIT_SOUND_DYN2 ( entity , channel , sample , volume , attenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
}
// play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename
2016-07-31 15:48:50 +02:00
void EMIT_SOUND_SUIT ( edict_t * entity , const char * sample )
2016-06-04 15:24:23 +02:00
{
float fvol ;
int pitch = PITCH_NORM ;
2016-07-31 15:48:50 +02:00
fvol = CVAR_GET_FLOAT ( " suitvolume " ) ;
if ( RANDOM_LONG ( 0 , 1 ) )
pitch = RANDOM_LONG ( 0 , 6 ) + 98 ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
if ( fvol > 0.05f )
2016-07-31 15:48:50 +02:00
EMIT_SOUND_DYN ( entity , CHAN_STATIC , sample , fvol , ATTN_NORM , 0 , pitch ) ;
2016-06-04 15:24:23 +02:00
}
// play a sentence, randomly selected from the passed in group id, over the HEV suit speaker
2016-07-31 15:48:50 +02:00
void EMIT_GROUPID_SUIT ( edict_t * entity , int isentenceg )
2016-06-04 15:24:23 +02:00
{
float fvol ;
int pitch = PITCH_NORM ;
2016-07-31 15:48:50 +02:00
fvol = CVAR_GET_FLOAT ( " suitvolume " ) ;
if ( RANDOM_LONG ( 0 , 1 ) )
pitch = RANDOM_LONG ( 0 , 6 ) + 98 ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
if ( fvol > 0.05f )
2016-07-31 15:48:50 +02:00
SENTENCEG_PlayRndI ( entity , isentenceg , fvol , ATTN_NORM , 0 , pitch ) ;
2016-06-04 15:24:23 +02:00
}
// play a sentence, randomly selected from the passed in groupname
2016-07-31 15:48:50 +02:00
void EMIT_GROUPNAME_SUIT ( edict_t * entity , const char * groupname )
2016-06-04 15:24:23 +02:00
{
float fvol ;
int pitch = PITCH_NORM ;
2016-07-31 15:48:50 +02:00
fvol = CVAR_GET_FLOAT ( " suitvolume " ) ;
if ( RANDOM_LONG ( 0 , 1 ) )
pitch = RANDOM_LONG ( 0 , 6 ) + 98 ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
if ( fvol > 0.05f )
2016-07-31 15:48:50 +02:00
SENTENCEG_PlayRndSz ( entity , groupname , fvol , ATTN_NORM , 0 , pitch ) ;
2016-06-04 15:24:23 +02:00
}
// ===================== 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.
// open materials.txt, get size, alloc space,
// save in array. Only works first time called,
// ignored on subsequent calls.
static char * memfgets ( byte * pMemFile , int fileSize , int & filePos , char * pBuffer , int bufferSize )
{
// Bullet-proofing
2016-07-31 15:48:50 +02:00
if ( ! pMemFile | | ! pBuffer )
2016-06-04 15:24:23 +02:00
return NULL ;
2016-07-31 15:48:50 +02:00
if ( filePos > = fileSize )
2016-06-04 15:24:23 +02:00
return NULL ;
int i = filePos ;
int last = fileSize ;
// fgets always NULL terminates, so only read bufferSize-1 characters
2016-07-31 15:48:50 +02:00
if ( last - filePos > ( bufferSize - 1 ) )
last = filePos + ( bufferSize - 1 ) ;
2016-06-04 15:24:23 +02:00
int stop = 0 ;
// Stop at the next newline (inclusive) or end of buffer
2016-07-31 15:48:50 +02:00
while ( i < last & & ! stop )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( pMemFile [ i ] = = ' \n ' )
2016-06-04 15:24:23 +02:00
stop = 1 ;
i + + ;
}
// If we actually advanced the pointer, copy it over
2016-07-31 15:48:50 +02:00
if ( i ! = filePos )
2016-06-04 15:24:23 +02:00
{
// We read in size bytes
int size = i - filePos ;
// copy it out
2016-07-31 15:48:50 +02:00
memcpy ( pBuffer , pMemFile + filePos , sizeof ( byte ) * size ) ;
2016-06-25 18:33:39 +02:00
2016-06-04 15:24:23 +02:00
// If the buffer isn't full, terminate (this is always true)
2016-07-31 15:48:50 +02:00
if ( size < bufferSize )
2016-06-04 15:24:23 +02:00
pBuffer [ size ] = 0 ;
// Update file pointer
filePos = i ;
return pBuffer ;
}
// No data read, bail
return NULL ;
}
// given texture name, find texture type
// if not found, return type 'concrete'
// NOTE: this routine should ONLY be called if the
// current texture under the player changes!
2018-04-08 18:59:11 +02:00
extern " C " char PM_FindTextureType ( char * name ) ;
2016-07-31 15:48:50 +02:00
char TEXTURETYPE_Find ( char * name )
2016-06-04 15:24:23 +02:00
{
2018-04-08 18:59:11 +02:00
return PM_FindTextureType ( name ) ;
2016-06-04 15:24:23 +02:00
}
// play a strike sound based on the texture that was hit by the attack traceline. VecSrc/VecEnd are the
// original traceline endpoints used by the attacker, iBulletType is the type of bullet that hit the texture.
// returns volume of strike instrument (crowbar) to play
2016-07-31 15:48:50 +02:00
float TEXTURETYPE_PlaySound ( TraceResult * ptr , Vector vecSrc , Vector vecEnd , int iBulletType )
2016-06-04 15:24:23 +02:00
{
2016-06-25 18:33:39 +02:00
// hit the world, try to play sound based on texture material type
2016-06-04 15:24:23 +02:00
char chTextureType ;
float fvol ;
float fvolbar ;
char szbuffer [ 64 ] ;
const char * pTextureName ;
float rgfl1 [ 3 ] ;
float rgfl2 [ 3 ] ;
2017-06-29 15:56:03 +02:00
const char * rgsz [ 4 ] ;
2016-06-04 15:24:23 +02:00
int cnt ;
float fattn = ATTN_NORM ;
2016-07-31 15:48:50 +02:00
if ( ! g_pGameRules - > PlayTextureSounds ( ) )
2019-10-13 13:49:25 +02:00
return 0.0f ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
CBaseEntity * pEntity = CBaseEntity : : Instance ( ptr - > pHit ) ;
2016-06-04 15:24:23 +02:00
chTextureType = 0 ;
2016-07-31 15:48:50 +02:00
if ( pEntity & & pEntity - > Classify ( ) ! = CLASS_NONE & & pEntity - > Classify ( ) ! = CLASS_MACHINE )
2016-06-04 15:24:23 +02:00
// hit body
chTextureType = CHAR_TEX_FLESH ;
else
{
// hit world
// find texture under strike, get material type
// copy trace vector into array for trace_texture
2016-07-31 15:48:50 +02:00
vecSrc . CopyToArray ( rgfl1 ) ;
vecEnd . CopyToArray ( rgfl2 ) ;
2016-06-04 15:24:23 +02:00
// get texture from entity or world (world is ent(0))
2016-07-31 15:48:50 +02:00
if ( pEntity )
pTextureName = TRACE_TEXTURE ( ENT ( pEntity - > pev ) , rgfl1 , rgfl2 ) ;
2016-06-04 15:24:23 +02:00
else
2016-07-31 15:48:50 +02:00
pTextureName = TRACE_TEXTURE ( ENT ( 0 ) , rgfl1 , rgfl2 ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( pTextureName )
2016-06-04 15:24:23 +02:00
{
// strip leading '-0' or '+0~' or '{' or '!'
2016-07-31 15:48:50 +02:00
if ( * pTextureName = = ' - ' | | * pTextureName = = ' + ' )
2016-06-04 15:24:23 +02:00
pTextureName + = 2 ;
2016-07-31 15:48:50 +02:00
if ( * pTextureName = = ' { ' | | * pTextureName = = ' ! ' | | * pTextureName = = ' ~ ' | | * pTextureName = = ' ' )
2016-06-04 15:24:23 +02:00
pTextureName + + ;
// '}}'
2016-07-31 15:48:50 +02:00
strcpy ( szbuffer , pTextureName ) ;
2016-06-04 15:24:23 +02:00
szbuffer [ CBTEXTURENAMEMAX - 1 ] = 0 ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
// ALERT( at_console, "texture hit: %s\n", szbuffer );
2016-06-04 15:24:23 +02:00
// get texture type
2016-07-31 15:48:50 +02:00
chTextureType = TEXTURETYPE_Find ( szbuffer ) ;
2016-06-04 15:24:23 +02:00
}
}
2016-07-31 15:48:50 +02:00
switch ( chTextureType )
2016-06-04 15:24:23 +02:00
{
default :
2016-07-31 15:48:50 +02:00
case CHAR_TEX_CONCRETE :
2019-10-13 13:49:25 +02:00
fvol = 0.9f ;
fvolbar = 0.6f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_step1.wav " ;
rgsz [ 1 ] = " player/pl_step2.wav " ;
cnt = 2 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_METAL :
2019-10-13 13:49:25 +02:00
fvol = 0.9f ;
fvolbar = 0.3f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_metal1.wav " ;
rgsz [ 1 ] = " player/pl_metal2.wav " ;
cnt = 2 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_DIRT :
2019-10-13 13:49:25 +02:00
fvol = 0.9f ;
fvolbar = 0.1f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_dirt1.wav " ;
rgsz [ 1 ] = " player/pl_dirt2.wav " ;
rgsz [ 2 ] = " player/pl_dirt3.wav " ;
cnt = 3 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_VENT :
2019-10-13 13:49:25 +02:00
fvol = 0.5f ;
fvolbar = 0.3f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_duct1.wav " ;
rgsz [ 1 ] = " player/pl_duct1.wav " ;
cnt = 2 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_GRATE :
2019-10-13 13:49:25 +02:00
fvol = 0.9f ;
fvolbar = 0.5f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_grate1.wav " ;
rgsz [ 1 ] = " player/pl_grate4.wav " ;
cnt = 2 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_TILE :
2019-10-13 13:49:25 +02:00
fvol = 0.8f ;
fvolbar = 0.2f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_tile1.wav " ;
rgsz [ 1 ] = " player/pl_tile3.wav " ;
rgsz [ 2 ] = " player/pl_tile2.wav " ;
rgsz [ 3 ] = " player/pl_tile4.wav " ;
cnt = 4 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_SLOSH :
2019-10-13 13:49:25 +02:00
fvol = 0.9f ;
fvolbar = 0.0f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " player/pl_slosh1.wav " ;
rgsz [ 1 ] = " player/pl_slosh3.wav " ;
rgsz [ 2 ] = " player/pl_slosh2.wav " ;
rgsz [ 3 ] = " player/pl_slosh4.wav " ;
cnt = 4 ;
break ;
2016-07-31 15:48:50 +02:00
case CHAR_TEX_WOOD :
2019-10-13 13:49:25 +02:00
fvol = 0.9f ;
fvolbar = 0.2f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " debris/wood1.wav " ;
rgsz [ 1 ] = " debris/wood2.wav " ;
rgsz [ 2 ] = " debris/wood3.wav " ;
cnt = 3 ;
break ;
case CHAR_TEX_GLASS :
case CHAR_TEX_COMPUTER :
2019-10-13 13:49:25 +02:00
fvol = 0.8f ;
fvolbar = 0.2f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " debris/glass1.wav " ;
rgsz [ 1 ] = " debris/glass2.wav " ;
rgsz [ 2 ] = " debris/glass3.wav " ;
cnt = 3 ;
break ;
case CHAR_TEX_FLESH :
2016-07-31 15:48:50 +02:00
if ( iBulletType = = BULLET_PLAYER_CROWBAR )
2019-10-13 13:49:25 +02:00
return 0.0f ; // crowbar already makes this sound
fvol = 1.0f ;
fvolbar = 0.2f ;
2016-06-04 15:24:23 +02:00
rgsz [ 0 ] = " weapons/bullet_hit1.wav " ;
rgsz [ 1 ] = " weapons/bullet_hit2.wav " ;
2019-10-13 13:49:25 +02:00
fattn = 1.0f ;
2016-06-04 15:24:23 +02:00
cnt = 2 ;
break ;
}
// did we hit a breakable?
2016-07-31 15:48:50 +02:00
if ( pEntity & & FClassnameIs ( pEntity - > pev , " func_breakable " ) )
2016-06-04 15:24:23 +02:00
{
// drop volumes, the object will already play a damaged sound
2019-10-13 13:49:25 +02:00
fvol / = 1.5f ;
fvolbar / = 2.0f ;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
else if ( chTextureType = = CHAR_TEX_COMPUTER )
2016-06-04 15:24:23 +02:00
{
// play random spark if computer
2019-10-13 13:49:25 +02:00
if ( ptr - > flFraction ! = 1.0f & & RANDOM_LONG ( 0 , 1 ) )
2016-06-04 15:24:23 +02:00
{
UTIL_Sparks ( ptr - > vecEndPos ) ;
2019-10-13 13:49:25 +02:00
float flVolume = RANDOM_FLOAT ( 0.7f , 1.0f ) ; //random volume range
2016-07-31 15:48:50 +02:00
switch ( RANDOM_LONG ( 0 , 1 ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
case 0 :
UTIL_EmitAmbientSound ( ENT ( 0 ) , ptr - > vecEndPos , " buttons/spark5.wav " , flVolume , ATTN_NORM , 0 , 100 ) ;
break ;
case 1 :
UTIL_EmitAmbientSound ( ENT ( 0 ) , ptr - > vecEndPos , " buttons/spark6.wav " , flVolume , ATTN_NORM , 0 , 100 ) ;
break ;
/*case 0:
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " buttons/spark5.wav " , flVolume , ATTN_NORM ) ;
break ;
case 1 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " buttons/spark6.wav " , flVolume , ATTN_NORM ) ;
break ; */
2016-06-04 15:24:23 +02:00
}
}
}
// play material hit sound
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( 0 ) , ptr - > vecEndPos , rgsz [ RANDOM_LONG ( 0 , cnt - 1 ) ] , fvol , fattn , 0 , 96 + RANDOM_LONG ( 0 , 0xf ) ) ;
//EMIT_SOUND_DYN( ENT( m_pPlayer->pev ), CHAN_WEAPON, rgsz[RANDOM_LONG( 0, cnt - 1 )], fvol, ATTN_NORM, 0, 96 + RANDOM_LONG( 0, 0xf ) );
2016-06-04 15:24:23 +02:00
return fvolbar ;
}
// ===================================================================================
//
// Speaker class. Used for announcements per level, for door lock/unlock spoken voice.
//
class CSpeaker : public CBaseEntity
{
public :
2016-07-31 15:48:50 +02:00
void KeyValue ( KeyValueData * pkvd ) ;
2016-06-04 15:24:23 +02:00
void Spawn ( void ) ;
void Precache ( void ) ;
2016-07-31 15:48:50 +02:00
void EXPORT ToggleUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
2016-06-04 15:24:23 +02:00
void EXPORT SpeakerThink ( void ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
virtual int ObjectCaps ( void ) { return ( CBaseEntity : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ) ; }
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
int m_preset ; // preset number
2016-06-04 15:24:23 +02:00
} ;
2016-06-25 18:33:39 +02:00
LINK_ENTITY_TO_CLASS ( speaker , CSpeaker )
2016-07-31 15:48:50 +02:00
2016-06-25 18:33:39 +02:00
TYPEDESCRIPTION CSpeaker : : m_SaveData [ ] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD ( CSpeaker , m_preset , FIELD_INTEGER ) ,
} ;
2016-06-25 18:33:39 +02:00
IMPLEMENT_SAVERESTORE ( CSpeaker , CBaseEntity )
2016-06-04 15:24:23 +02:00
//
// ambient_generic - general-purpose user-defined static sound
//
2016-07-31 15:48:50 +02:00
void CSpeaker : : Spawn ( void )
2016-06-04 15:24:23 +02:00
{
2017-06-29 15:56:03 +02:00
const char * szSoundFile = STRING ( pev - > message ) ;
2016-06-04 15:24:23 +02:00
2019-09-24 00:00:37 +02:00
if ( ! m_preset & & ( FStringNull ( pev - > message ) | | szSoundFile [ 0 ] = = ' \0 ' ) )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
ALERT ( at_error , " SPEAKER with no Level/Sentence! at: %f, %f, %f \n " , ( double ) pev - > origin . x , ( double ) pev - > origin . y , ( double ) pev - > origin . z ) ;
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
SetThink ( & CBaseEntity : : SUB_Remove ) ;
return ;
}
2016-07-31 15:48:50 +02:00
pev - > solid = SOLID_NOT ;
pev - > movetype = MOVETYPE_NONE ;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
SetThink ( & CSpeaker : : SpeakerThink ) ;
2019-10-13 13:49:25 +02:00
pev - > nextthink = 0.0f ;
2016-06-04 15:24:23 +02:00
// allow on/off switching via 'use' function.
SetUse ( & CSpeaker : : ToggleUse ) ;
2016-07-31 15:48:50 +02:00
Precache ( ) ;
2016-06-04 15:24:23 +02:00
}
2019-10-13 13:49:25 +02:00
# define ANNOUNCE_MINUTES_MIN 0.25f
# define ANNOUNCE_MINUTES_MAX 2.25f
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
void CSpeaker : : Precache ( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if ( ! FBitSet ( pev - > spawnflags , SPEAKER_START_SILENT ) )
2016-06-04 15:24:23 +02:00
// set first announcement time for random n second
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + RANDOM_FLOAT ( 5.0f , 15.0f ) ;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
void CSpeaker : : SpeakerThink ( void )
2016-06-04 15:24:23 +02:00
{
2019-11-16 10:29:09 +01:00
const char * szSoundFile = " " ;
2019-10-13 13:49:25 +02:00
float flvolume = pev - > health * 0.1f ;
float flattenuation = 0.3f ;
2016-06-04 15:24:23 +02:00
int flags = 0 ;
int pitch = 100 ;
// Wait for the talkmonster to finish first.
2016-07-31 15:48:50 +02:00
if ( gpGlobals - > time < = CTalkMonster : : g_talkWaitTime )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
pev - > nextthink = CTalkMonster : : g_talkWaitTime + RANDOM_FLOAT ( 5.0f , 10.0f ) ;
2016-06-04 15:24:23 +02:00
return ;
}
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( m_preset )
2016-06-04 15:24:23 +02:00
{
// go lookup preset text, assign szSoundFile
2016-07-31 15:48:50 +02:00
switch ( m_preset )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
case 1 :
szSoundFile = " C1A0_ " ;
break ;
case 2 :
szSoundFile = " C1A1_ " ;
break ;
case 3 :
szSoundFile = " C1A2_ " ;
break ;
case 4 :
szSoundFile = " C1A3_ " ;
break ;
case 5 :
szSoundFile = " C1A4_ " ;
break ;
case 6 :
szSoundFile = " C2A1_ " ;
break ;
case 7 :
szSoundFile = " C2A2_ " ;
break ;
case 8 :
szSoundFile = " C2A3_ " ;
break ;
case 9 :
szSoundFile = " C2A4_ " ;
break ;
case 10 :
szSoundFile = " C2A5_ " ;
break ;
case 11 :
szSoundFile = " C3A1_ " ;
break ;
case 12 :
szSoundFile = " C3A2_ " ;
break ;
2016-06-04 15:24:23 +02:00
}
2016-06-25 18:33:39 +02:00
}
else
2017-06-29 15:56:03 +02:00
szSoundFile = STRING ( pev - > message ) ;
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( szSoundFile [ 0 ] = = ' ! ' )
2016-06-04 15:24:23 +02:00
{
// play single sentence, one shot
2016-07-31 15:48:50 +02:00
UTIL_EmitAmbientSound ( ENT ( pev ) , pev - > origin , szSoundFile ,
flvolume , flattenuation , flags , pitch ) ;
2016-06-04 15:24:23 +02:00
// shut off and reset
2019-10-13 13:49:25 +02:00
pev - > nextthink = 0.0f ;
2016-06-04 15:24:23 +02:00
}
else
{
// make random announcement from sentence group
2016-07-31 15:48:50 +02:00
if ( SENTENCEG_PlayRndSz ( ENT ( pev ) , szSoundFile , flvolume , flattenuation , flags , pitch ) < 0 )
ALERT ( at_console , " Level Design Error! \n SPEAKER has bad sentence group name: %s \n " , szSoundFile ) ;
2016-06-04 15:24:23 +02:00
// set next announcement time for random 5 to 10 minute delay
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + RANDOM_FLOAT ( ANNOUNCE_MINUTES_MIN * 60.0f , ANNOUNCE_MINUTES_MAX * 60.0f ) ;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
CTalkMonster : : g_talkWaitTime = gpGlobals - > time + 5.0f ; // time delay until it's ok to speak: used so that two NPCs don't talk at once
2016-06-04 15:24:23 +02:00
}
return ;
}
//
// ToggleUse - if an announcement is pending, cancel it. If no announcement is pending, start one.
//
2016-07-31 15:48:50 +02:00
void CSpeaker : : ToggleUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
int fActive = ( pev - > nextthink > 0.0f ) ;
2016-06-04 15:24:23 +02:00
// fActive is TRUE only if an announcement is pending
2016-06-25 18:33:39 +02:00
2016-07-31 15:48:50 +02:00
if ( useType ! = USE_TOGGLE )
2016-06-04 15:24:23 +02:00
{
// ignore if we're just turning something on that's already on, or
// turning something off that's already off.
2016-07-31 15:48:50 +02:00
if ( ( fActive & & useType = = USE_ON ) | | ( ! fActive & & useType = = USE_OFF ) )
2016-06-04 15:24:23 +02:00
return ;
}
2016-07-31 15:48:50 +02:00
if ( useType = = USE_ON )
2016-06-04 15:24:23 +02:00
{
// turn on announcements
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-04 15:24:23 +02:00
return ;
}
2016-07-31 15:48:50 +02:00
if ( useType = = USE_OFF )
2016-06-04 15:24:23 +02:00
{
// turn off announcements
2019-10-13 13:49:25 +02:00
pev - > nextthink = 0.0f ;
2016-06-04 15:24:23 +02:00
return ;
}
// Toggle announcements
2016-07-31 15:48:50 +02:00
if ( fActive )
2016-06-04 15:24:23 +02:00
{
// turn off announcements
2019-10-13 13:49:25 +02:00
pev - > nextthink = 0.0f ;
2016-06-04 15:24:23 +02:00
}
else
{
// turn on announcements
2019-10-13 13:49:25 +02:00
pev - > nextthink = gpGlobals - > time + 0.1f ;
2016-06-25 18:33:39 +02:00
}
2016-06-04 15:24:23 +02:00
}
// KeyValue - load keyvalue pairs into member data
// NOTE: called BEFORE spawn!
2016-07-31 15:48:50 +02:00
void CSpeaker : : KeyValue ( KeyValueData * pkvd )
2016-06-04 15:24:23 +02:00
{
// preset
2016-07-31 15:48:50 +02:00
if ( FStrEq ( pkvd - > szKeyName , " preset " ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_preset = atoi ( pkvd - > szValue ) ;
2016-06-04 15:24:23 +02:00
pkvd - > fHandled = TRUE ;
}
else
CBaseEntity : : KeyValue ( pkvd ) ;
2016-04-17 22:30:05 +02:00
}