commit 5a0c286ddc43e22dedebd3bf0295eb3519f69392 Author: deidara Date: Fri May 1 06:57:31 2026 +0300 Initial commit: custom-rounds plugin with documentation and config diff --git a/README.md b/README.md new file mode 100644 index 0000000..845031a --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# ArcaneGame Custom Rounds Core + +Плагин кастомных раундов для CS:GO серверов на SourceMod. Администраторы выбирают режим на следующий раунд через меню — и весь сервер играет в изменённых условиях. + +## Функции + +- 7 режимов кастомных раундов: + - **AWP Only** — только AWP + нож + - **AWP Only [NoScope]** — AWP без прицела (zoom заблокирован) + - **HE Only** — только гранаты HE с бесконечным боезапасом + - **Ножевой раунд** — только ножи + - **Scout Only** — только SSG-08 + нож + - **Низкая гравитация** — гравитация уменьшена (настраивается) + - **1 HP** — у всех игроков минимальное здоровье +- Сохранение и восстановление снаряжения игроков после кастомного раунда +- Блокировка покупок во время режима +- Интеграция AG Coins (награда за победу / убийства / хедшоты / выживание) +- Объявления в чат о текущем и следующем режиме +- Настройка через ConVars и AutoExecConfig + +## Зависимости + +- [SourceMod](https://www.sourcemod.net/) 1.10+ +- `agcoins_bridge` — библиотека AG Coin (входит в систему ArcaneGame) + +## Установка + +1. Скомпилировать `scripting/ArcaneGame_CustomRounds_Core.sp` +2. Положить `.smx` в `addons/sourcemod/plugins/` +3. Убедиться, что `agcoins_bridge` загружен на сервере +4. Перезапустить сервер — конфиг создастся автоматически в `cfg/sourcemod/ArcaneGame_CustomRounds_Core.cfg` + +## Команды + +| Команда | Доступ | Описание | +|---|---|---| +| `!cr` / `sm_cr` | Группы DEIDARA / TESTER или по флагу | Открыть меню кастомных раундов | + +## ConVars + +| ConVar | По умолчанию | Описание | +|---|---|---| +| `sm_cr_access_flag` | `b` | Флаг доступа для sm_cr (fallback) | +| `sm_cr_access_use_overrides` | `1` | Разрешить доступ через admin_overrides.cfg | +| `sm_cr_coins_enable` | `1` | Включить AG Coin награды | +| `sm_cr_coins_win` | `0` | Монеты за победу в кастомном раунде | +| `sm_cr_coins_kill` | `0` | Монеты за убийство | +| `sm_cr_coins_headshot` | `0` | Монеты за хедшот | +| `sm_cr_coins_survive` | `0` | Монеты за выживание победителю | +| `sm_cr_lowgravity_value` | `0.40` | Значение гравитации (Low Gravity) | +| `sm_cr_onehp_value` | `1` | Здоровье игроков в режиме 1 HP | +| `sm_cr_announce` | `1` | Показывать объявления в чат | + +## Уровни доступа + +Доступ к меню имеют: +- Группы SM **DEIDARA** и **TESTER** +- Любой игрок с флагом, указанным в `sm_cr_access_flag` (если `sm_cr_access_use_overrides = 1`) + +## Версия + +`1.1.0` — Автор: deidara.dev diff --git a/scripting/ArcaneGame_CustomRounds_Core.sp b/scripting/ArcaneGame_CustomRounds_Core.sp new file mode 100644 index 0000000..24b4f9c --- /dev/null +++ b/scripting/ArcaneGame_CustomRounds_Core.sp @@ -0,0 +1,810 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include +#include +#include "agcoins_bridge" + +#define CR_PREFIX "\x04[ArcaneGame CR]\x01" +#define CR_REASON_MAX 128 + +enum CustomRoundType +{ + CR_None = 0, + CR_AWP, + CR_NoScope, + CR_HE, + CR_Knife, + CR_Scout, + CR_LowGravity, + CR_OneHP +}; + +public Plugin myinfo = +{ + name = "ArcaneGame Custom Rounds Core", + author = "deidara.dev", + description = "Core plugin for custom rounds with AG Coin integration", + version = "1.1.0", + url = "https://deidara.dev" +}; + +CustomRoundType g_PendingRound = CR_None; +CustomRoundType g_CurrentRound = CR_None; + +bool g_RoundLive = false; +bool g_ModeApplied = false; + +bool g_PlayerParticipated[MAXPLAYERS + 1]; +int g_PlayerKills[MAXPLAYERS + 1]; +int g_PlayerHeadshots[MAXPLAYERS + 1]; + +bool g_LoadoutSaved[MAXPLAYERS + 1]; +bool g_RestoreLoadoutOnSpawn[MAXPLAYERS + 1]; +char g_SavedPrimary[MAXPLAYERS + 1][64]; +char g_SavedSecondary[MAXPLAYERS + 1][64]; +char g_SavedMelee[MAXPLAYERS + 1][64]; + +ConVar gCvarAccessFlag; +ConVar gCvarAccessUseOverrides; +ConVar gCvarCoinsEnable; +ConVar gCvarCoinsWin; +ConVar gCvarCoinsKill; +ConVar gCvarCoinsHeadshot; +ConVar gCvarCoinsSurvive; +ConVar gCvarLowGravityValue; +ConVar gCvarOneHPValue; +ConVar gCvarAnnounce; +ConVar gCvarInfiniteAmmo; + +int g_OldInfiniteAmmo = 0; + +public void OnPluginStart() +{ + RegConsoleCmd("sm_cr", Command_CR, "Opens Custom Rounds menu"); + + HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); + HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_spawn", Event_PlayerSpawn, EventHookMode_Post); + HookEvent("player_death", Event_PlayerDeath, EventHookMode_Post); + + AddCommandListener(CommandListener_BuyBlock, "buy"); + AddCommandListener(CommandListener_BuyBlock, "autobuy"); + AddCommandListener(CommandListener_BuyBlock, "rebuy"); + AddCommandListener(CommandListener_ZoomBlock, "zoom"); + + gCvarAccessFlag = CreateConVar("sm_cr_access_flag", "b", "Fallback admin flag for sm_cr access when using admin_overrides.cfg access. Example: b.", FCVAR_NONE); + gCvarAccessUseOverrides = CreateConVar("sm_cr_access_use_overrides", "1", "Allow access to sm_cr via admin_overrides.cfg in addition to DEIDARA and TESTER groups.", FCVAR_NONE, true, 0.0, true, 1.0); + gCvarCoinsEnable = CreateConVar("sm_cr_coins_enable", "1", "Enable AG Coin rewards for custom rounds.", FCVAR_NONE, true, 0.0, true, 1.0); + gCvarCoinsWin = CreateConVar("sm_cr_coins_win", "0", "Coins for winning a custom round.", FCVAR_NONE, true, 0.0); + gCvarCoinsKill = CreateConVar("sm_cr_coins_kill", "0", "Coins for each kill during a custom round.", FCVAR_NONE, true, 0.0); + gCvarCoinsHeadshot = CreateConVar("sm_cr_coins_headshot", "0", "Coins for each headshot during a custom round.", FCVAR_NONE, true, 0.0); + gCvarCoinsSurvive = CreateConVar("sm_cr_coins_survive", "0", "Coins for surviving a custom round win.", FCVAR_NONE, true, 0.0); + gCvarLowGravityValue = CreateConVar("sm_cr_lowgravity_value", "0.40", "Gravity value for Low Gravity round.", FCVAR_NONE, true, 0.1, true, 1.0); + gCvarOneHPValue = CreateConVar("sm_cr_onehp_value", "1", "Health value for One HP round.", FCVAR_NONE, true, 1.0, true, 100.0); + gCvarAnnounce = CreateConVar("sm_cr_announce", "1", "Show chat messages about custom rounds.", FCVAR_NONE, true, 0.0, true, 1.0); + gCvarInfiniteAmmo = FindConVar("sv_infinite_ammo"); + + AutoExecConfig(true, "ArcaneGame_CustomRounds_Core"); +} + +public void OnMapStart() +{ + ResetRoundState(true); +} + +public void OnClientDisconnect(int client) +{ + ResetClientStats(client); +} + +public Action Command_CR(int client, int args) +{ + if (client <= 0 || !IsClientInGame(client)) + { + ReplyToCommand(client, "[CR] Команда доступна только в игре."); + return Plugin_Handled; + } + + if (!HasCustomRoundsAccess(client)) + { + PrintToChat(client, "%s \x02У тебя нет доступа к этому меню.", CR_PREFIX); + return Plugin_Handled; + } + + OpenMainMenu(client); + return Plugin_Handled; +} + +bool HasCustomRoundsAccess(int client) +{ + if (client <= 0 || !IsClientInGame(client)) + { + return false; + } + + AdminId admin = GetUserAdmin(client); + if (admin == INVALID_ADMIN_ID) + { + return false; + } + + if (IsClientInAllowedAdminGroup(admin, "DEIDARA") || IsClientInAllowedAdminGroup(admin, "TESTER")) + { + return true; + } + + if (!GetConVarBool(gCvarAccessUseOverrides)) + { + return false; + } + + int requiredFlags = ReadFlagStringToBits(); + return CheckCommandAccess(client, "sm_cr", requiredFlags, false); +} + +bool IsClientInAllowedAdminGroup(AdminId admin, const char[] expectedGroupName) +{ + char groupName[64]; + int groupCount = GetAdminGroupCount(admin); + + for (int i = 0; i < groupCount; i++) + { + GroupId groupId = GetAdminGroup(admin, i, groupName, sizeof(groupName)); + if (groupId != INVALID_GROUP_ID && StrEqual(groupName, expectedGroupName, false)) + { + return true; + } + } + + return false; +} + +int ReadFlagStringToBits() +{ + char flagString[32]; + gCvarAccessFlag.GetString(flagString, sizeof(flagString)); + + int bits = ReadFlagString(flagString); + if (bits == 0) + { + bits = ADMFLAG_GENERIC; + } + + return bits; +} + +void OpenMainMenu(int client) +{ + Menu menu = new Menu(MenuHandler_Main); + + char currentName[64]; + char pendingName[64]; + char title[256]; + GetRoundDisplayName(g_CurrentRound, currentName, sizeof(currentName)); + GetRoundDisplayName(g_PendingRound, pendingName, sizeof(pendingName)); + Format(title, sizeof(title), "Кастомные раунды\n \nТекущий: %s\nСледующий: %s", currentName, pendingName); + menu.SetTitle(title); + + menu.AddItem("1", "AWP Only"); + menu.AddItem("2", "AWP Only [NoScope]"); + menu.AddItem("3", "Только HE [беск. гранаты]"); + menu.AddItem("4", "Ножевой раунд"); + menu.AddItem("5", "Только Scout"); + menu.AddItem("6", "Низкая гравитация"); + menu.AddItem("7", "Режим 1 HP"); + menu.AddItem("8", "Отменить следующий кастомный раунд"); + + menu.ExitButton = true; + menu.Display(client, 20); +} + +public int MenuHandler_Main(Menu menu, MenuAction action, int client, int item) +{ + if (action == MenuAction_End) + { + delete menu; + return 0; + } + + if (action != MenuAction_Select) + { + return 0; + } + + char info[8]; + menu.GetItem(item, info, sizeof(info)); + int value = StringToInt(info); + + switch (value) + { + case 1: QueueCustomRound(client, CR_AWP); + case 2: QueueCustomRound(client, CR_NoScope); + case 3: QueueCustomRound(client, CR_HE); + case 4: QueueCustomRound(client, CR_Knife); + case 5: QueueCustomRound(client, CR_Scout); + case 6: QueueCustomRound(client, CR_LowGravity); + case 7: QueueCustomRound(client, CR_OneHP); + case 8: CancelPendingRound(client); + } + + return 0; +} + +void QueueCustomRound(int client, CustomRoundType roundType) +{ + g_PendingRound = roundType; + + if (GetConVarBool(gCvarAnnounce)) + { + char roundName[64]; + GetRoundDisplayName(roundType, roundName, sizeof(roundName)); + PrintToChatAll("%s \x01Администратор \x03%N\x01 выбрал режим: \x04%s\x01. Он начнётся в следующем раунде.", CR_PREFIX, client, roundName); + } +} + +void CancelPendingRound(int client) +{ + if (g_PendingRound == CR_None) + { + PrintToChat(client, "%s \x02Сейчас нет запланированного кастомного раунда.", CR_PREFIX); + return; + } + + if (GetConVarBool(gCvarAnnounce)) + { + char roundName[64]; + GetRoundDisplayName(g_PendingRound, roundName, sizeof(roundName)); + PrintToChatAll("%s \x01Администратор \x03%N\x01 отменил режим: \x04%s\x01.", CR_PREFIX, client, roundName); + } + + g_PendingRound = CR_None; +} + +public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + g_RoundLive = true; + g_ModeApplied = false; + + ResetAllPlayerStats(); + + if (g_PendingRound != CR_None) + { + g_CurrentRound = g_PendingRound; + g_PendingRound = CR_None; + } + + if (g_CurrentRound != CR_None) + { + CreateTimer(0.2, Timer_ApplyRoundMode, _, TIMER_FLAG_NO_MAPCHANGE); + + if (GetConVarBool(gCvarAnnounce)) + { + char roundName[64]; + GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName)); + PrintToChatAll("%s \x01Кастомный раунд начался: \x04%s\x01.", CR_PREFIX, roundName); + } + } +} + +public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + int winnerTeam = event.GetInt("winner"); + + if (g_CurrentRound != CR_None) + { + AwardEndRoundCoins(winnerTeam); + PrepareLoadoutRestoreForNextSpawn(); + } + + ResetRoundState(false); + g_RoundLive = false; +} + +public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(event.GetInt("userid")); + if (client <= 0 || !IsClientInGame(client)) + { + return; + } + + if (g_RestoreLoadoutOnSpawn[client] && g_CurrentRound == CR_None) + { + CreateTimer(0.15, Timer_RestoreClientLoadout, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + return; + } + + if (g_CurrentRound == CR_None || !g_RoundLive) + { + return; + } + + CreateTimer(0.15, Timer_ApplyModeToClient, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); +} + +public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + if (g_CurrentRound == CR_None) + { + return; + } + + int attacker = GetClientOfUserId(event.GetInt("attacker")); + int victim = GetClientOfUserId(event.GetInt("userid")); + bool headshot = event.GetBool("headshot"); + + if (attacker > 0 && attacker <= MaxClients && attacker != victim && IsClientInGame(attacker)) + { + g_PlayerKills[attacker]++; + + if (headshot) + { + g_PlayerHeadshots[attacker]++; + } + + int killCoins = GetConVarInt(gCvarCoinsKill); + if (killCoins > 0) + { + char reason[CR_REASON_MAX]; + char roundName[64]; + GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName)); + Format(reason, sizeof(reason), "CustomRound Kill (%s)", roundName); + CR_GiveCoins(attacker, killCoins, reason); + } + + int headshotCoins = GetConVarInt(gCvarCoinsHeadshot); + if (headshot && headshotCoins > 0) + { + char reason[CR_REASON_MAX]; + char roundName[64]; + GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName)); + Format(reason, sizeof(reason), "CustomRound Headshot (%s)", roundName); + CR_GiveCoins(attacker, headshotCoins, reason); + } + } +} + + +public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) +{ + if (client <= 0 || client > MaxClients || !IsClientInGame(client) || !IsPlayerAlive(client)) + { + return Plugin_Continue; + } + + if (g_CurrentRound == CR_NoScope) + { + if (buttons & IN_ATTACK2) + { + buttons &= ~IN_ATTACK2; + return Plugin_Changed; + } + + if (GetEntProp(client, Prop_Send, "m_bIsScoped") != 0) + { + SetEntProp(client, Prop_Send, "m_bIsScoped", 0); + SetEntProp(client, Prop_Send, "m_iFOV", 0); + } + } + + return Plugin_Continue; +} + +public Action Timer_ApplyRoundMode(Handle timer) +{ + if (g_CurrentRound == CR_None) + { + return Plugin_Stop; + } + + g_ModeApplied = true; + + ApplyGlobalModeSettings(true); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || !IsPlayerAlive(client)) + { + continue; + } + + ApplyModeToClient(client); + } + + return Plugin_Stop; +} + +public Action Timer_ApplyModeToClient(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0 || !IsClientInGame(client) || !IsPlayerAlive(client)) + { + return Plugin_Stop; + } + + if (g_CurrentRound == CR_None) + { + return Plugin_Stop; + } + + ApplyModeToClient(client); + return Plugin_Stop; +} + +void ApplyModeToClient(int client) +{ + g_PlayerParticipated[client] = true; + SaveClientLoadout(client); + + switch (g_CurrentRound) + { + case CR_AWP: + { + StripPlayerWeapons(client); + GivePlayerItem(client, "weapon_knife"); + GivePlayerItem(client, "weapon_awp"); + SetEntProp(client, Prop_Data, "m_iHealth", 100); + SetEntityGravity(client, 1.0); + } + case CR_NoScope: + { + StripPlayerWeapons(client); + GivePlayerItem(client, "weapon_knife"); + GivePlayerItem(client, "weapon_awp"); + SetEntProp(client, Prop_Send, "m_bIsScoped", 0); + SetEntProp(client, Prop_Send, "m_iFOV", 0); + SetEntProp(client, Prop_Data, "m_iHealth", 100); + SetEntityGravity(client, 1.0); + } + case CR_HE: + { + StripPlayerWeapons(client); + GivePlayerItem(client, "weapon_knife"); + GivePlayerItem(client, "weapon_hegrenade"); + SetEntProp(client, Prop_Data, "m_iHealth", 100); + SetEntityGravity(client, 1.0); + } + case CR_Knife: + { + StripPlayerWeapons(client); + GivePlayerItem(client, "weapon_knife"); + SetEntProp(client, Prop_Data, "m_iHealth", 100); + SetEntityGravity(client, 1.0); + } + case CR_Scout: + { + StripPlayerWeapons(client); + GivePlayerItem(client, "weapon_knife"); + GivePlayerItem(client, "weapon_ssg08"); + SetEntProp(client, Prop_Data, "m_iHealth", 100); + SetEntityGravity(client, 1.0); + } + case CR_LowGravity: + { + SetEntProp(client, Prop_Data, "m_iHealth", 100); + SetEntityGravity(client, GetConVarFloat(gCvarLowGravityValue)); + } + case CR_OneHP: + { + SetEntProp(client, Prop_Data, "m_iHealth", GetConVarInt(gCvarOneHPValue)); + SetEntityGravity(client, 1.0); + } + } +} + +void GiveDefaultCombatLoadout(int client) +{ + GivePlayerItem(client, "weapon_glock"); + GivePlayerItem(client, "weapon_ak47"); + + int team = GetClientTeam(client); + if (team == CS_TEAM_CT) + { + StripPlayerWeapons(client); + GivePlayerItem(client, "weapon_knife"); + GivePlayerItem(client, "weapon_hkp2000"); + GivePlayerItem(client, "weapon_m4a1"); + } +} + +void ApplyGlobalModeSettings(bool enable) +{ + if (gCvarInfiniteAmmo == null) + { + return; + } + + if (enable) + { + g_OldInfiniteAmmo = gCvarInfiniteAmmo.IntValue; + + if (g_CurrentRound == CR_HE) + { + gCvarInfiniteAmmo.IntValue = 1; + } + } + else + { + gCvarInfiniteAmmo.IntValue = g_OldInfiniteAmmo; + } +} + +void ResetRoundState(bool fullReset) +{ + ApplyGlobalModeSettings(false); + + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client)) + { + SetEntityGravity(client, 1.0); + } + + ResetClientStats(client); + } + + g_ModeApplied = false; + g_CurrentRound = CR_None; + + if (fullReset) + { + g_PendingRound = CR_None; + } +} + +void ResetAllPlayerStats() +{ + for (int client = 1; client <= MaxClients; client++) + { + ResetClientStats(client); + } +} + +void ResetClientStats(int client) +{ + if (client < 1 || client > MaxClients) + { + return; + } + + g_PlayerParticipated[client] = false; + g_PlayerKills[client] = 0; + g_PlayerHeadshots[client] = 0; + + if (!g_RestoreLoadoutOnSpawn[client]) + { + g_LoadoutSaved[client] = false; + g_SavedPrimary[client][0] = '\0'; + g_SavedSecondary[client][0] = '\0'; + g_SavedMelee[client][0] = '\0'; + } +} + +void AwardEndRoundCoins(int winnerTeam) +{ + int winCoins = GetConVarInt(gCvarCoinsWin); + int surviveCoins = GetConVarInt(gCvarCoinsSurvive); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || !g_PlayerParticipated[client]) + { + continue; + } + + if (winnerTeam >= CS_TEAM_T && GetClientTeam(client) == winnerTeam) + { + if (winCoins > 0) + { + char reason[CR_REASON_MAX]; + char roundName[64]; + GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName)); + Format(reason, sizeof(reason), "CustomRound Win (%s)", roundName); + CR_GiveCoins(client, winCoins, reason); + } + + if (surviveCoins > 0 && IsPlayerAlive(client)) + { + char reason[CR_REASON_MAX]; + char roundName[64]; + GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName)); + Format(reason, sizeof(reason), "CustomRound Survive (%s)", roundName); + CR_GiveCoins(client, surviveCoins, reason); + } + } + } +} + + +void SaveClientLoadout(int client) +{ + if (g_LoadoutSaved[client]) + { + return; + } + + SaveWeaponClassnameFromSlot(client, 0, g_SavedPrimary[client], sizeof(g_SavedPrimary[])); + SaveWeaponClassnameFromSlot(client, 1, g_SavedSecondary[client], sizeof(g_SavedSecondary[])); + SaveWeaponClassnameFromSlot(client, 2, g_SavedMelee[client], sizeof(g_SavedMelee[])); + g_LoadoutSaved[client] = true; +} + +void SaveWeaponClassnameFromSlot(int client, int slot, char[] buffer, int maxlen) +{ + buffer[0] = '\0'; + + int weapon = GetPlayerWeaponSlot(client, slot); + if (weapon == -1 || !IsValidEntity(weapon)) + { + return; + } + + GetEntityClassname(weapon, buffer, maxlen); +} + +void PrepareLoadoutRestoreForNextSpawn() +{ + for (int client = 1; client <= MaxClients; client++) + { + if (g_LoadoutSaved[client]) + { + g_RestoreLoadoutOnSpawn[client] = true; + } + } +} + +public Action Timer_RestoreClientLoadout(Handle timer, any userid) +{ + int client = GetClientOfUserId(userid); + if (client <= 0 || !IsClientInGame(client) || !IsPlayerAlive(client)) + { + return Plugin_Stop; + } + + RestoreClientLoadout(client); + return Plugin_Stop; +} + +void RestoreClientLoadout(int client) +{ + if (!g_RestoreLoadoutOnSpawn[client]) + { + return; + } + + StripPlayerWeapons(client); + + bool gaveSomething = false; + + if (g_SavedMelee[client][0] != '\0') + { + GivePlayerItem(client, g_SavedMelee[client]); + gaveSomething = true; + } + + if (g_SavedSecondary[client][0] != '\0') + { + GivePlayerItem(client, g_SavedSecondary[client]); + gaveSomething = true; + } + + if (g_SavedPrimary[client][0] != '\0') + { + GivePlayerItem(client, g_SavedPrimary[client]); + gaveSomething = true; + } + + if (!gaveSomething) + { + GivePlayerItem(client, "weapon_knife"); + } + + g_RestoreLoadoutOnSpawn[client] = false; + g_LoadoutSaved[client] = false; + g_SavedPrimary[client][0] = '\0'; + g_SavedSecondary[client][0] = '\0'; + g_SavedMelee[client][0] = '\0'; +} + +bool CR_GiveCoins(int client, int amount, const char[] reason) +{ + if (amount <= 0) + { + return false; + } + + if (!GetConVarBool(gCvarCoinsEnable)) + { + return false; + } + + if (!IsClientInGame(client)) + { + return false; + } + + if (!AGC_IsLoaded()) + { + return false; + } + + if (!AGC_GetClientStatus(client)) + { + return false; + } + + bool result = AGC_AddCoins(client, amount, reason); + + if (result) + { + PrintToChat(client, "%s \x01Ты получил \x04%d AG Coin\x01. Причина: \x03%s\x01.", CR_PREFIX, amount, reason); + } + + return result; +} + +public Action CommandListener_BuyBlock(int client, const char[] command, int argc) +{ + if (client <= 0 || !IsClientInGame(client) || g_CurrentRound == CR_None) + { + return Plugin_Continue; + } + + switch (g_CurrentRound) + { + case CR_AWP, CR_NoScope, CR_HE, CR_Knife, CR_Scout: + { + PrintCenterText(client, "Покупка отключена во время кастомного раунда"); + return Plugin_Handled; + } + } + + return Plugin_Continue; +} + +public Action CommandListener_ZoomBlock(int client, const char[] command, int argc) +{ + if (client <= 0 || !IsClientInGame(client)) + { + return Plugin_Continue; + } + + if (g_CurrentRound == CR_NoScope) + { + return Plugin_Handled; + } + + return Plugin_Continue; +} + +void StripPlayerWeapons(int client) +{ + int weapon; + + for (int slot = 0; slot <= 4; slot++) + { + while ((weapon = GetPlayerWeaponSlot(client, slot)) != -1) + { + RemovePlayerItem(client, weapon); + AcceptEntityInput(weapon, "Kill"); + } + } +} + +void GetRoundDisplayName(CustomRoundType roundType, char[] buffer, int maxlen) +{ + switch (roundType) + { + case CR_AWP: strcopy(buffer, maxlen, "AWP Only"); + case CR_NoScope: strcopy(buffer, maxlen, "AWP Only [NoScope]"); + case CR_HE: strcopy(buffer, maxlen, "HE Only"); + case CR_Knife: strcopy(buffer, maxlen, "Ножевой раунд"); + case CR_Scout: strcopy(buffer, maxlen, "Scout Only"); + case CR_LowGravity: strcopy(buffer, maxlen, "Низкая гравитация"); + case CR_OneHP: strcopy(buffer, maxlen, "1 HP"); + default: strcopy(buffer, maxlen, "Нет"); + } +} \ No newline at end of file