Изменения после выхода обучающих видео

С момента выхода оригинальных обучающих видео (5-6 лет назад) Denizen получил множество обновлений! На этой странице собраны изменения, о которых стоит знать ради актуальных best-practices.

Рекомендованный редактор

В обучающих видео в качестве редактора скриптов показывался Notepad++. Это был просто чуть более удобный текстовый редактор. С тех пор у нас появился полноценный скриптовый редактор — расширение для VS Code.

Подробности — на странице «Настройка редактора скриптов».

Обратите внимание, что в связи с этим расширение .yml больше не используется. Вместо него — .dsc.

Мы теперь в Discord

В обучающих видео показано общение с ботом в IRC — если вы попробовали зайти в этот IRC, вы уже видели сообщения о том, что мы переехали в Discord. Вероятно, вы и так это знаете, раз уж добрались до этого руководства.

Но на всякий случай: мы в Discord — заходите!. Там есть живая поддержка людьми, а также бот-помощник для поиска по документации, проверки скриптов и т. п.

Синтаксис с двоеточием

Синтаксис с фигурными скобками заменён на синтаксис с двоеточием. Как описано на странице «Команда if», синтаксис с двоеточием позволяет размещать несколько команд внутри команд вроде if и foreach. В отличие от синтаксиса со скобками, никакой закрывающий символ не нужен — как только вы вернётесь к предыдущему уровню отступов, скрипт продолжит выполняться как обычно.

Если у вас есть скрипты на синтаксисе со скобками, вы можете быстро перевести их на синтаксис с двоеточием: удалите все закрывающие фигурные скобки (}), замените открывающую ({) на двоеточие и уберите лишние пробелы между двоеточием и последним другим символом в строке.

Вот пример такого перевода:

old_brace_syntax:
    type: task
    script:
    - if <player.has_flag[test]> {
        - narrate "This is an example of braced syntax!"
        }
    - narrate "This narrate will always run!"
new_colon_syntax:
    type: task
    script:
    - if <player.has_flag[test]>:
        - narrate "This is an example of colon syntax!"
    - narrate "This narrate will always run!"

Синтаксис с двоеточием проще, чище выглядит, и в качестве бонуса Denizen ещё и парсит его быстрее!

Синтаксис определений

В команде define произошло два важных изменения: во-первых, изменился синтаксис самого тега определения, а во-вторых, теперь команда define поддерживает data actions.

В видео использовались две разные формы синтаксиса: первая — «древний» процентный синтаксис (вроде %this%), вторая — «старый» синтаксис с тегами (вроде <def[this]>). Теги определений больше так не выглядят. Теперь они выглядят как <[this]> (для определения с именем this, разумеется). Дополнительные детали об использовании и модификации определений — на странице «Определения».

Учтите, что %name% считается древним и полностью неподдерживаемым, его использовать нельзя. А вот <def[name]> — это более старый/альтернативный синтаксис для современного <[name]> (та же база тегов, только теперь вместо тега с именем def доступен пустой тег).

Вот пример перевода со старого синтаксиса на новый:

old_definition_syntax:
    type: task
    script:
    - define name <player.name>
    # Древний
    - narrate "Hello, %name%!"
    # Старый
    - narrate "Hello, <def[name]>!"
new_definition_syntax:
    type: task
    script:
    - define name <player.name>
    - narrate "Hello, <[name]>!"

Изменения в команде while

Раньше команда while принимала только один аргумент, поэтому для вычисления условий приходилось использовать теги вроде <player.health.is[>=].to[10]>]>.

Этого ограничения больше нет — команда while теперь поддерживает операторы, как и команда if.

Вот пример task-скрипта, который ждёт, пока у игрока здоровье не упадёт ниже 10, и затем предупреждает его — со старым и современным синтаксисом:

old_while_syntax:
    type: task
    script:
    - narrate "Challenge: don't lose too much health!"
    - while <player.health.is[>=].to[10]>:
        - wait 1s
    - narrate "Your health got too low! You lose!"
