Scripting tutorial/ru

From Dragon Age Toolset Wiki
Jump to: navigation, search
Инструкция по скриптам
Начало / Русская DA Builder Wiki / Поделиться ВКонтакте
Скрипты
все статьи категории
описание функций

Последним фрагментом нашей маленькой обучающей игре будет установка флага MONSTERS_SLAIN в нашем Plot-файле так, чтобы когда игрок выполнит задание, НПС, давший это задание, узнал об этом. Делается это с помощью скрипта.

Синтаксис написания скриптов в DA похож на синтаксис языка С. Этот учебник содержит несколько приёмов программирования, и мы надеемся, что даже с небольшими познаниями в написании скриптов, вы сумеете извлечь из него кое-что полезное.

Событийно-ориентированная модель Dragon Age

Скрипты DA являются событийно-ориентированными. Событие (Event) – это пакет информации в специальном формате, которое передаётся игре, чтобы вызвать какую-либо реакцию на него. События генерируются скриптами движка игры и посылаются скрипту объекта, обрабатывающему события.

События различаются по типу, объекту назначения, задержке времени и пакету параметров (произвольное количество численных, объектных, плавающих и строковых переменных).

Для примера, cобытие EVENT_TYPE_ATTACK_IMPACT (en) генерируется движком игры каждый раз, когда атака поражает цель. Оно содержит идентификаторы атакующего и цели, было ли попадание критическим, и количество ущерба, принесённого попаданием. Оно посылается в скрипт событий атакующего, который обрабатывает информацию, содержащуюся в событии, необходимым образом (обычно снимает с цели очки здоровья).

В нашем случае нам нет необходимости генерировать собственные события. Мы указали, что все монстры в хижине являются членами команды № 1, а игра генерирует событие, когда все члены какой-либо команды убиты. Наш новый скрипт отреагирует на это событие и установит значение флага в Plot-файле, указывая, что все монстры уничтожены и задание выполнено.

События, имеющиеся в игре практически на любой случай, перечислены по ссылке События в DA (en). Событие, о котором мы недавно упомянули и которое посылается при уничтожении какой-либо команды, называется EVENT_TYPE_TEAM_DESTROYED (en) и содержит номер команды.

Создание простого скрипта, обрабатывающего события

Создайте новый скрипт и назовите его hut_monsters_slain.

В большинстве случаев скрипт запускается с функции, которая называется main. Таким образом, большинство скриптов начинается со следующих строк:

void main()
{

}

Код, который будет выполнять нашу задачу, должен содержаться между фигурными скобками. Функция main не возвращает какой-либо результат, таким образом, тип функции должен быть void (en), и не содержит параметров ().

Константы событий

Далее мы должны убедиться, что скрипт поймёт, что от него требуется, когда мы будем делать ссылки на события. Каждое событие имеет своё численное обозначение, но программисту было бы практически нереально ориентироваться только на это, вместо этого можно использовать более удобочитаемые константы.

Эти константы определены в скрипте events_h. Суффикс "_h" указывает на то, что скрипт не является самостоятельным и содержит основные константы и функции игры, которые могут использоваться только из других скриптов.

Для подключения такого скрипта необходимо в самом начале нашего скрипта вписать следующую строку:

#include "events_h"

Все команды #include должны располагаться в самом начале скрипта, перед любыми другими конструкциями. Если же их несколько, то порядок их расположения относительно друг друга не имеет значения.

Чтобы проверить, знает ли наш скрипт такое событие, как EVENT_TYPE_TEAM_DESTROYED, можно посмотреть на окно, расположенное в окне редактора скриптов справа. Изначально это окно показывает список доступных функций (кнопка "f()"), чтобы отобразить константы, нажмите на кнопку "C".

Scripting constant browser.png

Даже без подключения каких-либо скриптов этот список будет весьма длинным, в противном же случае, можно и запутаться в них. Чтобы найти какую-либо константу введите в поле справа от кнопок любую часть имени константы.

Scripting constant browser filtered.png

Для того, чтобы добавить в скрипт константу, просто дважды щёлкните по её имени в списке. Константа добавится в то место, где в скрипте установлен курсор. Такой способ поможет избежать каких-либо опечаток в названии. Также вы можете напечатать начало названия константы или функции и нажать Ctrl-Space вследствие чего появится список доступных значений.

Извлечение информации о текущем событии

Когда событие посылается в скрипт, его функция main начинает выполняться, но пока вы не сделаете определённые действия скрипт ничего не будет знать о принятом событии.

Первое, что мы должны сделать, это установить что за событие происходит. Мы определим переменную типа event, которую назовём ev. Для этого мы используем функцию GetCurrentEvent (en). Итак, первой строкой в нашем скрипте будет:

event ev = GetCurrentEvent();

Каждое событие принадлежит определённому типу и, как описано выше, имеет своё численное обозначение. Мы создадим численную переменную, которую назовём nEventType и которая будет хранить численное обозначение нашего события. Для её определения мы используем функцию GetEventType (en), которая извлечёт нужную нам информацию из переменной ev. Следующей строкой нашего скрипта будет:

int nEventType = GetEventType(ev);

Остальная информация, которая передаётся с событием, зависит от того, что это за событие. Таким образом, остальная часть скрипта будет обрабатывать специфична для нашего события. Чтобы скрипт обрабатывал именно наше событие, мы используем конструкцию switch. Конечно в случае если скрипт обрабатывает немного событий, можно использовать конструкцию if, но использование конструкции switch будет предпочтительнее при дальнейшем расширении скрипта.

