#pragma semicolon 1
#pragma compress 1
#include <amxmodx>
#include <amxmisc>
#include <cstrike>
#include <reapi>
#define PLUGIN "Migraine [ReAPI] + Menu"
#define VERSION "1.1.0"
#define AUTHOR "ConnorMcLeod | Gramm "
#define MAX_PLAYERS 32
#define MaskEnt(%0) (1 << ((%0) & 31))
new g_bitMigraineux;
#define SetUserReversed(%0) (g_bitMigraineux |= MaskEnt(%0))
#define ClearUserReversed(%0) (g_bitMigraineux &= ~MaskEnt(%0))
#define HasUserMigraine(%0) (g_bitMigraineux & MaskEnt(%0))
enum globalState { None, Terrorists, Cts, All }
new Float:g_fAngle[MAX_PLAYERS + 1];
new g_iGlobalState;
new Float:g_fGlobalAngle = 180.0;
new HookChain:g_hPostThink;
new HookChain:g_hSpawn;
// ===== СОСТОЯНИЕ МЕНЮ (для админа) =====
new g_iMenuTargetType[33]; // 0=игрок, 1=@ALL, 2=@T, 3=@CT
new g_iMenuTargetPlayer[33]; // если targetType=0
new g_iMenuOnOff[33]; // 0/1
new Float:g_fMenuAngle[33]; // угол
new const Float:g_fAnglesList[] = { 90.0, 180.0, 270.0, -180.0, -90.0, 360.0, -360.0 };
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
// Старые команды (консоль)
register_concmd("amx_migraine", "AdminCommand_Migraine", ADMIN_BAN, "- <name/@CT/@T/@ALL> <0/1> [angle]");
register_concmd("amx_migraine_info", "AdminCommand_MigraineInfo", ADMIN_BAN, "- показывает список игроков с эффектом");
// Меню
register_clcmd("say /migraine", "CmdOpenMigraineMenu");
register_clcmd("say_team /migraine", "CmdOpenMigraineMenu");
register_concmd("amx_migraine_menu", "CmdOpenMigraineMenu", ADMIN_BAN, "- открыть меню Migraine");
}
public client_putinserver(id)
{
ClearUserReversed(id);
g_fAngle[id] = 0.0;
// Значения меню по умолчанию
g_iMenuTargetType[id] = 0;
g_iMenuTargetPlayer[id] = id;
g_iMenuOnOff[id] = 1;
g_fMenuAngle[id] = 180.0;
CheckForwards();
}
public client_disconnected(id)
{
ClearUserReversed(id);
g_fAngle[id] = 0.0;
CheckForwards();
}
// ==================== ХУКИ ====================
public OnPlayerSpawn(id)
{
// Post Spawn
if (!is_user_alive(id))
return HC_CONTINUE;
switch (g_iGlobalState)
{
case Terrorists, Cts:
{
if (g_iGlobalState == _:cs_get_user_team(id))
{
SetUserReversed(id);
g_fAngle[id] = g_fGlobalAngle;
}
else
{
ClearUserReversed(id);
g_fAngle[id] = 0.0;
}
}
case All:
{
SetUserReversed(id);
g_fAngle[id] = g_fGlobalAngle;
}
default:
{
ClearUserReversed(id);
g_fAngle[id] = 0.0;
}
}
CheckForwards();
if (HasUserMigraine(id))
RequestFrame("ApplyAngleAfterSpawn", id);
return HC_CONTINUE;
}
public ApplyAngleAfterSpawn(id)
{
if (is_user_alive(id) && HasUserMigraine(id))
{
new Float:vecPunch[3];
vecPunch[2] = g_fAngle[id];
set_entvar(id, var_punchangle, vecPunch);
}
}
public OnPlayerPostThink(id)
{
if (!is_user_alive(id) || !HasUserMigraine(id))
return HC_CONTINUE;
new Float:vecPunch[3];
vecPunch[2] = g_fAngle[id];
set_entvar(id, var_punchangle, vecPunch);
return HC_CONTINUE;
}
// ==================== ПРИМЕНЕНИЕ ЭФФЕКТА (ядро) ====================
stock Migraine_ResetPunch(id)
{
new Float:vec[3];
set_entvar(id, var_punchangle, vec);
}
stock Migraine_ResetAllPunch()
{
for (new i = 1; i <= MaxClients; i++)
{
if (is_user_connected(i))
Migraine_ResetPunch(i);
g_fAngle[i] = 0.0;
ClearUserReversed(i);
}
}
stock Migraine_Apply(admin, const target[], onoff, Float:angle)
{
// Ограничение угла
if (angle > 360.0) angle = 360.0;
else if (angle < -360.0) angle = -360.0;
g_fGlobalAngle = angle;
new szAdminName[32];
get_user_name(admin, szAdminName, charsmax(szAdminName));
// ======= ГЛОБАЛЬНЫЕ ЦЕЛИ (@...) =======
if (target[0] == '@')
{
if (!onoff)
{
g_bitMigraineux = 0;
g_iGlobalState = None;
g_fGlobalAngle = 180.0;
Migraine_ResetAllPunch();
client_print_color(0, print_team_default,
"^4[Migraine]^1 Администратор ^4%s^1 отключил эффект для всех.", szAdminName);
CheckForwards();
return 1;
}
new szTeamName[64];
switch (target[1])
{
case 'a', 'A': { g_iGlobalState = All; copy(szTeamName, charsmax(szTeamName), "всех игроков"); }
case 't', 'T': { g_iGlobalState = Terrorists; copy(szTeamName, charsmax(szTeamName), "Террористов"); }
case 'c', 'C': { g_iGlobalState = Cts; copy(szTeamName, charsmax(szTeamName), "Контр-Террористов"); }
default: return 0;
}
for (new i = 1; i <= MaxClients; i++)
{
if (!is_user_connected(i))
continue;
if (g_iGlobalState == All ||
(g_iGlobalState == Terrorists && cs_get_user_team(i) == CS_TEAM_T) ||
(g_iGlobalState == Cts && cs_get_user_team(i) == CS_TEAM_CT))
{
SetUserReversed(i);
g_fAngle[i] = g_fGlobalAngle;
if (is_user_alive(i))
{
new Float:vecPunch[3];
vecPunch[2] = g_fAngle[i];
set_entvar(i, var_punchangle, vecPunch);
}
}
else
{
ClearUserReversed(i);
g_fAngle[i] = 0.0;
if (is_user_alive(i))
Migraine_ResetPunch(i);
}
}
client_print_color(0, print_team_default,
"^4[Migraine]^1 Администратор ^4%s^1 перевернул экран для ^3%s^1 на ^4%.0f^1 градусов.",
szAdminName, szTeamName, g_fGlobalAngle);
CheckForwards();
return 1;
}
// ======= ОДНА ЦЕЛЬ (ник / #userid) =======
new iPlayer = cmd_target(admin, target, CMDTARGET_OBEY_IMMUNITY | CMDTARGET_ALLOW_SELF);
if (!iPlayer) return 0;
if (onoff)
{
SetUserReversed(iPlayer);
g_fAngle[iPlayer] = g_fGlobalAngle;
if (is_user_alive(iPlayer))
{
new Float:vecPunch[3];
vecPunch[2] = g_fAngle[iPlayer];
set_entvar(iPlayer, var_punchangle, vecPunch);
}
client_print_color(0, print_team_default,
"^4[Migraine]^1 Администратор ^4%s^1 перевернул экран игроку ^3%n^1 на ^4%.0f^1 градусов.",
szAdminName, iPlayer, g_fGlobalAngle);
}
else
{
ClearUserReversed(iPlayer);
g_fAngle[iPlayer] = 0.0;
Migraine_ResetPunch(iPlayer);
client_print_color(0, print_team_default,
"^4[Migraine]^1 Администратор ^4%s^1 вернул нормальный экран игроку ^3%n^1.",
szAdminName, iPlayer);
}
CheckForwards();
return 1;
}
// ==================== КОМАНДЫ ====================
public AdminCommand_Migraine(id, level, cid)
{
if (!cmd_access(id, level, cid, 3))
return PLUGIN_HANDLED;
new szTarget[32], szOnOff[2], szAngle[16];
read_argv(1, szTarget, charsmax(szTarget));
read_argv(2, szOnOff, charsmax(szOnOff));
new iOnOff;
if (szOnOff[0] == '1') iOnOff = 1;
else if (szOnOff[0] == '0') iOnOff = 0;
else return PLUGIN_HANDLED;
new Float:angle = 180.0;
if (read_argc() >= 4)
{
read_argv(3, szAngle, charsmax(szAngle));
angle = str_to_float(szAngle);
}
Migraine_Apply(id, szTarget, iOnOff, angle);
return PLUGIN_HANDLED;
}
public AdminCommand_MigraineInfo(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED;
new iCount, szName[32], szTeam[16];
console_print(id, "^n========================================");
console_print(id, "[Migraine] Список игроков с эффектом:");
console_print(id, "========================================");
switch (g_iGlobalState)
{
case All: console_print(id, "Режим: ВСЕ игроки (%.0f градусов)", g_fGlobalAngle);
case Terrorists: console_print(id, "Режим: Террористы (%.0f градусов)", g_fGlobalAngle);
case Cts: console_print(id, "Режим: Контр-Террористы (%.0f градусов)", g_fGlobalAngle);
default: console_print(id, "Режим: Отключен");
}
console_print(id, "----------------------------------------");
for (new i = 1; i <= MaxClients; i++)
{
if (is_user_connected(i) && HasUserMigraine(i))
{
get_user_name(i, szName, charsmax(szName));
switch (cs_get_user_team(i))
{
case CS_TEAM_T: copy(szTeam, charsmax(szTeam), "T");
case CS_TEAM_CT: copy(szTeam, charsmax(szTeam), "CT");
case CS_TEAM_SPECTATOR: copy(szTeam, charsmax(szTeam), "SPEC");
default: copy(szTeam, charsmax(szTeam), "NONE");
}
console_print(id, "%2d. %s [%s] - Угол: %.0f %s",
++iCount, szName, szTeam, g_fAngle[i], is_user_alive(i) ? "" : "(мертв)");
}
}
if (!iCount) console_print(id, "Нет игроков с активным эффектом.");
else
{
console_print(id, "----------------------------------------");
console_print(id, "Всего игроков с эффектом: %d", iCount);
}
console_print(id, "========================================^n");
return PLUGIN_HANDLED;
}
// ==================== МЕНЮ ====================
public CmdOpenMigraineMenu(id)
{
if (!(get_user_flags(id) & ADMIN_BAN))
return PLUGIN_HANDLED;
// Если "слетело" — ставим нормальную цель
if (!is_user_connected(g_iMenuTargetPlayer[id]))
g_iMenuTargetPlayer[id] = id;
if (g_fMenuAngle[id] == 0.0)
g_fMenuAngle[id] = 180.0;
ShowMigraineMenu(id);
return PLUGIN_HANDLED;
}
ShowMigraineMenu(id)
{
new menu = menu_create("\yMigraine Menu \r[ReAPI]", "MigraineMenu_Handler");
new sz[192];
new const targetNames[][] = { "Игрок", "@ALL", "@T", "@CT" };
formatex(sz, charsmax(sz), "\wЦель: \y%s", targetNames[g_iMenuTargetType[id]]);
menu_additem(menu, sz, "1");
if (g_iMenuTargetType[id] == 0)
{
formatex(sz, charsmax(sz), "\wИгрок: \y%n \d(следующий)", g_iMenuTargetPlayer[id]);
menu_additem(menu, sz, "2");
}
else
{
menu_additem(menu, "\dИгрок: (не требуется)", "2");
}
formatex(sz, charsmax(sz), "\wРежим: \y%s", g_iMenuOnOff[id] ? "ON (перевернуть)" : "OFF (выключить)");
menu_additem(menu, sz, "3");
formatex(sz, charsmax(sz), "\wУгол: \y%.0f \d(клик — изменить)", g_fMenuAngle[id]);
menu_additem(menu, sz, "4");
menu_additem(menu, "\rПрименить", "5");
menu_additem(menu, "\wСписок в консоль", "6");
menu_setprop(menu, MPROP_EXITNAME, "Выход");
menu_display(id, menu);
}
public MigraineMenu_Handler(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu);
return PLUGIN_HANDLED;
}
new info[8], access, callback;
menu_item_getinfo(menu, item, access, info, charsmax(info), _, _, callback);
new key = str_to_num(info);
switch (key)
{
case 1:
{
g_iMenuTargetType[id]++;
if (g_iMenuTargetType[id] > 3) g_iMenuTargetType[id] = 0;
}
case 2:
{
if (g_iMenuTargetType[id] == 0)
{
new start = g_iMenuTargetPlayer[id];
if (start < 1 || start > MaxClients) start = 0;
for (new i = 1; i <= MaxClients; i++)
{
new p = start + i;
if (p > MaxClients) p -= MaxClients;
if (is_user_connected(p))
{
g_iMenuTargetPlayer[id] = p;
break;
}
}
}
}
case 3:
{
g_iMenuOnOff[id] = !g_iMenuOnOff[id];
}
case 4:
{
new idx = -1;
for (new i = 0; i < sizeof(g_fAnglesList); i++)
{
if (floatabs(g_fAnglesList[i] - g_fMenuAngle[id]) < 0.1)
{
idx = i;
break;
}
}
idx++;
if (idx >= sizeof(g_fAnglesList)) idx = 0;
g_fMenuAngle[id] = g_fAnglesList[idx];
}
case 5:
{
new target[32];
switch (g_iMenuTargetType[id])
{
case 0: formatex(target, charsmax(target), "#%d", get_user_userid(g_iMenuTargetPlayer[id]));
case 1: copy(target, charsmax(target), "@ALL");
case 2: copy(target, charsmax(target), "@T");
case 3: copy(target, charsmax(target), "@CT");
}
Migraine_Apply(id, target, g_iMenuOnOff[id], g_fMenuAngle[id]);
}
case 6:
{
AdminCommand_MigraineInfo(id, ADMIN_BAN, 0);
}
}
menu_destroy(menu);
ShowMigraineMenu(id);
return PLUGIN_HANDLED;
}
// ==================== ДИНАМИЧЕСКИЕ ХУКИ ====================
CheckForwards()
{
static bool:bEnabled = false;
new bool:bNeed = (g_bitMigraineux != 0);
if (bNeed == bEnabled)
return;
if (bNeed)
{
if (!g_hPostThink)
{
g_hPostThink = RegisterHookChain(RG_CBasePlayer_PostThink, "OnPlayerPostThink", false);
g_hSpawn = RegisterHookChain(RG_CBasePlayer_Spawn, "OnPlayerSpawn", true); // post
}
else
{
EnableHookChain(g_hPostThink);
EnableHookChain(g_hSpawn);
}
}
else
{
if (g_hPostThink) DisableHookChain(g_hPostThink);
if (g_hSpawn) DisableHookChain(g_hSpawn);
}
bEnabled = bNeed;
}