This adds an ability for mods developers to override hardcoded sounds with custom ones. Alibek Omarov (3): engine: add basic sounds.lst implementation engine: client: use soundlist to acquire random sounds for temp entities engine: server: use soundlist to acquire random sounds for physics Documentation/extensions/sounds.lst.md | 66 ++++++ engine/client/cl_tent.c | 124 ++++++----- engine/common/common.h | 28 +++ engine/common/host.c | 2 + engine/common/sounds.c | 272 +++++++++++++++++++++++++ engine/server/sv_move.c | 36 +--- engine/server/sv_phys.c | 8 +- 7 files changed, 438 insertions(+), 98 deletions(-) create mode 100644 Documentation/extensions/sounds.lst.md create mode 100644 engine/common/sounds.c -- 2.34.1
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~a1batross/xash3d-fwgs/patches/49038/mbox | git am -3Learn more about email & git
--- Documentation/extensions/sounds.lst.md | 66 ++++++ engine/common/common.h | 28 +++ engine/common/host.c | 2 + engine/common/sounds.c | 272 +++++++++++++++++++++++++ 4 files changed, 368 insertions(+) create mode 100644 Documentation/extensions/sounds.lst.md create mode 100644 engine/common/sounds.c diff --git a/Documentation/extensions/sounds.lst.md b/Documentation/extensions/sounds.lst.md new file mode 100644 index 00000000..77104d44 --- /dev/null +++ b/Documentation/extensions/sounds.lst.md @@ -0,0 +1,66 @@ +# sounds.lst.md + +Using sounds.lst located in scripts folder, modder can override some of the hardcoded sounds in temp entities and server physics. + +File format: +``` +<group name> +{ + <path1> + <path2> + <path3> +} + +<group2 name> <path with %d> <min number> <max number> +``` + +* Sounds can use any supported sound format (WAV or MP3). +* The path must be relative to the sounds/ folder in the game or base directory root, addon folder, or archive root. +* Groups can be empty or omitted from the file to load no sound. +* Groups can either list a set of files or specify a format string and a range. +* Anything after // will be considered a comment and ignored. +* Behavior is undefined if the group was listed multiple times. + +Currently supported groups are: +|Group name|Usage| +|----------|-----| +|`BouncePlayerShell`|Used for BOUNCE_SHELL tempentity hitsound| +|`BounceWeaponShell`|Used for BOUCNE_SHOTSHELL tempentity hitsound| +|`BounceConcrete`|Used for BOUNCE_CONCRETE tempentity hitsound| +|`BounceGlass`|Used for BOUCNE_GLASS| +|`BounceMetal`|Used for BOUNCE_METAL| +|`BounceFlesh`|Used for BOUNCE_FLESH| +|`BounceWood`|Used for BOUNCE_WOOD| +|`Ricochet`|Used for BOUNCE_SHRAP and ricochet tempentities| +|`Explode`|Used for tempentity explosions| +|`EntityWaterEnter`|Used for entity entering water| +|`EntityWaterExit`|Used for entity exiting water| +|`PlayerWaterEnter`|Used for player entering water| +|`PlayerWaterExit`|Used for player exiting water| + +## Example + +This example is based on defaults sounds used in Half-Life: + +``` +BouncePlayerShell "player/pl_shell%d.wav" 1 3 +BounceWeaponShell "weapons/sshell%d.wav" 1 3 +BounceConcrete "debris/concrete%d.wav" 1 3 +BounceGlass "debris/glass%d.wav" 1 4 +BounceMetal "debris/metal%d.wav" 1 6 +BounceFlesh "debris/flesh%d.wav" 1 7 +BounceWood "debris/wood%d.wav" 1 4 +Ricochet "weapons/ric%d.wav" 1 5 +Explode "weapons/explode%d" 3 5 +EntityWaterEnter "player/pl_wade%d.wav" 1 4 +EntityWaterExit "player/pl_wade%d.wav" 1 4 +PlayerWaterEnter +{ + "player/pl_wade1.wav" +} + +PlayerWaterExit +{ + "player/pl_wade2.wav" +} +``` diff --git a/engine/common/common.h b/engine/common/common.h index 26c02c62..31f1bc8c 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -815,6 +815,34 @@ void NET_MasterClear( void ); void NET_MasterShutdown( void ); qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat ); +// +// sounds.c +// +typedef enum soundlst_group_e +{ + BouncePlayerShell = 0, + BounceWeaponShell, + BounceConcrete, + BounceGlass, + BounceMetal, + BounceFlesh, + BounceWood, + Ricochet, + Explode, + PlayerWaterEnter, + PlayerWaterExit, + EntityWaterEnter, + EntityWaterExit, + + SoundList_Groups // must be last +} soundlst_group_t; + +int SoundList_Count( soundlst_group_t group ); +const char *SoundList_GetRandom( soundlst_group_t group ); +const char *SoundList_Get( soundlst_group_t group, int idx ); +void SoundList_Init( void ); +void SoundList_Shutdown( void ); + #ifdef REF_DLL #error "common.h in ref_dll" #endif diff --git a/engine/common/host.c b/engine/common/host.c index f990f1cc..65ae4267 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1310,6 +1310,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa HTTP_Init(); ID_Init(); + SoundList_Init(); if( Host_IsDedicated() ) { @@ -1412,6 +1413,7 @@ void EXPORT Host_Shutdown( void ) SV_ShutdownFilter(); CL_Shutdown(); + SoundList_Shutdown(); Mod_Shutdown(); NET_Shutdown(); HTTP_Shutdown(); diff --git a/engine/common/sounds.c b/engine/common/sounds.c new file mode 100644 index 00000000..b1e3ed27 --- /dev/null +++ b/engine/common/sounds.c @@ -0,0 +1,272 @@ +/* +sounds.c - sounds.lst parser +Copyright (C) 2024 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "common.h" + +enum soundlst_type_e +{ + SoundList_None, + SoundList_Range, + SoundList_List +}; + +static const char *soundlst_groups[SoundList_Groups] = +{ + "BouncePlayerShell", + "BounceWeaponShell", + "BounceConcrete", + "BounceGlass", + "BounceMetal", + "BounceFlesh", + "BounceWood", + "Ricochet", + "Explode", + "PlayerWaterEnter", + "PlayerWaterExit", + "EntityWaterEnter", + "EntityWaterExit", +}; + +typedef struct soundlst_s +{ + enum soundlst_type_e type; + char *snd; + int min; // the string length if type is group + int max; // the string count if type is group +} soundlst_t; + +soundlst_t soundlst[SoundList_Groups]; + +static void SoundList_Free( soundlst_t *lst ) +{ + if( lst->snd ) + { + Mem_Free( lst->snd ); + lst->snd = NULL; + } + + lst->min = lst->max = 0; + lst->type = SoundList_None; +} + +void SoundList_Shutdown( void ) +{ + int i; + + for( i = 0; i < SoundList_Groups; i++ ) + SoundList_Free( &soundlst[i] ); +} + +int SoundList_Count( enum soundlst_group_e group ) +{ + soundlst_t *lst = &soundlst[group]; + + switch( lst->type ) + { + case SoundList_Range: + return lst->max - lst->min + 1; + case SoundList_List: + return lst->max; + } + + return 0; +} + +const char *SoundList_Get( enum soundlst_group_e group, int i ) +{ + static string temp; + soundlst_t *lst = &soundlst[group]; + + if( i < 0 || i >= SoundList_Count( group )) + return NULL; + + switch( lst->type ) + { + case SoundList_Range: + Q_snprintf( temp, sizeof( temp ), lst->snd, lst->min + i ); + return temp; + case SoundList_List: + return &lst->snd[i * lst->min]; + } + + return NULL; +} + +const char *SoundList_GetRandom( enum soundlst_group_e group ) +{ + int count = SoundList_Count( group ); + int idx = COM_RandomLong( 0, count - 1 ); + + return SoundList_Get( group, idx ); +} + +static qboolean SoundList_ParseGroup( soundlst_t *lst, char **file ) +{ + string token; + int count = 0, slen = 0, i; + char *p; + + p = *file; + + for( ; p && *p; count++, p = COM_ParseFile( p, token, sizeof( token ))) + { + int len = Q_strlen( token ) + 1; + if( slen < len ) + slen = len; + + if( !Q_strcmp( token, "}" )) + break; + + if( !Q_strcmp( token, "{" )) + { + Con_Printf( "%s: expected '}' but got '{' during group list parse\n", __func__ ); + return false; + } + else if( !COM_CheckStringEmpty( token )) + { + Con_Printf( "%s: expected '}' but got EOF during group list parse\n", __func__ ); + return false; + } + } + + lst->type = SoundList_List; + lst->min = slen; + lst->max = count; + lst->snd = Mem_Malloc( host.mempool, count * slen ); // allocate single buffer for the whole group + + for( i = 0; i < count; i++ ) + { + *file = COM_ParseFile( *file, token, sizeof( token )); + + Q_strncpy( &lst->snd[i * slen], token, slen ); + } + + return true; +} + +static qboolean SoundList_ParseRange( soundlst_t *lst, char **file ) +{ + string token, snd; + char *p; + int i; + + lst->type = SoundList_Range; + *file = COM_ParseFile( *file, snd, sizeof( snd )); + + // validate format string + for( i = 0, p = snd; p; i++, p = Q_strchr( p, '%' )); + if( i != 1 ) + { + Con_Printf( "%s: invalid range string %s\n", __func__, snd ); + return false; + } + + *file = COM_ParseFile( *file, token, sizeof( token )); + if( !Q_isdigit( token )) + { + Con_Printf( "%s: %s must be a digit\n", __func__, token ); + return false; + } + lst->min = Q_atoi( token ); + + *file = COM_ParseFile( *file, token, sizeof( token )); + if( !Q_isdigit( token )) + { + Con_Printf( "%s: %s must be a digit\n", __func__, token ); + return false; + } + lst->max = Q_atoi( token ); + lst->snd = copystring( snd ); + + return true; +} + +static qboolean SoundList_Parse( char *file ) +{ + string token; + string name; + int i; + + while( file && *file ) + { + const char *group; + soundlst_t *lst = NULL; + file = COM_ParseFile( file, token, sizeof( token )); + + for( i = 0; i < SoundList_Groups; i++ ) + { + if( !Q_strcmp( token, soundlst_groups[i] )) + lst = &soundlst[i]; + } + + if( !lst ) + { + Con_Printf( "%s: unexpected token %s, must be group name\n", __func__, token ); + goto cleanup; + } + + file = COM_ParseFile( file, token, sizeof( token )); + + // group is a range + if( Q_strcmp( token, "{" )) + { + if( !SoundList_ParseRange( lst, &file )) + goto cleanup; + } + else + { + if( !SoundList_ParseGroup( lst, &file )) + goto cleanup; + } + } + + return true; + +cleanup: + SoundList_Shutdown(); + return false; +} + +// I wish we had #embed already +static const char default_sounds_lst[] = +"BouncePlayerShell \"player/pl_shell%d.wav\" 1 3\n" +"BounceWeaponShell \"weapons/sshell%d.wav\" 1 3\n" +"BounceConcrete \"debris/concrete%d.wav\" 1 3\n" +"BounceGlass \"debris/glass%d.wav\" 1 4\n" +"BounceMetal \"debris/metal%d.wav\" 1 6\n" +"BounceFlesh \"debris/flesh%d.wav\" 1 7\n" +"BounceWood \"debris/wood%d.wav\" 1 4\n" +"Ricochet \"weapons/ric%d.wav\" 1 5\n" +"Explode \"weapons/explode%d\" 3 5\n" +"EntityWaterEnter \"player/pl_wade%d.wav\" 1 4\n" +"EntityWaterExit \"player/pl_wade%d.wav\" 1 4\n" +"PlayerWaterEnter\n" +"{\n" +" \"player/pl_wade1.wav\"\n" +"}\n" +"\n" +"PlayerWaterExit\n" +"{\n" +" \"player/pl_wade2.wav\"\n" +"}\n"; + +void SoundList_Init( void ) +{ + char *pfile; + + pfile = FS_LoadFile( "scripts/sounds.lst", NULL, false ); + + if( !pfile || !SoundList_Parse( pfile )) + SoundList_Parse( (char *)default_sounds_lst ); +} -- 2.34.1
--- engine/client/cl_tent.c | 124 +++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 66 deletions(-) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 7023a7f9..86235c3d 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -57,36 +57,6 @@ const char *cl_default_sprites[] = "sprites/shellchrome.spr", }; -const char *cl_player_shell_sounds[] = -{ - "player/pl_shell1.wav", - "player/pl_shell2.wav", - "player/pl_shell3.wav", -}; - -const char *cl_weapon_shell_sounds[] = -{ - "weapons/sshell1.wav", - "weapons/sshell2.wav", - "weapons/sshell3.wav", -}; - -const char *cl_ricochet_sounds[] = -{ - "weapons/ric1.wav", - "weapons/ric2.wav", - "weapons/ric3.wav", - "weapons/ric4.wav", - "weapons/ric5.wav", -}; - -const char *cl_explode_sounds[] = -{ - "weapons/explode3.wav", - "weapons/explode4.wav", - "weapons/explode5.wav", -}; - static void CL_PlayerDecal( int playerIndex, int textureIndex, int entityIndex, float *pos ); /* @@ -148,6 +118,7 @@ client resources not precached by server */ void CL_AddClientResources( void ) { + const char *snd; char filepath[MAX_QPATH]; int i; @@ -163,37 +134,37 @@ void CL_AddClientResources( void ) } // then check sounds - for( i = 0; i < ARRAYSIZE( cl_player_shell_sounds ); i++ ) + for( i = 0; ( snd = SoundList_Get( BouncePlayerShell, i )); i++ ) { - Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_player_shell_sounds[i] ); + Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd ); if( !FS_FileExists( filepath, false )) - CL_AddClientResource( cl_player_shell_sounds[i], t_sound ); + CL_AddClientResource( snd, t_sound ); } - for( i = 0; i < ARRAYSIZE( cl_weapon_shell_sounds ); i++ ) + for( i = 0; ( snd = SoundList_Get( BounceWeaponShell, i )); i++ ) { - Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_weapon_shell_sounds[i] ); + Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd ); if( !FS_FileExists( filepath, false )) - CL_AddClientResource( cl_weapon_shell_sounds[i], t_sound ); + CL_AddClientResource( snd, t_sound ); } - for( i = 0; i < ARRAYSIZE( cl_explode_sounds ); i++ ) + for( i = 0; ( snd = SoundList_Get( Explode, i )); i++ ) { - Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_explode_sounds[i] ); + Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd ); if( !FS_FileExists( filepath, false )) - CL_AddClientResource( cl_explode_sounds[i], t_sound ); + CL_AddClientResource( snd, t_sound ); } #if 0 // ric sounds was precached by server-side - for( i = 0; i < ARRAYSIZE( cl_ricochet_sounds ); i++ ) + for( i = 0; ( snd = SoundList_Get( Ricochet, i )); i++ ) { - Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_ricochet_sounds[i] ); + Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd ); if( !FS_FileExists( filepath, false )) - CL_AddClientResource( cl_ricochet_sounds[i], t_sound ); + CL_AddClientResource( snd, t_sound ); } #endif } @@ -301,7 +272,7 @@ play collide sound static void CL_TempEntPlaySound( TEMPENTITY *pTemp, float damp ) { float fvol; - char soundname[32]; + const char *soundname = NULL; qboolean isshellcasing = false; int zvel; @@ -312,36 +283,39 @@ static void CL_TempEntPlaySound( TEMPENTITY *pTemp, float damp ) switch( pTemp->hitSound ) { case BOUNCE_GLASS: - Q_snprintf( soundname, sizeof( soundname ), "debris/glass%i.wav", COM_RandomLong( 1, 4 )); + soundname = SoundList_GetRandom( BounceGlass ); break; case BOUNCE_METAL: - Q_snprintf( soundname, sizeof( soundname ), "debris/metal%i.wav", COM_RandomLong( 1, 6 )); + soundname = SoundList_GetRandom( BounceMetal ); break; case BOUNCE_FLESH: - Q_snprintf( soundname, sizeof( soundname ), "debris/flesh%i.wav", COM_RandomLong( 1, 7 )); + soundname = SoundList_GetRandom( BounceFlesh ); break; case BOUNCE_WOOD: - Q_snprintf( soundname, sizeof( soundname ), "debris/wood%i.wav", COM_RandomLong( 1, 4 )); + soundname = SoundList_GetRandom( BounceWood ); break; case BOUNCE_SHRAP: - Q_strncpy( soundname, cl_ricochet_sounds[COM_RandomLong( 0, 4 )], sizeof( soundname ) ); + soundname = SoundList_GetRandom( Ricochet ); break; case BOUNCE_SHOTSHELL: - Q_strncpy( soundname, cl_weapon_shell_sounds[COM_RandomLong( 0, 2 )], sizeof( soundname ) ); + soundname = SoundList_GetRandom( BounceWeaponShell ); isshellcasing = true; // shell casings have different playback parameters fvol = 0.5f; break; case BOUNCE_SHELL: - Q_strncpy( soundname, cl_player_shell_sounds[COM_RandomLong( 0, 2 )], sizeof( soundname ) ); + soundname = SoundList_GetRandom( BouncePlayerShell ); isshellcasing = true; // shell casings have different playback parameters break; case BOUNCE_CONCRETE: - Q_snprintf( soundname, sizeof( soundname ), "debris/concrete%i.wav", COM_RandomLong( 1, 3 )); + soundname = SoundList_GetRandom( BounceConcrete ); break; default: // null sound return; } + if( !soundname ) + return; + zvel = abs( pTemp->entity.baseline.origin[2] ); // only play one out of every n @@ -1491,7 +1465,7 @@ void GAME_EXPORT R_FunnelSprite( const vec3_t org, int modelIndex, int reverse ) =============== R_SparkEffect -Create a streaks + richochet sprite +Create a streaks + ricochet sprite =============== */ void GAME_EXPORT R_SparkEffect( const vec3_t pos, int count, int velocityMin, int velocityMax ) @@ -1507,18 +1481,25 @@ R_RicochetSound Make a random ricochet sound ============== */ -static void R_RicochetSound_( const vec3_t pos, int sound ) +static void R_RicochetSoundByName( const vec3_t pos, const char *name ) { - sound_t handle; - - handle = S_RegisterSound( cl_ricochet_sounds[sound] ); - + sound_t handle; + handle = S_RegisterSound( name ); S_StartSound( pos, 0, CHAN_AUTO, handle, VOL_NORM, 1.0, 100, 0 ); } +static void R_RicochetSoundByIndex( const vec3_t pos, int idx ) +{ + const char *name = SoundList_Get( Ricochet, idx ); + if( name ) + R_RicochetSoundByName( pos, name ); +} + void GAME_EXPORT R_RicochetSound( const vec3_t pos ) { - R_RicochetSound_( pos, COM_RandomLong( 0, 4 )); + const char *name = SoundList_GetRandom( Ricochet ); + if( name ) + R_RicochetSoundByName( pos, name ); } /* @@ -1665,8 +1646,12 @@ void GAME_EXPORT R_Explosion( vec3_t pos, int model, float scale, float framerat if( !FBitSet( flags, TE_EXPLFLAG_NOSOUND )) { - hSound = S_RegisterSound( cl_explode_sounds[COM_RandomLong( 0, 2 )] ); - S_StartSound( pos, 0, CHAN_STATIC, hSound, VOL_NORM, 0.3f, PITCH_NORM, 0 ); + const char *name = SoundList_GetRandom( Explode ); + if( name ) + { + hSound = S_RegisterSound( name ); + S_StartSound( pos, 0, CHAN_STATIC, hSound, VOL_NORM, 0.3f, PITCH_NORM, 0 ); + } } } @@ -1909,6 +1894,7 @@ void CL_ParseTempEntity( sizebuf_t *msg ) cl_entity_t *pEnt; dlight_t *dl; sound_t hSound; + const char *name; if( cls.legacymode ) iSize = MSG_ReadByte( msg ); @@ -1968,8 +1954,11 @@ void CL_ParseTempEntity( sizebuf_t *msg ) pos[2] = MSG_ReadCoord( &buf ); R_BlobExplosion( pos ); - hSound = S_RegisterSound( cl_explode_sounds[0] ); - S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 ); + if(( name = SoundList_Get( Explode, 0 ))) + { + hSound = S_RegisterSound( name ); + S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 ); + } break; case TE_SMOKE: pos[0] = MSG_ReadCoord( &buf ); @@ -2022,8 +2011,11 @@ void CL_ParseTempEntity( sizebuf_t *msg ) dl->die = cl.time + 0.5; dl->decay = 300; - hSound = S_RegisterSound( cl_explode_sounds[0] ); - S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 0.6f, PITCH_NORM, 0 ); + if(( name = SoundList_Get( Explode, 0 ))) + { + hSound = S_RegisterSound( name ); + S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 ); + } break; case TE_BSPDECAL: case TE_DECAL: @@ -2251,8 +2243,8 @@ void CL_ParseTempEntity( sizebuf_t *msg ) CL_DecalShoot( CL_DecalIndex( decalIndex ), entityIndex, 0, pos, 0 ); R_BulletImpactParticles( pos ); flags = COM_RandomLong( 0, 0x7fff ); - if( flags < 0x3fff ) - R_RicochetSound_( pos, flags % 5 ); + if( flags < 0x3fff && ( count = SoundList_Count( Ricochet ))) + R_RicochetSoundByIndex( pos, flags % count ); break; case TE_SPRAY: case TE_SPRITE_SPRAY: -- 2.34.1
--- engine/server/sv_move.c | 36 ++++++------------------------------ engine/server/sv_phys.c | 8 ++++++-- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index e62bfd46..25cd894a 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -150,21 +150,9 @@ void SV_WaterMove( edict_t *ent ) if( flags & FL_INWATER ) { // leave the water. - switch( COM_RandomLong( 0, 3 )) - { - case 0: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade1.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - case 1: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade2.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - case 2: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade3.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - case 3: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade4.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - } + const char *snd = SoundList_GetRandom( EntityWaterExit ); + if( snd ) + SV_StartSound( ent, CHAN_BODY, snd, 1.0f, ATTN_NORM, 0, 100 ); ent->v.flags = flags & ~FL_INWATER; } @@ -197,21 +185,9 @@ void SV_WaterMove( edict_t *ent ) if( watertype == CONTENTS_WATER ) { // entering the water - switch( COM_RandomLong( 0, 3 )) - { - case 0: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade1.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - case 1: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade2.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - case 2: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade3.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - case 3: - SV_StartSound( ent, CHAN_BODY, "player/pl_wade4.wav", 1.0f, ATTN_NORM, 0, 100 ); - break; - } + const char *snd = SoundList_GetRandom( EntityWaterEnter ); + if( snd ) + SV_StartSound( ent, CHAN_BODY, snd, 1.0f, ATTN_NORM, 0, 100 ); } ent->v.flags = flags | FL_INWATER; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 29fd6a9f..f65954fd 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -1384,7 +1384,9 @@ static void SV_CheckWaterTransition( edict_t *ent ) if( ent->v.watertype == CONTENTS_EMPTY ) { // just crossed into water - SV_StartSound( ent, CHAN_AUTO, "player/pl_wade1.wav", 1.0f, ATTN_NORM, 0, 100 ); + const char *snd = SoundList_GetRandom( PlayerWaterEnter ); + if( snd ) + SV_StartSound( ent, CHAN_AUTO, snd, 1.0f, ATTN_NORM, 0, 100 ); ent->v.velocity[2] *= 0.5f; } @@ -1418,7 +1420,9 @@ static void SV_CheckWaterTransition( edict_t *ent ) if( ent->v.watertype != CONTENTS_EMPTY ) { // just crossed into water - SV_StartSound( ent, CHAN_AUTO, "player/pl_wade2.wav", 1.0f, ATTN_NORM, 0, 100 ); + const char *snd = SoundList_GetRandom( PlayerWaterExit ); + if( snd ) + SV_StartSound( ent, CHAN_AUTO, snd, 1.0f, ATTN_NORM, 0, 100 ); } ent->v.watertype = CONTENTS_EMPTY; ent->v.waterlevel = 0; -- 2.34.1