Плагин для запрета урона ТТ на респе

tolstomorda

Участник
Сообщения
6
Реакции
0
Друзья, добрый день!
К сожалению, не нашёл подходящего плагина со следующим функционалом: террористы, находящиеся на своём респауне имеют некоторое время, чтобы его покинуть. О чем им будут приходить уведомления в чат. По истечению времени они не смогут наносить урон, пока не покинут обозначенную зону.
Не найдя подходящего решения, для общего понимания я просмотрел уроки от Riko (за что ему большое спасибо), а также воспользовался уже двумя существующими темами (тема 1 и тема 2 ) и существующим плагином (Map Zone).
В результате появился мой плагин-франкенштейн, являющийся дополнением к Map Zone.
К сожалению, плагин не реагирует, когда террорист находится внутри зоны, реагирует только тогда, когда он эту зону покидает.
Прошу вашей помощи в оптимизации кода, до которой мне как до луны. Укажите на ошибки, куда копать, где почитать. Как снизить нагрузку на сервер.
Я уверен, что в плагине много всего лишнего и некорректного. Понимаю, что плагин всего на 100 строк, но и этого с моим уровнем знаний слишком много.
Спасибо всем, кто поможет.


PHP:
#include <sdktools>
#include <sdkhooks>
#include <sourcemod>
#include <cstrike>
#include <colors>
#define VALUE 0


public Plugin:myinfo =
{
    name        = "Respanw zone",
    author      = "tolstomorda",
    description = "Запрет урона в myzone",
    version     = "1.0",
    url         = "https://csgo.su/dd2only"
}

public OnPluginStart()
{
    HookEventEx("round_start", OnRoundStart, EventHookMode_PostNoCopy);
}

public OnRoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
    // Активируем нашу зону в начале раунда
    ServerCommand("sm_actzone myzone");
}

public Action:OnEnteredProtectedZone(zone, client, const String:prefix[])
{
    // Не забывайте что наказание в Map Zone должно быть "нестандартным"
    static Handle:ShowZones   = INVALID_HANDLE;
    if (!ShowZones) ShowZones = FindConVar("sm_zones_show_messages");
        if (GetClientTeam(client) == CS_TEAM_T)
        {
            if (1 <= client <= MaxClients)
            {
                decl String:m_iName[MAX_NAME_LENGTH*2];
                GetEntPropString(zone, Prop_Data, "m_iName", m_iName, sizeof(m_iName));

                // Когда игрок заходит в нашу зону
                if (StrEqual(m_iName[8], "myzone", false))
                {
                    // Запретим стрельбу, если игрок находится внутри зоны больше положенного времени
                    CreateTimer(1.0, Timer_ActivateMyZone, client, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
                    
                }
            }
        }
}

public Action:Timer_ActivateMyZone(Handle:timer, client)
{
    // Т.к. таймер повторяется каждую секунду, создадим счетчик
    static TimesRepeated = 0;

    switch (++TimesRepeated)
    {
        case 10, 20, 30: // Каждые 10 секунд...
        {
            
                    // ... через сколько секунд перестанет наноситься урон
            PrintToChat(client, "%s Террористы не будут наносить урон, находясь на респауне, через %i сек!", 40 - TimesRepeated);
                
        }
        case 40: // После 40 секунд отключим нанесение урона
        {
            // Обнулим счетчик таймера
            TimesRepeated = 0;
            SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
            PrintToChat(client, "%s Вы не можете наносить урон противникам, пока находитесь в этой зоне!")
            // Остановим повторяющийся таймер
            return Plugin_Stop;
        }
    }

    return Plugin_Continue;
}


public Action:OnLeftProtectedZone(zone, client, const String:prefix[])
{
    static Handle:ShowZones   = INVALID_HANDLE;
    if (!ShowZones) ShowZones = FindConVar("sm_zones_show_messages");

    if (1 <= client <= MaxClients)
    {
        decl String:m_iName[MAX_NAME_LENGTH*2];
        GetEntPropString(zone, Prop_Data, "m_iName", m_iName, sizeof(m_iName));

        if (StrEqual(m_iName[8], "myzone", false))
        {
            // Разрешим нанесение урона
            SDKUnhook(client, SDKHook_OnTakeDamage, OnTakeDamage);
            if (GetConVarBool(ShowZones) && IsPlayerAlive(client))
            {
                PrintToChat(client, "%s Вы снова можете наносить урон противникам!", prefix);
            }
        }
    }
}

public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
    if (attacker)
    {
        damage *= VALUE;
        return Plugin_Changed;
    }

    return Plugin_Continue;
}
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
у тебя плагин не будет правильно работать: ты перехватываешь получение урона террористами зашедшими в твою зону и через 40 секунд нахождения в этой зоне отключаешь получение ими урона
 

