← Все статьи

Ачивки как квестовая система

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

Открыть конструктор ачивок

Как устроено дерево

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

harbor:harbor_quest_1        (корень, своя вкладка)
  └─ harbor:harbor_hunt      "parent": "harbor:harbor_quest_1"
       └─ harbor:harbor_end  "parent": "harbor:harbor_hunt"

Поле background — текстуру, которой замощён фон вкладки — несёт только корень. На дочерней ачивке оно не делает ничего, поэтому конструктор, который понимает разницу, полезнее шаблона.

Триггер impossible — в этом весь смысл

Каждой ачивке нужен хотя бы один критерий, а критерию — триггер. В ванили их много: убить кого-то, войти куда-то, подобрать предмет. Но настоящий квест редко ложится ровно на один из них. «Доставить письмо начальнику порта» — не ванильное событие.

Поэтому берут триггер, который не срабатывает никогда:

"criteria": {
  "delivered": {
    "trigger": "minecraft:impossible"
  }
}

Ничто в мире её не завершит. Это делаете вы, из своей логики:

/advancement grant @s only harbor:harbor_quest_1

Команда ставится туда, где квест реально заканчивается: командный блок у пирса, функция, вызванная из торговли с жителем, проверка внутри вашей tick-функции. Ачивка перестаёт быть детектором и становится записью — тем, что помнит, что этот игрок дошёл досюда, переживает рестарты и открывает следующее.

Именно на этом шаблоне построена почти любая кастомная квестовая система на ванильном сервере.

Награды — и та, что всех удивляет

К ачивке можно подвесить четыре вида наград:

"rewards": {
  "experience": 50,
  "recipes": ["harbor:reinforced_block"],
  "loot": ["harbor:quest_bundle"],
  "function": "harbor:on_quest_complete"
}

Награда function — самая мощная, потому что превращает ачивку в хук: выполнил — запустились произвольные команды. Выдать предмет, запустить bossbar, выставить скорборд, открыть ворота.

А награда recipes — та, что всех удивляет, и она объясняет загадку с другой стороны сайта: кастомный рецепт не появляется в книге, пока его что-то не разблокирует, и вот это самое «что-то». Ваниль держит скрытую ачивку под каждый свой рецепт, которая только этим и занимается. Если ваш рецепт крафтится, но в книге его нет — вы просто ещё не написали этот файл.

Тестирование без прохождения квеста

Весь цикл закрывают две команды:

/advancement grant @s only harbor:harbor_quest_1
/advancement revoke @s only harbor:harbor_quest_1

Замените only на until, from или through, чтобы двигать целую ветку разом — это сильно экономит клики при отладке цепочки из пяти шагов. А revoke позволяет прогнать тот же шаг ещё раз, не создавая новый мир.

Два переименования, ломающие старые примеры

Что изменилосьБылоСтало
Папка, в 1.21advancements/advancement/
Иконка, в 1.20.5{"item": "minecraft:map"}{"id": "minecraft:map"}

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

Мелочи, стоящие вечера

Куда идти дальше

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