// *************************************************************************************//
// Плагин загружен с www.neugomon.ru //
// Автор: Neygomon [ https://neugomon.ru/members/1/ ] //
// Официальная тема поддержки: https://neugomon.ru/threads/34/ //
// При копировании материала ссылка на сайт www.neugomon.ru ОБЯЗАТЕЛЬНА! //
// *************************************************************************************//
/*
Original code: Freedo.m
*/
#include <amxmodx>
#include <reapi>
#pragma semicolon 1
#define ACCESS_LEVEL_IMMUNITY (ADMIN_MENU|ADMIN_LEVEL_H)
// Уровень доступа позволяющий беспрепятственно сидеть в зрителях
#define TIME_AFK_CHECK 15.0
// Интервал между проверками игроков, чем меньше значение, тем больше нагрузка на сервер.
// В режиме NOROUND (для CSDM) ставьте маленькое значение TIME_AFK_CHECK, так как таймер обнуляется при спавне.
#define MAX_AFK_WARNING 3 // Количество предупреждений после которых последует наказание.
#define TIME_SPECT_CHECK 60.0 // Интервал между проверками зрителей, чем меньше значение, тем больше нагрузка на сервер.
#define MAX_SPECT_CHECK_PL 2 // Количество проверок игрока на нахождение в зрителях, после которых его кикнет
#define MIN_PLAYERS_CHECK 30 // Минимальное количество игроков, когда включается функция проверки зрителей.
// #define NOROUND // Включает поддержку серверов с бесконечным раундом. Например CSDM, GunGame
#define BOMB_TRANSFER
// Передавать ли бомбу игрокам, если игрок AFK.
// Закомментируйте, если хотите, чтобы бомба просто выкидывалась
// Игнорируется при включенном NOROUND
#define get_bit(%1,%2) (%1 & (1 << (%2 & 31)))
#define set_bit(%1,%2) %1 |= (1 << (%2 & 31))
#define clr_bit(%1,%2) %1 &= ~(1 << (%2 & 31))
new Float:g_fOldOrigin[33][3], Float:g_fOldAngles[33][3];
new g_bitValid;
#if defined NOROUND
new g_bitSpec;
#endif
new g_iWarning[33];
new pnum, players[32];
new g_count[33];
public plugin_init()
{
#if defined NOROUND
RegisterHookChain(RG_CBasePlayer_Spawn, "PlrSpwn_Post", true);
#define VERSION "1.4.1 [NoRnd]"
#else
register_logevent("LeRoundStart", 2, "1=Round_Start");
#define VERSION "1.4.1 [Rnd]"
#endif
register_plugin("AFK Control", VERSION, "neygomon");
set_task(TIME_SPECT_CHECK, "SpectatorCheck", .flags = "b");
}
public client_putinserver(id)
{
if(is_user_bot(id) || is_user_hltv(id) || get_user_flags(id) & ACCESS_LEVEL_IMMUNITY)
clr_bit(g_bitValid, id);
else set_bit(g_bitValid, id);
g_count[id] = 0;
#if defined NOROUND
clr_bit(g_bitSpec, id);
#endif
}
#if defined NOROUND
public client_disconnected(id)
remove_task(id);
public PlrSpwn_Post(id)
if(is_user_alive(id))
LeRoundStart(id);
#endif
public LeRoundStart(id)
{
#if defined NOROUND
if(!get_bit(g_bitSpec, id))
{
get_entvar(id, var_origin, g_fOldOrigin[id]);
get_entvar(id, var_angles, g_fOldAngles[id]);
if(!task_exists(id))
set_task(TIME_AFK_CHECK, "AfkCheck", id, .flags = "b");
else change_task(id, TIME_AFK_CHECK);
}
#else
static freezetime;
if(!freezetime) freezetime = get_cvar_pointer("mp_freezetime");
if(get_pcvar_num(freezetime) > 0)
GoCheckPlayers();
else set_task(1.0, "GoCheckPlayers");
#endif
}
public GoCheckPlayers()
{
get_players(players, pnum, "ah");
for(new i; i < pnum; i++)
{
g_iWarning[players] = 0;
get_entvar(players, var_origin, g_fOldOrigin[players]);
get_entvar(players, var_angles, g_fOldAngles[players]);
}
if(!task_exists(87892789))
set_task(TIME_AFK_CHECK, "AfkCheck", 87892789, .flags = "b");
else change_task(87892789, TIME_AFK_CHECK);
}
public AfkCheck(id)
{
if(id == 87892789)
get_players(players, pnum, "ah");
else if(!is_user_connected(id))
return;
else players[0] = id, pnum = 1;
for(new i, Float:fNewOrigin[3], Float:fNewAngles[3], szName[32]; i < pnum; i++)
{
get_entvar(players, var_origin, fNewOrigin);
get_entvar(players, var_angles, fNewAngles);
if(!xs_vec_equal(g_fOldOrigin[players], fNewOrigin) || !xs_vec_equal(g_fOldAngles[players], fNewAngles))
{
g_iWarning[players] = 0;
xs_vec_copy(fNewOrigin, g_fOldOrigin[players]);
xs_vec_copy(fNewAngles, g_fOldAngles[players]);
continue;
}
get_entvar(players, var_netname, szName, charsmax(szName));
if(++g_iWarning[players] >= MAX_AFK_WARNING)
{
user_kill(players, 1);
engclient_cmd(players, "jointeam", "6");
client_cmd(players, "spk events/friend_died");
ChatColor(0, players, "^1[^4AFKControl^1] ^4Игрок ^3%s ^4был перемещен в зрители, так как был ^3AFK", szName);
#if defined NOROUND
set_bit(g_bitSpec, players);
remove_task(players);
#endif
}
else
{
client_cmd(players, "spk events/tutor_msg");
ChatColor(players, 0, "^1[^4AFKControl^1] ^4Вы не проявляете активность! Предупреждения: ^3%i/%i", g_iWarning[players], MAX_AFK_WARNING);
}
#if !defined NOROUND
if(get_entvar(players, var_weapons) & (1 << CSW_C4))
{
ChatColor(0, players, "^1[^4AFKControl^1] ^4У игрока ^3%s ^4отобрана бомба, так как находится ^3AFK", szName);
#if defined BOMB_TRANSFER
rg_transfer_c4(players, 0);
#else
engclient_cmd(players, "drop", "weapon_c4");
#endif
}
#endif
}
}
public SpectatorCheck()
{
if(get_playersnum() < MIN_PLAYERS_CHECK)
return;
new players[32], pnum;
get_players(players, pnum, "h");
for(new i, szName[32]; i < pnum; i++)
{
if(!get_bit(g_bitValid, players))
continue;
switch(get_member(players, m_iTeam))
{
case 0, 3:
{
if(++g_count[players] >= MAX_SPECT_CHECK_PL)
{
get_entvar(players, var_netname, szName, charsmax(szName));
ChatColor(0, players, "^1[^4AFKControl^1] ^4Игрок^3 %s ^4удален за длительное нахождение в спектрах.", szName);
server_cmd("kick #%d Вы были кикнуты из-за длительного нахождения в зрителях.", get_user_userid(players));
}
}
}
}
}
stock bool:xs_vec_equal(const Float:vec1[], const Float:vec2[])
return (vec1[0] == vec2[0]) && (vec1[1] == vec2[1]) && (vec1[2] == vec2[2]);
stock xs_vec_copy(const Float:vecIn[], Float:vecOut[])
{
vecOut[0] = vecIn[0];
vecOut[1] = vecIn[1];
vecOut[2] = vecIn[2];
}
stock ChatColor(id, id2, const szMessage[], any:...)
{
if(id && !is_user_connected(id))
return;
new szMsg[190]; vformat(szMsg, charsmax(szMsg), szMessage, 4);
message_begin(id ? MSG_ONE : MSG_ALL, 76, .player = id);
write_byte(id2 ? id2 : id);
write_string(szMsg);
message_end();
}