В теме 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;
}

 

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


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

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

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


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

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

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

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

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


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

Войти

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


Войти

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

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

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

    • gooding
      От gooding
      Всем здравствуйте, уважаемые читатели. Я много времени потратил на доработку данного игрового мода, чтобы его выставить на форум.
      Лог доработок можно посмотреть внизу, а скриншоты игрового мода внизу и верху.
       
       

    • gooding
      От gooding


      Скачать файл PAR RP | Уникальный мод без багов
      Всем здравствуйте, уважаемые читатели. Я много времени потратил на доработку данного игрового мода, чтобы его выставить на форум.
      Лог доработок можно посмотреть внизу, а скриншоты игрового мода внизу и верху.
       
       

      Добавил gooding Добавлено 20.11.2024 Категория Моды Автор GOODING (Я)  
    • Kalinin
      От Kalinin
      1. Набираю команду для создания CRMP сервера.
      2. Вакансии  которые свободны. Связь через TG @kalinin_sql
      Нам нужны;
      1.Кодеры
      2.Маперы
      3.СММ
      Так-же дружная работа. По поводу зарплаты будем обговаривать все лично в беседе ТГ
       
    • whccompany
      От whccompany
      Rostov Role Play | Rostov RP | CRMP Mobile Project объявляет набор людей в свою команду: Нам требуются:
       
      1. Маппера:
      Сдельная оплата труда от 250 до 3000 рублей/ за 1 работу
       
      2. Специалист по Text Draw:
      Сдельная оплата труда от 400 до 3500 рублей/ за одну работу
       
      3. Pawn разработчик:
      Заработная плата от 10 000 рублей/месяц
       
      4. Программист для создания лаунчера и сайта UCP с автодонатом:
      Заработная плата от 10 000 рублей/месяц
       
      Контакты для связи:
       
      ВКонтакте
       
      Telegram