jedioutcast/code/client/snd_ambient.cpp

1088 lines
23 KiB
C++
Raw Normal View History

2013-04-04 14:52:42 +00:00
// Ambient Sound System (ASS!)
// leave this as first line for PCH reasons...
//
#include "../server/exe_headers.h"
#pragma warning ( disable : 4710 ) //not inlined
#include "client.h"
#include "snd_ambient.h"
static const int MAX_SET_VOLUME = 255;
static void AS_GetGeneralSet( ambientSet_t & );
static void AS_GetLocalSet( ambientSet_t & );
static void AS_GetBModelSet( ambientSet_t & );
//Current set and old set for crossfading
static int currentSet = -1;
static int oldSet = -1;
static int crossDelay = 1000; //1 second
static int currentSetTime = 0;
static int oldSetTime = 0;
// Globals for debug purposes
static int numSets = 0;
// Main ambient sound group
static CSetGroup aSets;
// Globals for speed, blech
static char *parseBuffer = NULL;
static int parseSize = 0;
static int parsePos = 0;
static char tempBuffer[1024];
//NOTENOTE: Be sure to change the mirrored code in g_spawn.cpp, and cg_main.cpp
typedef map< sstring_t, unsigned char, less<sstring_t>, allocator< unsigned char > > namePrecache_m;
static namePrecache_m pMap;
// Used for enum / string matching
static const char *setNames[NUM_AS_SETS] =
{
"generalSet",
"localSet",
"bmodelSet",
};
// Used for enum / function matching
static const parseFunc_t parseFuncs[NUM_AS_SETS] =
{
AS_GetGeneralSet,
AS_GetLocalSet,
AS_GetBModelSet,
};
// Used for keyword / enum matching
static const char *keywordNames[NUM_AS_KEYWORDS]=
{
"timeBetweenWaves",
"subWaves",
"loopedWave",
"volRange",
"radius",
"type",
"amsdir",
"outdir",
"basedir",
};
/*
-------------------------
Free
-------------------------
*/
void CSetGroup::Free( void )
{
vector < ambientSet_t * >::iterator ai;
for ( ai = m_ambientSets.begin(); ai != m_ambientSets.end(); ai++ )
{
Z_Free ( (*ai) );
}
m_ambientSets.clear();
m_setMap.clear();
m_numSets = 0;
}
/*
-------------------------
AddSet
-------------------------
*/
ambientSet_t *CSetGroup::AddSet( const char *name )
{
ambientSet_t *set;
//Allocate the memory
set = (ambientSet_t *) Z_Malloc( sizeof( ambientSet_t ), TAG_AMBIENTSET, qtrue);
//Set up some defaults
Q_strncpyz(set->name,name,sizeof(set->name));
set->loopedVolume = MAX_SET_VOLUME;
set->masterVolume = MAX_SET_VOLUME;
set->radius = 250;
set->time_start = 10;
set->time_end = 25;
set->volRange_start = MAX_SET_VOLUME;
set->volRange_end = MAX_SET_VOLUME;
m_ambientSets.insert( m_ambientSets.end(), set );
set->id = m_numSets++;
//Map the name to the pointer for reference later
m_setMap[name] = set;
return set;
}
/*
-------------------------
GetSet
-------------------------
*/
ambientSet_t *CSetGroup::GetSet( const char *name )
{
map < sstring_t, ambientSet_t *>::iterator mi;
if ( name == NULL )
return NULL;
mi = m_setMap.find( name );
if ( mi == m_setMap.end() )
return NULL;
return (*mi).second;
}
ambientSet_t *CSetGroup::GetSet( int ID )
{
if ( m_ambientSets.empty() )
return NULL;
if ( ID < 0 )
return NULL;
if ( ID >= m_numSets )
return NULL;
return m_ambientSets[ID];
}
/*
===============================================
File Parsing
===============================================
*/
/*
-------------------------
AS_GetSetNameIDForString
-------------------------
*/
static int AS_GetSetNameIDForString( const char *name )
{
//Make sure it's valid
if ( name == NULL || name[0] == NULL )
return -1;
for ( int i = 0; i < NUM_AS_SETS; i++ )
{
if ( stricmp( name, setNames[i] ) == 0 )
return i;
}
return -1;
}
/*
-------------------------
AS_GetKeywordIDForString
-------------------------
*/
static int AS_GetKeywordIDForString( const char *name )
{
//Make sure it's valid
if ( name == NULL || name[0] == NULL )
return -1;
for ( int i = 0; i < NUM_AS_KEYWORDS; i++ )
{
if ( stricmp( name, keywordNames[i] ) == 0 )
return i;
}
return -1;
}
/*
-------------------------
AS_SkipLine
Skips a line in the character buffer
-------------------------
*/
static void AS_SkipLine( void )
{
if ( parsePos > parseSize ) // needed to avoid a crash because of some OOR access that shouldn't be done
return;
while ( (parseBuffer[parsePos] != '\n') && (parseBuffer[parsePos] != '\r') )
{
parsePos++;
if ( parsePos > parseSize )
return;
}
parsePos++;
}
/*
-------------------------
AS_GetTimeBetweenWaves
getTimeBetweenWaves <start> <end>
-------------------------
*/
static void AS_GetTimeBetweenWaves( ambientSet_t &set )
{
int startTime, endTime;
//Get the data
sscanf( parseBuffer+parsePos, "%s %d %d", &tempBuffer, &startTime, &endTime );
//Check for swapped start / end
if ( startTime > endTime )
{
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf(S_COLOR_YELLOW"WARNING: Corrected swapped start / end times in a \"timeBetweenWaves\" keyword\n");
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
int swap = startTime;
startTime = endTime;
endTime = swap;
}
//Store it
set.time_start = startTime;
set.time_end = endTime;
AS_SkipLine();
}
/*
-------------------------
AS_GetSubWaves
subWaves <directory> <wave1> <wave2> ...
-------------------------
*/
static void AS_GetSubWaves( ambientSet_t &set )
{
char dirBuffer[512], waveBuffer[256], waveName[1024];
//Get the directory for these sets
sscanf( parseBuffer+parsePos, "%s %s", &tempBuffer, &dirBuffer );
//Move the pointer past these two strings
parsePos += ((strlen(keywordNames[SET_KEYWORD_SUBWAVES])+1) + (strlen(dirBuffer)+1));
//Get all the subwaves
while ( parsePos <= parseSize )
{
2013-04-04 18:24:26 +00:00
//Get the data
sscanf( parseBuffer+parsePos, "%s", &waveBuffer );
2013-04-04 14:52:42 +00:00
if ( set.numSubWaves > MAX_WAVES_PER_GROUP )
{
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf(S_COLOR_YELLOW"WARNING: Too many subwaves on set \"%s\"\n", set.name );
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
}
2013-04-04 18:24:26 +00:00
else
2013-04-04 14:52:42 +00:00
{
2013-04-04 18:24:26 +00:00
//Construct the wave name (pretty, huh?)
strcpy( (char *) waveName, "sound/" );
strncat( (char *) waveName, (const char *) dirBuffer, 1024 );
strncat( (char *) waveName, "/", 512 );
strncat( (char *) waveName, (const char *) waveBuffer, 512 );
strncat( (char *) waveName, ".wav", 512 );
//Place this onto the sound directory name
//Precache the file at this point and store off the ID instead of the name
if ( ( set.subWaves[set.numSubWaves++] = S_RegisterSound( waveName ) ) <= 0 )
{
#ifndef FINAL_BUILD
Com_Printf(S_COLOR_YELLOW"WARNING: Unable to load ambient sound \"%s\"\n", waveName);
#endif
}
2013-04-04 14:52:42 +00:00
}
//Move the pointer past this string
parsePos += strlen(waveBuffer)+1;
if ( ( (parseBuffer+parsePos)[0] == '\n') || ( (parseBuffer+parsePos)[0] == '\r') )
break;
}
AS_SkipLine();
}
/*
-------------------------
AS_GetLoopedWave
loopedWave <name>
-------------------------
*/
static void AS_GetLoopedWave( ambientSet_t &set )
{
char waveBuffer[256], waveName[1024];
//Get the looped wave name
sscanf( parseBuffer+parsePos, "%s %s", &tempBuffer, &waveBuffer );
//Construct the wave name
strcpy( (char *) waveName, "sound/" );
strncat( (char *) waveName, (const char *) waveBuffer, 1024 );
strncat( (char *) waveName, ".wav", 1024 );
//Precache the file at this point and store off the ID instead of the name
if ( ( set.loopedWave = S_RegisterSound( waveName ) ) <= 0 )
{
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf(S_COLOR_YELLOW"WARNING: Unable to load ambient sound \"%s\"\n", waveName);
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
}
AS_SkipLine();
}
/*
-------------------------
AS_GetVolumeRange
-------------------------
*/
static void AS_GetVolumeRange( ambientSet_t &set )
{
int min, max;
//Get the data
sscanf( parseBuffer+parsePos, "%s %d %d", &tempBuffer, &min, &max );
//Check for swapped min / max
if ( min > max )
{
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf(S_COLOR_YELLOW"WARNING: Corrected swapped min / max range in a \"volRange\" keyword\n");
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
int swap = min;
min = max;
max = swap;
}
//Store the data
set.volRange_start = min;
set.volRange_end = max;
AS_SkipLine();
}
/*
-------------------------
AS_GetRadius
-------------------------
*/
static void AS_GetRadius( ambientSet_t &set )
{
//Get the data
sscanf( parseBuffer+parsePos, "%s %d", &tempBuffer, &set.radius );
AS_SkipLine();
}
/*
-------------------------
AS_GetGeneralSet
-------------------------
*/
static void AS_GetGeneralSet( ambientSet_t &set )
{
int keywordID;
//The other parameters of the set come in a specific order
while ( parsePos <= parseSize )
{
int iFieldsScanned = sscanf( parseBuffer+parsePos, "%s", &tempBuffer );
if (iFieldsScanned <= 0)
return;
keywordID = AS_GetKeywordIDForString( (const char *) &tempBuffer );
//Find and parse the keyword info
switch ( keywordID )
{
case SET_KEYWORD_TIMEBETWEENWAVES:
AS_GetTimeBetweenWaves( set );
break;
case SET_KEYWORD_SUBWAVES:
AS_GetSubWaves( set );
break;
case SET_KEYWORD_LOOPEDWAVE:
AS_GetLoopedWave( set );
break;
case SET_KEYWORD_VOLRANGE:
AS_GetVolumeRange( set );
break;
default:
//Check to see if we've finished this group
if ( AS_GetSetNameIDForString( (const char *) &tempBuffer ) == -1 )
{
//Ignore comments
if ( tempBuffer[0] == ';' )
return;
//This wasn't a set name, so it's an error
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf( S_COLOR_YELLOW"WARNING: Unknown ambient set keyword \"%s\"\n", tempBuffer );
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
}
return;
break;
}
}
}
/*
-------------------------
AS_GetLocalSet
-------------------------
*/
static void AS_GetLocalSet( ambientSet_t &set )
{
int keywordID;
//The other parameters of the set come in a specific order
while ( parsePos <= parseSize )
{
int iFieldsScanned = sscanf( parseBuffer+parsePos, "%s", &tempBuffer );
if (iFieldsScanned <= 0)
return;
keywordID = AS_GetKeywordIDForString( (const char *) &tempBuffer );
//Find and parse the keyword info
switch ( keywordID )
{
case SET_KEYWORD_TIMEBETWEENWAVES:
AS_GetTimeBetweenWaves( set );
break;
case SET_KEYWORD_SUBWAVES:
AS_GetSubWaves( set );
break;
case SET_KEYWORD_LOOPEDWAVE:
AS_GetLoopedWave( set );
break;
case SET_KEYWORD_VOLRANGE:
AS_GetVolumeRange( set );
break;
case SET_KEYWORD_RADIUS:
AS_GetRadius( set );
break;
default:
//Check to see if we've finished this group
if ( AS_GetSetNameIDForString( (const char *) &tempBuffer ) == -1 )
{
//Ignore comments
if ( tempBuffer[0] == ';' )
return;
//This wasn't a set name, so it's an error
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf( S_COLOR_YELLOW"WARNING: Unknown ambient set keyword \"%s\"\n", tempBuffer );
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
}
return;
break;
}
}
}
/*
-------------------------
AS_GetBModelSet
-------------------------
*/
static void AS_GetBModelSet( ambientSet_t &set )
{
int keywordID;
//The other parameters of the set come in a specific order
while ( parsePos <= parseSize )
{
int iFieldsScanned = sscanf( parseBuffer+parsePos, "%s", &tempBuffer );
if (iFieldsScanned <= 0)
return;
keywordID = AS_GetKeywordIDForString( (const char *) &tempBuffer );
//Find and parse the keyword info
switch ( keywordID )
{
case SET_KEYWORD_SUBWAVES:
AS_GetSubWaves( set );
break;
default:
//Check to see if we've finished this group
if ( AS_GetSetNameIDForString( (const char *) &tempBuffer ) == -1 )
{
//Ignore comments
if ( tempBuffer[0] == ';' )
return;
//This wasn't a set name, so it's an error
2013-04-04 18:24:26 +00:00
#ifndef FINAL_BUILD
2013-04-04 14:52:42 +00:00
Com_Printf( S_COLOR_YELLOW"WARNING: Unknown ambient set keyword \"%s\"\n", tempBuffer );
2013-04-04 18:24:26 +00:00
#endif
2013-04-04 14:52:42 +00:00
}
return;
break;
}
}
}
/*
-------------------------
AS_ParseSet
Parses an individual set group out of a set file buffer
-------------------------
*/
static qboolean AS_ParseSet( int setID, CSetGroup &sg )
{
ambientSet_t *set;
const char *name;
//Make sure we're not overstepping the name array
if ( setID > NUM_AS_SETS )
return qfalse;
//Reset the pointers for this run through
parsePos = 0;
name = setNames[setID];
//Iterate through the whole file and find every occurance of a set
while ( parsePos <= parseSize )
{
//Check for a valid set group
if ( strncmp( parseBuffer+parsePos, name, strlen(name) ) == 0 )
{
//Update the debug info
numSets++;
//Push past the set specifier and on to the name
parsePos+=strlen(name)+1; //Also take the following space out
//Get the set name (this MUST be first)
sscanf( parseBuffer+parsePos, "%s", &tempBuffer );
AS_SkipLine();
//Test the string against the precaches
if ( tempBuffer[0] )
{
//Not in our precache listings, so skip it
if ( ( pMap.find( (const char *) &tempBuffer ) == pMap.end() ) )
continue;
}
//Create a new set
set = sg.AddSet( (const char *) &tempBuffer );
//Run the function to parse the data out
parseFuncs[setID]( *set );
continue;
}
//If not found on this line, go down another and check again
AS_SkipLine();
}
return qtrue;
}
/*
-------------------------
AS_ParseHeader
Parses the directory information out of the beginning of the file
-------------------------
*/
static void AS_ParseHeader( void )
{
char typeBuffer[128];
int keywordID;
while ( parsePos <= parseSize )
{
sscanf( parseBuffer+parsePos, "%s", &tempBuffer );
keywordID = AS_GetKeywordIDForString( (const char *) &tempBuffer );
switch ( keywordID )
{
case SET_KEYWORD_TYPE:
sscanf( parseBuffer+parsePos, "%s %s", &tempBuffer, &typeBuffer );
if ( !stricmp( (const char *) typeBuffer, "ambientSet" ) )
{
return;
}
Com_Error( ERR_DROP, "AS_ParseHeader: Set type \"%s\" is not a valid set type!\n", typeBuffer );
break;
case SET_KEYWORD_AMSDIR:
//TODO: Implement
break;
case SET_KEYWORD_OUTDIR:
//TODO: Implement
break;
case SET_KEYWORD_BASEDIR:
//TODO: Implement
break;
}
AS_SkipLine();
}
}
/*
-------------------------
AS_ParseFile
Opens and parses a sound set file
-------------------------
*/
static qboolean AS_ParseFile( const char *filename, CSetGroup &sg )
{
//Open the file and read the information from it
parseSize = FS_ReadFile( filename, (void **) &parseBuffer );
if ( parseSize <= 0 )
return qfalse;
//Parse the directory information out of the file
AS_ParseHeader();
//Parse all the relevent sets out of it
for ( int i = 0; i < NUM_AS_SETS; i++ )
AS_ParseSet( i, sg );
//Free the memory and close the file
FS_FreeFile( parseBuffer );
return qtrue;
}
/*
===============================================
Main code
===============================================
*/
/*
-------------------------
AS_Init
Loads the ambient sound sets and prepares to play them when needed
-------------------------
*/
void AS_Init( void )
{
numSets = 0;
//Setup the structure
aSets.Init();
}
/*
-------------------------
AS_AddPrecacheEntry
-------------------------
*/
void AS_AddPrecacheEntry( const char *name )
{
2013-04-04 18:24:26 +00:00
if (!stricmp(name,"#clear"))
{
pMap.clear();
}
else
{
pMap[ name ] = 1;
}
2013-04-04 14:52:42 +00:00
}
/*
-------------------------
AS_ParseSets
Called on the client side to load and precache all the ambient sound sets
-------------------------
*/
void AS_ParseSets( void )
{
AS_Init();
//Parse all the sets
if ( AS_ParseFile( AMBIENT_SET_FILENAME, aSets ) == qfalse )
{
Com_Error ( ERR_FATAL, S_COLOR_RED"ERROR: Couldn't load ambient sound sets from %s", AMBIENT_SET_FILENAME );
}
Com_Printf( "AS_ParseFile: Loaded %d of %d ambient set(s)\n", pMap.size(), numSets );
int iErrorsOccured = 0;
for (namePrecache_m::iterator it = pMap.begin(); it != pMap.end(); ++it)
{
const char* str = (*it).first.c_str();
ambientSet_t *aSet = aSets.GetSet( str );
if (!aSet)
{
// I print these red instead of yellow because they're going to cause an ERR_DROP if they occur
Com_Printf( S_COLOR_RED"ERROR: AS_ParseSets: Unable to find ambient soundset \"%s\"!\n",str);
iErrorsOccured++;
}
}
if (iErrorsOccured)
{
Com_Error( ERR_DROP, "....%d missing sound sets! (see above)\n", iErrorsOccured);
}
2013-04-04 18:24:26 +00:00
// //Done with the precache info, it will be rebuilt on a restart
// pMap.clear(); // do NOT do this here now
2013-04-04 14:52:42 +00:00
}
/*
-------------------------
AS_Free
Frees up the ambient sound system
-------------------------
*/
void AS_Free( void )
{
aSets.Free();
currentSet = -1;
oldSet = -1;
currentSetTime = 0;
oldSetTime = 0;
numSets = 0;
}
/*
===============================================
Sound code
===============================================
*/
/*
-------------------------
AS_UpdateSetVolumes
Fades volumes up or down depending on the action being taken on them.
-------------------------
*/
static void AS_UpdateSetVolumes( void )
{
ambientSet_t *old, *current;
float scale;
int deltaTime;
//Get the sets and validate them
current = aSets.GetSet( currentSet );
if ( current == NULL )
return;
if ( current->masterVolume < MAX_SET_VOLUME )
{
deltaTime = cls.realtime - current->fadeTime;
scale = ((float)(deltaTime)/(float)(crossDelay));
current->masterVolume = (int)((scale) * (float)MAX_SET_VOLUME);
}
if ( current->masterVolume > MAX_SET_VOLUME )
current->masterVolume = MAX_SET_VOLUME;
//Only update the old set if it's still valid
if ( oldSet == -1 )
return;
old = aSets.GetSet( oldSet );
if ( old == NULL )
return;
//Update the volumes
if ( old->masterVolume > 0 )
{
deltaTime = cls.realtime - old->fadeTime;
scale = ((float)(deltaTime)/(float)(crossDelay));
old->masterVolume = MAX_SET_VOLUME - (int)((scale) * (float)MAX_SET_VOLUME);
}
if ( old->masterVolume <= 0 )
{
old->masterVolume = 0;
oldSet = -1;
}
}
/*
-------------------------
S_UpdateCurrentSet
Does internal maintenance to keep track of changing sets.
-------------------------
*/
static void AS_UpdateCurrentSet( int id )
{
ambientSet_t *old, *current;
//Check for a change
if ( id != currentSet )
{
//This is new, so start the fading
oldSet = currentSet;
currentSet = id;
old = aSets.GetSet( oldSet );
current = aSets.GetSet( currentSet );
2013-04-04 18:02:27 +00:00
// Ste, I just put this null check in for now, not sure if there's a more graceful way to exit this function - dmv
if( !current )
{
return;
}
2013-04-04 14:52:42 +00:00
if ( old )
{
old->masterVolume = MAX_SET_VOLUME;
old->fadeTime = cls.realtime;
}
2013-04-04 18:02:27 +00:00
2013-04-04 14:52:42 +00:00
current->masterVolume = 0;
//Set the fading starts
current->fadeTime = cls.realtime;
}
//Update their volumes if fading
AS_UpdateSetVolumes();
}
/*
-------------------------
AS_PlayLocalSet
Plays a local set taking volume and subwave playing into account.
Alters lastTime to reflect the time updates.
-------------------------
*/
static void AS_PlayLocalSet( vec3_t listener_origin, vec3_t origin, ambientSet_t *set, int entID, int *lastTime )
{
unsigned char volume;
vec3_t dir;
float volScale, dist, distScale;
int time = cls.realtime;
//Make sure it's valid
if ( set == NULL )
return;
VectorSubtract( origin, listener_origin, dir );
dist = VectorLength( dir );
//Determine the volume based on distance (NOTE: This sits on top of what SpatializeOrigin does)
distScale = ( dist < ( set->radius * 0.5f ) ) ? 1 : ( set->radius - dist ) / ( set->radius * 0.5f );
volume = ( distScale > 1.0f || distScale < 0.0f ) ? 0 : (unsigned char) ( set->masterVolume * distScale );
//Add the looping sound
if ( set->loopedWave )
S_AddAmbientLoopingSound( origin, volume, set->loopedWave );
//Check the time to start another one-shot subwave
if ( ( time - *lastTime ) < ( ( Q_irand( set->time_start, set->time_end ) ) * 1000 ) )
return;
//Update the time
*lastTime = time;
//Scale the volume ranges for the subwaves based on the overall master volume
volScale = (float) volume / (float) MAX_SET_VOLUME;
volume = (unsigned char) Q_irand( (int)(volScale*set->volRange_start), (int)(volScale*set->volRange_end) );
//Add the random subwave
if ( set->numSubWaves )
S_StartAmbientSound( origin, entID, volume, set->subWaves[Q_irand( 0, set->numSubWaves-1)] );
}
/*
-------------------------
AS_PlayAmbientSet
Plays an ambient set taking volume and subwave playing into account.
Alters lastTime to reflect the time updates.
-------------------------
*/
static void AS_PlayAmbientSet( vec3_t origin, ambientSet_t *set, int *lastTime )
{
unsigned char volume;
float volScale;
int time = cls.realtime;
//Make sure it's valid
if ( set == NULL )
return;
//Add the looping sound
if ( set->loopedWave )
S_AddAmbientLoopingSound( origin, (unsigned char) set->masterVolume, set->loopedWave );
//Check the time to start another one-shot subwave
if ( ( time - *lastTime ) < ( ( Q_irand( set->time_start, set->time_end ) ) * 1000 ) )
return;
//Update the time
*lastTime = time;
//Scale the volume ranges for the subwaves based on the overall master volume
volScale = (float) set->masterVolume / (float) MAX_SET_VOLUME;
volume = Q_irand( (int)(volScale*set->volRange_start), (int)(volScale*set->volRange_end) );
//Allow for softer noises than the masterVolume, but not louder
if ( volume > set->masterVolume )
volume = set->masterVolume;
//Add the random subwave
if ( set->numSubWaves )
S_StartAmbientSound( origin, 0, volume, set->subWaves[Q_irand( 0, set->numSubWaves-1)] );
}
/*
-------------------------
S_UpdateAmbientSet
Does maintenance and plays the ambient sets (two if crossfading)
-------------------------
*/
void S_UpdateAmbientSet ( const char *name, vec3_t origin )
{
ambientSet_t *current, *old;
ambientSet_t *set = aSets.GetSet( name );
if ( set == NULL )
return;
//Update the current and old set for crossfading
AS_UpdateCurrentSet( set->id );
current = aSets.GetSet( currentSet );
old = aSets.GetSet( oldSet );
if ( current )
AS_PlayAmbientSet( origin, set, &currentSetTime );
if ( old )
AS_PlayAmbientSet( origin, old, &oldSetTime );
}
/*
-------------------------
S_AddLocalSet
-------------------------
*/
int S_AddLocalSet( const char *name, vec3_t listener_origin, vec3_t origin, int entID, int time )
{
ambientSet_t *set;
int currentTime = 0;
set = aSets.GetSet( name );
if ( set == NULL )
return cls.realtime;
currentTime = time;
AS_PlayLocalSet( listener_origin, origin, set, entID, &currentTime );
return currentTime;
}
/*
-------------------------
AS_GetBModelSound
-------------------------
*/
sfxHandle_t AS_GetBModelSound( const char *name, int stage )
{
ambientSet_t *set;
set = aSets.GetSet( name );
if ( set == NULL )
return -1;
//Stage must be within a valid range
if ( ( stage > ( set->numSubWaves - 1 ) ) || ( stage < 0 ) )
return -1;
return set->subWaves[ stage ];
}