EventWML

Материал из Wesnoth Life Wiki
Перейти к: навигация, поиск

< Данная статья еще недопереведена! Английский оригинал можно прочесть тут >

Тег [event]

Тег [event] может быть вложен в [scenario], [unit_type] и [era], и используется для описания последовательности операций, выполняемых в сценарии в определенный момент. Когда он прописан в теге [scenario] (включая [multiplayer], [tutorial] и [test]), событие возникает только в данном конкретном сценарии, когда в [unit_type] — в любом сценарии, где появляется этот боец (после его появления), а когда в теге [era], то в любом сценарии при использовании этой эры.

Тег содержит ключи и подтеги, определяющие, при каких условиях он будет вызван. Важнейший из них — ключ name. Без него ошибки не будет, но и событие никогда не произойдет. Поэтому он может рассматриваться как обязательный. Все остальные ключи опциональны и не влияют на сам факт возникновения события тем или иным путем.

Заметка по терминологии: Слово "event" в данном случае можно считать сокращением от "event handler", т.к. технически тег является не самим событием игры, а обработчиком, вызываемом при событиях типа 'name'. Однако, в большинстве вопросов это различие непринципиально, поэтому в текущей документации мы обычно будем называть обработчики просто "событиями".

Обязательный ключ 'name'

Использование:

name=<value>

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

Lexicon side note: It is not uncommon to refer to these values as the 'trigger' for an event and, furthermore, to call an event by its 'trigger' name. For example, in an event containing name=moveto, a person might refer to the event as a 'moveto event' and/or refer to the 'moveto trigger' in the event or even talk about the 'event trigger' when referring to the moveto value of the 'name' key in that event. Some or all of this usage can, in fact, be found throughout this page.

name может принимать и список условий возникновения, состоящий как из предопределенных, так и пользовательских имен (имейте в виду, что без first_time_only=no событие срабатывает один раз вообще, а не по разу для каждого имени).

Пример:

name=attacker misses,defender misses

Предопределенные значения

Далее перечислены все предопределенные события движка с указанием условий их возникновения. Любое значение ключа name, которого нет в этом списке, относится к пользовательским и может быть вызвано только через [fire_event] откуда-то еще. Пробелы в именах можно заменять прочерком (к примеру, name=new turn и name=new_turn эквивалентны).

preload
Возникает перед событием prestart и при загрузке сейвов - в любом случае, перед тем, как что-либо будет показано на экране. Может быть использовано для установки окружения Lua: загрузки библиотек, объявления вспомогательных функций, и т.д.
Примечание: В отличие от prestart и start, это событие происходит при каждой загрузке, т.е. более одного раза. Так что не забывайте устанавливать first_time_only=no.
prestart
Возникает перед событием start - до того, как что-либо будет показано на экране. Может быть использовано для установки таких вещей, как принадлежность деревень.
Примечание: Событие возникает только раз, и по определению безразлично к ключу first_time_only.
start
Возникает после отображения карты, но перед началом сценария - перед тем, как игроки смогут что-либо сделать. Неплохо подходит для вступительных диалогов.
Примечание: Событие возникает только раз, и по определению безразлично к ключу first_time_only.
new turn
Возникает в начале каждого хода (не хода стороны). Прежде, чем произойдет какое-либо из подобных событий, переменной turn_number присваивается значение текущего хода.
turn end
Возникает в конце каждого хода (не хода стороны). Переменная side_number содержит последнюю завершившую ход сторону.
turn X end
Возникает в конце хода X.
side turn
Возникает перед получением стороной своей очереди ходить. Передо всеми событиями этого типа, переменной side_number присваивается значение активной игровой стороны. Они происходят раньше деятельности хилеров, вычисления дохода, восстановления статуса и передвижения бойцов.
ai turn
Triggered just before the AI is invoked for a side. This is called after side turn, and thus the WML variable side_number still holds the number of this side. Note that this event might be called several times per turn in case that fallbacks to human or droiding is involved. I.e. it happens at the middle of turn of human side 1 if the human player droids his side. It happens after the selection of ai to play the turn but before AI is told that new turn has come.
Note: This event currently breaks replays since it is not explicitly saved in a replay and there is no AI involved in replays...
turn refresh
Как и side turn, возникает перед ходом какой-либо стороны, но уже после восстановления, лечения, дохода и т.п.
Несмотря на то, что никакого обновления на первом ходу не происходит, события восстановления хода вызываются и на нем.
turn X
Возникает в начале хода X, самым первым изо всех подобных событий:
1) turn X
2) new turn
3) side turn
4) side X turn
5) side turn X
6) side X turn Y
7) turn refresh
8) side X turn refresh
9) turn X refresh
10) side X turn Y refresh
side X turn Y
Возникает вначале хода Y стороны X.
side X turn
Возникает в начале каждого хода стороны X.
side turn X
Срабатывает в начале хода X для каждой стороны.
side X turn Y refresh
Возникает при восстановлении хода Y для стороны X.
side X turn refresh
Возникает при каждом восстановлении хода стороны X.
turn X refresh
Возникает по восстановлению хода X для каждой стороны.
side turn end
Происходит по завершению хода какой-либо стороной. Как и в случае с событиями начала хода, есть еще несколько вариантов для разных комбинаций хода и стороны. Очередность их возникновения такова:
1) side turn end
2) side X turn end
3) side turn X end
4) side X turn Y end
5) turn end
6) turn X end
time over
Возникает при завершении времени, определенного лимитом ходов (т.е. на ходу turns, заданном в теге [scenario]).
enemies defeated
Возникает при убийстве всех бойцов с canrecruit=yes (т.е. лидеров), не союзных со стороной 1. Оно происходит раньше, чем событие victory, и, более того, позволяет вместо победы объявить поражение.
victory
Возникает при победе в сценарии. В т.ч. к вызову события приводит [endlevel] с result=victory. It helps debugging if the victory event allows you to safely advance to any of the possible next maps after using the ":n" command. Scenarios where key units are picked up before the victory, or where some action chosen earlier determines which map to advance to, make it hard to quickly test scenarios in a campaign.
defeat
Возникает при поражении в сценарии. В т.ч. к вызову события приводит [endlevel] с result=defeat.


