Меняем путь: команда If¶
Что такое команда «If»?¶
Вы узнали, как с помощью событий запускать набор команд при наступлении в игре определённой ситуации. Вы узнали, как с помощью тегов менять поведение команд в зависимости от деталей этой ситуации.
Теперь пришло время познакомиться с командой if, которая объединяет обе эти идеи: выбор набора команд для запуска в зависимости от деталей ситуации.
Команда if делает ровно то, что написано на её «этикетке»: говорит «если что-то истинно, выполни вот эти команды. Иначе — не выполняй».
Так как же выглядит команда If?¶
Не переживайте, команды if пишутся довольно просто и выглядят в скрипте Denizen ровно так же, как всё остальное.
Вот базовый формат:
- if (some condition here):
- (some commands)
- (go here)
Как видите, if — это команда (записывается так же, как и любая другая, в начале строки, с каким-то условием (условиями) в качестве входных аргументов),
но в конце у неё стоит : (так же, как в строке события),
а вложенные команды смещены дополнительным отступом внутрь. Самое важное здесь — не забыть про отступ у вложенных команд.
Если вложенные команды не сдвинуть на шаг, Denizen не поймёт, что они должны были находиться внутри if, и выполнит их вне зависимости от условия.
Давайте посмотрим, как это может выглядеть в настоящем скрипте…
magic_healing_bell:
type: world
events:
after player right clicks bell:
- if <player.health_percentage> < 25:
- heal
- actionbar "<&[base]>The bell has healed you!"

