Promocionar servidor
csb-multikill-tracker / addons / amxmodx / scripting / csb_multikill_tracker.sma
194 lines 4.7 KiB Pawn Raw Permalink History
/*
 * CSB Multikill Tracker
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * Detects double, triple and quad kills by counting a player's kills inside a
 * short rolling time window, and aces by counting five kills in one round.
 * Multikills are announced in chat with a sound; aces are persisted per
 * SteamID in nVault and shown with /aces.
 *
 * 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 Multikill Tracker"
new const VERSION[] = "1.0.0"
new const AUTHOR[]  = "counter-strike-boost.com"

#define WINDOW_KILLS 5

new Float:g_fKillTimes[33][WINDOW_KILLS]
new g_iKillPos[33]
new g_iRoundKills[33]
new g_iAces[33]

new g_iVault
new g_pWindow

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

    g_pWindow = register_cvar("csb_multikill_window", "3.0")

    register_event("DeathMsg", "onDeath", "a")
    register_logevent("onRoundStart", 2, "1=Round_Start")

    register_clcmd("say /aces", "cmdAces")
    register_clcmd("say_team /aces", "cmdAces")

    g_iVault = nvault_open("csb_multikill")
}

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

public client_putinserver(id)
{
    g_iRoundKills[id] = 0
    g_iKillPos[id]    = 0
    g_iAces[id]       = 0

    for (new i = 0; i < WINDOW_KILLS; i++)
        g_fKillTimes[id][i] = 0.0

    loadPlayer(id)
}

public client_disconnect(id)
{
    savePlayer(id)
}

public onRoundStart()
{
    for (new id = 1; id <= 32; id++)
        g_iRoundKills[id] = 0
}

public onDeath()
{
    new killer = read_data(1)
    new victim = read_data(2)

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

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

    new Float:now = get_gametime()
    new Float:window = get_pcvar_float(g_pWindow)

    /* record this kill in the ring buffer */
    g_fKillTimes[killer][g_iKillPos[killer]] = now
    g_iKillPos[killer] = (g_iKillPos[killer] + 1) % WINDOW_KILLS

    /* count kills inside the window, and the newest one */
    new inWindow = 0

    for (new i = 0; i < WINDOW_KILLS; i++)
    {
        if (g_fKillTimes[killer][i] > 0.0 && now - g_fKillTimes[killer][i] <= window)
            inWindow++
    }

    g_iRoundKills[killer]++

    new name[32]
    get_user_name(killer, name, charsmax(name))

    if (inWindow == 2)
    {
        client_print(0, print_chat, "[CSB] %s gets a DOUBLE kill!", name)
        client_cmd(0, "spk buttons/blip2.wav")
    }
    else if (inWindow == 3)
    {
        client_print(0, print_chat, "[CSB] %s is on fire - TRIPLE kill!", name)
        client_cmd(0, "spk buttons/blip2.wav")
    }
    else if (inWindow >= 4)
    {
        client_print(0, print_chat, "[CSB] %s is UNSTOPPABLE - QUAD kill!", name)
        client_cmd(0, "spk buttons/bell1.wav")
    }

    if (g_iRoundKills[killer] == 5)
    {
        g_iAces[killer]++
        savePlayer(killer)

        client_print(0, print_chat, "[CSB] ACE! %s wiped the whole enemy team this round!", name)
        client_cmd(0, "spk buttons/bell1.wav")

        set_hudmessage(255, 60, 60, -1.0, 0.32, 0, 0.0, 4.0, 0.5, 0.5, 2)
        show_hudmessage(0, "ACE by %s!", name)
    }
}

public cmdAces(id)
{
    client_print(id, print_chat, "[CSB] You have %d ace(s).", g_iAces[id])

    new best = 0, bestCount = 0

    for (new p = 1; p <= 32; p++)
    {
        if (is_user_connected(p) && g_iAces[p] > bestCount)
        {
            bestCount = g_iAces[p]
            best = p
        }
    }

    if (best > 0)
    {
        new name[32]
        get_user_name(best, name, charsmax(name))
        client_print(id, print_chat, "[CSB] Server ace leader online: %s (%d).", name, bestCount)
    }

    return PLUGIN_HANDLED
}

loadPlayer(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), "aces_%s", auth)
    g_iAces[id] = nvault_get(g_iVault, key)
}

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

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

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

    formatex(key, charsmax(key), "aces_%s", auth)
    num_to_str(g_iAces[id], val, charsmax(val))
    nvault_set(g_iVault, key, val)
}