Следующие события провоцируются действиями бойцов (moveto, attack) или происходящими с ними явлениями (recruit, advance). При возникновении любого из этих событий, главный его виновник целиком сохраняется в переменную unit (далее будем называть его "первычным бойцом"), а соучастник - в second_unit (далее будем называть "вторичным бойцом"). Координаты бойцов сохраняются соответственно в x1,y1 и x2,y2. К тому же, в событиях attack, attacker_hits, defender_hits, die и last_breath доступны переменные weapon и second_weapon. Подробней об автоматически сохраняемых переменных описано в SyntaxWML.


moveto
Возникает после передвижения первичного бойца по карте. Обычно используется с фильтром локаций для отслеживания его попадания в определенную область. Первичные координаты $x1 и $y1 указывают на поле, где боец остановился, а $x2 и $y2 - откуда он пришел; пройденные же им территории не учитываются.
Если боец захватывет деревню, то событие capture происходит раньше, чем это.
An [allow_undo] tag anywhere within a moveto event will cancel any lack of undo functionality the event would have caused. Note that undo functionality will only move the unit back to its former location; it will not undo other changes to the game caused by the event. Thus it is up to the scenario designer to use this tag correctly.
sighted
Важно: Событие "sighted" в целом очень глючное, особенно при включенной задержке обновления пелены. Рекомендуется любой ценой его избегать, особенно если в обработчике вы хотите изменять состояние игры (например, устанавливать переменные). Бессмысленно даже пытаться документировать все исключения, при которых sighted работает неправильно или не работает вообще - имейте это в виду.
Альтернативы: Иногда его можно заменить событием moveto с проверкой ближайших территорий через фильтр локаций. В некоторых случаях может быть полезен filter_vision. Ну и в меинлайне есть макрос ON_SIGHTING, обеспечивающий обходной путь.
Событие sighted возникает, когда с первичного бойца слетает пелена или туман войны. Это может случиться, когда вторичный боец подходит на достаточно близкое расстояние и обнаруживает первичного. Либо первичный боец может вылезти "из тумана" и быть обнаруженным сразу всеми членами вражеской стороны - в таком случае second_unit не присвается никакое значение (один из багов события).
Примечание: Более серьезный баг заключается в том, что событие срабатывает только когда боец просто так перемещается между локациями; но не когда он идет в атаку, и за одно открывает какого-нибудь вражеского бойца. Это делает sighted крайне ненадежным.
(версия 1.11 и выше) Исправлено поведение при передвижении с атакой. Однако, большинство других багов все еще остается.
enter_hex
(версия 1.11 и выше) Возникает при заходе на каждый гексагон во время передвижения. Кооринаты данного поля сохраняются как $x1,$y1, а предыдущего (с которое перешли) - как $x2,$y2. If this event is handled without using [allow_undo], then movement is interrupted, stopping the unit where it is.
Note: This event behaves a bit unusually if the hex is occupied (and the moving unit is simply passing through). When this happens, $x1,$y1 is still the hex where the event was triggered, but the moving unit (stored in $unit) will be located somewhere earlier in the route (the most recent unoccupied hex). That is, $x1,$y1 will not equal $unit.x,$unit.y (a condition that can be used to detect when the entered hex is occupied). The moving unit will have already spent its movement points to enter the event's hex even though it is has not actually moved from the most recent unoccupied hex.
exit_hex
(версия 1.11 и выше) Возникает при выходе с каждого гексагона во время передвижения. Кооринаты данного поля сохраняются как $x1,$y1, а следующего (на которое боец собирается перейти) - как $x2,$y2. If this event is handled without using [allow_undo], then movement is interrupted, stopping the unit where it is.
Note: This event behaves a bit unusually if the hex is occupied (and the moving unit is simply passing through). When this happens, $x1,$y1 is still the hex where the event was triggered, but the moving unit (stored in $unit) will be located somewhere earlier in the route (the most recent unoccupied hex). That is, $x1,$y1 will not equal $unit.x,$unit.y (a condition that can be used to detect when the exited hex is occupied). The moving unit will have already spent its movement points to enter the event's hex even though it is has not actually moved from the most recent unoccupied hex.
attack
Возникает, когда первичный боец атакует вторичного.
attack end
То же, что attack, но возникает после боя, а не перед ним. Однако, раньше, чем всевозможные die (если кто-то умирает).
attacker hits
Возникает, когда первичный боец (нападающий) попадает по вторичному (обороняющемуся). Устанавливает переменной damage_inflicted количество нанесенного урона.
attacker misses
Возникает, когда нападающий промахивается.
defender hits
Возникает, когда по первичному бойцу (нападающему) попадает контратака вторичного (обороняющегося). Устанавливает переменной damage_inflicted количество нанесенного урона.
defender misses
Возникает, когда обороняющийся промахивается.
petrified
Возникает, когда по первичному бойцу попадает окаменяющая атака вторичного бойца (см. stones в AbilitiesWML).
last breath
Возникает при убийстве первичным бойцов вторичного, перед анимацией смерти. Используйте вместо name=die, когда хотите, чтоб умирающий перед смертью что-то сказал.
die
Возникает при смерти первичного бойца от рук вторичного.
Примечание: Умерший боец не удаляется из игры до окончания данного события. Так что во-первых он еще обнаруживается стандартными фильтрами (кроме [have_unit]); во-вторых можно манипулировать с его данными; а также он все еще занимает ячейку карты, и другие бойцы не могут на нее зайти. Для предотвращения данного поведения можно воспользоваться командой [kill], но учтите, что она отменит запуск остальных событий, связанных с его смертью.
capture
Возникает, когда боец захватывает деревню (перед событием moveto). Деревня может быть как нейтральной, так и принадлежащей другой стороне — предыдущий хозяин прописывается в переменной $owner_side (для нейтральных — 0). Но простое посещение своей деревни захватом не считается. Не вызывают событий захвата и деревни, становящиеся ничейными (через [capture_village]).
recruit
Возникает после вербовки первичного бойца вторичным (в фильтре события можно задать определенный вид бойцов).
prerecruit
Возникает после вербовки первичного бойца вторичным, но перед его отображением в игре.
recall
Возникает после призыва первичного бойца вторичным.
prerecall
Возникает после призыва первичного бойца вторичным, но перед его отображением в игре.
advance
Возникает перед повышением уровня бойца, будь то смена класса или УПМУ (если у игрока есть выбор ветки развития, то после этого диалога). В случае если обработчик удаляет бойца или уменьшает его опыт до такой степени, что перестает хватать на уровень, то повышение уровня соответственно отменяется. Однако, в стабильной версии отмена УПМУ не работает - баг. (версия 1.11 и выше) Ошибка с отменой устранена. Заплатку не перенесли на 1.10 из-за возможных проблем с совместимостью.
post advance
Возникает после продвижения бойца на новый уровень (т.е. после смены класса или УПМУ).
select
Возникает при выделении бойца; а в версиях до 1.11 также в конце его передвижения, т.к. в этом случае игра поддерживает выделение через перевыбор.
Примечание: В сетевых играх данное событие вызывается только в клиенте выделяющего бойцов игрока; так что если вы поместите в обработчик какой-то код, меняющий состояние игры, он приведет к ошибкам синхроницации.'
menu item X
Возникает при выборе пункта меню с id=X.
Примечание: Если в пункте меню определен тег [command], то прописанные в нем операции могут выполниться как до события, так и после него — тут уж никаких гарантий.

