Модификация мода Map Zones

x330122

Участник
Сообщения
357
Реакции
152
Хай всем ! :ab: Есть версия этого плагина, где кастомные зоны уже конкретно можно делить. А не одна кастомная зона на весь плагин. [Любая игра] - Map Zones
 
M

MihailM

@Tetragromaton,
Re: [ANY] Map Zones (Зоны на карте)

Больше примеров использования "кастомных" зон.
PHP:
#include <sdktools>
#include <sdkhooks>
#undef REQUIRE_EXTENSIONS // Чтобы плагин загрузился, нужно отметить дополнения как "необязательные"
#include <cstrike>
#include <tf2_stocks>
#tryinclude <dodhooks> // Для компиляции
#define MAX_EDICTS (1 << 11)

// Для дуэльной зоны
new bool:InDuelingZone[MAXPLAYERS + 1] = {false, ...}, numDuellers[MAX_EDICTS];

public Plugin:myinfo =
{
    name        = "SM Custom Zones",
    author      = "Root",
    description = "Тестовый плагин \"кастомных\" зон.",
    version     = "1.0",
    url         = "http://www.dodsplugins.com/"
}

public OnPluginStart()
{
    // HookEventEx не будет выдавать ошибку, если такого события не существует (например в DoD:S)
    HookEventEx("round_freeze_end", OnRoundStart);
}

public OnEntityCreated(entity, const String:classname[])
{
    // Количество дуэлянтов ограничено! :D
    numDuellers[entity] = 0;
}

public OnEntityDestroyed(entity)
{
    // Если игрок ушел с сервера, значит он больше не в дуэльной зоне
    if (1 <= entity <= MaxClients)
    {
        InDuelingZone[entity] = false;
    }
    if (numDuellers[entity])
    {
        // Если вдруг удалилась дуэльная зона, тогда "обезвредим" всех дуэлянтов
        for (new i = 1; i <= MaxClients; i++)
            InDuelingZone[i] = false;
    }
}

public OnRoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
    // Когда раунд начался, активируем 'no rush' зону для терроров
    ServerCommand("sm_actzone norush");
    CreateTimer(1.0, Timer_ActivateZone, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}

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

    switch (++TimesRepeated)
    {
        case 10, 20, 30: // Каждые 10 секунд...
        {
            for (new i = 1; i <= MaxClients; i++)
            {
                if (IsClientInGame(i))
                {
                    // ... будем показывать террористам ...
                    if (GetClientTeam(i) != CS_TEAM_T)
                        continue;

                    // ... через сколько секунд они смогут рашить
                    PrintToChat(i, "Вы сможете атаковать только через %i сек!", 40 - TimesRepeated);
                }
            }
        }
        case 40: // После 40 секунд отключим зону
        {
            // Обнулим счетчик таймера
            TimesRepeated = 0;

            // Активируем зону (по её названию) через консоль
            ServerCommand("sm_diactzone norush");

            PrintToChatAll("Готовьтесь к смерти!");

            // Остановим повторяющийся таймер
            return Plugin_Stop;
        }
    }

    return Plugin_Continue;
}

/**
* OnEnteredProtectedZone()
*
* Когда игрок входит в зону.
*
* @param zone        Индекс зоны в которую вошел игрок.
* @param client    Индекс игрока, который вошел в зону.
* @param prefix    Префикс, отображаемый в чате (когда игрок входит в зону).
* @noreturn
* -------------------------------------------------------------------------- */
public Action:OnEnteredProtectedZone(zone, client, const String:prefix[])
{
    // Заполучим переменную sm_zones_show_messages, которая показывает сообщения
    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], String:szMsg[PLATFORM_MAX_PATH];
        GetEntPropString(zone, Prop_Data, "m_iName", m_iName, sizeof(m_iName));

        // Пропустим первые 8 символов чтобы игнорировать префикс зоны (sm_zone )
        if (StrEqual(m_iName[8], "chat", false))
        {
            // Тестовая зона
            Format(szMsg, sizeof(szMsg), "Вы зашли в зону под названием \"%s\".", m_iName[8]);
        }

        // Зона неуязвимости
        else if (StrEqual(m_iName[8], "godmode", false))
        {
            // Сделаем игрока неуязвимым при помощи плагина, представленного ниже
            // https://forums.alliedmods.net/showthread.php?p=979550
            ServerCommand("sm_god #%i 1", GetClientUserId(client));
            strcopy(szMsg, sizeof(szMsg), "Вы зашли в зону бессмертия!");
        }

        // Зона пониженной гравитации
        else if (StrEqual(m_iName[8], "lowgrav", false))
        {
            // Применим только половинную гравитацию для игрока
            SetEntityGravity(client, 0.5);
            strcopy(szMsg, sizeof(szMsg), "Вы зашли в зону пониженной гравитации!");
        }

        // А это зона где игроки могут закупиться, но не могут стрелять
        else if (StrEqual(m_iName[8], "spawnoshoot", false))
        {
            if (SDKHookEx(client, SDKHook_PostThinkPost, PostThinkPost))
                strcopy(szMsg, sizeof(szMsg), "Вы не сможете стрелять пока находитесь в этой зоне!");
        }

        // Зона возвращения в респаун
        else if (StrEqual(m_iName[8], "spawntele", false))
        {
            // Возродим (т.е. возвратим) игрока на свою базу
            switch (EngineVersion:GetEngineVersion())
            {
                // CS:S и CS:GO использует свою функцию возрожедния
                case Engine_CSS, Engine_CSGO: CS_RespawnPlayer(client);
                case Engine_TF2: TF2_RespawnPlayer(client); // И TF2 тоже

#if defined _dodhooks_included
                case Engine_DODS: RespawnPlayer(client); // Для DoD:S необходимо использовать дополнение DoD Hooks
#endif
                default: TeleportEntity(client, Float:{123.0, 456.0, 789.0}, NULL_VECTOR, NULL_VECTOR); // Можно просто телепортируем игрока в нужные координаты
            }

            // Форматируем сообщение
            strcopy(szMsg, sizeof(szMsg), "You shall not pass, motherfucker!");
        }

        // Дуэльная зона
        else if (StrEqual(m_iName[8], "dueling", false))
        {
            // Увеличим кол-во дуэлянтов
            // Если дуэлянтов меньше или равно 2, то...
            if (++numDuellers[zone] <= 2)
            {
                // ... заблокируем получаемый урон от любых игроков, находящихся вне дуэльной зоны
                InDuelingZone[client] = true;
                SDKHook(client, SDKHook_TraceAttack,  TraceAttack);
                SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
                strcopy(szMsg, sizeof(szMsg), "Вы зашли в зону для дуэлей!");

                // Сделаем так чтобы иные игроки не смогли входить в зону
                //SetEntProp(zone, Prop_Send, "m_CollisionGroup", 17);
            }
        }

        // Проверим должно ли отображаться сообщение сразу как только игрок вошел в зону?
        if (GetConVarBool(ShowZones))
        {
            PrintToChat(client, "%s%s", prefix, szMsg);
        }
    }
}

