#pragma semicolon 1
#pragma newdecls required
#include <sdktools_stringtables>
#include <clientprefs>
#define OVERLAYS_CONFIG "configs/kill_overlays.cfg"
#define MAX_OVERLAYS 6
static const char
PL_VER[] = "1.4.1_03.05.2025 (rewritten by Grey83)",
//Заменили TEAM на массив из двух строк
TEAM[2][] = {"Terrorist", "Counter-Terrorist"},
COLOR[][] = {"\x07FFFFFF", "\x04", "\x03", "\x01"};
public Plugin myinfo =
{
name = "Kill Overlay Manager",
version = PL_VER,
description = "Shows different overlays based on player kills in round",
author = "MODER SHOP",
url = "https://modershop.ru https://hlmod.net/resources/4477/"
}
Handle
g_hOverlayCookie;
bool
bCSS,
bLate,
bShow[MAXPLAYERS+1],
bStartMessage;
int
iColor,
iKills[MAXPLAYERS+1],
iOverlay[MAXPLAYERS+1] = {-1, ...};
char
sOverlay[2][MAX_OVERLAYS][PLATFORM_MAX_PATH];
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
bCSS = GetEngineVersion() == Engine_CSS;
bLate = late;
return APLRes_Success;
}
public void OnPluginStart()
{
LoadTranslations("killoverlay.phrases");
CreateConVar("sm_killoverlay_version", PL_VER, "Kill Overlay Manager Version", FCVAR_SPONLY|FCVAR_DONTRECORD|FCVAR_NOTIFY);
ConVar cvar = CreateConVar("sm_killoverlay_msgcolor", "1", "Color of round start message (0=white, 1=green, 2=team color)", _, true, _, true, 2.0);
cvar.AddChangeHook(CVarChange);
CVarChange(cvar, NULL_STRING, NULL_STRING);
RegConsoleCmd("sm_kill", Cmd_Overlay, "Toggle kill overlays on/off");
g_hOverlayCookie = RegClientCookie("kill_overlay_pref", "Kill Overlay Preference", CookieAccess_Protected);
HookEvent("round_freeze_end", Event_RoundStart, EventHookMode_PostNoCopy);
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
HookEvent("player_team", Event_PlayerTeam);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_death", Event_PlayerDeath);
if(!bLate) return;
for(int i; ++i <= MaxClients;) if(IsClientInGame(i) && AreClientCookiesCached(i)) OnClientCookiesCached(i);
}
public void CVarChange(ConVar cvar, const char[] oldValue, const char[] newValue)
{
iColor = cvar.IntValue;
if(!iColor && !bCSS) iColor = 3;
}
public void OnMapStart()
{
for(int i; i < MAX_OVERLAYS; i++) sOverlay[0][i][0] = sOverlay[1][i][0] = 0;
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), OVERLAYS_CONFIG);
KeyValues kv = new KeyValues("KillOverlays");
if(!kv.ImportFromFile(path))
{
LogError("Config \"%s\" not found.", path);
delete kv;
return;
}
bStartMessage = kv.GetNum("show_roundstart_message", 1) == 1;
// Используем TEAM[0] и TEAM[1] вместо TEAM[pos]
if(!PrecacheOverlay(kv, 0) && !PrecacheOverlay(kv, 1)) SetFailState("Config is empty!");
delete kv;
}
bool PrecacheOverlay(KeyValues kv, int teamIndex)
{
kv.Rewind();
//Используем TEAM[teamIndex] вместо TEAM[pos]
if(!kv.JumpToKey(TEAM[teamIndex]))
{
LogError("Can't find section \"%s\".", TEAM[teamIndex]);
return false;
}
int num;
char key[8], buffer[PLATFORM_MAX_PATH], path[PLATFORM_MAX_PATH];
for(int i; i < MAX_OVERLAYS; i++)
{
FormatEx(key, sizeof(key), "kill%d", i);
kv.GetString(key, buffer, PLATFORM_MAX_PATH);
if(!TrimString(buffer))
{
LogError("Empty string for key \"%s\".", key);
continue;
}
FormatEx(path, sizeof(path), "materials/%s.vmt", buffer);
if(!PrecacheModel(path, true))
{
LogError("Can't precache \"%s\" for \"%s\".", path, key);
continue;
}
AddFileToDownloadsTable(path);
FormatEx(path, sizeof(path), "materials/%s.vtf", buffer);
AddFileToDownloadsTable(path);
strcopy(sOverlay[teamIndex][i], sizeof(sOverlay[][]), buffer);
num++;
}
if(num < MAX_OVERLAYS) LogError("Added overlays for %s: %i/%i.", TEAM[teamIndex], num, MAX_OVERLAYS);
return !!num;
}
public void OnClientCookiesCached(int client)
{
if(IsFakeClient(client)) return;
char sValue[4];
GetClientCookie(client, g_hOverlayCookie, sValue, sizeof(sValue));
bShow[client] = sValue[0] != '0';
}
public void OnClientDisconnect(int client)
{
iKills[client] = 0;
iOverlay[client] = -1;
}
public Action Cmd_Overlay(int client, int args)
{
if(!client) return Plugin_Handled;
bShow[client] ^= true;
SetClientCookie(client, g_hOverlayCookie, bShow[client] ? "1" : "0");
if(bShow[client])
{
ShowCurrentOverlay(client);
PrintToChat(client, "\x04[KILL OVERLAY] \x01%T", "Overlay_Enabled", client, iKills[client]);
}
else
{
ClientCommand(client, "r_screenoverlay \"\"");
PrintToChat(client, "\x04[KILL OVERLAY] \x01%T", "Overlay_Disabled", client);
}
return Plugin_Handled;
}
public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
{
CreateTimer(0.5, Timer_ShowInitialOverlays);
if(bStartMessage) PrintToChatAll("\x04[KILL OVERLAY] %s%t", COLOR[iColor], "RoundStart_Message");
}
public Action Timer_ShowInitialOverlays(Handle timer)
{
for(int i; ++i <= MaxClients;) if(IsValidClient(i) && iKills[i] != iOverlay[i]) SendOverlay(i);
return Plugin_Stop;
}
public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast)
{
for(int i = 1; i <= MaxClients; i++) OnClientDisconnect(i);
}
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));
if(client && !IsFakeClient(client) && iOverlay[client] == -1) SendOverlay(client);
}
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)
{
int victim = GetClientOfUserId(event.GetInt("userid"));
if(victim) OnClientDisconnect(victim);
int attacker = GetClientOfUserId(event.GetInt("attacker"));
if(attacker && !IsFakeClient(attacker))
{
iKills[attacker]++;
if(bShow[attacker]) ShowCurrentOverlay(attacker);
}
}
void ShowCurrentOverlay(int client)
{
if(!IsValidClient(client)) return;
int team = GetClientTeam(client);
//Добавляем проверку на границы массива
if(team < 2 || team > 3)
{
ClientCommand(client, "r_screenoverlay \"\"");
iOverlay[client] = -1;
return;
}
int kills = iKills[client];
if(kills > 5) kills = 5;
if(iOverlay[client] != kills) SendOverlay(client, team, kills);
}
public void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));
if(!client) return;
iOverlay[client] = -1;
iKills[client] = 0;
SendOverlay(client, event.GetInt("team"), _, true);
}
void SendOverlay(int client, int team = -1, int kills = 0, bool timer = false)
{
if(!bShow[client]) return;
if(team == -1) team = GetClientTeam(client);
//Добавляем проверку на границы массива
if(team < 2 || team > 3 || !sOverlay[team - 2][kills][0]) return;
if(!timer)
{
ClientCommand(client, "r_screenoverlay \"%s\"", sOverlay[team - 2][kills]);
iOverlay[client] = kills > 0 ? kills : 0;
}
else CreateTimer(0.5, Timer_ShowOverlayAfterTeamChange, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
}
public Action Timer_ShowOverlayAfterTeamChange(Handle timer, int userid)
{
int client = GetClientOfUserId(userid);
if(client) SendOverlay(client);
return Plugin_Stop;
}
bool IsValidClient(int client)
{
return IsClientInGame(client) && !IsFakeClient(client);
}