switch(nEventType)
    {
         case EVENT_TYPE_TEAM_DESTROYED:
         {
             //Здесь должен быть код, обрабатывающий событие
             break;
         }
    }

Из документации мы знаем, что событие EVENT_TYPE_TEAM_DESTROYED содержит только одну информацию, а именно номер команды. Это целочисленное значение, а значит мы можем извлечь его с помощью функции GetEventInteger (en). Эта функция требует наличия двух параметров, само событие как объект (мы его сохранили в переменную ev) и индекс запрашиваемого нами значения, в нашем случае это 0 (как указано в документации).

Мы также помним, что монстры в хижине у нас принадлежат команде № 1, таким образом нам надо обновить Plot-файл тогда, когда команда № 1 будет полностью уничтожена. Сделать это можно с помощью конструкции if:

if (GetEventInteger(ev,0) == 1)
{
    //Здесь должен быть код, обновляющий Plot-файл
}

Взаимодействие с Plot-файлами

Для взаимодействия с Plot-файлами нам необходимо чтобы скрипт знал, где какие флаги находятся. Это делается тем же способом что и со скриптами, содержащими константы, а именно подключением к скрипту.

Plot-файлы не являются скриптами, поэтому для того, чтобы игра поняла, что речь идёт о Plot-файле, необходимо использовать префикс plt_. Plot-файл, взаимодействие с которым нам нужно обеспечить называется clear_the_hut, таким образом в начале скрипта, там где мы перечисляем подключаемые файлы, нужно ввести следующую строку:

#include "plt_clear_the_hut"

Теперь у нашего скрипта есть доступ к флагам Plot-файла и он может считывать их значения и изменять их. Также изменился и список констант, в который добавились PLT_CLEAR_THE_HUT - сам Plot-файл, и целочисленные: MONSTERS_SLAIN, QUEST_ACCEPTED и REWARD_RECEIVED, которые идентифицируют флаги во вновь подключенном Plot-файле.

Изменение состояния флагов осуществляется при помощи функции WR_SetPlotFlag (en), которая определена в подключаемом скрипте wrappers_h. Скрипт wrappers_h содержит функции, которые используются для операций, требующих низкоуровневый доступ к ресурсам игры. Для подключения этого скрипта добавьте в начало вашего скрипта следующую строку:

#include "wrappers_h"

В нашем случае мы хотим установить флаг MONSTERS_SLAIN. Функция WR_SetPlotFlag требует три параметра: идентификатор Plot-файла, идентификатор флага, и значение, которое мы хотим установить:

WR_SetPlotFlag(PLT_CLEAR_THE_HUT, MONSTERS_SLAIN, TRUE);

Ассоциирование скрипта событий с объектом

Наш скрипт практически готов, но пока мы не привяжем его к нужному объекту, который может получать события, он не сможет быть запущенным.

У большинства объектов в DA в свойствах имеется поле, называемое Script. Согласно документации события EVENT_TYPE_TEAM_DESTROYED оно посылается локации (Area), в котором находится уничтоженная команда, таким образом нам надо открыть локацию hut_interior и найти в его свойствах поле Script. Для локации по умолчанию выбирается скрипт событий area_core, который обрабатывает события локации согласно обычным установкам.

Нам же надо указать в этом поле наш скрипт hut_monsters_slain, для этого нажмите на кнопку выбора (Ellipsis.png) и выберите его. Теперь, когда эта локация будет получать какие-либо события, обрабатывать их будет наш скрипт.

Передача события другим обработчикам

Теперь нам осталось решить только одну проблему. Как упомянуто выше, множество событий, которые принимает объект, обрабатываются основным его скриптом, в случае с локацией это area_core. Наш же скрипт, которым мы заменили основной, обрабатывает только одно событие, остальные же события, которые получает локация, обрабатываться не будут.

Глупо было бы переносить функции, обрабатывающие остальные события, в наш скрипт, для чего есть несколько причин: очень большая работа, возможность допустить ошибки, затруднённое последующее обновление скрипта. К счастью есть способ, который позволит нам после выполнения нашего обработчика передать полученное событие на обработку основному скрипту объекта, а именно функция HandleEvent (en). Она требует два параметра, идентификатор события, которое следует обработать и идентификатор скрипта, в который передаётся событие.

Константы, определяющие основные скрипты, находятся в скрипте global_objects_h, но мы не будем подключать этот скрипт к нашему, так как он уже подключён к скрипту wrappers_h. Таким образом, всё что мы должны сделать, это вставить следующую строку после конструкции switch нашего скрипта:

HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);

Константа RESOURCE_SCRIPT_AREA_CORE определена в global_objects_h, который мы не стали подключать к нашему скрипту, так как это уже сделано в скрипте wrappers_h.

После этого, каждое событие, получаемое нашей локацией hut_interior area, первоначально будет обрабатываться нашим скриптом, с последующей передачей управления основному её скрипту area_core для обработки обычных событий.

Окончательный текст нашего скрипта будет выглядеть следующим образом:

#include "events_h"
#include "plt_clear_The_hut"
#include "wrappers_h"

void main()
{   
    event ev = GetCurrentEvent();
    int nEventType = GetEventType(ev);
    
    switch(nEventType)
    {
         case EVENT_TYPE_TEAM_DESTROYED:
         {
              if(GetEventInteger(ev,0) == 1)
              {
                   WR_SetPlotFlag(PLT_CLEAR_THE_HUT, MONSTERS_SLAIN, TRUE);
              }
              break;
         }
    }
    HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);
}


Язык: English  • русский