tolstomorda

Участник
Сообщения
6
Реакции
0
у тебя плагин не будет правильно работать: ты перехватываешь получение урона террористами зашедшими в твою зону и через 40 секунд нахождения в этой зоне отключаешь получение ими урона
Спасибо, я на сервере это почувствовал.
А что отвечает за нанесение урона противникам?
И почему могут не поступать уведомления в чат тому, кто заходит в зону?
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
Вот так попробуй:
PHP:
#include <sdktools>

#include <sdkhooks>
#include <cstrike>

#define SHORT_CD    1
#define LONG_CD        5

new bool:bLate,
    bool:bMsg,
    bool:bInGame[MAXPLAYERS+1],
    bool:bCanCauseDmg[MAXPLAYERS+1],
    bool:bInZone[MAXPLAYERS+1],
    iCooldown[MAXPLAYERS+1],
    iTimesRepeated[MAXPLAYERS+1],
    iFirstEntry[MAXPLAYERS+1],
    iTime,
    iTeam[MAXPLAYERS+1];

public Plugin:myinfo =
{
    name        = "No Tspawn camp",
    author        = "Grey83",
    description    = "Prohibition camp for terrorists in respawn zone",
    version        = "1.0.0",
    url            = "http://steamcommunity.com/groups/grey83ds"
}

public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
    bLate = late;
}

public OnPluginStart()
{
    if(!LibraryExists("sm_zones")) SetFailState("Plugin 'Map Zones' not exists!");
    else
    {
        new Handle:hMsg = FindConVar("sm_zones_show_messages");
        if(hMsg != INVALID_HANDLE) bMsg = GetConVarBool(hMsg);
    }

    HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
    HookEvent("player_team", Event_TeamChanged);

    if(bLate)
    {
        ServerCommand("sm_actzone myzone");
        for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i)) OnClientPostAdminCheck(i);
        bLate = false;
    }

    CreateTimer(1.0, Timer_Check, _, TIMER_REPEAT);
}

public OnClientPostAdminCheck(client)
{
    bInGame[client] = true;
    ResetValues(client);
    SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
}

public OnClientDisconnect(client)
{
    iTeam[client] = bInGame[client] = false;
}

public Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
    LoopClients();
    // Активируем нашу зону в начале раунда
    ServerCommand("sm_actzone myzone");
}

public Event_TeamChanged(Handle:event, const String:name[], bool:dontBroadcast)
{
    static client;
    if((client = GetClientOfUserId(GetEventInt(event, "userid")))) iTeam[client] = GetClientTeam(client);
}

stock LoopClients()
{
    for(new i = 1; i <= MaxClients; i++) ResetValues(i);
}

stock ResetValues(client)
{
    bCanCauseDmg[client] = true;
    iCooldown[client] = iTimesRepeated[client] = iFirstEntry[client] = bInZone[client] = false;
}