/**
* OnLeftProtectedZone()
*
* Когда игрок выходит из зоны.
*
* @param zone        Индекс зоны из которой вышел игрок.
* @param client    Индекс игрока, который вышел из зоны.
* @param prefix    Префикс, отображаемый в чате (когда игрок выходит из зоны).
* @noreturn
* -------------------------------------------------------------------------- */
public Action:OnLeftProtectedZone(zone, client, const String:prefix[])
{
    static Handle:ShowZones   = INVALID_HANDLE;

    // Сохраним носитель переменной (через static) чтобы в дальнейшем переменную не запрашивать
    if (!ShowZones) ShowZones = FindConVar("sm_zones_show_messages");

    if (1 <= client <= MaxClients)
    {
        // Необходимо проверять зоны по их названию, иначе "нестандартные" зоны были бы полной чушью
        decl String:m_iName[MAX_NAME_LENGTH*2], String:szMsg[PLATFORM_MAX_PATH];
        GetEntPropString(zone, Prop_Data, "m_iName", m_iName, sizeof(m_iName));

        // И здесь пропустим префиксы 'sm_zone '
        if (StrEqual(m_iName[8], "chat", false))
        {
            Format(szMsg, sizeof(szMsg), "Вы вышли из зоны \"%s\".", m_iName[8]);
        }
        // Имена зон должны строго соответствовать!
        else if (StrEqual(m_iName[8], "godmode", false))
        {
            // Отключим бессмертие после выхода из зоны неуязвимости
            ServerCommand("sm_god #%i 0", GetClientUserId(client));
            strcopy(szMsg, sizeof(szMsg), "Вы снова простой смертный!");
        }
        else if (StrEqual(m_iName[8], "lowgrav", false))
        {
            // Вернем стандартную гравитацию
            SetEntityGravity(client, 1.0);
            strcopy(szMsg, sizeof(szMsg), "Вы вышли из зоны пониженной гравитации!");
        }
        else if (StrEqual(m_iName[8], "spawnoshoot", false))
        {
            SDKUnhook(client, SDKHook_PostThinkPost, PostThinkPost);
            strcopy(szMsg, sizeof(szMsg), "Вы покинули зону и снова можете стрелять!"); // Звучит двойственно...
        }
        else if (StrEqual(m_iName[8], "dueling", false))
        {
            // Для предотвращения ошибок надо сверить что один из дуэлянтов покинул зону
            if (InDuelingZone[client])
            {
                numDuellers[zone]--;
                InDuelingZone[client] = false;
                SDKUnhook(client, SDKHook_TraceAttack,  TraceAttack);
                SDKUnhook(client, SDKHook_OnTakeDamage, OnTakeDamage);
                strcopy(szMsg, sizeof(szMsg), "Вы вышли из дуэльной зоны!");
                //SetEntProp(zone, Prop_Send, "m_CollisionGroup", 11);
            }
        }
        // И так далее...

        // Когда игрок умирает, считается что он покинул зону
        if (GetConVarBool(ShowZones) && IsPlayerAlive(client))
        {
            // Отобразим сообщение только когда живой игрок покидает зону
            PrintToChat(client, "%s%s", prefix, szMsg);
        }
    }
}

public PostThinkPost(client)
{
    SetEntPropFloat(client, Prop_Send, "m_flNextAttack", GetGameTime() + 0.5);
}

public Action:TraceAttack(victim, &attacker, &inflictor, &Float:damage, &damagetype, &ammotype, hitbox, hitgroup)
{
    return (InDuelingZone[attacker] && InDuelingZone[victim]) ? Plugin_Continue : Plugin_Handled;
}

public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
    return (InDuelingZone[attacker] && InDuelingZone[victim]) ? Plugin_Continue : Plugin_Handled;
}
Оффтоп
 
Сверху Снизу