Учебный пример

Содержание:

Подготовка рабочего места и расходных материалов

Прежде чем начать, убедимся что у нас есть все необходимое для комфортного выноса мозга.Первое что нам понадобится это JNGP и утилита Fly Data Processor. Можно, конечно, обойтись и без JNGP, но это достаточно сложная задача, заслуживающая написания отдельной статьи, так что если у вас какие-то личные разногласия с JNGP, самое время их решить.
JNGP
Fly Data Processor beta 0.2
Также нам понадобится карта в формате w3x, с которой мы и будем работать.
Для тех, кто раньше не пользовался Fly Data Processor, подробнее остановимся на его установке.
  • устанавливаем Java 7
  • скачиваем архив Fly Data Processor beta 0.2 по ссылке выше
  • находим папку, в которую установлен JNGP
  • делам резервную копию файла wehack.lua
  • удаляем папку fly из папки с JNGP, если таковая там внезапно обнаружилась
  • извлекаем содержимое архива с заменой файлов (wehack.lua должен быть заменен, а в папке с JNGP должна появиться папка Fly из архива)
  • перезапускаем JNGP, если он был запущен

Постановка задачи

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

База данных

Чтобы не забивать голову сложными алгоритмами реализуем базу данных на хештаблице. Желающие могут использовать любой другой способ хранения данных - на функциональность примера это не повлияет.
Из постановки задачи ясно что нам понадобится как минимум два поля в базе данных для каждой способности - время перезарядки и коэффициент, влияющий на получаемый от ауры урон.
Для доступа к этим данным определим две функции:
» function getAbilityCooldown takes integer id returns real
return LoadReal(table0,id,0)
» function getAbilityModifier takes integer id returns real
return LoadReal(table0,id,1)
Также нам понадобится способ поместить способность в базу данных:
» function saveAbilityData takes integer id, real cool, real mod returns nothing
SaveReal(table0,id,0,cool)
SaveReal(table0,id,1,mod)
Также обязательно нужно не забыть создать глобальную хештаблицу table0 и инициализировать ее.
На это можно считать работу по созданию базы данных завершенной - заполнение и использование базы будет рассмотрено дальше.

Шаблоны

Ну вот и пришло время заняться написанием шаблонов. Мы будем использовать их для автоматического заполнения базы данных.
Для учебного примера, содержащего не слишком большое количество способностей, нет смысла создавать сложную структуру сохранения данных, так что разместим запись всех данных в одной функции.
Можно, конечно, записать все данные, вызвав функцию saveAbilityData столько раз, сколько у нас способностей, и передав ей нужные параметры, но делать это вручную долго и неудобно, да и следить за актуальностью данных в базе достаточно неприятная обязанность. Поэтому воспользуемся директивой list чтобы сгенерировать вызов функции saveAbilityData для каждой способности.
» function saveAbilityDataAll takes nothing returns nothing
<list abilities as a>
   call saveAbilityData('${a}',${a.acdn[1]},0)
</list>
примечания:

<list abilities as a> директива перечисления. Здесь abilities это стандартная коллекция объектов, в которую помещаютс явсе способности, a - объявление локальной переменной, в которую будет помещаться указатель на объект и которая будет видна только внутри данной директивы

'${a}' обращение к напрямую к объекту возвращает его равкод, а поместив этот равкод в одинарные кавычки получаем число, соответствующее данному равкоду (запись равкода в одинарных кавычках для получения числа это родная особенность синтаксиса jass)

${a.acdn[1]} такая конструкция позволяет обратиться к значениям многоуровневых полей. В данном случае a это переменная, содержащая ссылку на объект, acdn - код поля времени перезарядки, [1] - обращение к значению поля acdn на первом уровне способности
Вот и все, при сохранении карты в функцию saveAbilityDataAll будут помещены вызовы функции saveAbilityData для каждой способности с нестандартными данными. Главное не забыть вызвать эту функцию перед началом игры.
Приведенный выше шаблон имеет несколько недостатков:
  • если у способности стандартное время перезарядки, то вместо ${a.acdn[1]} будет подставлено пустое место
  • не учитывается коэффициент урона от ауры
