Initial commit: custom-rounds plugin with documentation and config
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,810 @@
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <cstrike>
|
||||
#include <sdkhooks>
|
||||
#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, "Нет");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user