/*
* CSB Match Damage Report
* Copyright (C) 2026 counter-strike-boost.com
*
* Competitive-style damage report. Damage is accumulated in Ham_TakeDamage per
* attacker/victim pair with hit counts; when a player dies they see exactly how
* much damage they dealt to and took from every opponent, and at round end a
* short summary of who traded whom is printed.
*
* Inspired by the CS:GO-style "Damage Report" plugins for AMX Mod X. 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
new const PLUGIN[] = "CSB Match Damage Report"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_iDmgDealt[33][33]
new g_iHitsDealt[33][33]
new g_iKilledBy[33] /* attacker who got the last kill on this victim */
new g_iKillTime[33] /* systime of that kill, for trade detection */
new g_pEnabled, g_pShowEnd
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_dmgreport_enabled", "1")
g_pShowEnd = register_cvar("csb_dmgreport_roundend", "1")
RegisterHam(Ham_TakeDamage, "player", "fwTakeDamage")
RegisterHam(Ham_Killed, "player", "fwKilled", 1)
register_event("HLTV", "eNewRound", "a", "1=0", "2=0")
}
public eNewRound()
{
for (new i = 1; i <= 32; i++)
{
g_iKilledBy[i] = 0
g_iKillTime[i] = 0
for (new j = 1; j <= 32; j++)
{
g_iDmgDealt[i][j] = 0
g_iHitsDealt[i][j] = 0
}
}
}
public fwTakeDamage(victim, inflictor, attacker, Float:damage, damagebits)
{
if (!get_pcvar_num(g_pEnabled))
return HAM_IGNORED
if (attacker < 1 || attacker > 32 || attacker == victim)
return HAM_IGNORED
if (!is_user_connected(attacker) || !is_user_connected(victim))
return HAM_IGNORED
new dmg = floatround(damage)
if (dmg < 1)
dmg = 1
g_iDmgDealt[attacker][victim] += dmg
g_iHitsDealt[attacker][victim]++
return HAM_IGNORED
}
public fwKilled(victim, attacker, shouldgib)
{
if (attacker >= 1 && attacker <= 32 && attacker != victim && is_user_connected(attacker))
{
g_iKilledBy[victim] = attacker
g_iKillTime[victim] = get_systime()
}
if (get_pcvar_num(g_pEnabled))
showReport(victim)
}
showReport(id)
{
if (!is_user_connected(id))
return
client_print(id, print_chat, "[CSB] -------- Damage Report --------")
new name[32]
for (new v = 1; v <= 32; v++)
{
if (v == id || !is_user_connected(v))
continue
new given = g_iDmgDealt[id][v]
new givenHits = g_iHitsDealt[id][v]
new taken = g_iDmgDealt[v][id]
new takenHits = g_iHitsDealt[v][id]
if (given == 0 && taken == 0)
continue
get_user_name(v, name, charsmax(name))
client_print(id, print_chat, "[CSB] to %s: %d dmg / %d hit(s) | from %s: %d dmg / %d hit(s)",
name, given, givenHits, name, taken, takenHits)
}
/* who killed us this round */
new killer = g_iKilledBy[id]
if (killer && g_iKillTime[id] && is_user_connected(killer))
{
get_user_name(killer, name, charsmax(name))
client_print(id, print_chat, "[CSB] You were killed by %s.", name)
}
}
public plugin_cfg()
{
register_logevent("onRoundEnd", 2, "1=Round_End")
}
public onRoundEnd()
{
if (!get_pcvar_num(g_pEnabled) || !get_pcvar_num(g_pShowEnd))
return
new players[32], num, pid
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
pid = players[i]
new best = 0, bestDmg = 0
for (new v = 1; v <= 32; v++)
{
if (v == pid)
continue
if (g_iDmgDealt[pid][v] > bestDmg)
{
bestDmg = g_iDmgDealt[pid][v]
best = v
}
}
if (best && bestDmg > 0)
{
new bname[32]
get_user_name(best, bname, charsmax(bname))
client_print(pid, print_chat, "[CSB] Round: most damage to %s (%d dmg).", bname, bestDmg)
}
}
}