public Action:OnEnteredProtectedZone(zone, client, const String:prefix[])
{
    if(!IsPlayerValid(client, CS_TEAM_T) || !IsPlayerAlive(client))
        return Plugin_Continue;

    // Проверяем наличие у зоны имени и его правильность
    decl String:name[MAX_NAME_LENGTH*2];
    if(GetEntPropString(zone, Prop_Data, "m_iName", name, sizeof(name)) < 10 || !StrEqual(name[8], "myzone", false))
        return Plugin_Continue;

    bInZone[client] = true;

    // если кулдаун закончился, то сохраняем новое время первого входа в зону и сбрасываем счётчик
    if(iCooldown[client] < iTime)
    {
        iFirstEntry[client] = iTime;
        iTimesRepeated[client] = 0;
    }
    // иначе, если со времени первого входа прошло более 39 секунд, то сразу запрещаем наносить урон
    // (для отсеивания самых хитрых, которые выбегают из зоны для сброса счётчика)
    else if(iTime - iFirstEntry[client] > 39)
    {
        iTimesRepeated[client] = 40;
        bCanCauseDmg[client] = false;
        if(!IsFakeClient(client) && bMsg) PrintToChat(client, "Вы не можете наносить урон противникам, пока находитесь в этой зоне!")
    }

    return Plugin_Continue;
}

public Action:Timer_Check(Handle:timer)
{
    iTime = GetTime()
    static i;
    for(i = 1; i <= MaxClients; i++)
    {
        // Если игрок не террорист, мёртв, не в зоне или уже не может наносить урон, то переходим к следующему
        if(!IsPlayerValid(i, CS_TEAM_T) || !IsPlayerAlive(i) || !bInZone[i] || !bCanCauseDmg[i]) continue;

        switch(++iTimesRepeated[i])
        {
            // ... через сколько секунд перестанет наноситься урон
            case 10,20,30: if(!IsFakeClient(i) && bMsg) PrintToChat(i, "Вы перестанtntт наносить урон, находясь на респауне, через %i сек!", 40 - iTimesRepeated[i]);
            case 40: // После 40 секунд отключим нанесение урона
            {
                bCanCauseDmg[i] = false;
                if(!IsFakeClient(i) && bMsg) PrintToChat(i, "Вы не можете наносить урон противникам, пока находитесь в этой зоне!")
            }
        }
    }

    return Plugin_Continue;
}

public Action:OnLeftProtectedZone(zone, client, const String:prefix[])
{
    if(!IsPlayerValid(client, CS_TEAM_T) || !IsPlayerAlive(client))
        return Plugin_Continue;

    // Проверяем наличие у зоны имени и его правильность
    decl String:name[MAX_NAME_LENGTH*2];
    if(GetEntPropString(zone, Prop_Data, "m_iName", name, sizeof(name)) < 10 || !StrEqual(name[8], "myzone", false))
        return Plugin_Continue;

    bInZone[client] = false;

    // Если игроку уже запрещено наносить урон, то кулдаун будет большим, если ещё нет - коротким
    iCooldown[client] = iTime + (bCanCauseDmg[client] ? SHORT_CD : LONG_CD);

    bCanCauseDmg[client] = true;
    if(!IsFakeClient(client) && bMsg) PrintToChat(client, "Вы снова можете наносить урон противникам!")

    return Plugin_Continue;
}

public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
    if(IsPlayerValid(victim, CS_TEAM_CT) && IsPlayerValid(attacker, CS_TEAM_T) && !bCanCauseDmg[attacker])
    {
        damage = 0.0;
        return Plugin_Changed;
    }

    return Plugin_Continue;
}

stock bool:IsPlayerValid(client, team)
{
    return 0 < client <= MaxClients && IsClientInGame(client) && iTeam[client] == team;
}
C-подобный:
//// no_tspawn_camp.sp
// Header size:           3016 bytes
// Code size:             5584 bytes
// Data size:             3800 bytes
// Stack/heap size:      16384 bytes; Total requirements:   28784 bytes
//
// Compilation Time: 0,25 sec
 

Вложения

  • no_tspawn_camp.sp
    5.9 КБ · Просмотры: 16
  • no_tspawn_camp.smx
    5.9 КБ · Просмотры: 9

tolstomorda