new_while_syntax:
    type: task
    script:
    - narrate "Challenge: don't lose too much health!"
    - while <player.health> >= 10:
        - wait 1s
    - narrate "Your health got too low! You lose!"

Заметьте, что конкретно этот стиль «подождать, пока что-то случится» был заменён на специализированную команду waituntil, которую можно использовать так:

waituntil_example:
    type: task
    script:
    - narrate "Challenge: don't lose too much health!"
    - waituntil rate:1s <player.health> < 10
    - narrate "Your health got too low! You lose!"

Изменения в assignment-скриптах

Раньше на NPC вешали несколько interact-скриптов и использовали условия, чтобы выбрать, какой из них запустится. В Denizen теперь есть шаги внутри interact-скриптов и команда zap, так что нужен (и поддерживается) только один interact-скрипт. Номера рядом с записями interact scripts в assignment-скрипте больше не нужны и их следует убрать.

Вот примеры старого и обновлённого синтаксиса:

old_assignment_script:
    type: assignment
    interact scripts:
    - 10 my_cool_npc_interaction
new_assignment_script:
    type: assignment
    interact scripts:
    - my_cool_npc_interaction

stop — это новый queue clear

Раньше для остановки выполняющейся очереди использовали queue clear. Эту команду обновили до stop.

Замена простая:

old_queue_clear:
    type: task
    script:
    - if <player.has_flag[buff]>:
        - queue clear
    - narrate "You don't have the necessary buff!"
new_stop:
    type: task
    script:
    - if <player.has_flag[buff]>:
        - stop
    - narrate "You don't have the necessary buff!"

Для проверки наличия флага используйте .has_flag[]

Раньше, в том числе в обучающем видео «Квест на убийство», .flag[] использовался и для получения значения флага, и для проверки, существует ли флаг вообще.

Теперь правильный способ проверить наличие флага — использовать .has_flag[]. .flag[] теперь используется только для чтения значения и только там, где заведомо известно, что флаг существует (или с fallback'ом). Вот пример:

old_flag:
    type: task
    script:
    - if <player.flag[VIP]>:
        - narrate "Your VIP level is <player.flag[VIP]>!"
new_has_flag:
    type: task
    script:
    - if <player.has_flag[VIP]>:
        - narrate "Your VIP level is <player.flag[VIP]>!"

Переделка системы флагов

В дополнение к тегу has_flag, в декабре 2020 года произошла полная внутренняя переделка системы флагов.

Самое релевантное изменение по сравнению с видео: тег <player.flag[NAME].expiration> из видео про квест на убийство теперь нужно писать как <player.flag_expiration[NAME].from_now>.

Из-за серьёзных общих изменений в системе флагов используйте более современную документацию, а не старые видео — загляните на страницу «Флаги».

Отмена событий теперь чуть сложнее

В видео про GUI-инвентари учили отменять обобщённое событие клика и выполнять действия в ответ на более конкретное событие.

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

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

Так что вместо on player clicks in my_inventory: теперь нужно on player clicks in my_inventory priority:100:, и аналогично для события drags. Приоритеты событий обрабатываются по порядку, по умолчанию равны 0. Так что сначала отработают все конкретные события со стандартным приоритетом, и только после них — общие события-отмены (теперь с приоритетом 100).

Теги с «крышкой» больше не используются

В видео показывался тег <^npc> как способ получить NPC из очереди до того, как будет использован аргумент npc:, и упоминался <^player> как аналогичная штука. Сейчас они считаются устаревшими/неактуальными — вместо них просто используйте define:

better_npc_handler:
    type: task
    script:
    - narrate "Let's pass NPCs backwards"
    - define npc <npc>
    - run my_other_task def:<[npc]> npc:<[some_other_npc]>

В примере выше команда define и тег <[npc]> заменяют то, что раньше было тегом <^npc>.

(Убрано из-за сочетания крайне редкого использования и того, что сложная внутренняя реализация нагружала движок скриптов, даже когда эти теги не использовались.)