Этот удобный пример скрипта мгновенно лечит игрока, который кликнул по блоку колокола, — но только если у него критически мало здоровья. (Мы будем постепенно дорабатывать этот пример на протяжении раздела, а когда дойдём до раздела о флагах, вернёмся к нему, чтобы добавить ограничение по времени, и тогда лечиться можно будет не чаще, чем раз в несколько минут.)
Скрипт также показывает игроку сообщение в actionbar, чтобы тот знал, что его вылечили, — с использованием базового цвета текста, заданного в вашем файле config.yml для Denizen.
Условия¶
В команде if условие можно записать несколькими способами.
В простейшем случае условие — это булев тег (который возвращает «true» или «false») — и этого уже достаточно. (Например, - if <player.on_fire>:).
Вложенные команды if выполняются, когда тег возвращает true, и не выполняются, когда тег возвращает false.
Такие простые булевы команды if можно инвертировать (выполнять вложенные команды, когда тег возвращает «false», и не выполнять, когда возвращает «true») с помощью символа ! (читается как «не»).
Например, - if !<player.on_fire>: выполнится только в том случае, если игрок не горит.
Условие может быть и просто каким-то тегом-значением (тегом, который возвращает значение сложнее булева) — в этом случае оно будет сравниваться с каким-то другим значением.
Базовый формат команды if, сравнивающей два значения, такой: - if (первое значение) (сравнение) (второе значение): (например, - if <player.name> == mcmonkey4eva:).
Одно из значений или оба сразу могут быть тегами. (Технически оба значения могут быть и просто статическим текстом, но тогда if либо всегда будет выполнять вложенные команды, либо никогда их не выполнит — такая команда if довольно бесполезна.).
Доступно несколько разных типов сравнения:
==— проверка на равенство (команда- if 3 == 3:выполнит вложенные команды, а- if 3 == 4:— нет) — читается как «если три равно трём, выполни вот эти команды».
!=— проверка на равенство, но с обратным результатом (т. е.- if 3 != 3:не выполнит вложенные команды, а- if 3 != 4:— выполнит) — символ!читается как «не», так что эту конструкцию можно прочитать как «если три не равно трём, выполни вот эти команды». Разумеется, в реальных скриптах одно из значений или оба будут приходить из тега.
>(больше),>=(больше или равно),<(меньше) и<=(меньше или равно) — числовые сравнения. Обратите внимание, что>и<=противоположны, как и<и>=. Небольшое дополнительное замечание: вы наверняка заметили, что символы<и>обычно обозначают тег, но в сравнениях используются в другом смысле — это нормально: в одном аргументе сравнения символы<и>одновременно не встречаются, так что они никогда не будут ошибочно приняты за тег.
containsиin— проверки вхождения в список, например:- if one|two contains one:или- if one in one|two:
matches— проверки по advanced-matcher, например:- if <player.item_in_hand> matches diamond_sword:
Комбинирование условий: венти-мокко-фрап с двойным сахаром и без сливок¶
Когда простого сравнения не хватает и нужно добавить ещё пару условий — не переживайте: команда if это позволяет!
Доступны два способа комбинирования:
Если вы хотите, чтобы набор команд выполнялся, только если все сравнения одновременно истинны, используйте
&&(читается как «и» — сам символ представляет собой двойной амперсанд). Например,- if <player.on_fire> && <player.health_percentage> < 25:выполнит вложенные команды, только если игрок горит и у него почти не осталось здоровья.
Если вы хотите, чтобы набор команд выполнялся, если хотя бы одно (или больше) из нескольких сравнений истинно, используйте
||(читается как «или» — сам символ представляет собой двойной «пайп»). Например,- if <player.on_fire> || <player.health_percentage> < 25:выполнит вложенные команды, если игрок горит (вне зависимости от его здоровья), либо если у игрока мало здоровья (вне зависимости от того, горит он или нет).
Если вам нужно проверить сразу много условий, их можно просто выстроить в цепочку: - if (условие 1) && (условие 2) && (условие 3) && (условие 4):.
Здесь только одно ограничение: в цепочке можно использовать либо только && от начала до конца, либо только || от начала до конца.
Смешивать их в одной цепочке нельзя… подумайте: - if (одно) && (два) || (три) && (четыре): — это вообще что значит?
Это значит, что должны выполниться или «одно и два», или «три и четыре»? Или что должны выполниться «одно, два или три» и при этом «четыре»?
Мы, люди, ещё можем попробовать угадать, что имелось в виду, но Denizen — просто программа, мысли он читать не умеет.
Решение этой проблемы было спрятано в самом вопросе: обратите внимание, как я сгруппировал части в двух возможных интерпретациях — части из одной группы я объединил, а остальные перечислил через запятую.
В Denizen для этого есть специальный синтаксис: круглые скобки () с пробелами вокруг служат маркерами группировки.
Как этим пользоваться?
Вот длинный, но относительно простой пример:
- if ( <player.name> == mcmonkey4eva && <player.health_percentage> > 90 ) || ( <player.on_fire> && <player.health_percentage> < 25 ):
- narrate "wow mcmonkey specifically is doing pretty well! That or somebody is dying from a fire..."
С помощью группировки, как в примере выше, можно собрать любую комбинацию условий, которая вам может понадобиться. Можно даже вкладывать группы друг в друга, если очень хочется, — но учтите, что слишком навороченные конструкции делают скрипт трудночитаемым, и иногда лучше перестроить его по-другому (хороший вариант мы рассмотрим в следующем подразделе).
Самое частое применение «If»¶
Польза от команды if в примере с «волшебным колоколом лечения» довольно прямолинейна: она позволила нам сузить условие, при котором выполняется набор команд.
Теперь это не просто «когда игрок кликает по колоколу», а «когда игрок кликает по колоколу и у него мало здоровья».
В целом, сужение условий, при которых выполняются команды, — это самое частое применение команды if.
В реальных скриптах это чаще всего выглядит чуть-чуть иначе, но смысл тот же:
magic_healing_bell:
type: world
events:
after player right clicks bell:
- if <player.health_percentage> > 25:
- stop
- heal
- actionbar "<&[base]>The bell has healed you!"
Используя команду stop внутри if, мы не даём скрипту выполнить команду лечения, если у игрока больше 25% здоровья.
По сути это тот же исходный скрипт, но записанный «наоборот»
(вместо «если у игрока меньше 25% здоровья, лечить его» мы говорим «если у игрока больше 25% здоровья, лечить его не надо»).
Это очень удобно, когда перед запуском основных команд нужно проверить несколько условий — их можно записать аккуратным списком
(а не упихивать в одну длинную строку через &&).
Развилка на дороге¶
До сих пор мы объясняли if с точки зрения «выполнить набор команд или не выполнять их» в зависимости от условия.
Это отлично подходит, чтобы добавить дополнительные требования к событию, при которых сработает набор команд, — но это не всё, что умеет if!
Команда if, как мы её пока понимаем, говорит: «если условие истинно, выполни эти команды, иначе — не выполняй».
Расширим это: «если условие истинно, выполни эти команды, иначе выполни другие команды».
Теперь наш скрипт может пойти по одной из двух дорожек в зависимости от условия в теге.
Сделать это нам поможет команда else.

