/*
* CSB Captains Pick
* Copyright (C) 2026 counter-strike-boost.com
*
* Captain draft. Two captains are chosen (the two highest-ranked players by
* frags, or named by an admin) and pick the remaining players alternately from a
* menu until both teams are full. Each pick is moved with cs_set_user_team, and a
* HUD shows whose turn it is together with the current rosters.
*
* 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
#include
new const PLUGIN[] = "CSB Captains Pick"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled
new bool:g_bActive
new g_iCapT, g_iCapCT
new g_iTurn /* 1 = T captain picks, 2 = CT captain picks */
new bool:g_bAssigned[33]
new g_iSync
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_captains_enabled", "1")
register_concmd("amx_captains", "cmdStart", ADMIN_MAP, "- start a captain draft")
register_concmd("amx_captains_stop", "cmdStop", ADMIN_MAP, "- cancel the current draft")
register_clcmd("say /pick", "cmdPick")
register_clcmd("say_team /pick", "cmdPick")
g_iSync = CreateHudSyncObj()
set_task(1.0, "taskHud", 0, _, _, "b")
}
public client_disconnected(id)
{
if (!g_bActive)
return
if (id == g_iCapT || id == g_iCapCT)
{
client_print(0, print_chat, "^x04[CSB Draft]^x01 A captain left. Draft cancelled.")
resetDraft()
}
}
resetDraft()
{
g_bActive = false
g_iCapT = 0
g_iCapCT = 0
g_iTurn = 0
for (new i = 1; i <= 32; i++)
g_bAssigned[i] = false
}
public cmdStart(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
if (!get_pcvar_num(g_pEnabled))
{
console_print(id, "[CSB] Captain draft is disabled.")
return PLUGIN_HANDLED
}
new players[32], num
get_players(players, num, "c")
if (num < 4)
{
console_print(id, "[CSB] Need at least 4 players.")
return PLUGIN_HANDLED
}
resetDraft()
/* two highest frag counts become captains */
new best1 = 0, best2 = 0
for (new i = 0; i < num; i++)
{
new pl = players[i]
if (!best1 || get_user_frags(pl) > get_user_frags(best1))
{
best2 = best1
best1 = pl
}
else if (!best2 || get_user_frags(pl) > get_user_frags(best2))
{
best2 = pl
}
}
g_iCapT = best1
g_iCapCT = best2
cs_set_user_team(g_iCapT, CS_TEAM_T)
cs_set_user_team(g_iCapCT, CS_TEAM_CT)
g_bAssigned[g_iCapT] = true
g_bAssigned[g_iCapCT] = true
g_bActive = true
g_iTurn = 1
client_print(0, print_chat, "^x04[CSB Draft]^x01 Captains: ^x03%n^x01 (T) and ^x03%n^x01 (CT). Type /pick on your turn.", g_iCapT, g_iCapCT)
openPickMenu(g_iCapT)
return PLUGIN_HANDLED
}
public cmdStop(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
resetDraft()
client_print(0, print_chat, "^x04[CSB Draft]^x01 Draft cancelled by admin.")
return PLUGIN_HANDLED
}
currentPicker()
{
return (g_iTurn == 1) ? g_iCapT : g_iCapCT
}
public cmdPick(id)
{
if (!g_bActive)
return PLUGIN_HANDLED
if (id != currentPicker())
{
client_print(id, print_chat, "^x04[CSB Draft]^x01 It is not your turn.")
return PLUGIN_HANDLED
}
openPickMenu(id)
return PLUGIN_HANDLED
}
openPickMenu(cap)
{
if (!is_user_connected(cap))
return
new title[64]
formatex(title, charsmax(title), "\yCSB Draft^n\wPick a player (%s):", (g_iTurn == 1) ? "T" : "CT")
new menu = menu_create(title, "pickHandler")
new players[32], num, added = 0
get_players(players, num, "c")
for (new i = 0; i < num; i++)
{
new pl = players[i]
if (g_bAssigned[pl])
continue
new entry[48], info[8]
formatex(entry, charsmax(entry), "%n", pl)
num_to_str(pl, info, charsmax(info))
menu_additem(menu, entry, info, 0)
added++
}
if (!added)
{
menu_destroy(menu)
finishDraft()
return
}
menu_setprop(menu, MPROP_EXIT, MEXIT_NEVER)
menu_display(cap, menu, 0)
}
public pickHandler(id, menu, item)
{
if (item == MENU_EXIT || !g_bActive || id != currentPicker())
{
menu_destroy(menu)
return PLUGIN_HANDLED
}
new info[8], name[32], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
menu_destroy(menu)
new pick = str_to_num(info)
if (pick < 1 || pick > 32 || !is_user_connected(pick) || g_bAssigned[pick])
{
openPickMenu(id)
return PLUGIN_HANDLED
}
if (g_iTurn == 1)
cs_set_user_team(pick, CS_TEAM_T)
else
cs_set_user_team(pick, CS_TEAM_CT)
g_bAssigned[pick] = true
client_print(0, print_chat, "^x04[CSB Draft]^x01 %n picked %n for %s.", id, pick, (g_iTurn == 1) ? "T" : "CT")
/* alternate the turn */
g_iTurn = (g_iTurn == 1) ? 2 : 1
if (remaining() > 0)
openPickMenu(currentPicker())
else
finishDraft()
return PLUGIN_HANDLED
}
remaining()
{
new players[32], num, left = 0
get_players(players, num, "c")
for (new i = 0; i < num; i++)
{
if (!g_bAssigned[players[i]])
left++
}
return left
}
finishDraft()
{
g_bActive = false
client_print(0, print_chat, "^x04[CSB Draft]^x01 Teams are set. Good luck!")
server_cmd("sv_restart 1")
}
public taskHud()
{
if (!g_bActive)
return
new cap = currentPicker()
if (!is_user_connected(cap))
return
new capName[32]
get_user_name(cap, capName, charsmax(capName))
set_hudmessage(255, 180, 0, 0.02, 0.15, 0, 0.0, 1.1, 0.0, 0.0, -1)
ShowSyncHudMsg(0, g_iSync, "DRAFT^n%s to pick (%s)^n%d player(s) left", capName, (g_iTurn == 1) ? "T" : "CT", remaining())
}