diff --git a/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg b/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg index d7403ed..7a4c178 100644 --- a/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg +++ b/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg @@ -523,6 +523,14 @@ zr_seffects_groan "5" // Default: "1" zr_seffects_death "1" +// Number of sound commands allowed within the time span, or total limit if time span is disabled. ['0' = Disable sound command limit] +// Default: "4" +zr_seffects_command_limit "4" + +// Time span for sound command limiter (in seconds). ['0' = Disable time span check | positive number = Time span] +// Default: "10" +zr_seffects_command_timespan "10" + // Ambient Sounds // Play an ambient sound to all players during gameplay. diff --git a/src/zr/commands.inc b/src/zr/commands.inc index 2fad823..9a04e00 100644 --- a/src/zr/commands.inc +++ b/src/zr/commands.inc @@ -45,6 +45,7 @@ CommandsInit() ZTeleOnCommandsCreate(); ZHPOnCommandsCreate(); VolOnCommandsCreate(); + ZombieSoundsOnCommandsCreate(); #if defined ADD_VERSION_INFO VersionOnCommandsCreate(); diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index 9c8af73..c5ec5b7 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -132,6 +132,8 @@ enum CvarsList Handle:CVAR_SEFFECTS_MOAN, Handle:CVAR_SEFFECTS_GROAN, Handle:CVAR_SEFFECTS_DEATH, + Handle:CVAR_SEFFECTS_COMMAND_LIMIT, + Handle:CVAR_SEFFECTS_COMMAND_TIMESPAN, Handle:CVAR_AMBIENTSOUNDS, Handle:CVAR_AMBIENTSOUNDS_FILE, Handle:CVAR_AMBIENTSOUNDS_LENGTH, @@ -410,9 +412,11 @@ CvarsCreate() g_hCvarsList[CVAR_VOICE_ZOMBIES_MUTE] = CreateConVar("zr_voice_zombies_mute", "0", "(Incomplete) Only allow humans to communicate, block verbal zombie communication."); // Zombie Sounds - g_hCvarsList[CVAR_SEFFECTS_MOAN] = CreateConVar("zr_seffects_moan", "30.0", "Time between emission of a moan sound from a zombie."); - g_hCvarsList[CVAR_SEFFECTS_GROAN] = CreateConVar("zr_seffects_groan", "5", "The probability that a groan sound will be emitted from a zombie when shot. ['100' = 1% chance | '50' = 2% chance | '1' = 100% chance]"); - g_hCvarsList[CVAR_SEFFECTS_DEATH] = CreateConVar("zr_seffects_death", "1", "Emit a death sound when a zombie dies."); + g_hCvarsList[CVAR_SEFFECTS_MOAN] = CreateConVar("zr_seffects_moan", "30.0", "Time between emission of a moan sound from a zombie."); + g_hCvarsList[CVAR_SEFFECTS_GROAN] = CreateConVar("zr_seffects_groan", "5", "The probability that a groan sound will be emitted from a zombie when shot. ['100' = 1% chance | '50' = 2% chance | '1' = 100% chance]"); + g_hCvarsList[CVAR_SEFFECTS_DEATH] = CreateConVar("zr_seffects_death", "1", "Emit a death sound when a zombie dies."); + g_hCvarsList[CVAR_SEFFECTS_COMMAND_LIMIT] = CreateConVar("zr_seffects_command_limit", "4", "Number of sound commands allowed within the time span, or total limit if time span is disabled. ['0' = Disable sound command limit]"); + g_hCvarsList[CVAR_SEFFECTS_COMMAND_TIMESPAN] = CreateConVar("zr_seffects_command_timespan", "10", "Time span for sound command limiter (in seconds). ['0' = Disable time span check | positive number = Time span]"); // Ambient Sounds g_hCvarsList[CVAR_AMBIENTSOUNDS] = CreateConVar("zr_ambientsounds", "1", "Play an ambient sound to all players during gameplay."); diff --git a/src/zr/infect.inc b/src/zr/infect.inc index 3ab9a3d..3585195 100644 --- a/src/zr/infect.inc +++ b/src/zr/infect.inc @@ -749,14 +749,8 @@ InfectFireEffects(client) VEffectsCreateExplosion(clientloc, flags); } - // If cvar contains path, then continue. - decl String:sound[PLATFORM_MAX_PATH]; - GetConVarString(g_hCvarsList[CVAR_INFECT_SOUND], sound, sizeof(sound)); - if (sound[0]) - { - // Emit infect sound from infected client. - SEffectsEmitSoundFromClient(client, sound, SNDLEVEL_SCREAMING); - } + // Emit scream sound if enabled. + ZombieSoundsScream(client); // If energy splash effect is enabled, then continue. new bool:esplash = GetConVarBool(g_hCvarsList[CVAR_INFECT_ESPLASH]); diff --git a/src/zr/soundeffects/soundeffects.inc b/src/zr/soundeffects/soundeffects.inc index c8d9c70..c70f564 100644 --- a/src/zr/soundeffects/soundeffects.inc +++ b/src/zr/soundeffects/soundeffects.inc @@ -87,6 +87,7 @@ SEffectsOnRoundEnd() // Forward event to sub-modules. VoiceOnRoundEnd(); AmbientSoundsOnRoundEnd(); + ZombieSoundsOnRoundEnd(); } /** diff --git a/src/zr/soundeffects/zombiesounds.inc b/src/zr/soundeffects/zombiesounds.inc index e7270e9..8b1263a 100644 --- a/src/zr/soundeffects/zombiesounds.inc +++ b/src/zr/soundeffects/zombiesounds.inc @@ -70,6 +70,16 @@ enum ZombieSounds */ new Handle:tSEffectsMoan[MAXPLAYERS + 1]; +/** + * Number of sound commands executed by the player. + */ +new g_SEffectsCommandCount[MAXPLAYERS + 1]; + +/** + * Timers for resetting sound command counters. + */ +new Handle:g_hSEffectsCommandTimer[MAXPLAYERS + 1]; + /** * Client is joining the server. * @@ -79,6 +89,10 @@ ZombieSoundsClientInit(client) { // Reset timer handle. tSEffectsMoan[client] = INVALID_HANDLE; + + // Reset command counter and make sure there's no timer running. + g_SEffectsCommandCount[client] = 0; + ZREndTimer(g_hSEffectsCommandTimer[client]); } /** @@ -96,6 +110,10 @@ ZombieSoundsOnClientSpawn(client) // Reset timer handle. tSEffectsMoan[client] = INVALID_HANDLE; + + // Reset command counter and kill timer. + g_SEffectsCommandCount[client] = 0; + ZREndTimer(g_hSEffectsCommandTimer[client]); } /** @@ -114,6 +132,10 @@ ZombieSoundsOnClientDeath(client) // Reset timer handle. tSEffectsMoan[client] = INVALID_HANDLE; + // Reset command counter and kill timer. + g_SEffectsCommandCount[client] = 0; + ZREndTimer(g_hSEffectsCommandTimer[client]); + // If player isn't a zombie, then stop. if (!InfectIsClientInfected(client)) { @@ -189,6 +211,21 @@ ZombieSoundsOnClientInfected(client) tSEffectsMoan[client] = CreateTimer(interval, ZombieSoundsMoanTimer, client, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT); } +/** + * Round ended. + */ +ZombieSoundsOnRoundEnd() +{ + ZombieSoundsResetCmdCounters(); + ZombieSoundsResetCmdTimers(); +} + +ZombieSoundsOnCommandsCreate() +{ + RegConsoleCmd("scream", ZombieSoundsScreamCommand, "Emits a scream sound, if the player is a zombie."); + RegConsoleCmd("moan", ZombieSoundsMoanCommand, "Emits a moan sound, if the player is a zombie."); +} + /** * Gets a random zombie sound from hl2 folder. * @@ -270,13 +307,147 @@ public Action:ZombieSoundsMoanTimer(Handle:timer, any:client) return Plugin_Stop; } + // Emit moan sound. + ZombieSoundsMoan(client); + + // Allow timer to continue. + return Plugin_Continue; +} + +/** + * Emits a moan sound from the specified client. + * + * @param client Client index. + */ +ZombieSoundsMoan(client) +{ // Get random moan sound. decl String:sound[SOUND_MAX_PATH]; ZombieSoundsGetRandomSound(sound, Moan); // Emit sound from client. SEffectsEmitSoundFromClient(client, sound, SNDLEVEL_SCREAMING); - - // Allow timer to continue. - return Plugin_Continue; +} + +/** + * Emits the scream sound (on infection) from the specified client. + * + * @param client Client index. + */ +ZombieSoundsScream(client) +{ + decl String:sound[PLATFORM_MAX_PATH]; + GetConVarString(g_hCvarsList[CVAR_INFECT_SOUND], sound, sizeof(sound)); + + // If cvar contains path, then continue. + if (sound[0]) + { + // Emit infect sound from infected client. + SEffectsEmitSoundFromClient(client, sound, SNDLEVEL_SCREAMING); + } +} + +/** + * Starts a reset timer for the client's command counter if not already started. + * + * @param client Client index. + */ +ZombieSoundsCmdTimerCheck(client) +{ + // Only create timer if it doesn't exist. + if (g_hSEffectsCommandTimer[client] == INVALID_HANDLE) + { + new Float:timespan = GetConVarFloat(g_hCvarsList[CVAR_SEFFECTS_COMMAND_TIMESPAN]); + + // Only create timer if time span is enabled. + if (timespan > 0.0) + { + g_hSEffectsCommandTimer[client] = CreateTimer(timespan, ZombieSoundsCmdTimer, client, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); + } + } +} + +/** + * Resets all command counters. + */ +ZombieSoundsResetCmdCounters() +{ + for (new client = 0; client <= MAXPLAYERS; client++) + { + g_SEffectsCommandCount[client] = 0; + } +} + +/** + * Stops all command counter timers. + */ +ZombieSoundsResetCmdTimers() +{ + for (new client = 0; client <= MAXPLAYERS; client++) + { + ZREndTimer(g_hSEffectsCommandTimer[client]); + } +} + +/** + * Returns whether a player is allowed to play a zombie sound or not. + * + * @param client Client index. + * @return True if allowed, false otherwise. + */ +bool:ZombieSoundsCommandAllowed(client) +{ + new limit = GetConVarInt(g_hCvarsList[CVAR_SEFFECTS_COMMAND_LIMIT]); + + if (limit <= 0 || + g_SEffectsCommandCount[client] < limit) + { + return true; + } + + return false; +} + +/** + * Scream command handler. + */ +public Action:ZombieSoundsScreamCommand(client, argc) +{ + if (IsClientInGame(client) && + IsPlayerAlive(client) && + InfectIsClientInfected(client) && + ZombieSoundsCommandAllowed(client)) + { + ZombieSoundsScream(client); + g_SEffectsCommandCount[client]++; + ZombieSoundsCmdTimerCheck(client); + } + + return Plugin_Handled; +} + +/** + * Moan command handler. + */ +public Action:ZombieSoundsMoanCommand(client, argc) +{ + if (IsClientInGame(client) && + IsPlayerAlive(client) && + InfectIsClientInfected(client) && + ZombieSoundsCommandAllowed(client)) + { + ZombieSoundsMoan(client); + g_SEffectsCommandCount[client]++; + ZombieSoundsCmdTimerCheck(client); + } + + return Plugin_Handled; +} + +/** + * Command counter reset timer. + */ +public Action:ZombieSoundsCmdTimer(Handle:timer, any:client) +{ + g_SEffectsCommandCount[client] = 0; } diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc index f97041c..25f603e 100644 --- a/src/zr/zombiereloaded.inc +++ b/src/zr/zombiereloaded.inc @@ -106,6 +106,50 @@ stock ZRCreateEligibleClientList(&Handle:arrayEligibleClients, bool:team = false return GetArraySize(arrayEligibleClients); } +/** + * Checks if a timer is currently running. + * + * @param timer The timer handle. + */ +stock bool:ZRIsTimerRunning(Handle:timer) +{ + // Return true if the handle isn't empty. + return (timer != INVALID_HANDLE); +} + +/** + * Wrapper functions for KilLTimer. + * Ends a timer if running, and resets its timer handle variable. + * + * @param timer The timer handle. + * @param kill True to kill the timer and reset the variable, false to only reset the variable. + * Using false is useful when calling from the timer callback, because the timer is already killed. + * + * @return True if the handle wasn't INVALID_HANDLE, false if the handle wasn't valid. + */ +stock bool:ZREndTimer(&Handle:timer, bool:kill = true) +{ + // If the timer is running, then kill it. + if (ZRIsTimerRunning(timer)) + { + // Kill if caller says to. + if (kill) + { + KillTimer(timer); + } + + // Reset variable. + timer = INVALID_HANDLE; + + return true; + } + + // Reset variable. + timer = INVALID_HANDLE; + + return false; +} + /** * Check if a client index is a valid player. *