Эти две проблемы можно решить самыми разными способами, рассмотрим один из них:
  • будем сохранять в базу только способности с нестандартным временем перезарядки
  • для хранения коэффициента получаемого урона добавим пользовательскую категорию с действительным параметром, а все способности, не принадлежащие к этой категории будем считать получающими полный урон от ауры
Для начала исключим из базы все способности со стандартным значением времени перезарядки:
» function saveAbilityDataAll takes nothing returns nothing
<list abilities as a>
   <if a.acdn[1]>call saveAbilityData('${a}',${a.acdn[1]},0)</if>
</list>
примечания:
<if a.acdn[1]> в директиве if обращение к полю объекта без использования логических операторов позволяет проверить наличие данного поля у объекта, таким образом в базу попадут только способности, для которых задано нестандартное время перезарядки.

Пользуясь директивой if можно было бы также назначить значение этого поля по умолчанию, которое использовалось бы в случае невозможности получить настоящее значение.
Теперь получим значение коэффициента получаемого урона:
» function saveAbilityDataAll takes nothing returns nothing
<list abilities as a>
   <if a.acdn[1]>
      <if a.modifier>
         call saveAbilityData('${a}',${a.acdn[1]},${a.modifier})
      <else>
         call saveAbilityData('${a}',${a.acdn[1]},1.0)
      </if>
   </if>
</list>
примечания:

<if a.modifier> проверка на наличие поля modifier у объекта, не следует путать с проверкой на принадлежность к категории - в общем случае эти две проверки не равнозначны.

Такая запись была выбрана из соображений удобства при прочтении, на деле никто не мешает записать все в одну строку, заключив в директиву <if a.modifier> не весь вызов функции, а только значение ее третьего параметра примерно так <if a.modifier>${a.modifier}<else>1.0</if>

Подготовительные работы

Редактор объектов

В качестве основы для ауры удобно взять ауру замедления торнадо с нулевым коэффициентом. Выдадим ей описание, отображение и баф.
Также подготовим несколько способностей для демонстрации воздействия ауры. Главное требование - нестандартное время перезарядки.
Одной или нескольким способностям выдадим нестандартный коэффициент получаемого от ауры урона. Для этого добавим к полю суффикс редактора (editor suffix) следующее:
@modifier:0.0
Вместо 0.0 можно указать любое число, просто обнуление урона проще всего заметить в игре.
Также важно не забыть выдать все эти способности каким-нибудь юнитам и разместить их на карте.

Триггеры

Самая простая версия триггера, обеспечивающего работу ауры, банальна до невозможности:
Событие - приведение способности в действие
Условие - проверка на наличие бафа от ауры
Действия - вычисление и нанесение урона
При использовании ГУИ, то получение значений из базы и вычисление урона будет происходить через custom script.
Для удобства можно вынести в отдельную функцию получение урона из базы данных:
» function getBaseDamageForAbility takes integer id returns real
    return getAbilityCooldown(id)*getAbilityModifier(id)
Ну и последний штрих - необходимо где-нибудь вызвать функцию saveAbilityDataAll чтобы заполнить нашу базу данных, собранными утилитой значениями.
Добавим для наглядности вывод наносимого урона в чат и можно запускать карту на проверку. Да, перед запуском карту обязательно нужно сохранить.
В прикрепленном файле - карта, получившаяся у меня в ходе написания этой статьи.


Views: 1 822

Shown only a small set of comments around the pointed one. Go to actual.

prog #1 - 10 years ago 0
Голосов: +0 / -0
Перерыв. Допишу завтра.
horhy123 #2 - 10 years ago 0
Голосов: +0 / -0
У меня возник вопрос: Можно ли во время игры узнать текущую силу атаки конкретного юнита, включая баффы и тому подобные модификаторы атаки?
prog #3 - 10 years ago 1
Голосов: +1 / -0
horhy123, напрямую - нет, для этого нужно с помощью утилиты собрать в базу все данные по модификаторам атаки, а затем написать систему, вычисляющую атаку на основании этих данных.

Shown only a small set of comments around the pointed one. Go to actual.