/*
* CSB Quake Sounds
* Copyright (C) 2026 counter-strike-boost.com
*
* The classic Quake announcer for CS 1.6. A DeathMsg hook tracks kill timings
* and streaks and plays firstblood, doublekill, multikill, ultrakill, godlike,
* headshot and humiliation (knife-kill) sounds. Each player can turn the sounds
* off for themselves with /qsounds, and the choice is saved in nVault.
*
* Inspired by the original "Quake Sounds" plugin (Vexd / the AMXX community).
* This is an independent GPL re-implementation; no original code is reused.
*
* 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. It 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 for details.
*/
#include
#include
#include
new const PLUGIN[] = "CSB Quake Sounds"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define NO_VAULT (-1)
enum _:Snd
{
SND_FIRSTBLOOD = 0,
SND_DOUBLE,
SND_MULTI,
SND_ULTRA,
SND_GODLIKE,
SND_HEADSHOT,
SND_HUMILIATION
}
new const g_szSounds[Snd][] =
{
"csb_quake/firstblood.wav",
"csb_quake/doublekill.wav",
"csb_quake/multikill.wav",
"csb_quake/ultrakill.wav",
"csb_quake/godlike.wav",
"csb_quake/headshot.wav",
"csb_quake/humiliation.wav"
}
new bool:g_bSoundOk[Snd]
new g_iCombo[33]
new Float:g_fLastKill[33]
new bool:g_bEnabled[33]
new g_szAuth[33][35]
new bool:g_bFirstBlood
new g_iVault = NO_VAULT
new g_pEnabled, g_pComboWindow
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_quake_enabled", "1")
g_pComboWindow = register_cvar("csb_quake_window", "3.0")
register_event("DeathMsg", "fwDeath", "a")
register_event("HLTV", "eNewRound", "a", "1=0", "2=0")
register_clcmd("say /qsounds", "cmdToggle")
register_clcmd("say_team /qsounds", "cmdToggle")
}
public plugin_precache()
{
new full[128]
for (new i = 0; i < Snd; i++)
{
formatex(full, charsmax(full), "sound/%s", g_szSounds[i])
if (file_exists(full))
{
precache_sound(g_szSounds[i])
g_bSoundOk[i] = true
}
else
{
log_amx("[CSB Quake] missing sound %s - that announce is disabled.", full)
}
}
}
public plugin_cfg()
{
g_iVault = nvault_open("csb_quake_sounds")
if (g_iVault == NO_VAULT)
log_amx("[CSB Quake] could not open nVault - toggles will not persist.")
}
public plugin_end()
{
if (g_iVault != NO_VAULT)
nvault_close(g_iVault)
}
public client_authorized(id)
{
g_iCombo[id] = 0
g_fLastKill[id] = 0.0
g_bEnabled[id] = true
if (is_user_bot(id) || is_user_hltv(id))
return
get_user_authid(id, g_szAuth[id], charsmax(g_szAuth[]))
if (g_iVault == NO_VAULT)
return
new value[4]
if (nvault_get(g_iVault, g_szAuth[id], value, charsmax(value)))
g_bEnabled[id] = (str_to_num(value) != 0)
}
public cmdToggle(id)
{
g_bEnabled[id] = !g_bEnabled[id]
client_print(id, print_chat, "[CSB] Quake sounds are now %s for you.", g_bEnabled[id] ? "ON" : "OFF")
if (g_iVault != NO_VAULT && g_szAuth[id][0])
{
new value[4]
num_to_str(g_bEnabled[id] ? 1 : 0, value, charsmax(value))
nvault_set(g_iVault, g_szAuth[id], value)
}
return PLUGIN_HANDLED
}
public eNewRound()
{
g_bFirstBlood = false
for (new i = 1; i <= 32; i++)
{
g_iCombo[i] = 0
g_fLastKill[i] = 0.0
}
}
public fwDeath()
{
if (!get_pcvar_num(g_pEnabled))
return
new killer = read_data(1)
new victim = read_data(2)
new headshot = read_data(3)
/* reset the victim's streak */
if (victim >= 1 && victim <= 32)
g_iCombo[victim] = 0
if (killer < 1 || killer > 32 || killer == victim || !is_user_connected(killer))
return
/* humiliation: killed with the knife -> tell the victim */
if (get_user_weapon(killer) == CSW_KNIFE)
playToOne(victim, SND_HUMILIATION)
/* first blood of the round */
if (!g_bFirstBlood)
{
g_bFirstBlood = true
playToAll(SND_FIRSTBLOOD)
}
/* headshot announce goes to the killer */
if (headshot)
playToOne(killer, SND_HEADSHOT)
/* combo window */
new Float:now = get_gametime()
new Float:window = get_pcvar_float(g_pComboWindow)
if (g_fLastKill[killer] > 0.0 && (now - g_fLastKill[killer]) <= window)
g_iCombo[killer]++
else
g_iCombo[killer] = 1
g_fLastKill[killer] = now
switch (g_iCombo[killer])
{
case 2: playToAll(SND_DOUBLE)
case 3: playToAll(SND_MULTI)
case 4: playToAll(SND_ULTRA)
}
if (g_iCombo[killer] >= 5)
playToAll(SND_GODLIKE)
}
playToAll(snd)
{
if (!g_bSoundOk[snd])
return
new players[32], num, pid
get_players(players, num, "c")
for (new i = 0; i < num; i++)
{
pid = players[i]
if (g_bEnabled[pid])
emit_sound(pid, CHAN_STATIC, g_szSounds[snd], 1.0, 0.0, 0, PITCH_NORM)
}
}
playToOne(id, snd)
{
if (!g_bSoundOk[snd] || id < 1 || id > 32 || !is_user_connected(id) || !g_bEnabled[id])
return
emit_sound(id, CHAN_STATIC, g_szSounds[snd], 1.0, 0.0, 0, PITCH_NORM)
}