Promotiraj s'rv'r
csb-ready-system / addons / amxmodx / scripting / csb_ready_system.sma
251 lines 5.6 KiB Pawn Raw Permalink History
/*
 * CSB Ready System
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * A pre-match readiness gate: every non-spectator types /ready, a HUD shows how
 * many players are ready, and once everyone is ready the plugin runs an LO3
 * (three restarts) and goes live. Admins can force the match live and any player
 * un-readying during the countdown aborts it.
 *
 * 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 <amxmisc>

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

#define TASK_HUD      4100
#define TASK_LO3      4200

new bool:g_bReady[33]
new bool:g_bLive
new bool:g_bCounting
new g_iRestartsLeft

new g_pEnabled, g_pMinPlayers, g_pAdminFlag

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

    g_pEnabled    = register_cvar("csb_ready_enabled", "1")
    g_pMinPlayers = register_cvar("csb_ready_minplayers", "2")
    g_pAdminFlag  = register_cvar("csb_ready_adminflag", "d")

    register_clcmd("say /ready",     "cmdReady")
    register_clcmd("say_team /ready", "cmdReady")
    register_clcmd("say /notready",  "cmdNotReady")
    register_clcmd("say_team /notready", "cmdNotReady")
    register_clcmd("say /forceready", "cmdForceReady")

    register_event("HLTV", "eNewRound", "a", "1=0", "2=0")

    set_task(1.0, "taskHud", TASK_HUD, _, _, "b")
}

public client_putinserver(id)
{
    g_bReady[id] = false
}

public client_disconnected(id)
{
    g_bReady[id] = false
}

public eNewRound()
{
    /* keep restarting the score to zero during LO3 */
}

public cmdReady(id)
{
    if (!get_pcvar_num(g_pEnabled) || g_bLive)
        return PLUGIN_HANDLED

    if (get_user_team(id) == 0)
    {
        client_print(id, print_chat, "[CSB] Join a team before readying up.")
        return PLUGIN_HANDLED
    }

    if (g_bReady[id])
    {
        client_print(id, print_chat, "[CSB] You are already ready.")
        return PLUGIN_HANDLED
    }

    g_bReady[id] = true

    new name[32]
    get_user_name(id, name, charsmax(name))
    chatAll("^x04[CSB]^x03 %s^x01 is ready.", name)

    checkAllReady()
    return PLUGIN_HANDLED
}

public cmdNotReady(id)
{
    if (!get_pcvar_num(g_pEnabled) || g_bLive)
        return PLUGIN_HANDLED

    if (!g_bReady[id])
        return PLUGIN_HANDLED

    g_bReady[id] = false

    new name[32]
    get_user_name(id, name, charsmax(name))
    chatAll("^x04[CSB]^x03 %s^x01 is NOT ready.", name)

    if (g_bCounting)
    {
        abortCountdown()
        chatAll("^x04[CSB]^x01 Countdown aborted - someone un-readied.")
    }

    return PLUGIN_HANDLED
}

public cmdForceReady(id)
{
    new flags = read_flags_cvar()

    if (!(get_user_flags(id) & flags))
    {
        client_print(id, print_chat, "[CSB] You do not have access to force the match live.")
        return PLUGIN_HANDLED
    }

    if (g_bLive)
        return PLUGIN_HANDLED

    new name[32]
    get_user_name(id, name, charsmax(name))
    chatAll("^x04[CSB]^x03 %s^x01 forced the match to go live.", name)

    startLive()
    return PLUGIN_HANDLED
}

read_flags_cvar()
{
    new sflag[8]
    get_pcvar_string(g_pAdminFlag, sflag, charsmax(sflag))
    return read_flags(sflag)
}

countPlayers(&ready, &total)
{
    new players[32], num, pid
    ready = 0
    total = 0
    get_players(players, num)

    for (new i = 0; i < num; i++)
    {
        pid = players[i]

        if (get_user_team(pid) == 0 || is_user_bot(pid))
            continue

        total++

        if (g_bReady[pid])
            ready++
    }
}

checkAllReady()
{
    if (g_bLive || g_bCounting)
        return

    new ready, total
    countPlayers(ready, total)

    if (total < get_pcvar_num(g_pMinPlayers))
        return

    if (ready == total && total > 0)
        startLive()
}

startLive()
{
    g_bCounting = true
    g_iRestartsLeft = 3
    chatAll("^x04[CSB]^x01 All players ready - going live! LO3 starting.")
    set_task(1.0, "taskLo3", TASK_LO3, _, _, "a", 3)
}

public taskLo3()
{
    g_iRestartsLeft--
    server_cmd("sv_restartround 1")
    server_cmd("sv_restart 1")

    if (g_iRestartsLeft > 0)
    {
        chatAll("^x04[CSB]^x01 Restart^x04 %d^x01 of 3...", 3 - g_iRestartsLeft)
    }
    else
    {
        g_bCounting = false
        g_bLive = true
        chatAll("^x04[CSB]^x01 *** MATCH IS LIVE *** GO GO GO!")
        client_print(0, print_center, "MATCH IS LIVE")
    }
}

abortCountdown()
{
    remove_task(TASK_LO3)
    g_bCounting = false
    g_iRestartsLeft = 0
}

public taskHud()
{
    if (!get_pcvar_num(g_pEnabled) || g_bLive)
        return

    new ready, total
    countPlayers(ready, total)

    if (total <= 0)
        return

    set_hudmessage(0, 200, 80, -1.0, 0.15, 0, 0.0, 1.1, 0.0, 0.0, -1)

    if (g_bCounting)
        show_hudmessage(0, "LIVE ON 3 - GET READY^nReady: %d / %d", ready, total)
    else
        show_hudmessage(0, "WARMUP - type /ready^nReady: %d / %d", ready, total)
}

chatAll(const fmt[], any:...)
{
    new msg[192]
    vformat(msg, charsmax(msg), fmt, 2)

    new players[32], num, msgid = get_user_msgid("SayText")
    get_players(players, num, "ch")

    for (new i = 0; i < num; i++)
    {
        message_begin(MSG_ONE, msgid, _, players[i])
        write_byte(players[i])
        write_string(msg)
        message_end()
    }
}