Promocionar servidor
csb-weapon-stats / addons / amxmodx / scripting / csb_weapon_stats.sma
267 lines 6.2 KiB Pawn Raw Permalink History
/*
 * CSB Weapon Stats
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * Keeps a per-weapon breakdown for every player: kills, headshots and damage
 * with each weapon (tracked from DeathMsg and Ham_TakeDamage), persisted per
 * SteamID in nVault. /wstats opens a MOTD table with your numbers, your
 * favourite weapon and the most-used weapon on the server this 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 <hamsandwich>
#include <nvault>

new const PLUGIN[] = "CSB Weapon Stats"
new const VERSION[] = "1.0.0"
new const AUTHOR[]  = "counter-strike-boost.com"

#define MAX_WEAPONS 20

new const g_szWeapons[][] =
{
    "ak47", "m4a1", "awp", "deagle", "usp", "glock18", "famas", "galil",
    "mp5navy", "p90", "scout", "aug", "sg552", "m3", "xm1014", "m249",
    "knife", "grenade", "hegrenade", "flashbang"
}

enum WStat
{
    WS_KILLS = 0,
    WS_HS,
    WS_DMG,
    WS_MAX
}

new g_iStats[33][MAX_WEAPONS][WS_MAX]
new g_iMapKills[MAX_WEAPONS]
new g_iVault

public plugin_init()
{
    register_plugin(PLUGIN, VERSION, AUTHOR)

    register_cvar("csb_wstats_enabled", "1")

    register_event("DeathMsg", "onDeath", "a")
    RegisterHam(Ham_TakeDamage, "player", "OnDamage", 1)

    register_clcmd("say /wstats", "cmdStats")
    register_clcmd("say_team /wstats", "cmdStats")

    g_iVault = nvault_open("csb_wstats")
}

public plugin_end()
{
    if (g_iVault != -1)
        nvault_close(g_iVault)
}

public client_putinserver(id)
{
    for (new w = 0; w < MAX_WEAPONS; w++)
    {
        for (new s = 0; s < WS_MAX; s++)
            g_iStats[id][w][s] = 0
    }

    loadPlayer(id)
}

public client_disconnect(id)
{
    savePlayer(id)
}

public onDeath()
{
    new killer = read_data(1)
    new victim = read_data(2)
    new hs     = read_data(3)
    new weapon[24]
    read_data(4, weapon, charsmax(weapon))

    if (killer < 1 || killer > 32 || killer == victim)
        return

    if (get_user_team(killer) == get_user_team(victim))
        return

    new w = weaponIndex(weapon)

    if (w == -1)
        return

    g_iStats[killer][w][WS_KILLS]++
    g_iMapKills[w]++

    if (hs)
        g_iStats[killer][w][WS_HS]++
}

public OnDamage(victim, inflictor, attacker, Float:damage, damagetype)
{
    if (attacker < 1 || attacker > 32 || attacker == victim)
        return HAM_IGNORED

    if (get_user_team(attacker) == get_user_team(victim))
        return HAM_IGNORED

    new wid = get_user_weapon(attacker)

    if (wid < 1 || wid > 29)
        return HAM_IGNORED

    new wname[24]
    get_weaponname(wid, wname, charsmax(wname))
    replace(wname, charsmax(wname), "weapon_", "")

    new w = weaponIndex(wname)

    if (w != -1)
        g_iStats[attacker][w][WS_DMG] += floatround(damage)

    return HAM_IGNORED
}

weaponIndex(const weapon[])
{
    for (new w = 0; w < MAX_WEAPONS; w++)
    {
        if (equali(weapon, g_szWeapons[w]))
            return w
    }

    return -1
}

public cmdStats(id)
{
    new motd[1536], len

    len = formatex(motd, charsmax(motd),
        "<html><body bgcolor=#101010 text=#e0e0e0><h2>CSB Weapon Stats</h2><table border=1 cellpadding=5><tr><th>Weapon</th><th>Kills</th><th>HS</th><th>Damage</th></tr>")

    new favW = -1, favKills = 0

    for (new w = 0; w < MAX_WEAPONS; w++)
    {
        if (g_iStats[id][w][WS_KILLS] < 1 && g_iStats[id][w][WS_DMG] < 1)
            continue

        len += formatex(motd[len], charsmax(motd) - len,
            "<tr><td>%s</td><td>%d</td><td>%d</td><td>%d</td></tr>",
            g_szWeapons[w], g_iStats[id][w][WS_KILLS],
            g_iStats[id][w][WS_HS], g_iStats[id][w][WS_DMG])

        if (g_iStats[id][w][WS_KILLS] > favKills)
        {
            favKills = g_iStats[id][w][WS_KILLS]
            favW = w
        }
    }

    len += formatex(motd[len], charsmax(motd) - len, "</table>")

    if (favW != -1)
    {
        len += formatex(motd[len], charsmax(motd) - len,
            "<p>Your favourite weapon: <b>%s</b> (%d kills)</p>",
            g_szWeapons[favW], favKills)
    }

    /* server favourite this map */
    new bestW = -1, bestKills = 0

    for (new w = 0; w < MAX_WEAPONS; w++)
    {
        if (g_iMapKills[w] > bestKills)
        {
            bestKills = g_iMapKills[w]
            bestW = w
        }
    }

    if (bestW != -1)
    {
        len += formatex(motd[len], charsmax(motd) - len,
            "<p>Most used weapon on the server this map: <b>%s</b> (%d kills)</p>",
            g_szWeapons[bestW], bestKills)
    }

    formatex(motd[len], charsmax(motd) - len, "</body></html>")

    show_motd(id, motd, "CSB Weapon Stats")
    return PLUGIN_HANDLED
}

loadPlayer(id)
{
    if (g_iVault == -1)
        return

    new auth[36], key[44], data[1024]
    get_user_authid(id, auth, charsmax(auth))

    if (auth[0] == 0 || equal(auth, "STEAM_ID_PENDING"))
        return

    formatex(key, charsmax(key), "wstats_%s", auth)

    if (nvault_get(g_iVault, key, data, charsmax(data)) < 1)
        return

    /* flat "k hs dmg" triples per weapon */
    new pos = 0, w = 0, s = 0
    new val[16]

    while (w < MAX_WEAPONS)
    {
        pos = argparse(data, pos, val, charsmax(val))

        if (pos == -1)
            break

        g_iStats[id][w][s] = str_to_num(val)

        s++

        if (s >= WS_MAX)
        {
            s = 0
            w++
        }
    }
}

savePlayer(id)
{
    if (g_iVault == -1)
        return

    new auth[36], key[44]
    get_user_authid(id, auth, charsmax(auth))

    if (auth[0] == 0 || equal(auth, "STEAM_ID_PENDING"))
        return

    formatex(key, charsmax(key), "wstats_%s", auth)

    new data[1024], len = 0

    for (new w = 0; w < MAX_WEAPONS; w++)
    {
        len += formatex(data[len], charsmax(data) - len, "%d %d %d ",
            g_iStats[id][w][WS_KILLS], g_iStats[id][w][WS_HS], g_iStats[id][w][WS_DMG])
    }

    nvault_set(g_iVault, key, data)
}