🇺🇦 Українська (Ukrainian - Ukraine)
🇺🇦 Українська (Ukrainian - Ukraine)
Зовнішній вигляд
🇺🇦 Українська (Ukrainian - Ukraine)
🇺🇦 Українська (Ukrainian - Ukraine)
Зовнішній вигляд
Ця сторінка написана для версії:
1.21.10
ПЕРЕДУМОВИ
Спершу переконайтеся, що ви виконали процес налаштування datagen.
По-перше, нам потрібно створити свого постачальника. Створіть клас, який extends FabricAdvancementProvider, і заповніть базові методи:
public class ExampleModAdvancementProvider extends FabricAdvancementProvider {
protected ExampleModAdvancementProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registryLookup) {
super(output, registryLookup);
}
@Override
public void generateAdvancement(RegistryWrapper.WrapperLookup wrapperLookup, Consumer<AdvancementEntry> consumer) {
}
}Щоб завершити налаштування, додайте цього провайдера до своєї DataGeneratorEntrypoint у методі onInitializeDataGenerator.
pack.addProvider(ExampleModAdvancementProvider::new);Досягнення складається з кількох різних компонентів. Разом із вимогами, які називаються «критерієм», він може мати:
AdvancementDisplay, який повідомляє грі, як показувати до досягнення гравцям,AdvancementRequirements, які є списками списків критеріїв, які вимагають заповнення принаймні одного критерію з кожного підсписку,AdvancementRewards, які гравець отримує за виконання досягнення.CriterionMerger, який повідомляє досягненню, як обробляти кілька критеріїв, іAdvancement, який організовує ієрархію, яку ви бачите на екрані «Досягнення».Ось просте досягнення для отримання ґрунту:
No lines matched.WARNING
Під час створення записів про досягнення пам’ятайте, що функція приймає Identifier досягнення у форматі String!
{
"criteria": {
"got_dirt": {
"conditions": {
"items": [
{
"items": "minecraft:dirt"
}
]
},
"trigger": "minecraft:inventory_changed"
}
},
"display": {
"background": "minecraft:textures/gui/advancements/backgrounds/adventure.png",
"description": "Now make a house from it",
"icon": {
"count": 1,
"id": "minecraft:dirt"
},
"title": "Your First Dirt Block"
},
"requirements": [
[
"got_dirt"
]
],
"sends_telemetry_event": true
}Щоб зрозуміти, додамо ще одне досягнення. Ми попрактикуємося додавати нагороди, використовувати кілька критеріїв і призначати батьківські досягнення:
final RegistryWrapper.Impl<Item> itemLookup = wrapperLookup.getOrThrow(RegistryKeys.ITEM);
AdvancementEntry appleAndBeef = Advancement.Builder.create()
.parent(getDirt)
.display(
Items.APPLE,
Text.literal("Apple and Beef"),
Text.literal("Ate an apple and beef"),
null, // Children don't need a background, the root advancement takes care of that
AdvancementFrame.CHALLENGE,
true,
true,
false
)
.criterion("ate_apple", ConsumeItemCriterion.Conditions.item(itemLookup, Items.APPLE))
.criterion("ate_cooked_beef", ConsumeItemCriterion.Conditions.item(itemLookup, Items.COOKED_BEEF))
.build(consumer, ExampleMod.MOD_ID + ":apple_and_beef");WARNING
У той час як datagen може бути на стороні клієнта, Criterions і Predicates знаходяться в основному вихідному наборі (обидві сторони), оскільки сервер повинен ініціювати та оцінювати їх.
criterion (у множині: criteria) — це те, що гравець може зробити (або що може статися з гравцем), що може бути зараховано для досягнення. У грі є багато критеріїв, які можна знайти в пакеті net.minecraft.advancement.criterion. Як правило, вам знадобиться новий критерій, лише якщо ви запровадите в гру спеціальну механіку.
Conditions оцінюються за критеріями. Критерій зараховується, лише якщо виконуються всі відповідні умови. Умови зазвичай виражаються присудком.
Predicate – це те, що приймає значення та повертає boolean. Наприклад, Predicate<Item> може повернути true, якщо предмет є діамантом, тоді як Predicate<LivingEntity> може повернути true, якщо сутність не є ворожою до селян.
По-перше, нам знадобиться нова механіка для впровадження. Скажімо гравцеві, який інструмент він використовував щоразу, коли ламав блок.
public class ExampleModDatagenAdvancement implements ModInitializer {
@Override
public void onInitialize() {
HashMap<Item, Integer> tools = new HashMap<>();
PlayerBlockBreakEvents.AFTER.register(((world, player, blockPos, blockState, blockEntity) -> {
if (player instanceof ServerPlayerEntity serverPlayer) { // Only triggers on the server side
Item item = player.getMainHandStack().getItem();
Integer usedCount = tools.getOrDefault(item, 0);
usedCount++;
tools.put(item, usedCount);
serverPlayer.sendMessage(Text.of("You've used \"" + item + "\" as a tool " + usedCount + " times!"));
}
}));
}
}Зауважте, що цей код дійсно поганий. HashMap не зберігається ніде постійно, тому він буде скидатися кожного разу, коли гра перезапускається. Це просто для того, щоб похизуватися критеріями. Почніть гру та спробуйте!
Далі створимо наш спеціальний критерій, UseToolCriterion. Йому знадобиться власний клас Conditions, тому ми створимо їх обидва одночасно:
public class UseToolCriterion extends AbstractCriterion<UseToolCriterion.Conditions> {
@Override
public Codec<Conditions> getConditionsCodec() {
return Conditions.CODEC;
}
public record Conditions(Optional<LootContextPredicate> playerPredicate) implements AbstractCriterion.Conditions {
public static Codec<UseToolCriterion.Conditions> CODEC = LootContextPredicate.CODEC.optionalFieldOf("player")
.xmap(Conditions::new, Conditions::player).codec();
@Override
public Optional<LootContextPredicate> player() {
return playerPredicate;
}
}
}Вау, це багато! Розберімо це.
UseToolCriterion – це AbstractCriterion, до якого можуть застосовуватися Умови.Conditions мають поле playerPredicate. Усі Conditions повинні мати предикат гравця (технічно LootContextPredicate).Conditions також мають CODEC. Цей Кодек є просто кодеком для його одного поля, playerPredicate, з додатковими інструкціями для перетворення між ними (xmap).INFO
Щоб дізнатися більше про кодеки, перегляньте сторінку кодеки.
Нам знадобиться спосіб перевірити, чи виконуються умови. Нумо додаймо допоміжний метод до Conditions:
public boolean requirementsMet() {
return true; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}Тепер, коли ми маємо критерій і його умови, нам потрібен спосіб його запустити. Додайте метод запуску до UseToolCriterion:
public void trigger(ServerPlayerEntity player) {
trigger(player, Conditions::requirementsMet);
}Майже готово! Далі нам потрібен екземпляр нашого критерію для роботи. Помістімо його в новий клас під назвою ModCriteria.
public class ModCriteria {
public static final UseToolCriterion USE_TOOL = Criteria.register(ExampleMod.MOD_ID + ":use_tool", new UseToolCriterion());
}Щоб переконатися, що наші критерії ініціалізуються в потрібний час, додайте порожній метод init:
// :::datagen-advancements:mod-criteria
public static final UseToolCriterion USE_TOOL = Criteria.register(ExampleMod.MOD_ID + ":use_tool", new UseToolCriterion());
// :::datagen-advancements:mod-criteria
// :::datagen-advancements:new-mod-criteria
public static final ParameterizedUseToolCriterion PARAMETERIZED_USE_TOOL = Criteria.register(ExampleMod.MOD_ID + ":parameterized_use_tool", new ParameterizedUseToolCriterion());
// :::datagen-advancements:mod-criteriaІ викличте це у своєму ініціалізаторі моду:
ModCriteria.init();Нарешті, нам потрібно запустити наші критерії. Додайте це туди, де ми надіслали повідомлення гравцеві в основному класі мода.
ModCriteria.USE_TOOL.trigger(serverPlayer);Ваш новий блискучий критерій готовий до використання! Нумо додамо до нашого постачальника:
AdvancementEntry breakBlockWithTool = Advancement.Builder.create()
.parent(getDirt)
.display(
Items.DIAMOND_SHOVEL,
Text.literal("Not a Shovel"),
Text.literal("That's not a shovel (probably)"),
null,
AdvancementFrame.GOAL,
true,
true,
false
)
.criterion("break_block_with_tool", ModCriteria.USE_TOOL.create(new UseToolCriterion.Conditions(Optional.empty())))
.build(consumer, ExampleMod.MOD_ID + ":break_block_with_tool");Запустіть завдання datagen ще раз, і ви отримаєте нове досягнення, з яким можна грати!
Це все добре, але що, якщо ми хочемо надати досягнення лише після виконання певної роботи 5 разів? А чому б не ще один у 10 разів? Для цього нам потрібно надати умові параметр. Ви можете залишитися з UseToolCriterion, або ви можете слідувати разом із новим ParameterizedUseToolCriterion. На практиці ви повинні мати лише параметризований, але ми збережемо обидва для цього підручника.
Попрацюймо знизу вгору. Нам потрібно буде перевірити, чи виконуються вимоги, тому відредагуємо наш метод Conditions#requirementsMet:
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}requiredTimes не існує, тому зробіть його параметром Conditions:
public record Conditions(Optional<LootContextPredicate> playerPredicate, int requiredTimes) implements AbstractCriterion.Conditions {
@Override
public Optional<LootContextPredicate> player() {
return playerPredicate;
}
// :::datagen-advancements:new-requirements-met
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}
// :::datagen-advancements:new-requirements-met
}
}Тепер наш кодек неправильний. Напишімо новий кодек для нових змін:
public static Codec<ParameterizedUseToolCriterion.Conditions> CODEC = RecordCodecBuilder.create(instance -> instance.group(
LootContextPredicate.CODEC.optionalFieldOf("player").forGetter(Conditions::player),
Codec.INT.fieldOf("requiredTimes").forGetter(Conditions::requiredTimes)
).apply(instance, Conditions::new));
// :::datagen-advancements:new-parameter
@Override
public Optional<LootContextPredicate> player() {
return playerPredicate;
}
// :::datagen-advancements:new-requirements-met
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}
// :::datagen-advancements:new-requirements-met
}
}Рухаючись далі, тепер нам потрібно виправити наш метод trigger:
public void trigger(ServerPlayerEntity player, int totalTimes) {
trigger(player, conditions -> conditions.requirementsMet(totalTimes));
}Якщо ви створили новий критерій, нам потрібно додати його до ModCriteria
public static final ParameterizedUseToolCriterion PARAMETERIZED_USE_TOOL = Criteria.register(ExampleMod.MOD_ID + ":parameterized_use_tool", new ParameterizedUseToolCriterion());
// :::datagen-advancements:mod-criteria
// :::datagen-advancements:mod-criteria-init
public static void init() {
}І назвіть це в нашому головному класі, там же, де старий:
ModCriteria.PARAMETERIZED_USE_TOOL.trigger(serverPlayer, usedCount);Додайте досягнення до свого постачальника:
AdvancementEntry breakBlockWithToolFiveTimes = Advancement.Builder.create()
.parent(breakBlockWithTool)
.display(
Items.GOLDEN_SHOVEL,
Text.literal("Not a Shovel Still"),
Text.literal("That's still not a shovel (probably)"),
null,
AdvancementFrame.GOAL,
true,
true,
false
)
.criterion("break_block_with_tool_five_times", ModCriteria.PARAMETERIZED_USE_TOOL.create(new ParameterizedUseToolCriterion.Conditions(Optional.empty(), 5)))
.build(consumer, ExampleMod.MOD_ID + ":break_block_with_tool_five_times");Запустіть datagen ще раз, і ви нарешті закінчили!