Загрузка данных
/**
* lw_bot_chaos_pack.sp
*
* Версия: 4.0.0 - Исправлены критические ошибки, оптимизирована производительность.
*/
#pragma semicolon 1
#pragma newdecls required
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#define PLUGIN_VERSION "4.0.0"
public Plugin myinfo =
{
name = "LW Bot Chaos Pack",
author = "LW & Community",
description = "Friendly Heavies, Cheater Snipers, Demoknights & Dance Epidemics!",
version = PLUGIN_VERSION,
url = "https://github.com/"
};
// ---------- Глобальные переменные ----------
bool g_bEnabled = true;
bool g_bLateLoaded = false;
// Настройки
float g_fFriendlyHeavyChance = 35.0;
float g_fCheaterSniperChance = 30.0;
float g_fDemoknightChance = 38.0;
float g_fInfectionChance = 50.0;
float g_fInfectionRadius = 500.0;
float g_fInfectionDuration = 40.0;
float g_fSpyChance = 80.0;
float g_fSandwichRange = 400.0;
float g_fSandwichCooldown = 3.0;
bool g_bDebug = false;
// Состояния ботов
bool g_bIsFriendly[MAXPLAYERS+1];
bool g_bIsCheaterSniper[MAXPLAYERS+1];
bool g_bIsDemoknight[MAXPLAYERS+1];
bool g_bIsInfected[MAXPLAYERS+1];
bool g_bIsParanoiaCooldown[MAXPLAYERS+1];
float g_fLastSandwichThrow[MAXPLAYERS+1];
float g_fNextThinkTime[MAXPLAYERS+1]; // для ограничения частоты обработки
// Состояния для читерного снайпера
enum CheaterState
{
STATE_SEARCH,
STATE_AIMING,
STATE_SHOOTING
}
CheaterState g_iCheaterState[MAXPLAYERS+1];
float g_fAimStartTime[MAXPLAYERS+1];
float g_fAimDuration[MAXPLAYERS+1];
// Танцевальная эпидемия
bool g_bIsInfectionActive = false;
Handle g_hInfectionTimer = null;
// Конвары
ConVar g_cvEnabled;
ConVar g_cvFriendlyHeavyChance;
ConVar g_cvCheaterSniperChance;
ConVar g_cvDemoknightChance;
ConVar g_cvInfectionChance;
ConVar g_cvInfectionRadius;
ConVar g_cvInfectionDuration;
ConVar g_cvSpyChance;
ConVar g_cvSandwichRange;
ConVar g_cvSandwichCooldown;
ConVar g_cvDebug;
// ---------- Инициализация ----------
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
g_bLateLoaded = late;
return APLRes_Success;
}
public void OnPluginStart()
{
g_cvEnabled = CreateConVar("lw_bvc_enabled", "1", "Включить/выключить плагин (1/0)", 0, true, 0.0, true, 1.0);
g_cvFriendlyHeavyChance = CreateConVar("lw_bvc_friendly_heavy_chance", "35.0", "Шанс стать дружелюбным хеви (в %)", 0, true, 0.0, true, 100.0);
g_cvCheaterSniperChance = CreateConVar("lw_bvc_cheater_sniper_chance", "30.0", "Шанс стать читерным снайпером (в %)", 0, true, 0.0, true, 100.0);
g_cvDemoknightChance = CreateConVar("lw_bvc_demoknight_chance", "38.0", "Шанс стать демонайтом для подрывника (в %)", 0, true, 0.0, true, 100.0);
g_cvInfectionChance = CreateConVar("lw_bvc_infection_chance", "50.0", "Шанс начать танцевальную эпидемию (в %)", 0, true, 0.0, true, 100.0);
g_cvInfectionRadius = CreateConVar("lw_bvc_infection_radius", "500.0", "Радиус заражения танцем (в юнитах)", 0, true, 0.0);
g_cvInfectionDuration = CreateConVar("lw_bvc_infection_duration", "40.0", "Длительность танцевальной эпидемии (в секундах)", 0, true, 0.0);
g_cvSpyChance = CreateConVar("lw_bvc_spy_chance", "80.0", "Шанс крика 'Шпион!' при обнаружении врага (в %)", 0, true, 0.0, true, 100.0);
g_cvSandwichRange = CreateConVar("lw_bvc_sandwich_range", "400.0", "Радиус кормления (в юнитах)", 0, true, 0.0);
g_cvSandwichCooldown = CreateConVar("lw_bvc_sandwich_cooldown", "3.0", "Задержка между бросками (в секундах)", 0, true, 0.0);
g_cvDebug = CreateConVar("lw_bvc_debug", "0", "Отладка (1/0)", 0, true, 0.0, true, 1.0);
AutoExecConfig(true, "lw_bot_chaos_pack");
g_cvEnabled.AddChangeHook(OnConVarChanged);
g_cvFriendlyHeavyChance.AddChangeHook(OnConVarChanged);
g_cvCheaterSniperChance.AddChangeHook(OnConVarChanged);
g_cvDemoknightChance.AddChangeHook(OnConVarChanged);
g_cvInfectionChance.AddChangeHook(OnConVarChanged);
g_cvInfectionRadius.AddChangeHook(OnConVarChanged);
g_cvInfectionDuration.AddChangeHook(OnConVarChanged);
g_cvSpyChance.AddChangeHook(OnConVarChanged);
g_cvSandwichRange.AddChangeHook(OnConVarChanged);
g_cvSandwichCooldown.AddChangeHook(OnConVarChanged);
g_cvDebug.AddChangeHook(OnConVarChanged);
HookEvent("player_hurt", Event_PlayerHurt);
HookEvent("player_healed", Event_PlayerHealed);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("round_start", Event_RoundStart);
HookEvent("round_end", Event_RoundEnd);
// Единый хук для всех ботов
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && IsFakeClient(i))
{
SDKHook(i, SDKHook_OnTakeDamage, OnTakeDamage);
SDKHook(i, SDKHook_PostThink, OnPostThink);
}
}
if (g_bLateLoaded)
{
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && IsFakeClient(i))
{
SDKHook(i, SDKHook_OnTakeDamage, OnTakeDamage);
SDKHook(i, SDKHook_PostThink, OnPostThink);
}
}
}
// Основной таймер для обработки всех ботов (раз в 0.5 сек)
CreateTimer(0.5, Timer_ProcessBots, _, TIMER_REPEAT);
// Таймер для болталки (раз в секунду)
CreateTimer(1.0, Timer_IdleChat, _, TIMER_REPEAT);
PrintToServer("[LW BCP] Плагин загружен! Версия %s", PLUGIN_VERSION);
}
// ---------- Обработчики CVars ----------
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
g_bEnabled = g_cvEnabled.BoolValue;
g_fFriendlyHeavyChance = g_cvFriendlyHeavyChance.FloatValue;
g_fCheaterSniperChance = g_cvCheaterSniperChance.FloatValue;
g_fDemoknightChance = g_cvDemoknightChance.FloatValue;
g_fInfectionChance = g_cvInfectionChance.FloatValue;
g_fInfectionRadius = g_cvInfectionRadius.FloatValue;
g_fInfectionDuration = g_cvInfectionDuration.FloatValue;
g_fSpyChance = g_cvSpyChance.FloatValue;
g_fSandwichRange = g_cvSandwichRange.FloatValue;
g_fSandwichCooldown = g_cvSandwichCooldown.FloatValue;
g_bDebug = g_cvDebug.BoolValue;
// Если плагин отключили — возвращаем ботам нормальное оружие
if (!g_bEnabled)
{
ResetAllBots();
}
}
// ---------- Сброс оружия у всех ботов (при отключении) ----------
void ResetAllBots()
{
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && IsFakeClient(i))
{
ResetBotWeapons(i);
g_bIsFriendly[i] = false;
g_bIsCheaterSniper[i] = false;
g_bIsDemoknight[i] = false;
}
}
}
void ResetBotWeapons(int client)
{
// Удаляем всё оружие
RemoveAllWeapons(client);
// Даём стандартное оружие для класса
TFClassType class = TF2_GetPlayerClass(client);
switch (class)
{
case TFClass_Scout: GivePlayerItem(client, "tf_weapon_scattergun");
case TFClass_Soldier: GivePlayerItem(client, "tf_weapon_rocketlauncher");
case TFClass_Pyro: GivePlayerItem(client, "tf_weapon_flamethrower");
case TFClass_DemoMan: GivePlayerItem(client, "tf_weapon_grenadelauncher");
case TFClass_Heavy: GivePlayerItem(client, "tf_weapon_minigun");
case TFClass_Engineer: GivePlayerItem(client, "tf_weapon_shotgun_primary");
case TFClass_Medic: GivePlayerItem(client, "tf_weapon_syringegun_medic");
case TFClass_Sniper: GivePlayerItem(client, "tf_weapon_sniperrifle");
case TFClass_Spy: GivePlayerItem(client, "tf_weapon_revolver");
}
// Также даём пистолет/медик-ган и т.д. можно не добавлять, главное чтоб не был безоружным.
}
// ---------- События ----------
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));
if (!client || !IsFakeClient(client) || !g_bEnabled) return;
// Сброс состояний
g_bIsFriendly[client] = false;
g_bIsCheaterSniper[client] = false;
g_bIsDemoknight[client] = false;
g_bIsInfected[client] = false;
g_bIsParanoiaCooldown[client] = false;
g_iCheaterState[client] = STATE_SEARCH;
g_fLastSandwichThrow[client] = 0.0;
g_fNextThinkTime[client] = 0.0;
// Снимаем хуки, если были (на всякий случай)
SDKUnhook(client, SDKHook_PostThink, OnCheaterSniperThink); // если остались старые
TFClassType class = TF2_GetPlayerClass(client);
// 1. Дружелюбный хеви
if (class == TFClass_Heavy && GetRandomFloat(0.0, 100.0) <= g_fFriendlyHeavyChance)
{
MakeFriendlyHeavy(client);
return;
}
// 2. Читерный снайпер (любой класс, кроме Heavy)
if (class != TFClass_Heavy && GetRandomFloat(0.0, 100.0) <= g_fCheaterSniperChance)
{
MakeCheaterSniper(client);
return;
}
// 3. Демонайт (только подрывник)
if (class == TFClass_DemoMan && GetRandomFloat(0.0, 100.0) <= g_fDemoknightChance)
{
MakeDemoknight(client);
return;
}
}
public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
{
StopInfection();
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && IsFakeClient(i))
{
g_bIsInfected[i] = false;
g_fNextThinkTime[i] = 0.0;
}
}
}
public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast)
{
StopInfection();
}
public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast)
{
int victim = GetClientOfUserId(event.GetInt("userid"));
if (!victim || !IsFakeClient(victim) || !g_bEnabled || g_bIsFriendly[victim]) return;
if (GetRandomFloat(0.0, 100.0) <= 50.0 && IsPlayerAlive(victim))
{
FakeClientCommand(victim, "voicemenu 0 0"); // "Medic!"
if (g_bDebug) PrintToServer("[LW BCP] Бот %N крикнул 'Medic!'", victim);
}
}
public void Event_PlayerHealed(Event event, const char[] name, bool dontBroadcast)
{
int patient = GetClientOfUserId(event.GetInt("patient"));
if (!patient || !IsFakeClient(patient) || !g_bEnabled || g_bIsFriendly[patient]) return;
if (IsPlayerAlive(patient))
{
FakeClientCommand(patient, "voicemenu 0 1"); // "Thanks!"
if (g_bDebug) PrintToServer("[LW BCP] Бот %N сказал 'Thanks!'", patient);
}
}
// ---------- Создание специальных ботов ----------
void MakeFriendlyHeavy(int client)
{
g_bIsFriendly[client] = true;
RemoveAllWeapons(client);
int sandwich = GivePlayerItem(client, "tf_weapon_lunchbox");
if (sandwich != -1)
{
SetEntProp(sandwich, Prop_Send, "m_iItemDefinitionIndex", 42);
EquipPlayerWeapon(client, sandwich);
}
int fists = GivePlayerItem(client, "tf_weapon_fists");
if (fists != -1)
{
SetEntProp(fists, Prop_Send, "m_iItemDefinitionIndex", 0);
EquipPlayerWeapon(client, fists);
}
SetCurrentWeapon(client, fists);
g_fLastSandwichThrow[client] = 0.0;
if (GetRandomFloat(0.0, 100.0) <= g_fInfectionChance)
StartKazotskyInfection(client);
if (g_bDebug) PrintToServer("[LW BCP] Хеви %N стал дружелюбным!", client);
}
void MakeCheaterSniper(int client)
{
TF2_SetPlayerClass(client, TFClass_Sniper);
RemoveAllWeapons(client);
int rifle = GivePlayerItem(client, "tf_weapon_sniperrifle");
if (rifle != -1)
{
SetEntProp(rifle, Prop_Send, "m_iItemDefinitionIndex", 14);
EquipPlayerWeapon(client, rifle);
}
g_bIsCheaterSniper[client] = true;
g_iCheaterState[client] = STATE_SEARCH;
if (g_bDebug) PrintToServer("[LW BCP] Бот %N стал читерным снайпером!", client);
}
void MakeDemoknight(int client)
{
TF2_SetPlayerClass(client, TFClass_DemoMan);
RemoveAllWeapons(client);
int boots = GivePlayerItem(client, "tf_weapon_boots");
if (boots != -1) { SetEntProp(boots, Prop_Send, "m_iItemDefinitionIndex", 44); EquipPlayerWeapon(client, boots); }
int shield = GivePlayerItem(client, "tf_weapon_chargable_melee_weapon");
if (shield != -1) { SetEntProp(shield, Prop_Send, "m_iItemDefinitionIndex", 131); EquipPlayerWeapon(client, shield); }
int sword = GivePlayerItem(client, "tf_weapon_sword");
if (sword != -1) { SetEntProp(sword, Prop_Send, "m_iItemDefinitionIndex", 132); EquipPlayerWeapon(client, sword); }
SetCurrentWeapon(client, sword);
g_bIsDemoknight[client] = true;
if (g_bDebug) PrintToServer("[LW BCP] Бот %N стал демонайтом!", client);
}
// ---------- Танцевальная эпидемия (упрощённая) ----------
void StartKazotskyInfection(int client)
{
if (!g_bEnabled || !IsClientInGame(client) || !IsPlayerAlive(client) || g_bIsInfectionActive) return;
// Просто заставляем бота сделать насмешку (не Kazotsky, но хоть что-то)
FakeClientCommand(client, "taunt");
g_bIsInfectionActive = true;
g_hInfectionTimer = CreateTimer(g_fInfectionDuration, Timer_StopInfection);
if (g_bDebug) PrintToServer("[LW BCP] Хеви %N начал эпидемию!", client);
SpreadInfection(client);
}
void SpreadInfection(int client)
{
float pos[3]; GetClientAbsOrigin(client, pos);
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !IsPlayerAlive(i) || i == client || g_bIsInfected[i]) continue;
float tpos[3]; GetClientAbsOrigin(i, tpos);
if (GetVectorDistance(pos, tpos) <= g_fInfectionRadius)
{
g_bIsInfected[i] = true;
FakeClientCommand(i, "taunt");
if (g_bDebug) PrintToServer("[LW BCP] %N заразился!", i);
CreateTimer(0.5, Timer_SpreadInfection, GetClientUserId(i));
}
}
}
void StopInfection()
{
if (g_hInfectionTimer != null)
{
KillTimer(g_hInfectionTimer);
g_hInfectionTimer = null;
}
g_bIsInfectionActive = false;
for (int i = 1; i <= MaxClients; i++) if (IsClientInGame(i)) g_bIsInfected[i] = false;
if (g_bDebug) PrintToServer("[LW BCP] Эпидемия остановлена!");
}
public Action Timer_StopInfection(Handle timer) { StopInfection(); return Plugin_Stop; }
public Action Timer_SpreadInfection(Handle timer, int userid)
{
int client = GetClientOfUserId(userid);
if (client && IsClientInGame(client) && IsPlayerAlive(client) && g_bIsInfectionActive)
SpreadInfection(client);
return Plugin_Stop;
}
// ---------- Основной таймер для обработки всех ботов ----------
public Action Timer_ProcessBots(Handle timer)
{
if (!g_bEnabled) return Plugin_Continue;
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !IsFakeClient(i) || !IsPlayerAlive(i)) continue;
// Обрабатываем только если прошло достаточно времени с последней обработки (не чаще 0.5 сек)
if (GetGameTime() - g_fNextThinkTime[i] < 0.5) continue;
g_fNextThinkTime[i] = GetGameTime();
if (g_bIsFriendly[i])
{
OnFriendlyHeavyThink(i);
}
else if (g_bIsCheaterSniper[i])
{
OnCheaterSniperThink(i);
}
else if (g_bIsDemoknight[i])
{
// Демонайт пока без специальной логики (можно добавить позже)
}
}
return Plugin_Continue;
}
// ---------- Логика дружелюбного хеви (теперь в таймере) ----------
void OnFriendlyHeavyThink(int client)
{
if (GetGameTime() - g_fLastSandwichThrow[client] < g_fSandwichCooldown) return;
int target = FindHungryEnemy(client, g_fSandwichRange);
if (target == -1) return;
// Повернуться к цели
float targetPos[3], clientPos[3];
GetClientEyePosition(target, targetPos);
GetClientEyePosition(client, clientPos);
float vec[3]; MakeVectorFromPoints(clientPos, targetPos, vec);
float ang[3]; GetVectorAngles(vec, ang);
TeleportEntity(client, NULL_VECTOR, ang, NULL_VECTOR);
// Бросок сэндвича
FakeClientCommand(client, "+attack2");
CreateTimer(0.2, Timer_StopAttack2, GetClientUserId(client));
g_fLastSandwichThrow[client] = GetGameTime();
if (g_bDebug) PrintToServer("[LW BCP] Хеви %N кинул сэндвич в %N", client, target);
}
int FindHungryEnemy(int client, float radius)
{
float pos[3]; GetClientAbsOrigin(client, pos);
int best = -1; float bestDist = radius + 1.0;
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !IsPlayerAlive(i) || i == client || GetClientTeam(i) == GetClientTeam(client)) continue;
float tpos[3]; GetClientAbsOrigin(i, tpos);
float dist = GetVectorDistance(pos, tpos);
if (dist > radius || !IsVisibleTo(client, i)) continue;
if (IsLookingAt(i, client, 90.0) && dist < bestDist)
{
bestDist = dist;
best = i;
}
}
return best;
}
public Action Timer_StopAttack2(Handle timer, int userid)
{
int client = GetClientOfUserId(userid);
if (client && IsClientInGame(client) && IsPlayerAlive(client))
FakeClientCommand(client, "-attack2");
return Plugin_Stop;
}
// ---------- Логика читерного снайпера (теперь в таймере) ----------
void OnCheaterSniperThink(int client)
{
switch (g_iCheaterState[client])
{
case STATE_SEARCH:
{
int target = FindCheaterSniperTarget(client);
if (target != -1)
{
g_iCheaterState[client] = STATE_AIMING;
g_fAimStartTime[client] = GetGameTime();
g_fAimDuration[client] = GetRandomFloat(1.0, 3.0);
float ang[3]; GetClientEyeAngles(client, ang);
ang[0] = 89.0;
TeleportEntity(client, NULL_VECTOR, ang, NULL_VECTOR);
if (g_bDebug) PrintToServer("[LW BCP] Снайпер %N начал прицел на %N", client, target);
}
}
case STATE_AIMING:
{
float ang[3]; GetClientEyeAngles(client, ang);
ang[1] += GetRandomFloat(-15.0, 15.0);
ang[0] = 89.0 + GetRandomFloat(-2.0, 2.0);
TeleportEntity(client, NULL_VECTOR, ang, NULL_VECTOR);
if (GetGameTime() - g_fAimStartTime[client] >= g_fAimDuration[client])
{
g_iCheaterState[client] = STATE_SHOOTING;
int target = FindCheaterSniperTarget(client);
if (target != -1)
{
AimAtHeadInstant(client, target);
CreateTimer(0.15, Timer_CheaterShot, GetClientUserId(client));
}
else
{
g_iCheaterState[client] = STATE_SEARCH;
}
}
}
case STATE_SHOOTING: { /* ждём таймер */ }
}
}
int FindCheaterSniperTarget(int client)
{
int target = -1;
float bestDist = 9999.0;
float pos[3]; GetClientAbsOrigin(client, pos);
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !IsPlayerAlive(i) || i == client || GetClientTeam(i) == GetClientTeam(client)) continue;
if (!IsVisibleTo(client, i)) continue;
float tpos[3]; GetClientAbsOrigin(i, tpos);
float dist = GetVectorDistance(pos, tpos);
// Приоритет 1: дружелюбный хеви
if (g_bIsFriendly[i])
{
if (dist < bestDist) { bestDist = dist; target = i; }
// Если нашли хеви, дальше не ищем (это приоритет)
// Но может быть несколько хеви, выбираем ближайшего
continue;
}
// Приоритет 2: снайпер
if (TF2_GetPlayerClass(i) == TFClass_Sniper && dist < bestDist)
{
bestDist = dist;
target = i;
}
}
return target;
}
void AimAtHeadInstant(int client, int target)
{
float targetPos[3], clientPos[3];
GetClientEyePosition(target, targetPos);
GetClientEyePosition(client, clientPos);
float vec[3]; MakeVectorFromPoints(clientPos, targetPos, vec);
float ang[3]; GetVectorAngles(vec, ang);
TeleportEntity(client, NULL_VECTOR, ang, NULL_VECTOR);
}
public Action Timer_CheaterShot(Handle timer, int userid)
{
int client = GetClientOfUserId(userid);
if (!client || !IsClientInGame(client) || !IsPlayerAlive(client)) return Plugin_Stop;
FakeClientCommand(client, "+attack");
CreateTimer(0.1, Timer_StopAttack, userid);
g_iCheaterState[client] = STATE_SEARCH;
if (g_bDebug) PrintToServer("[LW BCP] Снайпер %N выстрелил!", client);
return Plugin_Stop;
}
public Action Timer_StopAttack(Handle timer, int userid)
{
int client = GetClientOfUserId(userid);
if (client && IsClientInGame(client) && IsPlayerAlive(client))
FakeClientCommand(client, "-attack");
return Plugin_Stop;
}
// ---------- Хуки защиты ----------
public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
{
if (!g_bEnabled || !IsFakeClient(victim) || !g_bIsFriendly[victim]) return Plugin_Continue;
if (!attacker || attacker == victim || !IsClientInGame(attacker)) return Plugin_Continue;
if (IsFakeClient(attacker))
{
if (TF2_GetPlayerClass(attacker) != TFClass_Soldier)
{
if (g_bDebug) PrintToServer("[LW BCP] Хеви %N защищён от бота %N", victim, attacker);
return Plugin_Handled;
}
}
// Игроки и солдаты-боты могут наносить урон
return Plugin_Continue;
}
public void OnPostThink(int client)
{
// Этот хук используется только для паранойи (один раз в 0.5 сек, но можно оставить)
// Для экономии мы не будем здесь делать тяжёлых операций.
if (!g_bEnabled || !IsFakeClient(client) || !IsPlayerAlive(client)) return;
if (g_bIsFriendly[client] || g_bIsCheaterSniper[client] || g_bIsDemoknight[client]) return;
// Паранойя: только для обычных ботов, раз в 5-10 секунд
if (!g_bIsParanoiaCooldown[client] && GetRandomFloat(0.0, 100.0) <= 1.0) // 1% шанс за кадр, но кадр ~0.02 сек -> не слишком часто
{
int target = FindRandomTarget(client, 1500.0);
if (target != -1 && IsLookingAt(client, target, 60.0))
{
if (GetRandomFloat(0.0, 100.0) <= 40.0)
{
FakeClientCommand(client, "voicemenu 1 1");
g_bIsParanoiaCooldown[client] = true;
CreateTimer(GetRandomFloat(5.0, 10.0), Timer_ResetParanoia, GetClientUserId(client));
}
}
}
}
public Action Timer_ResetParanoia(Handle timer, int userid)
{
int client = GetClientOfUserId(userid);
if (client && IsClientInGame(client)) g_bIsParanoiaCooldown[client] = false;
return Plugin_Stop;
}
// ---------- Вспомогательные функции ----------
void RemoveAllWeapons(int client)
{
for (int i = 0; i < 5; i++)
{
int weapon = GetPlayerWeaponSlot(client, i);
if (weapon != -1)
{
RemovePlayerItem(client, weapon);
AcceptEntityInput(weapon, "Kill");
}
}
}
void SetCurrentWeapon(int client, int weapon)
{
if (weapon != -1) SetEntPropEnt(client, Prop_Send, "m_hActiveWeapon", weapon);
}
bool IsVisibleTo(int client, int target)
{
float start[3], end[3];
GetClientEyePosition(client, start);
GetClientEyePosition(target, end);
TR_TraceRayFilter(start, end, MASK_VISIBLE, RayType_End, TraceRayFilter, target);
return !TR_DidHit();
}
public bool TraceRayFilter(int entity, int mask, int data) { return entity != data; }
int FindRandomTarget(int client, float radius)
{
int targets[MAXPLAYERS]; int count = 0;
float pos[3]; GetClientAbsOrigin(client, pos);
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !IsPlayerAlive(i) || i == client || GetClientTeam(i) != GetClientTeam(client)) continue;
float tpos[3]; GetClientAbsOrigin(i, tpos);
if (GetVectorDistance(pos, tpos) <= radius) targets[count++] = i;
}
if (count == 0) return -1;
return targets[GetRandomInt(0, count-1)];
}
bool IsLookingAt(int client, int target, float degrees)
{
float clientEyeAngles[3], clientPos[3], targetPos[3], vecToTarget[3];
GetClientEyeAngles(client, clientEyeAngles);
GetClientAbsOrigin(client, clientPos);
GetClientAbsOrigin(target, targetPos);
MakeVectorFromPoints(clientPos, targetPos, vecToTarget);
NormalizeVector(vecToTarget, vecToTarget);
float lookVec[3];
GetAngleVectors(clientEyeAngles, lookVec, NULL_VECTOR, NULL_VECTOR);
NormalizeVector(lookVec, lookVec);
float dot = GetVectorDotProduct(lookVec, vecToTarget);
float angle = RadToDeg(ArcCosine(dot));
return angle <= degrees;
}
// ---------- Таймер болталки ----------
public Action Timer_IdleChat(Handle timer)
{
if (!g_bEnabled) return Plugin_Continue;
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !IsFakeClient(i) || !IsPlayerAlive(i)) continue;
if (g_bIsFriendly[i])
{
if (GetRandomFloat(0.0, 100.0) <= 5.0)
{
int cmd = GetRandomInt(0, 1);
FakeClientCommand(i, cmd ? "voicemenu 0 1" : "voicemenu 2 0");
}
}
else if (!g_bIsCheaterSniper[i] && !g_bIsDemoknight[i])
{
if (GetRandomFloat(0.0, 100.0) <= 2.0)
{
int menu = GetRandomInt(0, 2);
int cmd = GetRandomInt(0, 4);
if (menu == 0 && cmd > 4) cmd = 4;
if (menu == 1 && cmd > 4) cmd = 4;
if (menu == 2 && cmd > 1) cmd = 1;
FakeClientCommand(i, "voicemenu %d %d", menu, cmd);
}
}
}
return Plugin_Continue;
}
// ---------- Завершение ----------
public void OnPluginEnd()
{
StopInfection();
ResetAllBots();
PrintToServer("[LW BCP] Плагин выгружен.");
}