Пользовательские события

События с произвольными именами можно вызывать тегом [fire_event]. Чаще всего они служат подпрограммами для предопределенных. Например, несколько различных sighted могут вызвать одно и то же собственное событие, изменяющее задачи сценария.

Опциональные ключи и теги

Они служат для более детальной настройки, как и при каких условиях должны обрабатываться те или иные события.

first_time_only

Указывает, должен ли обработчик быть удален из сценария после выполнения. Принимает логические значения:
first_time_only=yes
Событие обрабатывается только раз, и никогда более. Это значение по умолчанию.
first_time_only=no
Событие обрабатывается каждый раз, когда соответствует определенным критериям.

id

Если указано, то при наличии уже объявленного события с таким же индентификатором, новое не добавляется. Также с его помощью можно удалять события, как описано далее.

remove

Указывает, что вместо добавления нового события нужно удалить одно из старых, заданное по идентификатору. Все остальное содержимое тега при этом игнорируется. Пример:
[event]
    id=id_of_event_to_remove
    remove=yes
[/event]

[filter]

Событие возникает только в случае, если первичный боец соответствует фильтру.

[filter_second]

То же, что [filter], но для вторичного бойца.

[filter_attack]

Позволяет задать дополнительные условия, исходя из того, какую атаку использовал первичный боец. Применим к событиям attack, attacker hits, attacker misses, defender hits, defender misses и attack end. Подробное описание фильтра смотрите в FilterWML, но наиболее используемые ключи следующие:

  • name: название атаки
  • range: дальность боя
  • special: особенности

