[Программирование в Pawn] Урок 1. Коротко о главном!



https://cheats-cs.ru/wp-content/uploads/2020/06/linechessset2.gif

Pawn — простой язык программирования с открытым исходным кодом. Сегодня мы начинаем серию гайдов по программированию на языке Pawn. Я считаю, что в данный момент появилось очень много новичков мало чего понимающих в программировании и постоянно задающие глупые вопросы. Именно для таких людей, я и решил написать гайды.

https://cheats-cs.ru/wp-content/uploads/2020/06/linechessset2.gif

Pawn — простой язык программирования с открытым исходным кодом. Сегодня мы начинаем серию гайдов по программированию на языке Pawn. Я считаю, что в данный момент появилось очень много новичков мало чего понимающих в программировании и постоянно задающие глупые вопросы. Именно для таких людей, я и решил написать гайды.


Не будем рассказывать о «вступлении» в программировании, о работе процессора, процесса компиляции и интерпретации ( or трансляции ) программы, а сразу попытаемся практично подойти к понятию программировании в Pawn
P.S
Если вы желаете больше влиться в мир программ, советую изучить следующие темы:

«Как работает процессор, регистры, архитектура пк»,
«Языки программирования, что такое трансляция и компиляция программы»,
«Вступление в С++, первые 8-14 уроков до темы «Классы»

Ну что же, давайте разбирать простейший плагин написанный на Pawn для CS 1.6 ( я решил взять броню, ибо считаю здесь есть всё для простого описания )

#include <amxmodx>
#include <fakemeta>
#include <zombieplague>

#define MAX_IN_ROUND 4 // Максимум в раунде

const g_item_cost = 15 // Цена

new const g_sound_buyarmor[] = { "items/tr_kevlar.wav" } // присваиваем константе массиву значение ( строку ) путь до звука

const g_armor_amount = 250 // Покупка за 1 раз
const g_armor_limit = 250 // Лимит
new g_armor_counter[33] = 0 // все 32 значения массива ( счёт начинается с 0 ) будут при компиляции/старта плагина равны 0
new extra_armor[512] // объявление массива

new g_itemid_humanarmor // объявление переменной


public plugin_precache()
{
    precache_sound(g_sound_buyarmor) // кэшируем звук покупки
}


public plugin_init()
{
    register_plugin("[ZP] Extra-Item: Anti-Infection Armor", "1", "Dambas") // регистрируем плагин
    register_event("ResetHUD","Start_round","be")                            // регистрируем событие смены худов CS
    formatex(extra_armor, charsmax(extra_armor), "%L", LANG_SERVER, "EXTRA_ARMOR"); // в массив extra_armor засовываем значение из lang файла которое равно EXTRA_ARMOR
    g_itemid_humanarmor = zp_register_extra_item(extra_armor , g_item_cost, ZP_TEAM_HUMAN) // присваиваем переменной возвращаемое значение функции
    register_dictionary("zombie_plague.txt");                                      // регистрируем lang файл
}


public Start_round(id)
{
    g_armor_counter[id] = 0 // обнуляем лимит покупок у всех игроков в начале раунда
}

