Ваши первые теги

Что такое тег?

Если вы идёте по этому руководству по порядку (а так и стоит делать!), то вы уже пользовались тегом. В руководстве по команде ex вы попробовали /ex narrate "hi <player.name>". Конструкция <player.name> — это и есть тег в Denizen. Это отдалённо похоже на «плейсхолдеры» из некоторых плагинов, но гораздо мощнее. Как нетрудно догадаться, тег player.name автоматически подставляет ваше имя пользователя.

../../_images/playernameexample.png

Теги в Denizen обозначаются <> вокруг описания того, какое значение должно заменить тег. С их помощью можно получить практически любую нужную вам информацию в любой момент.

Самые распространённые теги

Самые ходовые теги — это цветовые теги, и это же одни из самых простых тегов в Denizen. Вы наверняка знакомы с базовой системой цветовых кодов в Minecraft — например, §7 даёт серый цвет. Вероятно, вам также встречались плагины, в которых можно написать &7, чтобы окрасить текст в серый. Учтите, что Minecraft внутри использует знак параграфа §, но в привычном для плагинов формате используется амперсанд & (потому что § трудно набирать на клавиатуре, а & — легко). Чтобы в Denizen сделать текст серым, вы пишете <&7> — это по-прежнему легко набирать, но оформлено через систему тегов (то есть взято в <>, показывая, что <&7> должно быть заменено реальным внутренним светло-серым кодом цвета с §). Вместо этого можно также написать <gray> и получить ровно тот же результат — выбор за вами (любите короткие теги или более понятные? Лучше знаете названия цветов или их внутренние ID-номера?). Также можно использовать <&color[gray]> или <element[мой текст].color[gray]>, или… кучу других способов записать то же самое. По мере прочтения этого руководства вы заметите, что наличие множества разных способов получить один и тот же результат — это в Denizen постоянная тема.

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

Как теги выглядят на практике?

Простой пример использования тега: - teleport <npc> <player.location> — телепортирует NPC (полученного через тег <npc>) к вашему местоположению (полученному через тег <player.location>).

Использование тегов может быть куда сложнее, например: - teleport <player.location.find_entities[living].within[<[range]>]> <player.cursor_on[<[distance]>].if_null[<player.location.forward[<[distance]>]>]> — не переживайте, если это вообще ни о чём вам не говорит: пример намеренно длинный и запутанный, просто чтобы дать почувствовать, насколько мощный инструмент теги в руках опытного скриптера (и это ещё только начало — ограничение тут только фантазия автора скрипта!). В базовых скриптах такие длинные теги почти никогда не нужны.

Так как же мне составить тег?

Как вы, наверное, уже догадались по длинному примеру выше — теги не ограничены каким-то фиксированным списком, который мы заранее собрали. Теги собираются скриптером из нескольких частей под конкретную задачу.

Части тега

Существует два базовых «типа» тегов, которые выступают строительными блоками для скриптера. Это: базовые теги и под-теги.

Базовые теги

Базовый тег — это что-то вроде <player>, который возвращает PlayerTag для ситуативно подходящего игрока. Бывают и внешне «многосоставные» базовые теги, например <server.motd> (хотя их иногда рассматривают и так: <server> возвращает «ServerTagBase», а <ServerTagBase.motd> — это уже под-тег к нему).

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

Под-теги

Под-тег — это что-то вроде <PlayerTag.name>, который возвращает ElementTag с именем соответствующего игрока. В документации под-тег всегда начинается с (Что-то)Tag, например PlayerTag или ItemTag. Всё, что не оформлено в таком виде, — это базовый тег.

Под-теги прицепляются в конец уже существующего тега. В законченном теге может быть сколько угодно под-тегов: ноль (только базовый), один (самый простой тег), пятьсот (невероятно длинный тег) — или любое другое количество.

Собираем всё вместе

Выше мы уже видели пример под-тега <PlayerTag.name>. Когда под-тег начинается с «PlayerTag», это, по сути, означает «подставьте сюда игрока»… Поскольку базовый тег <player> возвращает игрока, а <PlayerTag.name> требует игрока в своей первой части, эти два кусочка можно объединить в <player.name>, и тогда тег вернёт имя ситуативно подходящего игрока. В случае с командой /ex тег <player.name> вернёт ваше имя.

Более длинные цепочки

Тег <PlayerTag.target> возвращает ту сущность, на которую смотрит игрок. Если применить его, глядя на другого игрока, он вернёт PlayerTag этого игрока. Значит, по правилу «подставляйте куда угодно всё, что возвращает валидный PlayerTag, если в этом месте требуется PlayerTag», можно подставить <player> и получить <player.target>. Если мы в нашем примере теоретически смотрим на другого игрока, то по тому же правилу подставляем получившийся тег в начало <PlayerTag.name>. В результате получается <player.target.name> — готовый рабочий тег, который возвращает имя игрока, на которого вы смотрите!

