1 post in this topic

В сегодняшнем уроке предлагаю немного разобрать сразу две темы - использование директив препроцессора (а конкретно - макросов) и использование таймеров. Ну и, конечно, не на абстрактном примере, а сразу в написании чего-либо полезного. Например - для запрета 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;
}

 

Share this post


Link to post
Share on other sites

Your content will need to be approved by a moderator

Guest
You are commenting as a guest. If you have an account, please sign in.
Reply to this topic...

×   You have pasted content with formatting.   Remove formatting

  Only 75 emoticons maximum are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

Loading...
Sign in to follow this  
Followers 0

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • Nate_Weny
      By Nate_Weny
      Компиляция: 
      C:\Users\Àäìèí\Desktop\enerhy\gamemodes\evolution.pwn(57653) : error 021: symbol already defined: "pc_cmd_goadminka"
      C:\Users\Àäìèí\Desktop\enerhy\gamemodes\evolution.pwn(57654) : warning 211: possibly unintended assignment
      C:\Users\Àäìèí\Desktop\enerhy\gamemodes\evolution.pwn(57665) : loose indentation
      C:\Users\Àäìèí\Desktop\enerhy\gamemodes\evolution.pwn(57669) : loose indentation
      Нашел команду сделал в мод, поменял пару параметров. Вылазит эта ошибка.
      Код:
      new adminkaon[MAX_PLAYERS]; new adminka; cmd:adminkaon(playerid, params[]) {         new adminaa[64];         if(PlayerInfo[playerid][pAlcoInvenxua] <= 12) return SendClientMessage(playerid, 0xBFC0C2FF, "Òû íå ìîæåøü èñïîëüçîâàòü ýòó êîìàíäó!");         if(sscanf(params,"i",params[0])) return SendClientMessage(playerid, -1, "Ââåäèòå /adminkaon [óðîâåíü]");         SetTimer("adminkaoon", 3000, 0);         adminka = params[0];         SendClientMessage(playerid, 0xFF0000, "Âû óñïåøíî çàïóñòèëè ðàçäà÷ó àäìèíêè");         format(adminaa,sizeof(adminaa),"Âíèìàíèå! Ðàçäà÷à àäìèíêè %s óðîâíÿ íà÷àëàñü!",params[0]);         SendClientMessageToAll(0xFFAAAA,adminaa);         return 1; } cmd:goadminka(playerid, params[]) {         if(adminkaon[playerid] = 0) return 1;         PlayerInfo[playerid][pAlcoInvenxua] = adminka;         SendClientMessage(playerid, 0xFF0000, "Ïîçäðàâëÿåì! Âû ïîëó÷èëè àäìèíêó. Ââåäèòå /alogin!");         return 1; } forward adminkaoon(playerid); public adminkaoon(playerid) {         for(new p; p < GetMaxPlayers(); p++)         {                 if(!IsPlayerConnected(p))continue;         {                         adminkaon[playerid] = 1;                 }         }     return 1; }  
    • Антон Нолмадов
      By Антон Нолмадов


      Просмотр файла ATOM ROLEPLAY | 0.3.7 ОРИГИНАЛ ОТ ВЛАДЕЛЬЦА
      И снова всем здраствуйте, сегодня в продажу входит игровой мод моего бывшего сервера Atom RolePlay 0.3.7 
      Что входит в продажу:
       
      Мод -
      Сборка сервера с худом -
      Сайт -
      Форум -
      Оформление вк
       
      Вкратце про функционал мода: 
      Система домов. 
      Система бизнеса. 
      Система атм. 
      Система радаров. 
      Система транспорта. 
      Система квестов. 
      Есть водный салон где каждый игрок сможет себе покупать лодку и яхту. 
      Есть воздушный транспорт. 
      Мод сделан под бонусник (точнее переделан), многие товары (яхты, автомобили, самолеты, скины) за донат.
       
      ВНИМАТЕЛЬНО!!!!!!!      ПОСЛЕ ПОКУПКИ ИГРОВОГО МОДА ОТПИШИТЕ МНЕ В ВК vk.com/jakenolman (НЕ РЕКЛАМА)
      Добавил Антон Нолмадов Добавлено 23.06.2021 Категория Моды Автор Anton Nolmadov  
    • Антон Нолмадов
      By Антон Нолмадов


      Просмотр файла SUPREME ROLEPLAY | CRMP 0.3.e
      И снова всем здраствуйте, сегодня в продажу входит игровой мод сервера Supreme RolePlay 0.3.e
      Что входит в продажу:
       
      Мод -
      Мод-пак
      Оформление вк
       
      Вкратце про функционал мода: 
      Система домов. 
      Система бизнеса. 
      Система атм. 
      Система радаров. 
      Система транспорта. 
      Система квестов. 
      Есть водный салон где каждый игрок сможет себе покупать лодку и яхту.
      Покупка вертолёта
      Личный мапинг
      Красивая карта 
      Много систем в фракциях
      Система тюнинга
      Есть воздушный транспорт. 
      Мод сделан под бонусник, многие товары (яхты, автомобили, самолеты, скины) за донат.
      Данный мод продавался примерной ценой 1000+
       
      ВНИМАТЕЛЬНО!!!!!!!      ПОСЛЕ ПОКУПКИ ИГРОВОГО МОДА ОТПИШИТЕ МНЕ В ВК vk.com/jakenolman (НЕ РЕКЛАМА)
      Добавил Антон Нолмадов Добавлено 23.06.2021 Категория Моды Автор Anton Nolmadov  
    • Антон Нолмадов
      By Антон Нолмадов
      И снова всем здраствуйте, сегодня в продажу входит игровой мод сервера Supreme RolePlay 0.3.e
      Что входит в продажу:
       
      Мод -
      Мод-пак
      Оформление вк
       
      Вкратце про функционал мода: 
      Система домов. 
      Система бизнеса. 
      Система атм. 
      Система радаров. 
      Система транспорта. 
      Система квестов. 
      Есть водный салон где каждый игрок сможет себе покупать лодку и яхту.
      Покупка вертолёта
      Личный мапинг
      Красивая карта 
      Много систем в фракциях
      Система тюнинга
      Есть воздушный транспорт. 
      Мод сделан под бонусник, многие товары (яхты, автомобили, самолеты, скины) за донат.
      Данный мод продавался примерной ценой 1000+
       
      ВНИМАТЕЛЬНО!!!!!!!      ПОСЛЕ ПОКУПКИ ИГРОВОГО МОДА ОТПИШИТЕ МНЕ В ВК vk.com/jakenolman (НЕ РЕКЛАМА)