Участник
Сообщения
6
Реакции
0
Вот так попробуй:
Спасибо за помощь!
Правильно ли я понимаю логику:
С начала раунда проходит 40 секунд и после этого тот, кто находится в зоне действия, не может наносить урон. Если он уже там был - кулдаун при выходе 5 сек, если только зашел - 1 сек?

Сильно ли будет отличать от такого варианта: не активация зоны через 40 сек с начала раунда, а активация зоны через 40 сек с момента, как игрок в неё зашел.
То есть у игрока будет в запасе 40 сек, чтобы её покинуть, каждые 10,20,30 сек предупреждение об этом, затем сообщение, что урон не наносится.
Как только игрок покидает зону - он сразу же может наносить урон.
Возможен такой вариант?
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
не активация зоны через 40 сек с начала раунда, а активация зоны через 40 сек с момента, как игрок в неё зашел.
То есть у игрока будет в запасе 40 сек, чтобы её покинуть, каждые 10,20,30 сек предупреждение об этом, затем сообщение, что урон не наносится.
Как только игрок покидает зону - он сразу же может наносить урон.
всё так, но зона всё-таки она активируется в начале раунда, т.к. игроки в этот момент спавнятся в этой зоне
С начала раунда проходит 40 секунд и после этого тот, кто находится в зоне действия, не может наносить урон. Если он уже там был - кулдаун при выходе 5 сек, если только зашел - 1 сек?
С момента входа в зону начинается отсчёт, если игрок вышел из зоны и вернулся в неё, пока не истёк кулдаун, то отсчёт не прерывается. Иначе счётчик обнуляется.
Если игрок находился в зоне менее 40 секунд, то кулдаун равняется 1 секунде, если 40 сек и больше, то кулдаун - 5 секунд.

Т.е. если счётчик отсчитал 40 секунд и заблокировал нанесение урона террористом, то ему нужно выйти минимум на 5 секунд, чтобы обнулить счётчик и снова получить на 40 секунд возможность наносить урон.
Если урон ещё не был заблокирован, то нужно выйти на 1 секунду из зоны, чтобы обнулить счётчик, по истечению которого ему будет заблокирована возможность нанести урон.

Правда не уверен, что это будет работать с гранатами: я не помню кто считается атакующим при взрыве гранаты: метнувший гранату или мир (энтити 0).

И ещё: плагин начинает работать сразу же при загрузке и не нужно дожидаться начала следующего раунда или перезапускать карту
 

tolstomorda

Участник
Сообщения
6
Реакции
0
@Grey83, установил плагин, зашел тестировать, но, к сожалению, не срабатывает. Не получил ни одного уведомления в чат. Зависит ли это от иммунитета, админ флагов?
Также заметил, что плагин стопорится на проверке:
PHP:
if(!LibraryExists("sm_zones")) SetFailState("Plugin 'Map Zones' not exists!");

Хотя по факту sm_zones запущен.
sm plugins load sm_zones.smx
[SM] Plugin sm_zones.smx is already loaded.
sm plugins load no_tspawn_camp1.smx
[SM] Plugin no_tspawn_camp1.smx failed to load: Error detected in plugin startup (see error logs).

Удалял эту проверку, тестил без неё. Но результат тот, что в первом предложении.

Также просьба пояснить этот блок:
PHP:
if(bLate)
    {
        ServerCommand("sm_actzone myzone");
        for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i)) OnClientPostAdminCheck(i);
        bLate = false;
    }
--- Добавлено позже ---
@Grey83, появился вариант №2 -> на основе плагина Dev Zones.
Для него есть дополнение: AntiCamp
В оригинале выглядит так:
PHP:
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <devzones>

new Handle:g_hClientTimers[MAXPLAYERS + 1] = {INVALID_HANDLE, ...};
new Handle:cvar_time;

public Plugin:myinfo =
{
    name = "SM DEV Zones - AntiCamp",
    author = "Franc1sco franug",
    description = "",
    version = "2.0",
    url = "http://www.clanuea.com/"
};

