Механизмы и свойства

Что такое механизм?

Механизмы — это способ напрямую изменить одно значение на объекте. Они чем-то похожи на мини-команды, заточенные под работу в рамках системы свойств.

Вот несколько примеров простых механизмов, используемых в контейнерах скриптов:

ultra_sword:
    type: item
    display name: Ultra Sword
    material: diamond_sword
    mechanisms:
        unbreakable: true

../../_images/ultra_sword.png

В этом скрипте через ключ mechanisms контейнера item механизм unbreakable устанавливается в значение true, благодаря чему предмет никогда не теряет прочность.

undead_golden_swordsman:
    type: entity
    entity_type: zombie
    mechanisms:
        item_in_hand: golden_sword
        item_in_offhand: golden_sword
        health_data: 80/80

../../_images/undead_golden_swordsman.png

Этот entity-скрипт создаёт зомби и использует механизмы item_in_hand и item_in_offhand, чтобы положить золотые мечи в обе руки. У него также 80 единиц здоровья при спавне и максимум 80 — это задаётся механизмом health_data.

Команда Adjust

Механизмы можно использовать не только в контейнерах скриптов! Если нужно поменять значение механизма в процессе работы, на помощь приходит команда adjust.

Например, если мы хотим написать скрипт, который по клику меняет цвет овцы:

random_sheep_colors:
    type: world
    events:
        after player right clicks sheep with:stick:
        - adjust <context.entity> color:<context.entity.allowed_colors.random>

../../_images/rainbow_sheep.gif

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

Команду adjust можно применять для настройки механизмов большинства типов объектов (игроков, сущностей, сервера, …), но в ряде случаев удобнее использовать другие способы применения механизмов.

Команда AdjustBlock

Команда adjustblock — это разновидность обычной команды adjust. Она заточена под настройку механизмов material на блоках (полный список можно посмотреть здесь).

Хотя область применения adjustblock более узкая, она всё равно бывает очень полезна. Например, этим скриптом можно заставить растение мгновенно вырасти до максимальной стадии (например, чтобы только что посаженная пшеница сразу стала взрослой):

plant_grower:
    type: world
    events:
        after player right clicks block with:stick:
        - if <player.cursor_on.material.supports[age]>:
            - adjustblock <player.cursor_on> age:<player.cursor_on.material.maximum_age>

../../_images/crop_growth.png

Как настраивать предметы

Свойства предметов в инвентаре тоже можно настраивать. Для этого используется команда inventory с аргументом adjust.

Может показаться, что менять предмет в руке игрока — это просто настроить сам предмет:

flint_fancifier_bad:
    type: world
    events:
        after player right clicks block with:flint:
        - adjust <player.item_in_hand> "display:Fancy Flint"
        - adjust <player.item_in_hand> "lore:Built with Denizen!"

Однако тег <player.item_in_hand> просто вернёт «flint», без указания, о каком конкретно экземпляре кремня идёт речь. По сути, скрипт выше пытается изменить абстрактное понятие «кремень», а не конкретный кремень в руке игрока. Если вы настраиваете не конкретный существующий в мире предмет, в мире ничего не изменится!

Поэтому обычно настраивать предмет принято через аргумент adjust команды inventory — так меняется именно тот предмет, который нужно.

Скрипт ниже правильно меняет предмет в руке игрока, когда тот кликает им:

flint_fancifier:
    type: world
    events:
        after player right clicks block with:flint:
        - inventory adjust slot:hand "display:Fancy Flint"
        - inventory adjust slot:hand "lore:Built with Denizen!"

../../_images/fancy_flint.png

Команда inventory adjust меняет именно тот предмет, который нужен, — для этого она и предназначена! Поскольку вы указываете конкретный объект — предмет в конкретном слоте инвентаря, — команда inventory adjust в состоянии изменить его как нужно.

Тег With

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

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

sword_duplicator:
    type: world
    events:
        after player right clicks bedrock with:diamond_sword:
        - ratelimit <player> 1h
        - give <player.item_in_hand.with[display=Your Free Duplicate Sword].with[durability=0]>
        - narrate "<&b>Sword duplicated!"

../../_images/dup_sword.png

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

Как видно из примера, тег with, как и любой тег, можно просто добавить в конец любого ItemTag, и поскольку он возвращает ItemTag, в цепочку можно добавить несколько тегов with подряд.

Учтите, что для объектов MaterialTag тоже существует тег with, позволяющий применять ту же логику к блокам.

Adjust определений

Команду adjust можно также использовать, чтобы модифицировать объект, хранящийся в определении, и автоматически сохранить результат обратно в то же определение.

