/*
* CSB Punish Menu
* Copyright (C) 2026 counter-strike-boost.com
*
* One unified moderation menu. Open it with say /punish (or amx_punish), pick a
* player, pick an action - slap, slay, freeze/unfreeze, gag, kick or ban - and
* for the serious actions pick a reason from a list loaded from an ini file so
* punishments are consistent and every action is written to the AMXX log.
*
* This is an independent GPL implementation; it is not based on any other
* plugin's source.
*
* 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 the GNU General
* Public License for more details: .
*/
#include
#include
#include
#include
new const PLUGIN[] = "CSB Punish Menu"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define ACCESS ADMIN_KICK
#define ACT_SLAP 1
#define ACT_SLAY 2
#define ACT_FREEZE 3
#define ACT_GAG 4
#define ACT_KICK 5
#define ACT_BAN 6
#define MAX_REASONS 32
new g_pEnabled, g_pSlapDmg, g_pBanTime, g_pFreezeTime
new g_iTarget[33] /* selected target userid */
new g_iAction[33] /* pending action awaiting a reason */
new bool:g_bFrozen[33]
new bool:g_bGagged[33]
new g_szReasons[MAX_REASONS][48]
new g_iReasonCount = 0
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_punish_enabled", "1")
g_pSlapDmg = register_cvar("csb_punish_slapdmg", "5")
g_pBanTime = register_cvar("csb_punish_bantime", "30")
g_pFreezeTime = register_cvar("csb_punish_freezetime", "5.0")
register_clcmd("amx_punish", "cmdPunish", ACCESS, "- opens the CSB punish menu")
register_clcmd("say /punish", "cmdPunish", ACCESS, "- opens the CSB punish menu")
register_clcmd("say_team /punish", "cmdPunish", ACCESS, "- opens the CSB punish menu")
}
public plugin_cfg()
{
loadReasons()
}
loadReasons()
{
g_iReasonCount = 0
new path[128]
get_localinfo("amxx_configsdir", path, charsmax(path))
if (!path[0])
copy(path, charsmax(path), "addons/amxmodx/configs")
add(path, charsmax(path), "/csb_punish_reasons.ini")
if (file_exists(path))
{
new line[48], len
for (new i = 0; i < 512; i++)
{
if (!read_file(path, i, line, charsmax(line), len))
break
trim(line)
if (!line[0] || line[0] == ';' || line[0] == '#')
continue
if (g_iReasonCount >= MAX_REASONS)
break
copy(g_szReasons[g_iReasonCount], charsmax(g_szReasons[]), line)
g_iReasonCount++
}
}
if (!g_iReasonCount)
{
copy(g_szReasons[g_iReasonCount++], charsmax(g_szReasons[]), "Cheating")
copy(g_szReasons[g_iReasonCount++], charsmax(g_szReasons[]), "Abusive language")
copy(g_szReasons[g_iReasonCount++], charsmax(g_szReasons[]), "Team attacking")
copy(g_szReasons[g_iReasonCount++], charsmax(g_szReasons[]), "Advertising")
}
}
public cmdPunish(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
if (!get_pcvar_num(g_pEnabled))
{
client_print(id, print_chat, "[CSB] The punish menu is disabled.")
return PLUGIN_HANDLED
}
showPlayerMenu(id)
return PLUGIN_HANDLED
}
showPlayerMenu(id)
{
new menu = menu_create("\yCSB Punish\w^nSelect a player", "menuPlayer")
new players[32], num, pid, name[32], info[8], line[64]
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
pid = players[i]
get_user_name(pid, name, charsmax(name))
formatex(info, charsmax(info), "%d", get_user_userid(pid))
formatex(line, charsmax(line), "%s %s", name, is_user_alive(pid) ? "" : "\r(dead)")
menu_additem(menu, line, info, 0)
}
menu_setprop(menu, MPROP_EXITNAME, "Close")
menu_display(id, menu, 0)
}
public menuPlayer(id, menu, item)
{
if (item == MENU_EXIT)
{
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)
g_iTarget[id] = str_to_num(info)
showActionMenu(id)
return PLUGIN_HANDLED
}
showActionMenu(id)
{
new target = find_player("k", g_iTarget[id])
if (!target)
{
client_print(id, print_chat, "[CSB] That player already left.")
showPlayerMenu(id)
return
}
new tname[32], title[96]
get_user_name(target, tname, charsmax(tname))
formatex(title, charsmax(title), "\yCSB Punish\w^nTarget: \r%s", tname)
new menu = menu_create(title, "menuAction")
menu_additem(menu, "Slap (light damage)", "1", 0)
menu_additem(menu, "Slay", "2", 0)
menu_additem(menu, g_bFrozen[target] ? "Unfreeze" : "Freeze", "3", 0)
menu_additem(menu, g_bGagged[target] ? "Ungag" : "Gag", "4", 0)
menu_additem(menu, "Kick", "5", 0)
menu_additem(menu, "Ban", "6", 0)
menu_setprop(menu, MPROP_EXITNAME, "Back")
menu_display(id, menu, 0)
}
public menuAction(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
showPlayerMenu(id)
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 action = str_to_num(info)
new target = find_player("k", g_iTarget[id])
if (!target)
{
client_print(id, print_chat, "[CSB] That player already left.")
return PLUGIN_HANDLED
}
if (get_user_flags(target) & ADMIN_IMMUNITY)
{
client_print(id, print_chat, "[CSB] That player is immune.")
showActionMenu(id)
return PLUGIN_HANDLED
}
/* gag/kick/ban need a reason - the rest fire straight away */
if (action == ACT_GAG || action == ACT_KICK || action == ACT_BAN)
{
/* ungag needs no reason */
if (action == ACT_GAG && g_bGagged[target])
{
applyAction(id, target, action, "")
showActionMenu(id)
return PLUGIN_HANDLED
}
g_iAction[id] = action
showReasonMenu(id)
return PLUGIN_HANDLED
}
applyAction(id, target, action, "")
showActionMenu(id)
return PLUGIN_HANDLED
}
showReasonMenu(id)
{
new menu = menu_create("\yCSB Punish\w^nSelect a reason", "menuReason")
new info[8]
for (new i = 0; i < g_iReasonCount; i++)
{
formatex(info, charsmax(info), "%d", i)
menu_additem(menu, g_szReasons[i], info, 0)
}
menu_setprop(menu, MPROP_EXITNAME, "Back")
menu_display(id, menu, 0)
}
public menuReason(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
showActionMenu(id)
return PLUGIN_HANDLED
}
new info[8], name[48], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
menu_destroy(menu)
new idx = str_to_num(info)
new reason[48]
if (idx >= 0 && idx < g_iReasonCount)
copy(reason, charsmax(reason), g_szReasons[idx])
else
copy(reason, charsmax(reason), "no reason given")
new target = find_player("k", g_iTarget[id])
if (!target)
{
client_print(id, print_chat, "[CSB] That player already left.")
return PLUGIN_HANDLED
}
applyAction(id, target, g_iAction[id], reason)
return PLUGIN_HANDLED
}
applyAction(id, target, action, const reason[])
{
new aname[32], tname[32]
getAdminName(id, aname, charsmax(aname))
get_user_name(target, tname, charsmax(tname))
new what[96]
switch (action)
{
case ACT_SLAP:
{
if (!is_user_alive(target)) { client_print(id, print_chat, "[CSB] %s is dead.", tname); return; }
user_slap(target, get_pcvar_num(g_pSlapDmg))
formatex(what, charsmax(what), "slapped %s", tname)
}
case ACT_SLAY:
{
if (!is_user_alive(target)) { client_print(id, print_chat, "[CSB] %s is dead.", tname); return; }
user_kill(target, 1)
formatex(what, charsmax(what), "slayed %s", tname)
}
case ACT_FREEZE:
{
toggleFreeze(target)
formatex(what, charsmax(what), "%s %s", g_bFrozen[target] ? "froze" : "unfroze", tname)
}
case ACT_GAG:
{
g_bGagged[target] = !g_bGagged[target]
formatex(what, charsmax(what), "%s %s (%s)", g_bGagged[target] ? "gagged" : "ungagged", tname,
reason[0] ? reason : "no reason")
}
case ACT_KICK:
{
formatex(what, charsmax(what), "kicked %s (%s)", tname, reason)
server_cmd("kick #%d ^"Kicked: %s^"", get_user_userid(target), reason)
}
case ACT_BAN:
{
new mins = get_pcvar_num(g_pBanTime)
formatex(what, charsmax(what), "banned %s for %d min (%s)", tname, mins, reason)
server_cmd("banid %d #%d kick", mins, get_user_userid(target))
server_cmd("writeid")
}
}
announce(aname, what)
}
toggleFreeze(target)
{
if (g_bFrozen[target])
{
g_bFrozen[target] = false
set_pev(target, pev_flags, pev(target, pev_flags) & ~FL_FROZEN)
set_user_maxspeed(target, -1.0)
return
}
g_bFrozen[target] = true
set_pev(target, pev_flags, pev(target, pev_flags) | FL_FROZEN)
set_user_maxspeed(target, 1.0)
new Float:ftime = get_pcvar_float(g_pFreezeTime)
if (ftime > 0.0)
{
remove_task(target + 7000)
set_task(ftime, "taskUnfreeze", target + 7000)
}
}
public taskUnfreeze(taskid)
{
new target = taskid - 7000
if (is_user_connected(target) && g_bFrozen[target])
{
g_bFrozen[target] = false
set_pev(target, pev_flags, pev(target, pev_flags) & ~FL_FROZEN)
set_user_maxspeed(target, -1.0)
}
}
public client_command(id)
{
if (!g_bGagged[id])
return PLUGIN_CONTINUE
new cmd[16]
read_argv(0, cmd, charsmax(cmd))
if (equali(cmd, "say") || equali(cmd, "say_team"))
{
client_print(id, print_chat, "[CSB] You are gagged by an admin.")
return PLUGIN_HANDLED_MAIN
}
return PLUGIN_CONTINUE
}
public client_disconnected(id)
{
g_bFrozen[id] = false
g_bGagged[id] = false
remove_task(id + 7000)
}
getAdminName(id, name[], len)
{
if (id)
get_user_name(id, name, len)
else
copy(name, len, "CONSOLE")
}
announce(const aname[], const what[])
{
client_print_color(0, print_team_default, "^x04[CSB]^x03 %s^x01 %s.", aname, what)
log_amx("[CSB Punish] %s %s", aname, what)
}