public OnPluginStart()
{
    cvar_time = CreateConVar("sm_devzones_anticamptime", "10", "Time in seconds before players must leave the zone or die");
}

public OnClientPutInServer(client)
{
    if (g_hClientTimers[client] != INVALID_HANDLE)
        KillTimer(g_hClientTimers[client]);
    g_hClientTimers[client] = INVALID_HANDLE;
}

public OnClientDisconnect(client)
{
    if (g_hClientTimers[client] != INVALID_HANDLE)
        KillTimer(g_hClientTimers[client]);
    g_hClientTimers[client] = INVALID_HANDLE;
}

public Zone_OnClientEntry(client, String:zone[])
{
    if(client < 1 || client > MaxClients || !IsClientInGame(client) ||!IsPlayerAlive(client))
        return;
       
    if((StrContains(zone, "AntiCampCT", false) == 0 && GetClientTeam(client) == 3) || (StrContains(zone, "AntiCampTT", false) == 0 && GetClientTeam(client) == 2))
    {
        new seconds = GetConVarInt(cvar_time);
        g_hClientTimers[client] = CreateTimer(seconds * 1.0, Timer_End, client, TIMER_REPEAT);
        PrintHintText(client, "You has entered in a AntiCamp Zone for your team\nYou have %i seconds for leave this zone or you will die", seconds);
    }
}

public Zone_OnClientLeave(client, String:zone[])
{
    if(client < 1 || client > MaxClients || !IsClientInGame(client) ||!IsPlayerAlive(client))
        return;
       
    if((StrContains(zone, "AntiCampCT", false) == 0 && GetClientTeam(client) == 3) || (StrContains(zone, "AntiCampTT", false) == 0 && GetClientTeam(client) == 2))
    {
        if (g_hClientTimers[client] != INVALID_HANDLE)
            KillTimer(g_hClientTimers[client]);
        g_hClientTimers[client] = INVALID_HANDLE;
    }
}

public Action:Timer_End(Handle:timer, any:client)
{
    if(IsPlayerAlive(client))
    {
        ForcePlayerSuicide(client);
        PrintToChatAll("%N have beeen killed for camp in a anticamp zone",client);
    }
    g_hClientTimers[client] = INVALID_HANDLE;
}

Вариант, который хотелось бы видеть, с запретом урона, а не с убийством:
PHP:
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <devzones>

new Handle:g_hClientTimers[MAXPLAYERS + 1] = {INVALID_HANDLE, ...};
new Handle:cvar_time;

public Plugin:myinfo =
{
    name = "SM DEV Zones - AntiCamp",
    author = "Franc1sco franug",
    description = "",
    version = "2.0",
    url = "http://www.clanuea.com/"
};

public OnPluginStart()
{
    cvar_time = CreateConVar("sm_devzones_anticamptime", "10", "Time in seconds before players must leave the zone or die");
}

public OnClientPutInServer(client)
{
    if (g_hClientTimers[client] != INVALID_HANDLE)
        KillTimer(g_hClientTimers[client]);
    g_hClientTimers[client] = INVALID_HANDLE;
}

public OnClientDisconnect(client)
{
    if (g_hClientTimers[client] != INVALID_HANDLE)
        KillTimer(g_hClientTimers[client]);
    g_hClientTimers[client] = INVALID_HANDLE;
}

public Zone_OnClientEntry(client, String:zone[])
{
    if(client < 1 || client > MaxClients || !IsClientInGame(client) ||!IsPlayerAlive(client))
        return;
       
    if((StrContains(zone, "AntiCampCT", false) == 0 && GetClientTeam(client) == 3) || (StrContains(zone, "AntiCampTT", false) == 0 && GetClientTeam(client) == 2))
    {
        new seconds = GetConVarInt(cvar_time);
        g_hClientTimers[client] = CreateTimer(seconds * 1.0, Timer_End, client);
        PrintToChat(client, "Вы находитесь в зоне респауна \nУ Вас есть %i секунд, чтобы покинуть эту зону или Вы не будете наносить урон", seconds);
    }
}