../../_images/howtagsfittogether.gif

PlayerTag — это игрок в виде тега

В этой формулировке нет ничего сложного. PlayerTag — это игрок, представленный в виде тега, кто бы мог подумать! Аналогично, NPCTag — это NPC в виде тега, EntityTag — любая сущность в виде тега, ItemTag — любой предмет в виде тега, и так далее — типов очень много, можно перечислять долго.

Одно слово из тех, что мы использовали раньше, может быть чуть менее очевидным: «ElementTag». ElementTag — это самый базовый тип значения, то есть вообще что угодно. ElementTag может быть числом, каким-то текстом, бинарными данными или даже игроком.

Стоп, вы же только что сказали, что игроки — это PlayerTag

Да, игроки — это PlayerTag. И одновременно ElementTag. И ещё EntityTag.

В Denizen у типов объектов могут быть подтипы. Хороший пример — PlayerTag и NPCTag оба имеют подтип EntityTag. Это значит, что везде, где требуется EntityTag (например, в теге <EntityTag.health>), туда можно подставить PlayerTag или NPCTag, и всё сработает ровно так, как ожидается (то есть <player.health> вернёт количество здоровья игрока).

ElementTag, как уже было сказано, — самый базовый тип значения. У любого типа объектов в Denizen есть подтип ElementTag. Это значит, что везде, где нужен ElementTag, можно подставить вообще любое значение. PlayerTag, NPCTag, ItemTag или что угодно ещё — всё это валидные входные значения для всего, что принимает ElementTag.

У некоторых тегов есть входные значения

Некоторым тегам нужно входное значение. Например, тег <util.random.int[<#>].to[<#>]> принимает два простых числовых значения на вход («от» и «до») и возвращает случайное число в этом диапазоне.

В простейшем случае эти входы можно просто заполнить тем типом значения, которое ожидается. Например, введите в игровую командную строку /ex narrate <util.random.int[1].to[10]> — и в чате, как и ожидается, появится случайное число от 1 до 10. Если повторить команду несколько раз, каждый раз будет выпадать новое случайное число (за исключением случаев, когда выпадает то же самое — так бывает, ведь случайность штука капризная).

../../_images/utilrandomexample.png

Тег как входное значение для тега?!

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

slowly_dying:
    type: task
    script:
    - narrate "You think you're so special with <player.health> health left?!"
    - hurt <player> <util.random.decimal[0].to[<player.health.div[2]>]>
    - narrate "Hah! Take that, <player.name>! Now you only have <player.health> health left!"

Каждый раз, когда запускается этот пример скрипта, игрок теряет случайное количество здоровья — до половины текущего, обычно меньше. Скрипт также сообщает ему, сколько здоровья было до и после урона.

Как видно в примере, тег <player.health.div[2]> был использован в качестве второго входного значения тега <util.random.decimal[<#.#>].to[<#.#>]>.

Этот внутренний тег собирается из <player> — чтобы получить привязанного игрока, <PlayerTag.health> — чтобы получить количество его здоровья, и <ElementTag.div[<#.#>]> — чтобы поделить значение (в данном случае на 2).

Замечание на будущее

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

Пояснения к терминологии

На этой странице встречаются слова, которые употребляются в Denizen в специфическом значении. По большей части из контекста должно быть понятно, что они значат… Но на всякий случай — короткие пояснения, чтобы вы точно понимали используемые термины:

  • «Возвращает» (returns) — в разговоре о тегах обычно используется во фразах вроде «тег <PlayerTag.name> возвращает имя игрока». Это значит, что при использовании этот тег заменяется на имя игрока. То есть - narrate "hi <player.name>" выведет в чат "hi bob". Слово «возвращает» в любом предложении можно мысленно заменить на «заменяется на»: «тег <PlayerTag.name> заменяется на имя игрока».

  • «Объект» (object) — обобщающий термин для какого-то значения, обычно в контексте обсуждения разных типов объектов. PlayerTag — это один тип объекта, NPCTag — другой. Когда вы работаете над скриптом, любой игрок — это объект, и этот объект относится к типу PlayerTag. Название каждого типа имеет вид «(что-то)Tag», чтобы подчеркнуть, что это (что-то) для использования в теге. Эта формулировка употребляется, даже когда речь не идёт напрямую о тегах. Совокупность всех объектов в контексте скриптинга иногда называют ObjectTags.