WarCraft 3: Добавление действий в редактор триггеров

» Раздел: Работа с MPQ и импортом

Добавление своих функций и действий в редактор триггеров WE

Требуется: TFT; минимальное знание JASS; умение работать с WinMPQ.

Введение - зачем это нужно

Иногда требуется много раз в триггерах карты выполнять одну и ту же последовательность действий. Типичный пример - добавить юнита в какой-либо массив юнитов, а это значит : 1) проверить, не содержится ли уже юнит в массиве, и если нет, то 2) отыскать первую незанятую ячейку массива и 3) записать туда юнита. Сам по себе скрипт (под скриптом в данном случае подразумевается не обязательно JASS-код, но и последовательность действий обычных триггеров), выполняющий это, довольно прост, но если нам это надо в нескольких десятках триггеров? В каждый писать этот скрипт?
Аналогично, если нам надо потом сделать проверку, находится ли юнит в массиве - это тоже цикл - что же, везде, где проверяем, вставлять этот цикл?
Часто используется такой способ: нужный скрипт выносится в отдельный триггер, который затем и вызывается, где это надо. При этом возникает 2 проблемы.
Во-первых, как передать значение (в нашем примере - добавляемого юнита) этому триггеру. Обычно все это пишут в глобальные переменные, к которым скрипт затем обращается, но это неудобно, если надо несколько раз подряд вызывать этот скрипт - всякий раз необходимо перед вызовом заполнять все глобальные переменные.
Во-вторых, что делать, если результатом работы скрипта должно быть значение (т.е. если скрипт "возвращает значение"), пример - проверка наличия юнита в массиве. Можно опять использовать глобальные переменные - в конце своей работы скрипт пишет возвращаемое значение в переменную. Но этот метод может глючить - если у нас этот скрипт может вызываться сразу несколькими триггерами, возможно изменение переменной до того, как вызывающий триггер успеет ей воспользоваться.
Согласитесь, было бы много удобнее, если бы в списке действий появилось ещё одно - "Добавить юнита в массив", где прямо указывается нужный юнит (указание массива целиком в JASS-е, увы, невозможно, поэтому мы будем работать просто с определенной глобальной переменной типа "массив юнитов"). А в списке логических условий - "Юнит такой-то содержится в массиве". Именно этим мы и займемся...

Часть 1. Оформляем нужный скрипт в виде функции

Для начала определимся, что же мы будем добавлять - просто действие, или же функцию, возвращающую значение. Разберу на примерах оба случая - добавление юнита в массив, и проверка, есть ли он там. В обоих случаях потребуется написать функцию на JASS.
Если у вас уже написан нужный скрипт в виде триггера, переведите его в текст и скопируйте его кусок от слов function Trig_XXX_Actions ... до первого слова endfunction.
В нашем примере скрипт получится примерно такой (я уже записал его с локальными переменными, оптимизация текстового скрипта, как и принцип его работы - предмет другой статьи):
function Trig_trigger1_Actions takes nothing returns nothing
local integer i = 0
loop
   exitwhen udg_UnitArray__ == null
   if udg_UnitArray__ == udg_NewUnit then
      return
   endif
   set i = i + 1
endloop
set udg_UnitArray__ = udg_NewUnit
endfunction
Теперь превратим это в функцию, которая использует передаваемые ей значения вместо глобальных переменных.
Подумаем, какие значения нужно передать функции, и запишем это в объявление функции после слова takes, формат записи:
function [имя функции] takes [тип 1-го параметра] [имя 1-го параметра], [тип 2-го параметра] [имя 2-го параметра] [, + еще параметры, если есть] returns [тип возвращаемого значения, если нет - nothing]
Имена функции и параметров - любые.
Например, функции добавления юнита в массив надо передать одно значение - юнита, возвращать ничего не нужно - заголовок такой:
function MyNewAction takes unit u returns nothing
В этом случае к переданному юниту можно обращаться, как к обычной переменной u типа unit, единственное отличие - её нельзя изменять (set u = ...).
После этого надо везде в скрипте заменить обращение к глобальным переменным обращением к переданным значениям. В примере : udg_NewUnit меняем на u. (массив udg_UnitArray придется оставить глобальной переменной - почему, см. выше). Полученный код и есть нужная функция:
function MyNewAction takes unit u returns nothing
local integer i = 0
loop
   exitwhen udg_UnitArray__ == null
   if udg_UnitArray__ == u then
      return
   endif
   set i = i + 1
