v1.2.0: 3 новых режима, полный save/restore инвентаря, MOTD, логирование, кулдаун
Новые режимы: - Scout NoScope (Scout без зума) - Deagle Only - Deagle HS Only (только хедшоты наносят урон) Багфиксы: - Полное сохранение инвентаря (гранаты, броня, шлем, дефузер, патроны) - Сброс state на disconnect/connect (новый игрок не получает чужой инвентарь) - Убрано двойное применение режима в начале раунда - m_iFOV=90 вместо 0 при сбросе зума - Удалён мёртвый GiveDefaultCombatLoadout Новый функционал: - Показ MOTD-картинки во freezetime (http://37.228.88.57/cr/<slug>.html) - Логирование запусков/отмен в addons/sourcemod/logs/custom_rounds.log - Кулдаун 5 раундов для обычных админов (DEIDARA/TESTER/z обходят) - Защита от спама очереди - Команда sm_cr_status
This commit is contained in:
@@ -4,19 +4,26 @@
|
||||
|
||||
## Функции
|
||||
|
||||
- 7 режимов кастомных раундов:
|
||||
- 10 режимов кастомных раундов:
|
||||
- **AWP Only** — только AWP + нож
|
||||
- **AWP Only [NoScope]** — AWP без прицела (zoom заблокирован)
|
||||
- **AWP NoScope** — AWP без прицела (zoom заблокирован)
|
||||
- **Scout Only** — только SSG-08 + нож
|
||||
- **Scout NoScope** — Scout без прицела
|
||||
- **Deagle Only** — только Deagle + нож
|
||||
- **Deagle HS Only** — только Deagle, **в тело — 0 урона, только хедшоты**
|
||||
- **HE Only** — только гранаты HE с бесконечным боезапасом
|
||||
- **Ножевой раунд** — только ножи
|
||||
- **Scout Only** — только SSG-08 + нож
|
||||
- **Низкая гравитация** — гравитация уменьшена (настраивается)
|
||||
- **1 HP** — у всех игроков минимальное здоровье
|
||||
- Сохранение и восстановление снаряжения игроков после кастомного раунда
|
||||
- Полное сохранение и восстановление инвентаря после кастомного раунда (основное оружие + патроны, запасное + патроны, нож, **все гранаты**, **броня**, **шлем**, **дефузер**)
|
||||
- Блокировка покупок во время режима
|
||||
- Блокировка зума для NoScope-режимов (через CommandListener + OnPlayerRunCmd)
|
||||
- Показ **MOTD-картинки** во freezetime в начале кастомного раунда (HTTP-страница, авто-закрытие через 12с)
|
||||
- Кулдаун **5 раундов** для обычных админов между запусками. Группы DEIDARA / TESTER и флаг `z` (Root) обходят кулдаун
|
||||
- Логирование действий админов (выбор/отмена режима) в `addons/sourcemod/logs/custom_rounds.log`
|
||||
- Защита от спама: один и тот же режим нельзя поставить в очередь дважды подряд
|
||||
- Интеграция AG Coins (награда за победу / убийства / хедшоты / выживание)
|
||||
- Объявления в чат о текущем и следующем режиме
|
||||
- Настройка через ConVars и AutoExecConfig
|
||||
|
||||
## Зависимости
|
||||
|
||||
@@ -35,6 +42,7 @@
|
||||
| Команда | Доступ | Описание |
|
||||
|---|---|---|
|
||||
| `!cr` / `sm_cr` | Группы DEIDARA / TESTER или по флагу | Открыть меню кастомных раундов |
|
||||
| `!cr_status` / `sm_cr_status` | Любой админ с доступом к меню | Показать текущий/следующий режим и кулдаун |
|
||||
|
||||
## ConVars
|
||||
|
||||
@@ -50,13 +58,67 @@
|
||||
| `sm_cr_lowgravity_value` | `0.40` | Значение гравитации (Low Gravity) |
|
||||
| `sm_cr_onehp_value` | `1` | Здоровье игроков в режиме 1 HP |
|
||||
| `sm_cr_announce` | `1` | Показывать объявления в чат |
|
||||
| `sm_cr_show_motd` | `1` | Показывать картинку режима во freezetime |
|
||||
| `sm_cr_cooldown_rounds` | `5` | Кулдаун в раундах для обычных админов (DEIDARA/TESTER/z обходят) |
|
||||
|
||||
## Уровни доступа
|
||||
|
||||
Доступ к меню имеют:
|
||||
- Группы SM **DEIDARA** и **TESTER**
|
||||
- Любой игрок с флагом, указанным в `sm_cr_access_flag` (если `sm_cr_access_use_overrides = 1`)
|
||||
| Уровень | Кулдаун | Возможности |
|
||||
|---|---|---|
|
||||
| Группа **DEIDARA** | Нет | Все режимы, все запуски |
|
||||
| Группа **TESTER** | Нет | Все режимы, все запуски |
|
||||
| Флаг `z` (Root) | Нет | Все режимы, все запуски |
|
||||
| Любой другой админ с флагом `sm_cr_access_flag` | **5 раундов** | Все режимы, но запуск раз в 5 раундов |
|
||||
| Без флага | Нет доступа | — |
|
||||
|
||||
## MOTD-картинка во freezetime
|
||||
|
||||
Когда стартует кастомный раунд, всем игрокам отображается HTML-страница на время freezetime+несколько секунд. Страница хостится по адресу:
|
||||
|
||||
```
|
||||
http://37.228.88.57/cr/<slug>.html
|
||||
```
|
||||
|
||||
Slug-ы: `awp`, `awp-noscope`, `scout`, `scout-noscope`, `deagle`, `deagle-hs`, `he`, `knife`, `lowgrav`, `onehp`.
|
||||
|
||||
По умолчанию страницы — стилизованные баннеры с заголовком режима. Чтобы заменить на реальные изображения — отредактируйте `<тег img>` в HTML на сервере по пути `/srv/cr-images/cr/<slug>.html`. URL картинок плагин дёргает динамически по slug-у, так что менять плагин не нужно.
|
||||
|
||||
Отключить показ полностью — `sm_cr_show_motd 0`.
|
||||
|
||||
## Логирование
|
||||
|
||||
Все запуски и отмены кастомных раундов пишутся в:
|
||||
|
||||
```
|
||||
addons/sourcemod/logs/custom_rounds.log
|
||||
```
|
||||
|
||||
Формат:
|
||||
```
|
||||
[YYYY-MM-DD HH:MM:SS] [FULL|REG] АдминИмя (STEAM_X:Y:Z) -> ВЫБРАЛ/ОТМЕНИЛ режим: <название> [раунд N]
|
||||
```
|
||||
|
||||
Где:
|
||||
- `FULL` — DEIDARA / TESTER / Root (без кулдауна)
|
||||
- `REG` — обычный админ (с кулдауном)
|
||||
|
||||
## Версия
|
||||
|
||||
`1.1.0` — Автор: deidara.dev
|
||||
`1.2.0` — Автор: deidara.dev
|
||||
|
||||
### Changelog
|
||||
|
||||
- **1.2.0**
|
||||
- Добавлены режимы: **Scout NoScope**, **Deagle Only**, **Deagle HS Only** (только хедшоты)
|
||||
- Полное сохранение/восстановление инвентаря: гранаты, броня, шлем, дефузер, патроны
|
||||
- Фикс: при заходе нового игрока на освободившийся слот не выдаётся чужой инвентарь
|
||||
- Фикс: убрано двойное применение режима в начале раунда
|
||||
- Фикс: `m_iFOV` восстанавливается в 90 (а не 0)
|
||||
- Фикс: удалён мёртвый код `GiveDefaultCombatLoadout`
|
||||
- **MOTD-картинка** во freezetime при старте кастомного раунда
|
||||
- **Логирование** действий в `custom_rounds.log` (с тегом FULL/REG)
|
||||
- **Кулдаун 5 раундов** для обычных админов (DEIDARA/TESTER/Root обходят)
|
||||
- Защита от спама очереди: один и тот же режим нельзя поставить дважды
|
||||
- Команда `sm_cr_status`
|
||||
- Унифицирован стиль API (методмапы вместо `GetConVarBool`)
|
||||
- **1.1.0** — Базовая версия с 7 режимами
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma semicolon 1
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#include <sourcemod>
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
#define CR_PREFIX "\x04[ArcaneGame CR]\x01"
|
||||
#define CR_REASON_MAX 128
|
||||
#define CR_LOG_FILE "logs/custom_rounds.log"
|
||||
#define CR_MOTD_BASE_URL "http://37.228.88.57/cr/"
|
||||
#define MAX_SAVED_GRENADES 6
|
||||
|
||||
enum CustomRoundType
|
||||
{
|
||||
@@ -18,6 +21,9 @@ enum CustomRoundType
|
||||
CR_HE,
|
||||
CR_Knife,
|
||||
CR_Scout,
|
||||
CR_ScoutNoScope,
|
||||
CR_Deagle,
|
||||
CR_DeagleHS,
|
||||
CR_LowGravity,
|
||||
CR_OneHP
|
||||
};
|
||||
@@ -27,7 +33,7 @@ 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",
|
||||
version = "1.2.0",
|
||||
url = "https://deidara.dev"
|
||||
};
|
||||
|
||||
@@ -43,10 +49,27 @@ int g_PlayerHeadshots[MAXPLAYERS + 1];
|
||||
|
||||
bool g_LoadoutSaved[MAXPLAYERS + 1];
|
||||
bool g_RestoreLoadoutOnSpawn[MAXPLAYERS + 1];
|
||||
|
||||
char g_SavedPrimary[MAXPLAYERS + 1][64];
|
||||
int g_SavedPrimaryClip[MAXPLAYERS + 1];
|
||||
int g_SavedPrimaryReserve[MAXPLAYERS + 1];
|
||||
|
||||
char g_SavedSecondary[MAXPLAYERS + 1][64];
|
||||
int g_SavedSecondaryClip[MAXPLAYERS + 1];
|
||||
int g_SavedSecondaryReserve[MAXPLAYERS + 1];
|
||||
|
||||
char g_SavedMelee[MAXPLAYERS + 1][64];
|
||||
|
||||
char g_SavedGrenades[MAXPLAYERS + 1][MAX_SAVED_GRENADES][32];
|
||||
int g_SavedGrenadeCount[MAXPLAYERS + 1];
|
||||
|
||||
int g_SavedArmor[MAXPLAYERS + 1];
|
||||
bool g_SavedHelmet[MAXPLAYERS + 1];
|
||||
bool g_SavedDefuser[MAXPLAYERS + 1];
|
||||
|
||||
int g_RoundCounter = 0;
|
||||
int g_LastCustomRoundNumber = -1000;
|
||||
|
||||
ConVar gCvarAccessFlag;
|
||||
ConVar gCvarAccessUseOverrides;
|
||||
ConVar gCvarCoinsEnable;
|
||||
@@ -57,6 +80,8 @@ ConVar gCvarCoinsSurvive;
|
||||
ConVar gCvarLowGravityValue;
|
||||
ConVar gCvarOneHPValue;
|
||||
ConVar gCvarAnnounce;
|
||||
ConVar gCvarShowMOTD;
|
||||
ConVar gCvarCooldownRounds;
|
||||
ConVar gCvarInfiniteAmmo;
|
||||
|
||||
int g_OldInfiniteAmmo = 0;
|
||||
@@ -64,6 +89,7 @@ int g_OldInfiniteAmmo = 0;
|
||||
public void OnPluginStart()
|
||||
{
|
||||
RegConsoleCmd("sm_cr", Command_CR, "Opens Custom Rounds menu");
|
||||
RegConsoleCmd("sm_cr_status", Command_CR_Status, "Show current/pending custom round and cooldown status");
|
||||
|
||||
HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
|
||||
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
|
||||
@@ -85,19 +111,67 @@ public void OnPluginStart()
|
||||
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);
|
||||
gCvarShowMOTD = CreateConVar("sm_cr_show_motd", "1", "Show MOTD image during freezetime when a custom round starts.", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
gCvarCooldownRounds = CreateConVar("sm_cr_cooldown_rounds", "5", "Cooldown in rounds for regular admins between custom rounds. DEIDARA/TESTER and ROOT bypass.", FCVAR_NONE, true, 0.0);
|
||||
gCvarInfiniteAmmo = FindConVar("sv_infinite_ammo");
|
||||
|
||||
AutoExecConfig(true, "ArcaneGame_CustomRounds_Core");
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (IsClientInGame(i))
|
||||
{
|
||||
OnClientPutInServer(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
FullResetClientState(client);
|
||||
SDKHook(client, SDKHook_TraceAttack, Hook_TraceAttack);
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
FullResetClientState(client);
|
||||
}
|
||||
|
||||
void FullResetClientState(int client)
|
||||
{
|
||||
if (client < 1 || client > MaxClients)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_PlayerParticipated[client] = false;
|
||||
g_PlayerKills[client] = 0;
|
||||
g_PlayerHeadshots[client] = 0;
|
||||
|
||||
g_LoadoutSaved[client] = false;
|
||||
g_RestoreLoadoutOnSpawn[client] = false;
|
||||
g_SavedPrimary[client][0] = '\0';
|
||||
g_SavedPrimaryClip[client] = 0;
|
||||
g_SavedPrimaryReserve[client] = 0;
|
||||
g_SavedSecondary[client][0] = '\0';
|
||||
g_SavedSecondaryClip[client] = 0;
|
||||
g_SavedSecondaryReserve[client] = 0;
|
||||
g_SavedMelee[client][0] = '\0';
|
||||
g_SavedGrenadeCount[client] = 0;
|
||||
for (int i = 0; i < MAX_SAVED_GRENADES; i++)
|
||||
{
|
||||
g_SavedGrenades[client][i][0] = '\0';
|
||||
}
|
||||
g_SavedArmor[client] = 0;
|
||||
g_SavedHelmet[client] = false;
|
||||
g_SavedDefuser[client] = false;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
ResetRoundState(true);
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
ResetClientStats(client);
|
||||
g_RoundCounter = 0;
|
||||
g_LastCustomRoundNumber = -1000;
|
||||
}
|
||||
|
||||
public Action Command_CR(int client, int args)
|
||||
@@ -118,6 +192,35 @@ public Action Command_CR(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_CR_Status(int client, int args)
|
||||
{
|
||||
if (client <= 0 || !IsClientInGame(client))
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char currentName[64], pendingName[64];
|
||||
GetRoundDisplayName(g_CurrentRound, currentName, sizeof(currentName));
|
||||
GetRoundDisplayName(g_PendingRound, pendingName, sizeof(pendingName));
|
||||
|
||||
PrintToChat(client, "%s Текущий: \x04%s\x01, следующий: \x04%s\x01.", CR_PREFIX, currentName, pendingName);
|
||||
|
||||
if (HasCustomRoundsAccess(client))
|
||||
{
|
||||
int remaining = GetCooldownRemaining(client);
|
||||
if (remaining > 0)
|
||||
{
|
||||
PrintToChat(client, "%s До конца кулдауна: \x04%d\x01 раунд(ов).", CR_PREFIX, remaining);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintToChat(client, "%s Кулдаун: нет.", CR_PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
bool HasCustomRoundsAccess(int client)
|
||||
{
|
||||
if (client <= 0 || !IsClientInGame(client))
|
||||
@@ -136,7 +239,7 @@ bool HasCustomRoundsAccess(int client)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!GetConVarBool(gCvarAccessUseOverrides))
|
||||
if (!gCvarAccessUseOverrides.BoolValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -145,6 +248,54 @@ bool HasCustomRoundsAccess(int client)
|
||||
return CheckCommandAccess(client, "sm_cr", requiredFlags, false);
|
||||
}
|
||||
|
||||
bool IsCooldownExempt(int client)
|
||||
{
|
||||
if (client <= 0 || !IsClientInGame(client))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AdminId admin = GetUserAdmin(client);
|
||||
if (admin == INVALID_ADMIN_ID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((GetUserFlagBits(client) & ADMFLAG_ROOT) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsClientInAllowedAdminGroup(admin, "DEIDARA") || IsClientInAllowedAdminGroup(admin, "TESTER"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int GetCooldownRemaining(int client)
|
||||
{
|
||||
if (IsCooldownExempt(client))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cd = gCvarCooldownRounds.IntValue;
|
||||
if (cd <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int passed = g_RoundCounter - g_LastCustomRoundNumber;
|
||||
if (passed >= cd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cd - passed;
|
||||
}
|
||||
|
||||
bool IsClientInAllowedAdminGroup(AdminId admin, const char[] expectedGroupName)
|
||||
{
|
||||
char groupName[64];
|
||||
@@ -180,22 +331,32 @@ void OpenMainMenu(int client)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_Main);
|
||||
|
||||
char currentName[64];
|
||||
char pendingName[64];
|
||||
char title[256];
|
||||
char currentName[64], pendingName[64], 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);
|
||||
|
||||
int cooldown = GetCooldownRemaining(client);
|
||||
if (cooldown > 0)
|
||||
{
|
||||
Format(title, sizeof(title), "Кастомные раунды\n \nТекущий: %s\nСледующий: %s\nКулдаун: %d р.", currentName, pendingName, cooldown);
|
||||
}
|
||||
else
|
||||
{
|
||||
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.AddItem("2", "AWP NoScope");
|
||||
menu.AddItem("3", "Scout Only");
|
||||
menu.AddItem("4", "Scout NoScope");
|
||||
menu.AddItem("5", "Deagle Only");
|
||||
menu.AddItem("6", "Deagle HS Only");
|
||||
menu.AddItem("7", "Только HE [беск. гранаты]");
|
||||
menu.AddItem("8", "Ножевой раунд");
|
||||
menu.AddItem("9", "Низкая гравитация");
|
||||
menu.AddItem("10", "Режим 1 HP");
|
||||
menu.AddItem("c", "Отменить следующий кастомный раунд");
|
||||
|
||||
menu.ExitButton = true;
|
||||
menu.Display(client, 20);
|
||||
@@ -216,18 +377,33 @@ public int MenuHandler_Main(Menu menu, MenuAction action, int client, int item)
|
||||
|
||||
char info[8];
|
||||
menu.GetItem(item, info, sizeof(info));
|
||||
|
||||
if (StrEqual(info, "c"))
|
||||
{
|
||||
CancelPendingRound(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int value = StringToInt(info);
|
||||
CustomRoundType selected = CR_None;
|
||||
|
||||
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);
|
||||
case 1: selected = CR_AWP;
|
||||
case 2: selected = CR_NoScope;
|
||||
case 3: selected = CR_Scout;
|
||||
case 4: selected = CR_ScoutNoScope;
|
||||
case 5: selected = CR_Deagle;
|
||||
case 6: selected = CR_DeagleHS;
|
||||
case 7: selected = CR_HE;
|
||||
case 8: selected = CR_Knife;
|
||||
case 9: selected = CR_LowGravity;
|
||||
case 10: selected = CR_OneHP;
|
||||
}
|
||||
|
||||
if (selected != CR_None)
|
||||
{
|
||||
QueueCustomRound(client, selected);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -235,14 +411,30 @@ public int MenuHandler_Main(Menu menu, MenuAction action, int client, int item)
|
||||
|
||||
void QueueCustomRound(int client, CustomRoundType roundType)
|
||||
{
|
||||
int cooldown = GetCooldownRemaining(client);
|
||||
if (cooldown > 0)
|
||||
{
|
||||
PrintToChat(client, "%s \x02Кулдаун: ещё \x04%d\x01\x02 раунд(ов) до запуска нового кастомного раунда.", CR_PREFIX, cooldown);
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_PendingRound == roundType)
|
||||
{
|
||||
PrintToChat(client, "%s \x02Этот режим уже в очереди.", CR_PREFIX);
|
||||
return;
|
||||
}
|
||||
|
||||
g_PendingRound = roundType;
|
||||
|
||||
if (GetConVarBool(gCvarAnnounce))
|
||||
char roundName[64];
|
||||
GetRoundDisplayName(roundType, roundName, sizeof(roundName));
|
||||
|
||||
if (gCvarAnnounce.BoolValue)
|
||||
{
|
||||
char roundName[64];
|
||||
GetRoundDisplayName(roundType, roundName, sizeof(roundName));
|
||||
PrintToChatAll("%s \x01Администратор \x03%N\x01 выбрал режим: \x04%s\x01. Он начнётся в следующем раунде.", CR_PREFIX, client, roundName);
|
||||
}
|
||||
|
||||
LogCRAction(client, "ВЫБРАЛ режим: %s", roundName);
|
||||
}
|
||||
|
||||
void CancelPendingRound(int client)
|
||||
@@ -253,13 +445,16 @@ void CancelPendingRound(int client)
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetConVarBool(gCvarAnnounce))
|
||||
char roundName[64];
|
||||
GetRoundDisplayName(g_PendingRound, roundName, sizeof(roundName));
|
||||
|
||||
if (gCvarAnnounce.BoolValue)
|
||||
{
|
||||
char roundName[64];
|
||||
GetRoundDisplayName(g_PendingRound, roundName, sizeof(roundName));
|
||||
PrintToChatAll("%s \x01Администратор \x03%N\x01 отменил режим: \x04%s\x01.", CR_PREFIX, client, roundName);
|
||||
}
|
||||
|
||||
LogCRAction(client, "ОТМЕНИЛ режим: %s", roundName);
|
||||
|
||||
g_PendingRound = CR_None;
|
||||
}
|
||||
|
||||
@@ -267,6 +462,7 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
g_RoundLive = true;
|
||||
g_ModeApplied = false;
|
||||
g_RoundCounter++;
|
||||
|
||||
ResetAllPlayerStats();
|
||||
|
||||
@@ -274,18 +470,24 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
g_CurrentRound = g_PendingRound;
|
||||
g_PendingRound = CR_None;
|
||||
g_LastCustomRoundNumber = g_RoundCounter;
|
||||
}
|
||||
|
||||
if (g_CurrentRound != CR_None)
|
||||
{
|
||||
CreateTimer(0.2, Timer_ApplyRoundMode, _, TIMER_FLAG_NO_MAPCHANGE);
|
||||
|
||||
if (GetConVarBool(gCvarAnnounce))
|
||||
if (gCvarAnnounce.BoolValue)
|
||||
{
|
||||
char roundName[64];
|
||||
GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName));
|
||||
PrintToChatAll("%s \x01Кастомный раунд начался: \x04%s\x01.", CR_PREFIX, roundName);
|
||||
}
|
||||
|
||||
if (gCvarShowMOTD.BoolValue)
|
||||
{
|
||||
ShowFreezeImageToAll(g_CurrentRound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +524,13 @@ public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast
|
||||
return;
|
||||
}
|
||||
|
||||
// Применяем мод per-spawn ТОЛЬКО если глобальный таймер уже отработал (опоздавший игрок).
|
||||
// Иначе глобальный Timer_ApplyRoundMode сам всех обработает — без двойного применения.
|
||||
if (!g_ModeApplied)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateTimer(0.15, Timer_ApplyModeToClient, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
@@ -345,21 +554,19 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast
|
||||
g_PlayerHeadshots[attacker]++;
|
||||
}
|
||||
|
||||
int killCoins = GetConVarInt(gCvarCoinsKill);
|
||||
int killCoins = gCvarCoinsKill.IntValue;
|
||||
if (killCoins > 0)
|
||||
{
|
||||
char reason[CR_REASON_MAX];
|
||||
char roundName[64];
|
||||
char reason[CR_REASON_MAX], 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);
|
||||
int headshotCoins = gCvarCoinsHeadshot.IntValue;
|
||||
if (headshot && headshotCoins > 0)
|
||||
{
|
||||
char reason[CR_REASON_MAX];
|
||||
char roundName[64];
|
||||
char reason[CR_REASON_MAX], roundName[64];
|
||||
GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName));
|
||||
Format(reason, sizeof(reason), "CustomRound Headshot (%s)", roundName);
|
||||
CR_GiveCoins(attacker, headshotCoins, reason);
|
||||
@@ -367,7 +574,6 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
@@ -375,7 +581,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if (g_CurrentRound == CR_NoScope)
|
||||
if (g_CurrentRound == CR_NoScope || g_CurrentRound == CR_ScoutNoScope)
|
||||
{
|
||||
if (buttons & IN_ATTACK2)
|
||||
{
|
||||
@@ -386,13 +592,35 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
if (GetEntProp(client, Prop_Send, "m_bIsScoped") != 0)
|
||||
{
|
||||
SetEntProp(client, Prop_Send, "m_bIsScoped", 0);
|
||||
SetEntProp(client, Prop_Send, "m_iFOV", 0);
|
||||
SetEntProp(client, Prop_Send, "m_iFOV", 90);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Hook_TraceAttack(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &ammotype, int hitbox, int hitgroup)
|
||||
{
|
||||
if (g_CurrentRound != CR_DeagleHS)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if (attacker <= 0 || attacker > MaxClients)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
// HITGROUP_HEAD = 1; всё остальное — 0 урона
|
||||
if (hitgroup != 1)
|
||||
{
|
||||
damage = 0.0;
|
||||
return Plugin_Changed;
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Timer_ApplyRoundMode(Handle timer)
|
||||
{
|
||||
if (g_CurrentRound == CR_None)
|
||||
@@ -401,7 +629,6 @@ public Action Timer_ApplyRoundMode(Handle timer)
|
||||
}
|
||||
|
||||
g_ModeApplied = true;
|
||||
|
||||
ApplyGlobalModeSettings(true);
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
@@ -410,7 +637,6 @@ public Action Timer_ApplyRoundMode(Handle timer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ApplyModeToClient(client);
|
||||
}
|
||||
|
||||
@@ -455,7 +681,7 @@ void ApplyModeToClient(int 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_Send, "m_iFOV", 90);
|
||||
SetEntProp(client, Prop_Data, "m_iHealth", 100);
|
||||
SetEntityGravity(client, 1.0);
|
||||
}
|
||||
@@ -482,34 +708,37 @@ void ApplyModeToClient(int client)
|
||||
SetEntProp(client, Prop_Data, "m_iHealth", 100);
|
||||
SetEntityGravity(client, 1.0);
|
||||
}
|
||||
case CR_ScoutNoScope:
|
||||
{
|
||||
StripPlayerWeapons(client);
|
||||
GivePlayerItem(client, "weapon_knife");
|
||||
GivePlayerItem(client, "weapon_ssg08");
|
||||
SetEntProp(client, Prop_Send, "m_bIsScoped", 0);
|
||||
SetEntProp(client, Prop_Send, "m_iFOV", 90);
|
||||
SetEntProp(client, Prop_Data, "m_iHealth", 100);
|
||||
SetEntityGravity(client, 1.0);
|
||||
}
|
||||
case CR_Deagle, CR_DeagleHS:
|
||||
{
|
||||
StripPlayerWeapons(client);
|
||||
GivePlayerItem(client, "weapon_knife");
|
||||
GivePlayerItem(client, "weapon_deagle");
|
||||
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));
|
||||
SetEntityGravity(client, gCvarLowGravityValue.FloatValue);
|
||||
}
|
||||
case CR_OneHP:
|
||||
{
|
||||
SetEntProp(client, Prop_Data, "m_iHealth", GetConVarInt(gCvarOneHPValue));
|
||||
SetEntProp(client, Prop_Data, "m_iHealth", gCvarOneHPValue.IntValue);
|
||||
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)
|
||||
@@ -542,7 +771,6 @@ void ResetRoundState(bool fullReset)
|
||||
{
|
||||
SetEntityGravity(client, 1.0);
|
||||
}
|
||||
|
||||
ResetClientStats(client);
|
||||
}
|
||||
|
||||
@@ -580,13 +808,21 @@ void ResetClientStats(int client)
|
||||
g_SavedPrimary[client][0] = '\0';
|
||||
g_SavedSecondary[client][0] = '\0';
|
||||
g_SavedMelee[client][0] = '\0';
|
||||
g_SavedGrenadeCount[client] = 0;
|
||||
for (int i = 0; i < MAX_SAVED_GRENADES; i++)
|
||||
{
|
||||
g_SavedGrenades[client][i][0] = '\0';
|
||||
}
|
||||
g_SavedArmor[client] = 0;
|
||||
g_SavedHelmet[client] = false;
|
||||
g_SavedDefuser[client] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AwardEndRoundCoins(int winnerTeam)
|
||||
{
|
||||
int winCoins = GetConVarInt(gCvarCoinsWin);
|
||||
int surviveCoins = GetConVarInt(gCvarCoinsSurvive);
|
||||
int winCoins = gCvarCoinsWin.IntValue;
|
||||
int surviveCoins = gCvarCoinsSurvive.IntValue;
|
||||
|
||||
for (int client = 1; client <= MaxClients; client++)
|
||||
{
|
||||
@@ -599,8 +835,7 @@ void AwardEndRoundCoins(int winnerTeam)
|
||||
{
|
||||
if (winCoins > 0)
|
||||
{
|
||||
char reason[CR_REASON_MAX];
|
||||
char roundName[64];
|
||||
char reason[CR_REASON_MAX], roundName[64];
|
||||
GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName));
|
||||
Format(reason, sizeof(reason), "CustomRound Win (%s)", roundName);
|
||||
CR_GiveCoins(client, winCoins, reason);
|
||||
@@ -608,8 +843,7 @@ void AwardEndRoundCoins(int winnerTeam)
|
||||
|
||||
if (surviveCoins > 0 && IsPlayerAlive(client))
|
||||
{
|
||||
char reason[CR_REASON_MAX];
|
||||
char roundName[64];
|
||||
char reason[CR_REASON_MAX], roundName[64];
|
||||
GetRoundDisplayName(g_CurrentRound, roundName, sizeof(roundName));
|
||||
Format(reason, sizeof(reason), "CustomRound Survive (%s)", roundName);
|
||||
CR_GiveCoins(client, surviveCoins, reason);
|
||||
@@ -626,15 +860,35 @@ void SaveClientLoadout(int 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[]));
|
||||
SaveWeaponSlot(client, 0, g_SavedPrimary[client], sizeof(g_SavedPrimary[]),
|
||||
g_SavedPrimaryClip[client], g_SavedPrimaryReserve[client]);
|
||||
SaveWeaponSlot(client, 1, g_SavedSecondary[client], sizeof(g_SavedSecondary[]),
|
||||
g_SavedSecondaryClip[client], g_SavedSecondaryReserve[client]);
|
||||
|
||||
int meleeWeapon = GetPlayerWeaponSlot(client, 2);
|
||||
if (meleeWeapon != -1 && IsValidEntity(meleeWeapon))
|
||||
{
|
||||
GetEntityClassname(meleeWeapon, g_SavedMelee[client], sizeof(g_SavedMelee[]));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_SavedMelee[client][0] = '\0';
|
||||
}
|
||||
|
||||
SaveAllGrenades(client);
|
||||
|
||||
g_SavedArmor[client] = GetEntProp(client, Prop_Send, "m_ArmorValue");
|
||||
g_SavedHelmet[client] = GetEntProp(client, Prop_Send, "m_bHasHelmet") != 0;
|
||||
g_SavedDefuser[client] = GetEntProp(client, Prop_Send, "m_bHasDefuser") != 0;
|
||||
|
||||
g_LoadoutSaved[client] = true;
|
||||
}
|
||||
|
||||
void SaveWeaponClassnameFromSlot(int client, int slot, char[] buffer, int maxlen)
|
||||
void SaveWeaponSlot(int client, int slot, char[] classBuf, int classBufLen, int &clipOut, int &reserveOut)
|
||||
{
|
||||
buffer[0] = '\0';
|
||||
classBuf[0] = '\0';
|
||||
clipOut = 0;
|
||||
reserveOut = 0;
|
||||
|
||||
int weapon = GetPlayerWeaponSlot(client, slot);
|
||||
if (weapon == -1 || !IsValidEntity(weapon))
|
||||
@@ -642,7 +896,59 @@ void SaveWeaponClassnameFromSlot(int client, int slot, char[] buffer, int maxlen
|
||||
return;
|
||||
}
|
||||
|
||||
GetEntityClassname(weapon, buffer, maxlen);
|
||||
GetEntityClassname(weapon, classBuf, classBufLen);
|
||||
|
||||
if (HasEntProp(weapon, Prop_Send, "m_iClip1"))
|
||||
{
|
||||
clipOut = GetEntProp(weapon, Prop_Send, "m_iClip1");
|
||||
}
|
||||
if (HasEntProp(weapon, Prop_Send, "m_iPrimaryReserveAmmoCount"))
|
||||
{
|
||||
reserveOut = GetEntProp(weapon, Prop_Send, "m_iPrimaryReserveAmmoCount");
|
||||
}
|
||||
}
|
||||
|
||||
void SaveAllGrenades(int client)
|
||||
{
|
||||
g_SavedGrenadeCount[client] = 0;
|
||||
|
||||
int offset = FindSendPropInfo("CCSPlayer", "m_hMyWeapons");
|
||||
if (offset <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char classname[32];
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int weapon = GetEntDataEnt2(client, offset + (i * 4));
|
||||
if (weapon == -1 || !IsValidEntity(weapon))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GetEntityClassname(weapon, classname, sizeof(classname));
|
||||
if (!IsGrenadeClassname(classname))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_SavedGrenadeCount[client] < MAX_SAVED_GRENADES)
|
||||
{
|
||||
strcopy(g_SavedGrenades[client][g_SavedGrenadeCount[client]], 32, classname);
|
||||
g_SavedGrenadeCount[client]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsGrenadeClassname(const char[] classname)
|
||||
{
|
||||
return StrEqual(classname, "weapon_hegrenade")
|
||||
|| StrEqual(classname, "weapon_flashbang")
|
||||
|| StrEqual(classname, "weapon_smokegrenade")
|
||||
|| StrEqual(classname, "weapon_molotov")
|
||||
|| StrEqual(classname, "weapon_incgrenade")
|
||||
|| StrEqual(classname, "weapon_decoy");
|
||||
}
|
||||
|
||||
void PrepareLoadoutRestoreForNextSpawn()
|
||||
@@ -687,16 +993,54 @@ void RestoreClientLoadout(int client)
|
||||
|
||||
if (g_SavedSecondary[client][0] != '\0')
|
||||
{
|
||||
GivePlayerItem(client, g_SavedSecondary[client]);
|
||||
int wep = GivePlayerItem(client, g_SavedSecondary[client]);
|
||||
if (wep != -1 && IsValidEntity(wep))
|
||||
{
|
||||
if (HasEntProp(wep, Prop_Send, "m_iClip1"))
|
||||
{
|
||||
SetEntProp(wep, Prop_Send, "m_iClip1", g_SavedSecondaryClip[client]);
|
||||
}
|
||||
if (HasEntProp(wep, Prop_Send, "m_iPrimaryReserveAmmoCount"))
|
||||
{
|
||||
SetEntProp(wep, Prop_Send, "m_iPrimaryReserveAmmoCount", g_SavedSecondaryReserve[client]);
|
||||
}
|
||||
}
|
||||
gaveSomething = true;
|
||||
}
|
||||
|
||||
if (g_SavedPrimary[client][0] != '\0')
|
||||
{
|
||||
GivePlayerItem(client, g_SavedPrimary[client]);
|
||||
int wep = GivePlayerItem(client, g_SavedPrimary[client]);
|
||||
if (wep != -1 && IsValidEntity(wep))
|
||||
{
|
||||
if (HasEntProp(wep, Prop_Send, "m_iClip1"))
|
||||
{
|
||||
SetEntProp(wep, Prop_Send, "m_iClip1", g_SavedPrimaryClip[client]);
|
||||
}
|
||||
if (HasEntProp(wep, Prop_Send, "m_iPrimaryReserveAmmoCount"))
|
||||
{
|
||||
SetEntProp(wep, Prop_Send, "m_iPrimaryReserveAmmoCount", g_SavedPrimaryReserve[client]);
|
||||
}
|
||||
}
|
||||
gaveSomething = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < g_SavedGrenadeCount[client]; i++)
|
||||
{
|
||||
if (g_SavedGrenades[client][i][0] != '\0')
|
||||
{
|
||||
GivePlayerItem(client, g_SavedGrenades[client][i]);
|
||||
gaveSomething = true;
|
||||
}
|
||||
}
|
||||
|
||||
SetEntProp(client, Prop_Send, "m_ArmorValue", g_SavedArmor[client]);
|
||||
SetEntProp(client, Prop_Send, "m_bHasHelmet", g_SavedHelmet[client] ? 1 : 0);
|
||||
if (GetClientTeam(client) == CS_TEAM_CT)
|
||||
{
|
||||
SetEntProp(client, Prop_Send, "m_bHasDefuser", g_SavedDefuser[client] ? 1 : 0);
|
||||
}
|
||||
|
||||
if (!gaveSomething)
|
||||
{
|
||||
GivePlayerItem(client, "weapon_knife");
|
||||
@@ -707,6 +1051,14 @@ void RestoreClientLoadout(int client)
|
||||
g_SavedPrimary[client][0] = '\0';
|
||||
g_SavedSecondary[client][0] = '\0';
|
||||
g_SavedMelee[client][0] = '\0';
|
||||
g_SavedGrenadeCount[client] = 0;
|
||||
for (int i = 0; i < MAX_SAVED_GRENADES; i++)
|
||||
{
|
||||
g_SavedGrenades[client][i][0] = '\0';
|
||||
}
|
||||
g_SavedArmor[client] = 0;
|
||||
g_SavedHelmet[client] = false;
|
||||
g_SavedDefuser[client] = false;
|
||||
}
|
||||
|
||||
bool CR_GiveCoins(int client, int amount, const char[] reason)
|
||||
@@ -716,7 +1068,7 @@ bool CR_GiveCoins(int client, int amount, const char[] reason)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetConVarBool(gCvarCoinsEnable))
|
||||
if (!gCvarCoinsEnable.BoolValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -755,7 +1107,7 @@ public Action CommandListener_BuyBlock(int client, const char[] command, int arg
|
||||
|
||||
switch (g_CurrentRound)
|
||||
{
|
||||
case CR_AWP, CR_NoScope, CR_HE, CR_Knife, CR_Scout:
|
||||
case CR_AWP, CR_NoScope, CR_HE, CR_Knife, CR_Scout, CR_ScoutNoScope, CR_Deagle, CR_DeagleHS:
|
||||
{
|
||||
PrintCenterText(client, "Покупка отключена во время кастомного раунда");
|
||||
return Plugin_Handled;
|
||||
@@ -772,7 +1124,7 @@ public Action CommandListener_ZoomBlock(int client, const char[] command, int ar
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if (g_CurrentRound == CR_NoScope)
|
||||
if (g_CurrentRound == CR_NoScope || g_CurrentRound == CR_ScoutNoScope)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
@@ -799,12 +1151,105 @@ 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_NoScope: strcopy(buffer, maxlen, "AWP 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_ScoutNoScope: strcopy(buffer, maxlen, "Scout NoScope");
|
||||
case CR_Deagle: strcopy(buffer, maxlen, "Deagle Only");
|
||||
case CR_DeagleHS: strcopy(buffer, maxlen, "Deagle HS Only");
|
||||
case CR_LowGravity: strcopy(buffer, maxlen, "Низкая гравитация");
|
||||
case CR_OneHP: strcopy(buffer, maxlen, "1 HP");
|
||||
default: strcopy(buffer, maxlen, "Нет");
|
||||
}
|
||||
}
|
||||
|
||||
void GetRoundUrlSlug(CustomRoundType roundType, char[] buffer, int maxlen)
|
||||
{
|
||||
switch (roundType)
|
||||
{
|
||||
case CR_AWP: strcopy(buffer, maxlen, "awp");
|
||||
case CR_NoScope: strcopy(buffer, maxlen, "awp-noscope");
|
||||
case CR_HE: strcopy(buffer, maxlen, "he");
|
||||
case CR_Knife: strcopy(buffer, maxlen, "knife");
|
||||
case CR_Scout: strcopy(buffer, maxlen, "scout");
|
||||
case CR_ScoutNoScope: strcopy(buffer, maxlen, "scout-noscope");
|
||||
case CR_Deagle: strcopy(buffer, maxlen, "deagle");
|
||||
case CR_DeagleHS: strcopy(buffer, maxlen, "deagle-hs");
|
||||
case CR_LowGravity: strcopy(buffer, maxlen, "lowgrav");
|
||||
case CR_OneHP: strcopy(buffer, maxlen, "onehp");
|
||||
default: strcopy(buffer, maxlen, "");
|
||||
}
|
||||
}
|
||||
|
||||
void ShowFreezeImageToAll(CustomRoundType mode)
|
||||
{
|
||||
char slug[32];
|
||||
GetRoundUrlSlug(mode, slug, sizeof(slug));
|
||||
if (slug[0] == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char url[256];
|
||||
Format(url, sizeof(url), "%s%s.html", CR_MOTD_BASE_URL, slug);
|
||||
|
||||
char title[64];
|
||||
GetRoundDisplayName(mode, title, sizeof(title));
|
||||
|
||||
KeyValues kv = new KeyValues("data");
|
||||
kv.SetString("title", title);
|
||||
kv.SetNum("type", 2); // MOTDPANEL_TYPE_URL
|
||||
kv.SetString("msg", url);
|
||||
kv.SetNum("customsvr", 1);
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if (!IsClientInGame(i) || IsFakeClient(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ShowVGUIPanel(i, "info", kv, true);
|
||||
}
|
||||
|
||||
delete kv;
|
||||
}
|
||||
|
||||
void LogCRAction(int client, const char[] format, any ...)
|
||||
{
|
||||
char message[256];
|
||||
VFormat(message, sizeof(message), format, 3);
|
||||
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, path, sizeof(path), CR_LOG_FILE);
|
||||
|
||||
char timestamp[32];
|
||||
FormatTime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
char adminName[MAX_NAME_LENGTH], adminSteam[32];
|
||||
if (client > 0 && IsClientInGame(client))
|
||||
{
|
||||
GetClientName(client, adminName, sizeof(adminName));
|
||||
if (!GetClientAuthId(client, AuthId_Steam2, adminSteam, sizeof(adminSteam)))
|
||||
{
|
||||
strcopy(adminSteam, sizeof(adminSteam), "UNKNOWN");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strcopy(adminName, sizeof(adminName), "CONSOLE");
|
||||
strcopy(adminSteam, sizeof(adminSteam), "CONSOLE");
|
||||
}
|
||||
|
||||
char roleTag[16];
|
||||
if (IsCooldownExempt(client))
|
||||
{
|
||||
strcopy(roleTag, sizeof(roleTag), "FULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcopy(roleTag, sizeof(roleTag), "REG");
|
||||
}
|
||||
|
||||
LogToFileEx(path, "[%s] [%s] %s (%s) -> %s [раунд %d]", timestamp, roleTag, adminName, adminSteam, message, g_RoundCounter);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user