[filter_second_attack]

То же, что [filter_attack], но для вторичного бойца.

[filter_condition]

Требует, чтобы заданные условные операции возвращали true. Этот фильтр можно использовать во всех видах событий, включая пользовательские.
note: This tag is meant to be used when the firing of an event shall be based on variables/conditions which cannot be retrieved from the filtered units.

[filter_side]

Для срабатывания события текущая сторона (обычно это $side_number) должна удовлетворять фильтру.
Примечание: Данный фильтр наиболее полезен для событий типа side turn и turn refresh. Тем не менее, какая-либо сторона значится при каждом событии, так что вполне можно задать [filter_side] внутри moveto и предотвратить обработку передвижения при несоответствии сторон.

delayed_variable_substitution

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

Выполнение операций

После удовлетворения всех условий, начинают выполняться прописанные в обработчике теги операций - один за другим, в том порядке, в котором были указаны.

Операции делятся на 3 основных типа:

Подобней описано в ActionWML.

Вложенные события

При создании событий есть один особый момент, который тоже можно считать своеобразной операцией. Если поместить один [event] внутрь другого [event], то вложенный тег будет активирован (создан) только после выполнения внешнего.

(Смотрите пример)

Отложенная подстановка переменных

Подстановка переменный во вложенных событиях может происходить как во время зарождения из материнских, так и в момент их собственного срабатывания. За это отвечает ключ вложенных событий delayed_variable_substitution.

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

Это поведение можно быть настроено с помощью специального синтаксиса указания переменных. Если вместо $variable вы используете $|variable, то получите "собственные" переменные события, даже при отключенном delayed_variable_substitution. Таким образом, можно одновременно использовать и те, и другие значения.

(Смотрите пример)

Сетевая безопасность

In multiplayer it is only safe to use WML that might require synchronization with other players because of input or random numbers (like [message] with input or options or [unstore_unit] where a unit might advance) in the following events. This is because in these cases WML needs data from other players to work right and/or do the same thing for all players. This data is only available after a network synchronization.

List of synchronized events:

  • moveto
  • sighted
  • attack
  • attack_end
  • attacker hits
  • attacker misses
  • defender hits
  • defender misses
  • stone
  • last breath
  • menu item X
  • die
  • capture
  • recruit
  • prerecruit
  • recall
  • prerecall
  • advance
  • post_advance
  • new turn
  • side turn
  • turn X
  • side X turn
  • side X turn Y
  • turn refresh

There is also the possibility of events that are normally synchronized when fired by the engine but can be non-synchronized when fired by WML tags from non-synchronized event. So when you are using them you must be extra careful. For example [unstore_unit] may trigger a unit advancement that will fire advance and post advance events.

Ловушка для неосторожных

You need to beware of using macros to generate events. If you include a macro expanding to an event definition twice, the event will be executed twice (not once) each time the trigger condition fires. Consider this code:

#define DOUBLE
    [event]
        name=multiply_by_2
        {VARIABLE_OP 2_becomes_4 multiply 2}
    [/event]
#enddef

{DOUBLE}
{DOUBLE}

{VARIABLE 2_becomes_4 2}

[fire_event]
    name=multiply_by_2
[/fire_event]

