/*
* 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 for details.
*/
#include
#include
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()
}
}