Гость

Создаем AntiDM

В теме 2 сообщения

В сегодняшнем уроке предлагаю немного разобрать сразу две темы - использование директив препроцессора (а конкретно - макросов) и использование таймеров. Ну и, конечно, не на абстрактном примере, а сразу в написании чего-либо полезного. Например - для запрета DM в определенных зонах.
 
Начнем с директив препроцессора.
Собственно, препроцессор - служебный программный инструмент, осуществляющий конвертацию кода программы в промежуточный для последующей компиляции в бинарный файл.  Директива препроцессора - строка в исходном коде вида #КлючевоеСловоПрепроцессора, определяющая дальнейшее поведение препроцессора и использование им неких служебных функций.
Макрос (#define) - определенная директива препроцессора, позволяющая при компиляции кода использовать замену исходного выражения на некоторое другое. Это крайне удобно, когда необходимо часто использовать какое-либо достаточно объемное выражение, но нет желания оборачивать его в отдельную функцию. 
Яркий пример - нахождение большего из двух чисел:

#define max(a,b) ((a) > (b) ? (a) : (b))

Перейдем к таймерам.
AMX-машина может вызывать определенные функции в объявленные программистом периоды времени. Какой из функций языка Pawn будет вызван такой таймер - зависит уже от функции:

 

Если вызываемая функция не имеет параметров - используется функция SetTimer:

SetTimer (funcname[], interval, bool:repeating)

Где funcname - имя вызываемой функции, interval - время следующего запуска функции в миллисекундах, repeating - повторять ли запуск функции или нет.

Если вызываемая функция имеет параметры - используется функция SetTimerEx:

SetTimerEx (funcname[], interval, repeating, const format[], {Float,_}:...)

Первые три параметра - аналогичны SetTimer. Далее идет строка, в которой задаются типы передаваемых переменных, после нее - все необходимые для передачи вызываемой функции параметры.

 

Обе эти функции возвращают целочисленный ID созданного таймера. Прибить такой таймер можно вызовом функции KillTimer.

Наверное, на этом с теорией можно покончить и перейти к практике.
Итак, поставим перед собой задачу: написать скрипт, отслеживающий нажатие игроком определенных кнопок, если он находится в определенной зоне. Если игрок в зоне и нажимает на эти кнопки - увеличить счетчик на единицу, если счетчик больше двух и четырех - вывести определенные сообщения, если больше пяти - закрыть соединение.

Подключим библиотеку a_samp, более нам ничего не нужно. И сразу же объявим следующий макрос:

#define PRESSED(%0) (((newkeys & (%0)) == (%0)) && ((oldkeys & (%0)) != (%0)))

Данный макрос помогает обернуть достаточно длинное выражение, которое, по сути, означает лишь одно - проверку на нажатие кнопки. Если будет необходимо, в следующих уроках я могу разобрать, как работает обработчик нажатия клавиш OnPlayerKeyStateChange.

Далее - объявим необходимые для работы переменные. Во-первых - отдельную структуру для хранения зон, состоящую из четырех чисел с плавающей точкой - трех координат и радиуса зоны. Также нужны массивы для счетчика нажатий и для всех зон.

new DM_Counter[MAX_PLAYERS];

enum DM_Checker
{
    Float:X,
    Float:Y,
    Float:Z,
    Float:Radius
}
new NoDM_Zones[][DM_Checker] =
{
    {2549.0840,-2204.8350,21.9583,45.0},// 8 БИТ
    {175.3027,784.5325,12.0010,27.0},//Перекрёсток
    {2130.7192,-2182.4277,21.9545,40.0},//Автошкола Южный
    {2745.0464,-2294.7100,17.6124,40.0},//ШтрафСтоянка
    {1908.3670654297,-2233.1806640625,10.894914627075,45.0},//Мэрия
    {2343.427734375,-1809.4577636719,22.09578704834,60.0},//АвтоСалон
    {2509.3679199219,-2126.5373535156,23.105073928833,20.0}//Респаун новичков
};

Координаты и радиусы приведены для примера.

Во время старта скрипта нам нужно запустить таймер сброса счетчика. Для этого необходима отдельная функция, которая будет доступна из любого места вызова, то есть - с публичным доступом, или public. Ее и объявим. Алгоритм простой - пробег по всему массиву DM_Counter, если в ячейке массива не ноль - обнулить.

forward DM_Counter_reset();
public DM_Counter_reset ()
{
    for (new i=0; i<MAX_PLAYERS; i++)
    {
        if (DM_Counter[i]!=0) SendClientMessage (i, 0x00FF0000, "Счётчик попыток DM сброшен.");
        DM_Counter[i]=0;
    }
}

Важно поднять прототип функции (forward DM_Counter_reset) выше места вызова самой функции и других функций, из которых она может быть вызвана.
Запускаем таймер при старте бинарника (для примера - OnFilterScriptInit)

public OnFilterScriptInit ()
{
    SetTimer("DM_Counter_reset", 10*1000, true);
    return 1;
}

Очень важно очистить данные перед подключением игрока. Не забудем это сделать в OnPlayerConnect.

public OnPlayerConnect(playerid)
{
    DM_Counter[playerid]=0;
    return 1;
}

Подготовка завершена.

Отслеживание нажатия кнопок проходит в автовызываемой функции (коллбэке) OnPlayerKeyStateChange. Его мы и будем рассматривать.
Для начала - переберем все возможные условия, которые нам нужны. Это:

  • Нажатие ЛКМ (KEY_FIRE);
  • Нажатие ЛКМ вместе с ПКМ (KEY_FIRE | KEY_HANDBRAKE);
  • Нажатие ПКМ и F (KEY_SECONDARY_ATTACK | KEY_HANDBRAKE).

Если такие клавиши нажаты - проверяем все зоны на присутствие в них игрока. Сделаем это с помощью IsPlayerInRangeOfPoint и пробежим циклом по всем зонам. Если игрок в зоне - завершаем цикл, увеличиваем счетчик предупреждений на единицу. Больше пяти? Закрываем соединение.

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
    if(PRESSED (KEY_FIRE) || PRESSED (KEY_FIRE | KEY_HANDBRAKE) || PRESSED (KEY_SECONDARY_ATTACK | KEY_HANDBRAKE))
     {
	for(new i=0;i<sizeof(NoDM_Zones);i++)
              {
                if(IsPlayerInRangeOfPoint(playerid,NoDM_Zones[i][Radius],NoDM_Zones[i][X],NoDM_Zones[i][Y],NoDM_Zones[i][Z]))
                {
                    if (DM_Counter[playerid] == 5)
                    {
                            new name[MAX_PLAYER_NAME], string[64+MAX_PLAYER_NAME];
                            GetPlayerName(playerid, name, sizeof(name));
                            format(string, sizeof(string), "Игрок %s был кикнут сервером. Причина: DM в зеленой зоне", name);
                            SendClientMessageToAll(0xAA3333FF, string);
                            Kick (playerid);
                    }
                    else
                    {
                            DM_Counter[playerid] = DM_Counter[playerid]+1;
                        if (DM_Counter[playerid]>=2) SendClientMessage (playerid, 0xFFFF0000, "DM в зеленой зоне запрещен!");
                        if (DM_Counter[playerid]>=4) SendClientMessage (playerid, 0xFFFF0000, "При дальнейших попытках DM Вы будете наказаны.");
                    }
                   }
            }
        }
    return 1;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Максимально тугое и сложное объясните, че к чему коннектить 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!


Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.


Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

  • Похожий контент

    • romanets111
      От romanets111
      Вообщем не могу запустить сервер, ничего не регистрация , зашёл на сервер и всё, на хостинге пишет Unknown
      Помогите решить проблему, возникает в консоле такое
       
      [22:02:07] [debug] Run time error 19: "File or function is not found"[22:02:07] [debug]  BS_ReadValue
      [22:02:07] [debug]  PR_SendPacket[22:02:07] [debug]  PR_Init
      [22:02:07] [debug]  PR_RegHandler[22:02:07] [debug]  BS_IgnoreBits
      [22:02:07] [debug]  BS_New[22:02:07] [debug]  BS_WriteValue
      [22:02:07] [debug]  BS_Delete[22:02:07] [debug]  BS_GetNumberOfBytesUsed
       
      Прошу вас помогите мне