2 posts 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

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • Nik_Rofid
      By Nik_Rofid
      Здравствуйте, можно ли на сервере копии радмира сделать так, что бы на машину можно было установить конкретный номер например ставится на машу рандом номер а будет х777хх77
    • Fazovskiy_
      By Fazovskiy_
      Приветствую хочу открыть данный мод дабы играть с друзьями сейчас столкнулся с проблемой при работе на подработках не выдается зарплата в копейках полазив в моде и бд нашёл из полезного только данную строку в моде 6574 или же give_kop(playerid,GetPVarInt(playerid,"JobY") ниже прикреплю файлы бд и мода и дам ссылку на целый мод для пробного запуска заранее спасибо за помощь (бд переименуйте на sql) ссылка на полный мод https://pawno-rus.ru/files/file/606-crmp-03e-akcent-rp-nastoyaschiy-mod-ckidka/?do=download&r=5201&confirm=1&t=1&csrfKey=23a7ad9fa7faeeae0f35a0a1c124d53d
      USSR.pwn
      BD.txt