public Zone_OnClientLeave(client, String:zone[])
{
    if(client < 1 || client > MaxClients || !IsClientInGame(client) ||!IsPlayerAlive(client))
        return;
       
    if((StrContains(zone, "AntiCampCT", false) == 0 && GetClientTeam(client) == 3) || (StrContains(zone, "AntiCampTT", false) == 0 && GetClientTeam(client) == 2))
    {
        if (g_hClientTimers[client] != INVALID_HANDLE)
            KillTimer(g_hClientTimers[client]);
        g_hClientTimers[client] = INVALID_HANDLE;
        PrintHintText(client, "Вы покинули зону респауна, теперь вы можете наносить урон");
    }
}

public Action:Timer_End(Handle:timer, any:client)
{
    static TimesRepeated = 0;

    for (TimesRepeated = 0; TimesRepeated < 35; TimesRepeated++)
    {

                    // ... через сколько секунд перестанет наноситься урон
            PrintHintText(client, "Если не покинете респаун, Вы не сможете наносить урон через %i сек!", 35 - TimesRepeated);
               
    }

    TimesRepeated = 0;
    if(IsPlayerAlive(client))
    {
        ForcePlayerSuicide(client);
        PrintToChatAll("%N был убит за кемпертсво на респауне",client); // Только вместо убийства прикрутить запрет урона
    }
    g_hClientTimers[client] = INVALID_HANDLE;

}
Тест "Нашего варианта" в целом успешен и выполнял свою функцию (отсчет и убийство), единственный косяк - PrintHintText не выводил каждую секунду предупреждение с обратным отсчетом, в чем может быть проблема?
 
Последнее редактирование:

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
последний блок для запуска плагина во время игры: при запуске активируентся зона, а потом делается хук на получение урона всеми игроками на сервере
Также заметил, что плагин стопорится на проверке:
PHP:
if(!LibraryExists("sm_zones")) SetFailState("Plugin 'Map Zones' not exists!");
Хотя по факту sm_zones запущен.
значит я что-то не так прописал в плагине

И вообще: какая версия SM у тебя? Может переделать всё под новый синтаксис?
 

tolstomorda

Участник
Сообщения
6
Реакции
0
последний блок для запуска плагина во время игры: при запуске активируентся зона, а потом делается хук на получение урона всеми игроками на сервере
значит я что-то не так прописал в плагине

И вообще: какая версия SM у тебя? Может переделать всё под новый синтаксис?
В принципе строка абсолютно логичная, но sm_zones почему-то не находит, разве есть еще код, касающийся проверки подгруженного плагина?
PHP:
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) // у тебя
APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) // на сайте https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2
Дело в String:error[]?
Версия 1.8.0.6039
Насчет синтаксиса - он кардинально на что-либо влияет?
Посмотри, пожалуйста, второй вариант в моем предыдущем сообщении.
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
AskPluginLoad2 вообще ни при чём: это проверка на то загрузили этот плагин во время игры или сервер автоматически его загрузил
Насчет синтаксиса - он кардинально на что-либо влияет?
мне удобнее писать на новом
Посмотри, пожалуйста, второй вариант в моем предыдущем сообщении.
у меня лучше, разве что сообщение в этом варианте выводится в хинт, а не в чат
Могу тоже так сделать, но тогда нужно будет делать проверку для ксго, т.к. для него нужно иначе выводить, емнип
--- Добавлено позже ---
@tolstomorda, глянул Dev Zones: через него будет удобнее сделать, т.к инклюд имеется и есть натив Zone_IsClientInZone()
 

tolstomorda

Участник
Сообщения
6
Реакции
0
@Grey83, это, наверное, мой косяк. Я и ставлю его на КС ГО.
Давай тогда остановимся на DevZones, помоги, пожалуйста, изменить смерть на запрет урона?
Можно ли выводить в хинт обратный отсчет таймера ежесекундно?
 
Сверху Снизу