Это довольно редкий сценарий, но иногда он бывает очень кстати. В частности, он позволяет использовать команды if для того, чтобы выбирать, какие механизмы нужно применить к объекту, до того как изменения реально будут выполнены.

Например:

sword_duplicator:
    type: world
    events:
        after player right clicks bedrock with:diamond_sword:
        - ratelimit <player> 1h
        - define sword <player.item_in_hand>
        - adjust def:sword "display:Your Free Duplicate Sword"
        - if <player.location> matches lava:
            - adjust def:sword "lore:<&c>Forged in lava."
            - adjust def:sword durability:0
        - give <[sword]>
        - narrate "<&b>Sword duplicated!"

../../_images/lava_sword.png

Этот скрипт похож на дубликатор меча из примера с тегом with, но теперь починенная копия выдаётся только в том случае, если игрок рискнул прыгнуть в лаву, прежде чем кликнуть по бедроку. (Можно закомментировать ratelimit, поставив # перед -, чтобы не ждать и спокойно протестировать скрипт.)

У такого способа настройки предметов есть ещё одно преимущество — он чуть аккуратнее, когда за раз нужно поменять много механизмов: всё пишется более линейно.

Свойства

«Свойства» — это система для отслеживания деталей объекта, у которого нет собственного уникального идентификатора.

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

Свойства особенно заметны по их использованию во внутренних описаниях объектов Denizen. Описание объекта — это полное переиспользуемое описание любого объекта; обычно перед ним ставится специальная пометка-нотация (например, i@). Это нужно в основном для внутреннего учёта данных, и чаще всего вы будете видеть это в отладочной консоли.

Свойства предметов

Самое частое место, где свойства встречаются на практике, — это предметы. В Denizen предмет — это материал и набор свойств. Например, если ударить сущность свежескрафченным алмазным мечом в режиме выживания, а затем выполнить /ex narrate <player.item_in_hand> и посмотреть на внутреннее описание Denizen для этого предмета, вы увидите что-то вроде i@diamond_sword[durability=1]. В данном случае durability — единственное свойство предмета, и его значение равно 1 — столько прочности меч потерял к этому моменту. Несколько свойств разделяются точками с запятой, например: i@diamond_sword[durability=1;display_name=Skullbuster]. (Напоминаем: не вписывайте сырую нотацию объектов прямо в скрипт — это формат внутренних данных, его нельзя использовать в скрипте напрямую.)

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

Свойства материалов

Материалы, как и предметы, не имеют уникальных идентификаторов. Каменный блок — это просто материал в определённой точке. Пшеничный блок чуть сложнее: это материал в точке со свойством age. Как вы видели выше в примере с adjustblock, использование механизма age позволяет менять это свойство у блока в мире. Когда вы читаете материал блока из локации, он вернёт все связанные с ним свойства. Например, так можно проверить, дымится ли костёр, разные параметры редстоуна или даже стороны грибного блока.

Материалы со свойствами, читаемые через тег вроде <player.cursor_on.material>, вернут значение вроде m@wheat[age=7] для полностью выросшей пшеницы. В этом случае age пшеницы равен 7.

Свойства сущностей

У сущностей тоже есть свойства! Свойства сущностей можно читать и менять так же, как свойства материалов, а посмотреть полный список свойств сущности можно с помощью тега <EntityTag.describe>. Вот пример вывода /ex narrate <player.target.describe> для овцы, перекрашенной нашим скриптом смены цвета:

e@sheep[age=0;has_ai=true;is_aware=true;color=orange;equipment=li@i@air|i@air|i@air|i@air|;health_data=8/8;speed=0.23]

Как видно из примера, все данные об овце собираются Denizen и выводятся в формате EntityTag с разными свойствами. Свойства разделяются точками с запятой, а хранящиеся в них данные зависят от природы конкретного свойства.

Эти свойства можно менять командой adjust или читать соответствующими тегами. Поскольку свойство называется color, логично предположить, что у объектов EntityTag есть и механизм, и тег с именем color. Команда /ex narrate "The <player.target.entity_type> you're looking at is colored <player.target.color>", например, выведет The SHEEP you're looking at is colored orange — на основе данных выше.

Использование свойств в скрипте

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

Вот пара примеров:

sand_cannon:
    type: world
    events:
        after player right clicks air with:sand:
        - shoot falling_block[fallingblock_type=sand] origin:<player> speed:1

Скрипт выше при клике игрока по воздуху с песком в руке запускает сущности falling_block и через синтаксис свойств указывает, что в качестве материала нужно использовать sand.

pumpkin_meloner:
    type: world
    events:
        after player right clicks pumpkin_stem with:golden_hoe:
        - modifyblock <context.location> melon_stem[age=7]

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

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