/* * CSB Admin Transfer * Copyright (C) 2026 counter-strike-boost.com * * amx_transfer moves a player between teams with * cs_set_user_team, keeping their frags, and optionally respawns them with * Ham_CS_RoundRespawn. amx_transfermenu lists players by team. The team score * is never touched and every move is announced. * * Inspired by the team commands in the AMX Mod X Admin Commands plugin. * Independent GPL re-implementation. * * 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 #include new const PLUGIN[] = "CSB Admin Transfer" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" new g_pRespawn public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pRespawn = register_cvar("csb_transfer_respawn", "1") register_concmd("amx_transfer", "cmdTransfer", ADMIN_LEVEL_A, " - move a player to a team") register_concmd("amx_transfermenu", "cmdMenu", ADMIN_LEVEL_A, "- pick a player and a team from a menu") } parseTeam(const arg[], &CsTeams:team) { if (equali(arg, "t") || equali(arg, "ter") || equali(arg, "terrorist")) team = CS_TEAM_T else if (equali(arg, "ct") || equali(arg, "counter")) team = CS_TEAM_CT else if (equali(arg, "spec") || equali(arg, "spectator")) team = CS_TEAM_SPECTATOR else return 0 return 1 } teamName(CsTeams:team, out[], len) { switch (team) { case CS_TEAM_T: copy(out, len, "Terrorists") case CS_TEAM_CT: copy(out, len, "Counter-Terrorists") case CS_TEAM_SPECTATOR: copy(out, len, "Spectator") default: copy(out, len, "Unassigned") } } doTransfer(admin, target, CsTeams:team) { new CsTeams:cur = cs_get_user_team(target) if (cur == team) { client_print(admin, print_chat, "[CSB] That player is already on that team.") return } /* cs_set_user_team keeps the player's frags and deaths intact */ cs_set_user_team(target, team) if (team != CS_TEAM_SPECTATOR && get_pcvar_num(g_pRespawn) && is_user_alive(target)) ExecuteHamB(Ham_CS_RoundRespawn, target) new aname[32], tname[32], tteam[24] admName(admin, aname, charsmax(aname)) get_user_name(target, tname, charsmax(tname)) teamName(team, tteam, charsmax(tteam)) client_print(0, print_chat, "[CSB] %s moved %s to %s.", aname, tname, tteam) log_amx("[CSB Transfer] %s moved %s to %s", aname, tname, tteam) } admName(id, out[], len) { if (id) get_user_name(id, out, len) else copy(out, len, "CONSOLE") } public cmdTransfer(id, level, cid) { if (!cmd_access(id, level, cid, 3)) return PLUGIN_HANDLED new arg[32], teamArg[16] read_argv(1, arg, charsmax(arg)) read_argv(2, teamArg, charsmax(teamArg)) new target = cmd_target(id, arg, CMDTARGET_OBEY_IMMUNITY) if (!target) return PLUGIN_HANDLED new CsTeams:team if (!parseTeam(teamArg, team)) { console_print(id, "[CSB] Team must be one of: t, ct, spec.") return PLUGIN_HANDLED } doTransfer(id, target, team) return PLUGIN_HANDLED } public cmdMenu(id, level, cid) { if (!cmd_access(id, level, cid, 1)) return PLUGIN_HANDLED showPlayers(id) return PLUGIN_HANDLED } showPlayers(id) { new menu = menu_create("\yTransfer - pick a player", "menuPlayers") new players[32], num, pid, name[32], info[8], line[64], team[24] get_players(players, num) for (new i = 0; i < num; i++) { pid = players[i] get_user_name(pid, name, charsmax(name)) teamName(cs_get_user_team(pid), team, charsmax(team)) num_to_str(pid, info, charsmax(info)) formatex(line, charsmax(line), "%s \r(%s)", name, team) menu_additem(menu, line, info, 0) } menu_display(id, menu, 0) } public menuPlayers(id, menu, item) { if (item == MENU_EXIT || item < 0) { 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 target = str_to_num(info) if (target < 1 || !is_user_connected(target)) { client_print(id, print_chat, "[CSB] That player left.") return PLUGIN_HANDLED } showTeams(id, target) return PLUGIN_HANDLED } showTeams(id, target) { new tname[32] get_user_name(target, tname, charsmax(tname)) new title[64] formatex(title, charsmax(title), "\yMove %s to:", tname) new menu = menu_create(title, "menuTeams") new info[16] formatex(info, charsmax(info), "%d t", target) menu_additem(menu, "\wTerrorists", info, 0) formatex(info, charsmax(info), "%d ct", target) menu_additem(menu, "\wCounter-Terrorists", info, 0) formatex(info, charsmax(info), "%d spec", target) menu_additem(menu, "\wSpectator", info, 0) menu_display(id, menu, 0) } public menuTeams(id, menu, item) { if (item == MENU_EXIT || item < 0) { menu_destroy(menu) return PLUGIN_HANDLED } new info[16], name[32], access, callback menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback) menu_destroy(menu) new targetArg[8], teamArg[8] strtok(info, targetArg, charsmax(targetArg), teamArg, charsmax(teamArg), ' ') new target = str_to_num(targetArg) if (target < 1 || !is_user_connected(target)) { client_print(id, print_chat, "[CSB] That player left.") return PLUGIN_HANDLED } new CsTeams:team if (!parseTeam(teamArg, team)) return PLUGIN_HANDLED doTransfer(id, target, team) return PLUGIN_HANDLED }