/*
* CSB Round End Sound
* Copyright (C) 2026 counter-strike-boost.com
*
* Plays a configurable sound at the end of every round depending on who won
* (Terrorists, CTs or a draw), with a special track for a flawless round - one
* where the winning team lost nobody. Sound paths are read from an ini so admins
* can swap them without editing code, every path is precached, and each player
* can mute the sounds for themselves with "say /rsounds".
*
* Inspired by the classic Round Sound plugins. Independent GPL re-implementation.
*
* 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
new const PLUGIN[] = "CSB Round End Sound"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define SND_T 0
#define SND_CT 1
#define SND_DRAW 2
#define SND_FLAWLESS 3
new g_szSnd[4][128]
new bool:g_bMuted[33]
new g_iDeadT, g_iDeadCT
new g_pEnabled
public plugin_precache()
{
/* sane defaults - overridden by the ini if present */
copy(g_szSnd[SND_T], charsmax(g_szSnd[]), "ambience/thunder_clap.wav")
copy(g_szSnd[SND_CT], charsmax(g_szSnd[]), "ambience/thunder_clap.wav")
copy(g_szSnd[SND_DRAW], charsmax(g_szSnd[]), "ambience/rain1.wav")
copy(g_szSnd[SND_FLAWLESS], charsmax(g_szSnd[]), "ambience/des_wind3.wav")
loadIni()
for (new i = 0; i < 4; i++)
{
if (g_szSnd[i][0])
precache_sound(g_szSnd[i])
}
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_rsnd_enabled", "1")
register_clcmd("say /rsounds", "cmdToggle")
register_clcmd("say_team /rsounds", "cmdToggle")
register_logevent("eventRoundStart", 2, "1=Round_Start")
register_event("DeathMsg", "eventDeath", "a", "1>0")
register_event("TextMsg", "eventTerWin", "a", "2Terrorists_Win")
register_event("TextMsg", "eventCtWin", "a", "2CTs_Win")
register_event("TextMsg", "eventDraw", "a", "2Round_Draw")
}
public client_putinserver(id)
{
g_bMuted[id] = false
}
loadIni()
{
new cfgdir[128], path[160]
get_localinfo("amxx_configsdir", cfgdir, charsmax(cfgdir))
if (!cfgdir[0])
return;
formatex(path, charsmax(path), "%s/csb_round_sounds.ini", cfgdir)
if (!file_exists(path))
return;
new f = fopen(path, "rt")
if (!f)
return;
new line[192], key[32], value[128]
while (!feof(f))
{
fgets(f, line, charsmax(line))
trim(line)
if (!line[0] || line[0] == ';' || line[0] == '#')
continue
strtok(line, key, charsmax(key), value, charsmax(value), '=')
trim(key)
trim(value)
if (!value[0])
continue
if (equali(key, "t_win"))
copy(g_szSnd[SND_T], charsmax(g_szSnd[]), value)
else if (equali(key, "ct_win"))
copy(g_szSnd[SND_CT], charsmax(g_szSnd[]), value)
else if (equali(key, "draw"))
copy(g_szSnd[SND_DRAW], charsmax(g_szSnd[]), value)
else if (equali(key, "flawless"))
copy(g_szSnd[SND_FLAWLESS], charsmax(g_szSnd[]), value)
}
fclose(f)
}
public cmdToggle(id)
{
g_bMuted[id] = !g_bMuted[id]
client_print(id, print_chat, "[CSB] Round-end sounds %s.", g_bMuted[id] ? "muted" : "unmuted")
return PLUGIN_HANDLED
}
public eventRoundStart()
{
g_iDeadT = 0
g_iDeadCT = 0
}
public eventDeath()
{
new victim = read_data(2)
if (victim < 1 || victim > 32)
return;
switch (get_user_team(victim))
{
case 1: g_iDeadT++;
case 2: g_iDeadCT++;
}
}
public eventTerWin()
{
playFor(g_iDeadT == 0 ? SND_FLAWLESS : SND_T)
}
public eventCtWin()
{
playFor(g_iDeadCT == 0 ? SND_FLAWLESS : SND_CT)
}
public eventDraw()
{
playFor(SND_DRAW)
}
playFor(idx)
{
if (!get_pcvar_num(g_pEnabled) || idx < 0 || idx > 3 || !g_szSnd[idx][0])
return;
new players[32], num
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
new pl = players[i]
if (g_bMuted[pl])
continue
client_cmd(pl, "spk %s", g_szSnd[idx])
}
}