Создать акаунт

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


https://cdn.dribbble.com/users/872671/screenshots/2751155/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;
Комментарии
Минимальная длина комментария - 50 знаков. комментарии модерируются

Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.

Смотрите также: