/*
* CSB Spectate Tools
* Copyright (C) 2026 counter-strike-boost.com
*
* Admin-only spectator helpers. amx_spectate opens a menu to jump straight into
* any player's first-person view (pev_iuser1 = in-eye, pev_iuser2 = target); a
* HUD shows the watched player's HP, armor and current weapon.
* amx_specme moves the admin to spectator while remembering the team, and
* amx_specback puts them back on 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
#include
#include
#define OBS_IN_EYE 4
new const PLUGIN[] = "CSB Spectate Tools"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled
new g_iSyncHud
new g_iWatch[33]
new g_iSavedTeam[33]
new bool:g_bSpecMe[33]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_spec_enabled", "1")
register_concmd("amx_spectate", "cmdSpectate", ADMIN_SLAY, "- menu to watch a player in first person")
register_concmd("amx_specme", "cmdSpecMe", ADMIN_SLAY, "- move yourself to spectator, keeping your team")
register_concmd("amx_specback", "cmdSpecBack", ADMIN_SLAY, "- return to the team you had before amx_specme")
g_iSyncHud = CreateHudSyncObj()
set_task(1.0, "taskHud", 0, _, _, "b")
}
public client_disconnected(id)
{
g_iWatch[id] = 0
g_iSavedTeam[id] = 0
g_bSpecMe[id] = false
}
public cmdSpectate(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
if (!get_pcvar_num(g_pEnabled))
{
console_print(id, "[CSB] Spectate tools are disabled (csb_spec_enabled 0).")
return PLUGIN_HANDLED
}
showSpecMenu(id)
return PLUGIN_HANDLED
}
showSpecMenu(id)
{
new menu = menu_create("\ySpectate Tools^n\wPick a player to watch:", "specMenuHandler")
new players[32], num, pid, name[32], info[8], line[64]
get_players(players, num, "a")
for (new i = 0; i < num; i++)
{
pid = players[i]
get_user_name(pid, name, charsmax(name))
num_to_str(pid, info, charsmax(info))
formatex(line, charsmax(line), "%s \r[%d hp]", name, get_user_health(pid))
menu_additem(menu, line, info, 0)
}
if (!num)
menu_additem(menu, "\rNo alive players right now", "-1", 0)
menu_setprop(menu, MPROP_TITLE, "\ySpectate Tools")
menu_display(id, menu, 0)
}
public specMenuHandler(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 is no longer available.")
return PLUGIN_HANDLED
}
watchPlayer(id, target)
return PLUGIN_HANDLED
}
watchPlayer(id, target)
{
/* make sure the admin is a spectator first */
new CsTeams:t = cs_get_user_team(id)
if (t != CS_TEAM_SPECTATOR)
{
if (t == CS_TEAM_T || t == CS_TEAM_CT)
g_iSavedTeam[id] = _:t
cs_set_user_team(id, CS_TEAM_SPECTATOR)
g_bSpecMe[id] = true
}
g_iWatch[id] = target
new tname[32]
get_user_name(target, tname, charsmax(tname))
client_print(id, print_chat, "[CSB] Watching %s. Use amx_specback to return to your team.", tname)
set_task(0.2, "taskApplyView", id)
}
public taskApplyView(id)
{
if (!is_user_connected(id))
return
new target = g_iWatch[id]
if (target < 1 || !is_user_connected(target))
return
set_pev(id, pev_iuser1, OBS_IN_EYE)
set_pev(id, pev_iuser2, target)
}
public cmdSpecMe(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
new CsTeams:t = cs_get_user_team(id)
if (t == CS_TEAM_SPECTATOR)
{
client_print(id, print_chat, "[CSB] You are already a spectator.")
return PLUGIN_HANDLED
}
if (t == CS_TEAM_T || t == CS_TEAM_CT)
g_iSavedTeam[id] = _:t
cs_set_user_team(id, CS_TEAM_SPECTATOR)
g_bSpecMe[id] = true
client_print(id, print_chat, "[CSB] Moved to spectator. amx_specback returns you to your team.")
return PLUGIN_HANDLED
}
public cmdSpecBack(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
if (!g_bSpecMe[id] || g_iSavedTeam[id] == 0)
{
client_print(id, print_chat, "[CSB] No saved team to return to.")
return PLUGIN_HANDLED
}
cs_set_user_team(id, CsTeams:g_iSavedTeam[id])
g_bSpecMe[id] = false
g_iSavedTeam[id] = 0
g_iWatch[id] = 0
client_print(id, print_chat, "[CSB] Returned to your team. You will respawn next round.")
return PLUGIN_HANDLED
}
public taskHud()
{
new players[32], num, id, target
get_players(players, num)
for (new i = 0; i < num; i++)
{
id = players[i]
target = g_iWatch[id]
if (target < 1 || !is_user_connected(target) || cs_get_user_team(id) != CS_TEAM_SPECTATOR)
continue
if (!is_user_alive(target))
{
set_hudmessage(255, 60, 60, 0.02, 0.25, 0, 0.0, 1.1, 0.0, 0.0, -1)
ShowSyncHudMsg(id, g_iSyncHud, "Watching: (dead)")
continue
}
new tname[32], wname[32]
get_user_name(target, tname, charsmax(tname))
new wid = get_user_weapon(target)
if (wid > 0)
get_weaponname(wid, wname, charsmax(wname))
else
copy(wname, charsmax(wname), "none")
replace(wname, charsmax(wname), "weapon_", "")
new armor = get_user_armor(target)
new hud[160]
formatex(hud, charsmax(hud), "%s^nHP %d AP %d^nWeapon: %s",
tname, get_user_health(target), armor, wname)
set_hudmessage(60, 220, 255, 0.02, 0.25, 0, 0.0, 1.1, 0.0, 0.0, -1)
ShowSyncHudMsg(id, g_iSyncHud, hud)
}
}