endloop
set udg_UnitArray__ = u
endfunction
Фунцкии, возвращающие значение, отличаются лишь тем, что после returns в заголовке стоит не nothing, а тип значения, которое она возвращает.
Кроме этого, надо в скрипте вместо записи конечного результата в переменную написать "return [значение]". Тип значения должен совпадать с указанным в заголовке. Как только происходит возврат значения, выполнение функции прерывается.
В конце функции return должен стоять обязательно - даже если скрипт написан так, что до выполнения этого return-а дело никогда не доходит.
Пример - функция, возвращающая логическое значение: true - если юнит есть в массиве, и false - в противном случае.
function MyNewFunc takes unit u returns boolean
local integer i = 0
loop
   exitwhen i > 1023
   if udg_UnitArray__ == u then
      return true
   endif
   set i = i + 1
endloop
return false
endfunction
Теперь о том, где писать эти функции, чтобы ими мог воспользоваться любой триггер на карте. Можно изменить скрипты игры (blizzard.j), но тогда любому желающему поиграть на вашей карте потребуется ваш blizzard.j, что категорически не подходит для мультиплеера (однако вполне уместно для крупных single-player модов). Так вот, в редакторе TFT разработчики сделали возможность добавить свой код в скрипт карты - это как раз то, что нам нужно. Для добавления нужно в редакторе триггеров нажать на значок карты, расположенный слева-сверху, над триггерными категориями, после чего вписать свои функции как обычный текстовый триггер.

Часть 2. Добавляем всё это в обычный редактор

