Как устроено дерево
Ачивка без поля 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.21 | advancements/ | advancement/ |
| Иконка, в 1.20.5 | {"item": "minecraft:map"} | {"id": "minecraft:map"} |
Переименование папки — более мерзкое из двух, ровно как и с loot-таблицами: пак загружается, числится включённым, а ачивки просто нет. Переименование иконки хотя бы обычно ругается в логе.
Мелочи, стоящие вечера
- Ачивка без
criteriaневалидна, даже если вы выдаёте её только командой. Дайте ей критерий сimpossible. parent, указывающий на несуществующий ID, заставляет дочернюю ачивку исчезнуть, а не выдать внятную ошибку.hiddenпрячет ачивку только до получения и только если она не корневая.- Вкладка вообще не появится, пока в ней нет хотя бы одной видимой этому игроку ачивки.
Куда идти дальше
Конструктор ачивок пишет файл и подходящую команду выдачи и знает, какие поля принадлежат корню, а какие — потомку. Про контейнер для файла — генератор датапаков; чтобы квесту было что выдавать, генератор loot-таблиц и генератор рецептов закрывают награды и разблокировки.