/* * 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) } }