{DEBUG_MSG "$2_becomes_4 should be 4"}

Он покажет, что переменная равна 8, а не 4.

Идентификаторы событий

Этой проблемы можно избежать, если установить событию идентификатор:

#define DOUBLE
    [event]
        name=multiply_by_2
        id=doubler_event
        {VARIABLE_OP 2_becomes_4 multiply 2}
    [/event]
#enddef

Events with the same ID will only be accepted once by the engine no matter how many times they are included, and will only be saved once to the scenario's savefile. Events with an ID can also be removed by using the remove key, i.e.:

[event]
    id=doubler_event
    remove=yes
[/event]

After that WML is encountered (at toplevel or after created from another event), the event with this ID is removed from the scenario wml, thus firing it has no effect. After an event is removed, it can still be re-added later.

Различные примеры и примечания

Пример диалога между первичным и вторичным бойцами

В теге [message] первичного бойца события можно указывать как unit, а вторичного - как second_unit.

[event]
    name=die
    [message]
        speaker=second_unit
        message= _ "Hahaha! I finally killed you!"
    [/message]

    [message]
        speaker=unit
        message= _ "It's not over yet! I'll come back to haunt you!"
    [/message]
[/event]

Пример вложенного события

Событие создано для портала, который должен открыться на 10 ходу. В этот момент будет выполнено внешнее событие, создав вложенное moveto. А потом, когда игрок зайдет в определенное место, сработает вложенное.

[event]
    name=turn 10
    [event]
        name=moveto
        [filter]
            x,y=5,8
        [/filter]
        # перемещение на поле 5,8 вызывает это событие только с 10 хода сценария
    [/event]
[/event]

An equivalent way of doing this would be to create a single moveto event with an [if] statement to check for turn number but using nested [event] tags is a convenient shortcut to accomplish this task without resorting to [if] statements.

Пример отложенной подстановки переменных

Этот код показывает номер хода, в котором было вызвано вложенное moveto:

[event]
    name=turn 10
    [event]
        name=moveto
        delayed_variable_substitution=yes
        [filter]
            x,y=5,8
        [/filter]
        {DEBUG_MSG "Ход $turn_number"} 
   [/event]
[/event]

Т.к. это стандартное значение ключа delayed_variable_substitution, такой код делает то же самое:

[event]
    name=turn 10
    [event]
        name=moveto
        [filter]
            x,y=5,8
        [/filter]
        {DEBUG_MSG "Ход $turn_number"}
   [/event]
[/event]

Но следующий код при срабатывании moveto всегда показывает "Ход 10", потому что подстановка осуществляется в момент зарождения события, а не его вызова:

[event]
    name=turn 10
    [event]
        name=moveto
        delayed_variable_substitution=no
        [filter]
            x,y=5,8
        [/filter]
        {DEBUG_MSG "Ход $turn_number"}
   [/event]
[/event]

А последний вариант индентичен двум первым. Он показывает ход срабатывания события moveto, несмотря на то, что delayed_variable_substitution=no. Это происходит благодаря особому синтаксису $|variable.

[event]
    name=turn 10
    [event]
        name=moveto
        delayed_variable_substitution=no
        [filter]
            x,y=5,8
        [/filter]
        {DEBUG_MSG "Ход $|turn_number"}
   [/event]
[/event]

Многократное вложение событий

Every delayed_variable_substitution=no causes a variable substitution run on the subevent where it occurs at the spawn time of this event and on all following subevents. For any specific event, variable substitution happens at least one time when the event is executed. For each delayed=no key appearing in itself or in an event of an "older" generation, which is not the toplevel event, an additional variable substitution run is made.

[event]# parent
    name=turn 2
    #delayed_variable_substitution=no # В материнском событии этот ключ игнорируется

    [event]# child
        name=turn 3
        delayed_variable_substitution=no # Causes variable substitution in the child, grandchild and great-grandchild event
        # at execution time of the parent event = spawn time of the child event.

        [event]# grandchild
            name=turn 4
            delayed_variable_substitution=yes # no variable substitution in the grandchild and great-grandchild event
            # at execution time of the child event = spawn time of the grandchild event

            [event]# great-grandchild
                name=turn 5
                {DEBUG_MSG $turn_number}# output: 2 - value from the variable substitution at execution time of the parent event,
                # caused by delayed=no in the child event

                {DEBUG_MSG $||turn_number}# output: "$turn_number"
                # Each variable substitution transforms a "$|" to a "$" (except when no | left).

                {DEBUG_MSG $|turn_number}# output: 5 - from the variable substitution at execution time
                # of the great-grandchild event
            [/event]
        [/event]
    [/event]
[/event]


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