Как выглядит команда Else?¶
Команда else выглядит почти так же, как if.
Вот базовый формат:
- if (some condition here):
- (commands for when the condition is 'true')
- else:
- (commands for 'false')
Всё очень просто! Это просто else без входных аргументов, оформленная так же, как if, но с одним обязательным требованием: она должна идти сразу за if.
Когда условие команды if оказывается ложным, выполняются команды внутри else.
Давайте используем else в нашем скрипте с волшебным колоколом…
magic_healing_bell:
type: world
events:
after player right clicks bell:
- if <player.health_percentage> < 25:
- heal
- actionbar "<&[base]>The bell has healed you!"
- else:
- actionbar "<&[error]>The bell does nothing: you're healthy enough already."
Прежний скрипт лечил игроков с уровнем здоровья ниже 25%. Теперь он ещё и пишет сообщение игрокам, у которых здоровья достаточно, чтобы они не удивлялись, почему колокол ничего не делает.
Может возникнуть вопрос: «а почему бы не использовать stop так, как было показано раньше, и поставить сообщение-отказ прямо перед stop?» — И так тоже можно!
Это прекрасный способ. Так в чём же тогда толк от else? Он особенно полезен, когда после блока if/else есть ещё команды, которые должны выполниться в любом случае.
Взгляните на этот скрипт:
magic_healing_bell:
type: world
events:
after player right clicks bell:
- if <player.health_percentage> > 90:
- actionbar "<&[error]>The bell does nothing: you're healthy enough already."
- stop
- if <player.health_percentage> < 25:
- actionbar "<&[base]>The bell has saved you!"
- else:
- actionbar "<&[base]>The bell has healed you!"
- heal
Этот скрипт откажется работать для игрока с 90% здоровья и вылечит его при любом здоровье ниже этого.
При этом он выдаст разные сообщения в зависимости от того, почти ли игрок мёртв или просто немного ранен.
Если бы мы здесь не использовали else, нам пришлось бы писать так:
magic_healing_bell:
type: world
events:
after player right clicks bell:
- if <player.health_percentage> > 90:
- actionbar "<&[error]>The bell does nothing: you're healthy enough already."
- stop
- if <player.health_percentage> < 25:
- actionbar "<&[base]>The bell has saved you!"
- heal
- stop
- actionbar "<&[base]>The bell has healed you!"
- heal
Так гораздо хуже! Пришлось дважды написать команду heal (не говоря уже о лишней команде stop).
Если вам кажется, что это «не так уж и плохо», представьте, во что это превратится в настоящем скрипте, где таких команд, выполняемых в любом случае, не одна, а целый список.
Вам пришлось бы дублировать весь список — благо, команда else избавляет от этого!
А у вилок разве не три-четыре зубца?¶

Команда else и сама по себе очень удобна, но у неё есть ещё кое-что!
Мы начали с одной команды if, которая позволяла выполнить или не выполнить кусок кода.
Потом добавили else — и теперь можем выбирать один из двух вариантов кода.
Дальше логично расширить это до выбора одного из многих вариантов.
Для этого существует else if!
Как написать «Else If»?¶
Вот базовый формат:
- if (first condition here):
- (commands for when the first condition is 'true')
- else if (second condition):
- (second condition 'true' commands)
- else:
- (commands for when all conditions were 'false')
Как видите, по сути это просто if внутри else.
Можно выстроить сколько угодно else if подряд, единственное ограничение — в начале цепочки всегда должен стоять обычный if.
Финальный else (без if) необязателен, но если вы его добавляете, он должен идти строго в конце.
Как это выглядит на практике?
magic_healing_bell:
type: world
events:
after player right clicks bell:
- if <player.health_percentage> > 90:
- actionbar "<&[error]>The bell does nothing: you're healthy enough already."
- else if <player.health_percentage> < 25:
- actionbar "<&[error]>The bell can't save you: you're too far gone."
- else:
- actionbar "<&[base]>The bell has healed you!"
- heal
Эта версия скрипта с волшебным колоколом не лечит, если игрок уже здоров, и не спасает от смерти — он лечит только при средней тяжести ранения.
Благодаря такой структуре команда stop нам больше вообще не нужна: heal выполняется лишь в одной из «веток».
Стоп, кто тут говорил про деревья?¶

Если у вас есть опыт в проектировании сложной логики — во-первых, зачем вы тогда вообще читаете это руководство? — но, что важнее, вы наверняка встречали термин «ветка» применительно к команде if.
Если нет, возможно, вы знакомы с идеей изображать пути выполнения в виде деревьев с множеством веток. Если и это вам не знакомо — не переживайте, сейчас объясню.
Разные пути, по которым может пойти выполнение команды if, иногда называют «ветками» (branches); обычно при таком словоупотреблении общую структуру скрипта называют «деревом» (tree).
Когда скрипт выполняется, он стартует от «корня» (это первая команда скрипта),
и «отращивает ветки» каждый раз, когда встречается команда if или подобная ей (любой поднабор команд, который выполняется лишь иногда).
Необязательно знать и использовать все эти термины или любые другие из тех, что мы вводим, — но если вы где-то увидите их в чужом коде и не поймёте, что имеется в виду, вспомните про этот подраздел и загляните сюда!
Ещё одна пара часто встречающихся терминов — «pass» и «fail» (дословно «прошёл» и «провалился»): команда if «проходит», когда её условие истинно (и вложенные команды выполняются).
«Проваливается» команда if, когда её условие ложно (и вложенные команды не выполняются).
Идём дальше¶
Если вы сейчас думаете: «Что ж, всё это прекрасно, но мне нужны куда более сложные способы ветвить скрипт»,
— значит, вы дошли до конца общего обзора команды if. В зависимости от того, что именно вам нужно,
можно просто поместить одну команду if внутрь другой (ничто не мешает, разве что скрипт начнёт выглядеть большим и страшным),
а если этого не хватает — продолжайте читать руководство: скорее всего, в одном из следующих разделов вы найдёте то, что ищете (например, раздел о циклах).