/*
* CSB Map Stats
* Copyright (C) 2026 counter-strike-boost.com
*
* Records per-map aggregates into nVault keyed by map name: rounds played,
* terrorist/counter-terrorist round wins, bombs planted, bombs defused and
* total round time. /mapstats prints the numbers for the current map, and at
* every round end a short chat line notes which side is dominating the map.
*
* 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 <https://www.gnu.org/licenses/> for details.
*/
#include <amxmodx>
#include <nvault>
new const PLUGIN[] = "CSB Map Stats"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_iRounds
new g_iTWins
new g_iCTWins
new g_iPlants
new g_iDefuses
new g_iTotalSecs
new Float:g_fRoundStart
new bool:g_bRoundLive
new g_iVault
new g_szMap[32]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
register_cvar("csb_mapstats_enabled", "1")
get_mapname(g_szMap, charsmax(g_szMap))
register_logevent("onRoundStart", 2, "1=Round_Start")
register_logevent("onRoundEnd", 2, "1=Round_End")
register_event("SendAudio", "onTWin", "a", "2&%!MRAD_terwin")
register_event("SendAudio", "onCTWin", "a", "2&%!MRAD_ctwin")
register_logevent("onPlant", 3, "2=Planted_The_Bomb")
register_logevent("onDefuse", 3, "2=Defused_The_Bomb")
register_clcmd("say /mapstats", "cmdStats")
register_clcmd("say_team /mapstats", "cmdStats")
g_iVault = nvault_open("csb_mapstats")
loadMap()
}
public plugin_end()
{
saveMap()
if (g_iVault != -1)
nvault_close(g_iVault)
}
public onRoundStart()
{
g_fRoundStart = get_gametime()
g_bRoundLive = true
}
public onRoundEnd()
{
if (g_bRoundLive)
{
g_iRounds++
g_iTotalSecs += floatround(get_gametime() - g_fRoundStart)
g_bRoundLive = false
printDominance()
}
}
public onTWin()
{
g_iTWins++
}
public onCTWin()
{
g_iCTWins++
}
public onPlant()
{
g_iPlants++
}
public onDefuse()
{
g_iDefuses++
}
printDominance()
{
new total = g_iTWins + g_iCTWins
if (total < 5)
return
if (g_iTWins > g_iCTWins * 3 / 2)
{
client_print(0, print_chat,
"[CSB] %s is T-sided so far: T %d - %d CT.", g_szMap, g_iTWins, g_iCTWins)
}
else if (g_iCTWins > g_iTWins * 3 / 2)
{
client_print(0, print_chat,
"[CSB] %s is CT-sided so far: CT %d - %d T.", g_szMap, g_iCTWins, g_iTWins)
}
}
public cmdStats(id)
{
new Float:avg = g_iRounds > 0 ? float(g_iTotalSecs) / float(g_iRounds) : 0.0
client_print(id, print_console, "----- CSB Map Stats: %s -----", g_szMap)
client_print(id, print_console, "rounds played: %d (average %.0f s)", g_iRounds, avg)
client_print(id, print_console, "T wins: %d CT wins: %d", g_iTWins, g_iCTWins)
client_print(id, print_console, "bombs planted: %d defused: %d", g_iPlants, g_iDefuses)
client_print(id, print_chat, "[CSB] Map stats printed to your console.")
return PLUGIN_HANDLED
}
loadMap()
{
if (g_iVault == -1)
return
new data[96]
if (nvault_get(g_iVault, g_szMap, data, charsmax(data)) < 1)
return
new a[12], b[12], c[12], d[12], e[12], f[12]
parse(data,
a, charsmax(a), b, charsmax(b), c, charsmax(c),
d, charsmax(d), e, charsmax(e), f, charsmax(f))
g_iRounds = str_to_num(a)
g_iTWins = str_to_num(b)
g_iCTWins = str_to_num(c)
g_iPlants = str_to_num(d)
g_iDefuses = str_to_num(e)
g_iTotalSecs = str_to_num(f)
}
saveMap()
{
if (g_iVault == -1)
return
new data[96]
formatex(data, charsmax(data), "%d %d %d %d %d %d",
g_iRounds, g_iTWins, g_iCTWins, g_iPlants, g_iDefuses, g_iTotalSecs)
nvault_set(g_iVault, g_szMap, data)
}