public zp_extra_item_selected(id, itemid) // описываем "форвард" для нашего плагина
{
    if (itemid == g_itemid_humanarmor) // проверка на то, что если мы выбрали предмет из нашего плагина
    {
        if (g_armor_counter[id] > MAX_IN_ROUND) // если лимит покупок у игрока не выше того, что мы указали в MAX_IN_ROUND продолжаем идти дальше
        {
            ColorChat(id, "!g[ZP] !yТы купил максимум брони!" ) // вызываем функцию которая отобразит сообщение игроку
            return ZP_PLUGIN_HANDLED // прекращаем данную функцию + возвращаем деньги игроку
        }
        else
        {
            set_pev(id, pev_armorvalue, float(min(pev(id, pev_armorvalue)+g_armor_amount, g_armor_limit))
            // Мы выберем минимальное ( min() ) значение из [b]значений броня игрока+количетсво покупаемой брони[/b] pev(id, pev_armorvalue)+g_armor_amount и [b]максимальное количество брони[/b]
[b]            // То есть, у игрока не может быть брони больше, чем то, что мы указали в g_armor_limit[/b]
            engfunc(EngFunc_EmitSound, id, CHAN_BODY, g_sound_buyarmor, 1.0, ATTN_NORM, 0, PITCH_NORM) // воспроизводим звук указанный в массиве g_sound_buyarmor
            g_armor_counter[id]++                               // уменьшаем для игрока [id] его лимит
            ColorChat(id, "!g[ZP] !yТы купил !g250 брони!" )    // вызываем функцию для вывода сообщению игрока
        }
    }
    return PLUGIN_HANDLED // заканчиваем работу функции
}

stock ColorChat(const id, const input[], any:...) // тут много сложного для новичков
{
        new count = 1, players[32] // создаем переменную и массив для игроков
        static msg[191]  // создаём статичную переменную, чтобы её значение не удалялось после окончания функции 
        vformat(msg, 190, input, 3) // из массива с любым размером в массив msg с размером 191
       
        replace_all(msg, 190, "!g", "^4") // меняем все !g на ^4
        replace_all(msg, 190, "!y", "^1") // меняем все !y на ^1
        replace_all(msg, 190, "!t", "^3") // меняем все !t на ^3
       
        if (id) players[0] = id; else get_players(players, count, "ch") // если id не 0, то в в ячейку массива players[0] кидаем id игрока ( для будущего вывода сообщения именно ему ), ИНАЧЕ получаем id всех игроков для будущего вывода всем игрокам
        {
                for(new i = 0; i < count; i++) // цикл от 0 до количество игроков 
                {
                        if(is_user_connected(players[i])) // проверяем на коннект игрока ( антикраш )
                        {
                                message_begin(MSG_ONE_UNRELIABLE, get_user_msgid("SayText"), _, players[i]) // запускаем сообщение, с параметром вывода текста
                                write_byte(players[i]) // кому выводим
                                write_string(msg) // что выводим
                                message_end() // заканчиваем сообщение
                        }
                }
        }
}

В любой программе есть 3 блока:

1. Блок подключения заголовочных файлов — в ней подключаются все библиотеки ( библиотека — набор готовых функций движка CS ), для разработки плагина
2. Блок описания функции int main() — так как в Pawn, описание данной функции отсутствует, будем считать этой функцией public plugin_int(). Эта функции является функцией с точкой входа. Когда вы запускаете свой сервер, именно данная функция вызовется первой.
3. Блок описания функций, стоков и событий — блок, в котором вы описываете собственные функции и функции событий которые вы описали в public plugin_init()

Переменная.
В любой программе можно объявлять переменные. Они могут быть глобальными, и локальными. Разница в том, что к глобальным переменным можно обращаться из любой части кода, а к локальным только в том, где они объявлены.
Так же, у любой переменной, есть свои свойства, а именно:

  • тип данных
  • значение
  • адрес
  • имя

В Pawn, для объявления переменной общего ( безтипного ) типа, используется структура: new ИмяПеременной;
Чтобы при объявлении сразу присвоить значения, используется оператор «=»: new ИмяПеременной = 10;
Чтобы принудительно задать тип переменной, нужно использовать структуру: new ТипДанных:ИмяПеременной.
Список возможных типов переменной:

  • Integer — целочисленный
  • Float — дробный ( пример: 0.045 )
  • Bool — логический ( либо true, либо false ВАЖНО: Не 0, или 1 )
  • Array — массив

Так же есть вид переменных которые не могут менять своё значение во время кода, и присваиваются при объявлении. Такие переменные называются константами. Структура объявления: new const ИмяПеременной = 5; Значение таких переменных нельзя изменить!

P.S
Нельзя забывать про так называемые «инструкции». Через них тоже можно объявлять переменные которые можно использовать в коде. Они схожи с константами, но их основное отличие, что во время компиляции они лишь заменяют своё имя, на их значение в коде
Структура объявления: #define ИМЯ_ПЕРЕМЕННОЙ ЗНАЧЕНИЕ 
#define MYNAME Player


Массивы.
Следующая структура, которую нужно понимать для программирования в Pawn. Массив, грубо говоря, строка из переменных, у которых есть свой адрес и значение.
Для объявления массива, используются структура: new ИмяМассива[РазмерМассива];
В любом языке программирования, так же можно объявить двумерный массив, это, опять же грубо говоря, таблица, в которой в каждой ячейке массива — хранится адрес другого массива. Общую структуру такого массива можно представить как таблицу

new TutMassiv[3][3];

for(new i=0; i < 3; i++)
for(new k=0; k < 3; i++)
TutMassiv[i][k] = i;
TutMassiv[0][0]=0 TutMassiv[1][0]=0 TutMassiv[2][0]=0
TutMassiv[0][1]=1 TutMassiv[1][1]=1 TutMassiv[2][1]=1
TutMassiv[0][2]=2 TutMassiv[1][2]=2 TutMassiv[2][2]=2

Чтобы задать значения массива сразу при объявлении, используется оператор «=» и фигурные скобки:

new tut[3] = { 5, 4, 10 }; - одномерный массив
new tutD[2][2] = { { 3, 2 }, { 4, 6 } }; - двумерный массив ( таблица )
new tutT[3][3][3] = 
{
  {
    {1, 2, 3}, {4, 5, 6}, {7, 8, 9}
  },
  {
    {1, 2, 3}, {4, 5, 6}, {7, 8, 9}
  },
  {
    {1, 2, 3}, {4, 5, 6}, {7, 8, 9}
  }
}; - трёхмерный массив ( пространство )

Функции.
Еще одна важнейшая часть для осознания Pawn’а. Функции нужны для того, чтобы не повторять много раз одни и те-же куски кода, а отделить их отдельным именем и вызывать при необходимости.
Например, вам нужно при убийстве игрока, давать ему броню, здоровье, уровень и еще много много бонусов. Чтобы не загрязнять код, лучше вызывать одну функцию в событии убийства.
Все функции в Pawn должны начинаться со слова public. Общая структура написания функций: public ИмяФункции(Атрибут, Атрибут2, Атрибут3, …, АтрибутN) { описании функции )
Чтобы вызвать функцию, нужно просто написать её имя, и отослать туда какие либо данные — атрибуты. Имена атрибутов, могут не совпадать с именами переменных которые вы туда отправляете ( НО! Типы должны обязательно совпадать, так как вы не можете присвоить типу bool тип Integer, это вызовет Warning 213 при компиляции )

public NameFunction(id, Health, Armor) // Описание функции
{
 set_user_health(id, Health);
 set_user_armor(id, Armor);
}


// code
NameFunction(id, 300, 300); // Вызов функции
// code

Циклы.
Нужны для автоматического повтора каких либо действий при совпадении определенного условия.
Существует 3 вида циклов:

  • for(идентификация счетчика; условие; инкремент ( прибавление значение счетчика ))
  • while(условие)
  • do{ реализация кода }while(условие)

Чтобы сделать бесконечный цикл, можно использовать конструкцию while(true) -> чтобы выйти из него, используйте операцию break
break — останавливает выполнения цикла
continue — переходит на следующий шаг цикла

Как это работает

  1. Цикл For
    for(new i=0; i < 5; i++) { Тело Цикла }
    Сначала мы объявляем локальную переменную i и присваиваем ей значение 0. Эта локальная переменная будет использоваться только внутри тела цикла. Можно использовать и не локальные переменные написав её внутри for без оператора new и присваивания.
    После инициализации счетчика, мы проверяем условие. Если i < 5, начинаем выполнять операции находящиеся внутри тела цикла. После последней операции цикла, мы добавляем к существующему значению i единицу ( т.е. i = i + 1 ). Это и описано в инкременте счетчика(..; i++)
    После чего снова идёт условие.
  2. Цикл while(i<5) { Тело Цикла }
    Здесь всё так же, только нам придётся внутри тела цикла самому увеличивать значение i. Цикл будет работать, пока условие в скобках после while будет выполняться
  3. Цикл do{ Тело Цикла }while(условие)
    Этот цикл, отличается от других тем, что сначала мы выполняем операции в теле цикла, а только потом проверяем условия, и если оно верно, то снова выполняем операции до момента пока условие не сработает.

Операция Ветвления — if(){} else if(){} else{}
Операции ветвления, или условные операции, самое частое и нужное в программировании. Они нужны для проверки какого либо условия и дальнейшего выполнения или изменения условия.
Обязательная структура:

if(условие)         // ЕСЛИ ...
{
    Операции
}
else if(условие)   // ИНАЧЕ ЕСЛИ...
{
   
}
..... // здесь может быть сколько угодно структур else if(условие)
else if(условие)
{
Операции
}
else         // ИНАЧЕ .... - выполняется, если не одно из else if не выполнилось
{
Операции
}

Если не одна операция ветвления не совпала с условием, и операции else нету, то ВСЯ операция ветвления просто игнорируется и код идёт дальше 


Функция public plugin_init(), и посмотрим, что делает каждая строчка

public plugin_init() // Функция которая вызывается при компиляции плагина или запуска сервера
{
    register_plugin("[ZP] Extra-Item: Anti-Infection Armor", "1", "Dambas")            // Регистрация плагина, и присваивание ему имени
    register_event("ResetHUD","Start_round","be")                                      // Регистрация события, и присваивания ему в данном плагине функции ( в данном случае ResetHUD )
    formatex(extra_armor, charsmax(extra_armor), "%L", LANG_SERVER, "EXTRA_ARMOR");    // formatex - функция работы со строкой, далее мы рассмотрим её поближе
    g_itemid_humanarmor = zp_register_extra_item(extra_armor , g_item_cost, ZP_TEAM_HUMAN) // Переменная как значение функции. Далее мы рассмотрим зачем это нужно
    register_dictionary("zombie_plague.txt");                                            // Подключение lang-файла для русификации плагина
}

Я собрал все нужные для новичка события для создания функций их реализации:

  • register_event — регистрирует некоторые из стандартных событий Half-Life. Полный список тут — События Half-Life
  • register_logevent — регистрирует события которые обычно пишутся в логах. Нам нужны чаще всего для начала и конца раунда
  • RegisterHam — не побоюсь для новичков сказать, что это нужно для того, чтобы отловить какое то событие. Но функционал намного выше. Нам чаще всего нужно для событий получение урона, смерти, спавна и других. Удобность — наличие id игрока
  • register_touch — если углубитесь в тему Pawn’а, то эта функция поможет вам отловить событие касание объектов друг об друга

Функция plugin_precache(), нужна для кэширования ваших ресурсов ( модели, звуки, спрайты ) на сервере и в дальнейшем для передачи их игроку.
Внутри данной функции можно использовать несколько функций кэширования ( разных библиотек )
Fakemeta: engfunc(имя константы fakemeta, имя кэшируемой переменной)

  • Константа EngFunc_PrecacheModel — для кэширования модели или спрайта. Пример: engfunc(EngFunc_PrecacheModel, Model)
  • Константа EngFunc_PrecacheSound — для кэширования звука. Пример engfunc(EngFunc_PrecacheSound, Sound)

Amxmodx: precache_(имя кэшируемой переменной) — Аналогично, но без использования константы

  • precache_model
  • precache_sound

FAQ: ( ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ НОВИЧКОВ )

  • С чего начать писать плагин, на что смотреть, что делать если я не понимаю как работает код

    &Я не понимаю как работает код&

    Для начала, посмотрите на название переменных и событиях описанных в public plugin_init()
    Поймите структуру программы, какие функции вызываются первыми, какие используются чаще, какие важные. По смыслу вы сможете понять, в каком месте кода вы должны искать.
    Например: Вопрос: Мне нужно в определенном плагине, изменить радиус взрыва ракеты.
    Для начала, мне нужно найти функцию, которая отвечает зазапуск самой ракеты. Потом найти, функцию которая отвечает за взрыв ракеты. После чего найти структуру message_begin, ведь скорее всего именно в ней указывается радиус взрыва

    &С чего начать писать плагин&

    Продумайте в голове все свои мысли. Изобразите структуру вашего плагина на листочке или в Paint ( если вы собрались браться за что то большое ). Потом обдумайте, какие библиотеки ваш лучше использовать. Если вы намереваетесь писать плагин связанный как либо внутри-игровыми объектами: задумайтесь что будет лучше, использовать модуль engine или fakemeta (изучите их для начала). Подумайте, как будут использоваться переменные, какие функции вы распишите, какие события затронете.


  • Чем отличается (id) от [id]
    Всё, что находится в круглых скобках — это вызов функции. Квадратные скобки — обращение к ячейки массива с идентификатором указанным внутри []

    То есть, set_user_health(id, Health) -> мы отправляем в функции число которое постоянно хранится в id
    А users[id] -> мы обращаемся к массиву users по номеру который записан в id
    Ни в коем случае не путайте!


  • Как исправлять Warning’и
    Начнем с того, что ошибки(error) и предупреждения(warning) — это разные вещи. Если в вашем плагине ошибка — он не скомпилируется. Если предупреждения — скомпилируются, но компилятор вас предупредит, что в каком то месте допущена синтаксическая ошибка, которая может фатально или не очень повлиять на работу сервера.
    Всё зависит от самого Warning’а. Обычно, переведя его содержания и посмотрев на строчку которая его вызвала — вы сами всё поймете.
    Но не со следующими:
    Warning 217, Warning 213 — иногда новички конкретно зависают над этими предупреждениями.

    Ну что же, давайте рассмотрим возможные варианты их появления

  • %Warning 213%
    Тут может быть несколько вариантов. Чаще всего, предупреждение о не соответствии тэгов появляется когда функция принимает переменную одного типа, а вы отправляете другой
    К примеру. Когда вы работаете с уроном, вы должны понимать, что любой урон в игре имеет тип Float. Поэтому работая с ним, вы либо при вызове функций должны сами присваивать ему тип Float, либо везде держать его в дробном состоянии

    Нашел хороший пример:
    set_pev(iPlayer, pev_velocity, Float:{0.0,0.0,0.0})
    В данной функции при её вызове, вы сами должны указать тип Float, чтобы не столкнуться с проблемой 213 предупреждения.

  • &Warning 217&
    Тут 2 варианта. Либо у вас не выровнены отступы ( где то Табулирование, где то лишние пробелы ), либо неправильная структура кода
    К неправильной структуре я отношу забытые фигурные скобки
    Запомните, в функциях ветвления и циклах, не использовать фигурные скобки можно только в ситуации, когда в теле цикла или ветвления используется только одна операция, то есть
    // Правильно
    if(X>5)
       a = 3;
    else
       a = 5;;
    // Неправильно
    if(X>5)
       a = 3;
       b = 6;
    else
       a = 5;



Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
CHEATS-CS.RU - Топовый контент по CS 1.6