Итак, у нас уже готовы новое действие MyNewAction и функция MyNewFunc: ими уже можно пользоваться в текстовых триггерах, или в обычных, через действие Custom Script: call MyNewAction(udg_Unit). Но хочется большего - добавить первую функцию в список действий, а вторую - в список логических функций.
Всю информацию о триггерных функциях редактор берет из 2-х файлов, находящихся в стандартных MPQ игры: UI\TriggerData.txt и UI\TriggerStrings.txt. Это - обычные текстовые файлы, которые поддаются редактированию обычным блокнотом.
Извлекаем их из war3xlocal.mpq куда-нибудь, и открываем.
1. TriggerData.txt
В этом файле последовательно описана вся информация о "начинке" редактора триггеров - типы переменных, события, условия, действия, функции. Как добавлять свои типы переменных (все выборы из нескольких пунктов в редакторе, напр. Show/Hide - это специальные типы переменных), напишу как-нибудь потом, а пока нас интересуют только действия и функции.
Описание действий начинается со строки [TriggerActions] - это где-то чуть ниже середины файла. На каждое действие отводится 3, в некоторых случаях - 4 строки. Действия располагаются ровно в том порядке, в котором они идут в списке редактора. Лучше добавлять свои действия в самый конец списка (прямо перед строкой [TriggerCalls] - легче будет отыскать их в редакторе, хотя это уже по вкусу - добавить можно куда угодно.
Как писать эти 3 строки для своих действий, объясню на готовом примере. Наше действие MyNewAction будет иметь такой вид:
MyNewAction=1,unit
_MyNewAction_Defaults=_
_MyNewAction_Category=TC_UNIT
1-я строка : после названия функции - цифра 0 или 1 - для каких версий вара, обеих или только TFT, действие предназначено. Все равно, что вы тут укажете - мы работаем только с редактором TFT (в RoC этот параметр вообще отсутствует).
Затем через запятую следуют все типы передаваемых значений, строго в том порядке, как они указаны в заголовке функции. Если параметров нет - ставится nothing.
2-я строка : через запятые указываются значения по умолчанию для соответствующих передаваемых значений. При отсутствии значения по умолчанию (тогда в редакторе триггеров соответствующее значение будет выделено красным) на его месте ставится знак подчеркивания. При отсутствии передаваемых значений вообще - ничего не ставится.
3-я строка : категория действия - для возможности сортировки по категориям в редакторе действий. Список возможных категорий находится в начале файла.
Функции добавляются похожим образом, но после строчки [TriggerCalls]. Опять же, поясню на примере нашей функции MyNewFunc:
MyNewFunc=1,0,boolean,unit
_MyNewFunc_Defaults=_
_MyNewFunc_Category=TC_UNIT
1-я строка : вначале идет версия вара (см.выше), потом 0 или 1 - может ли данная функция использоваться при регистрации событий, затем - тип возвращаемого значения, и только потом - типы передаваемых значений - тут все так же, как и для действий.
2-3 строки - полностью аналогично действиям.
2. TriggerStrings.txt
В этом файле находятся все текстовые строки для событий, условий, действий и функций.
Для каждой функции отведено 3 строки : название функции, её Action Text (это где нужно кликать по подчеркнутым значениям), и подсказка. Все текстовые строки заключаются в кавычки.
Описания действий начинаются со строки [TriggerActionStrings]
Описание действия MyNewAction будет выглядеть так:
MyNewAction="Add Unit To Array"
MyNewAction="Add ",~Unit," to array"
MyNewActionHint=
1-я строка - название - то, что будет отображаться в списке действий.
2-я строка - вот тут следует определить строку, где выбираются значения. Вначале придумываем, как же будет звучать наш текст - Add Unit to array, затем выбираем, где там будет "подчеркнутый текст" (слово "Unit"), эти части отделяем от основной строки кавычками, запятыми и тильдой (~), если подчеркнутая часть идет в начале строки, то кавычка и запятая не ставятся. Если в TriggerData.txt указано значение по умолчанию, то выделенный текст автоматически заменится на это значение, иначе - он будет выделен красным цветом.
В итоге это действие в редакторе действий будет выглядеть так:
Add !!!!Unit!!!! to array
ВАЖНО: Параметры в этой строке должны указываться строго в том порядке, как они указаны в TriggerData.txt. Отсюда совет - при создании функции с несколькими параметрами располагайте их таким образом, чтобы можно было составить хоть сколько-нибудь грамотное предложение.
3-я строка - подсказка, которая выводится в редакторе серым цветом под Action Text-ом. Можно написать все, что угодно :).
Еще одно замечание: любой русский текст обязательно должен быть в кодировке UTF-8, если не знаете, что это такое - пишите лучше по-английски.
Для функций, описания которых идут после строки [TriggerCallStrings], все абсолютно так же. Чтобы завершить наш пример, приведу описание функции MyNewFunc:
MyNewFunc="Unit Is In Array"
MyNewFunc=~Unit," is in array"
MyNewFuncHint=
Теперь, чтобы воспользоваться новыми действиями в редакторе, надо добавить только что отредактированные TriggerData.txt и TriggerStrings.txt обратно в MPQ игры. Лучше всего для этих целей подходит War3Patch.mpq, за его неимением (в TFT 1.07) сойдет и War3xlocal.mpq - именно в этих файлах редактор и ищет в первую очередь. Открываем WinMPQ, в нем открываем War3Patch.mpq (не забудьте обязательно сделать его резервную копию!!!), добавляем оба наших файла, указав префикс "UI\".
Всё! Запускаем WE и наслаждаемся... Если что-то напутали с текстовыми строками (не указали всех параметров, или, наоборот, указали лишние) - редактор известит вас об этом сообщением "Data section and text section has a different number of parameters" при открытии любой карты.
По поводу совместимости: сыграть на вашей карте с нестандартными действиями сможет любой, а вот открыть её редактором сможет только тот, у кого тоже есть исправленные версии текстовых файлов. Делайте выводы :).

Просмотров: 8 736

Iron #1 - 11 лет назад 0
PS: для особо ... умных. Не забудьте закинуть используемые функции в кастом код карты. Для наглядного осмотра данного способа скачайте WEU в нем лежит папочка UI в которой лежат TriggerData.txt и TriggerStrings.txt для дополнительных функций в WEU
GF RaiseD #2 - 5 лет назад (отредактировано ) 0
Какой вообще смысл делать подобное. Если умеешь скрипты писать то попросту незачем добавлять их в оболочку. Бесполезный хлам.