🇺🇦 Українська (Ukrainian - Ukraine)
🇺🇦 Українська (Ukrainian - Ukraine)
Зовнішній вигляд
🇺🇦 Українська (Ukrainian - Ukraine)
🇺🇦 Українська (Ukrainian - Ukraine)
Зовнішній вигляд
Ця сторінка написана для версії:
1.21.4
Ця сторінка написана для версії:
1.21.4
ПЕРЕДУМОВИ
Спершу переконайтеся, що ви виконали процес налаштування datagen.
По-перше, нам потрібно створити свого постачальника. Створіть клас, який extends FabricAdvancementProvider
, і заповніть базові методи:
public class FabricDocsReferenceAdvancementProvider extends FabricAdvancementProvider {
protected FabricDocsReferenceAdvancementProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registryLookup) {
super(output, registryLookup);
}
@Override
public void generateAdvancement(RegistryWrapper.WrapperLookup wrapperLookup, Consumer<AdvancementEntry> consumer) {
}
}
Щоб завершити налаштування, додайте цього провайдера до своєї DataGeneratorEntrypoint
у методі onInitializeDataGenerator
.
pack.addProvider(FabricDocsReferenceAdvancementProvider::new);
Досягнення складається з кількох різних компонентів. Разом із вимогами, які називаються «критерієм», він може мати:
AdvancementDisplay
, який повідомляє грі, як показувати до досягнення гравцям,AdvancementRequirements
, які є списками списків критеріїв, які вимагають заповнення принаймні одного критерію з кожного підсписку,AdvancementRewards
, які гравець отримує за виконання досягнення.CriterionMerger
, який повідомляє досягненню, як обробляти кілька критеріїв, іAdvancement
, який організовує ієрархію, яку ви бачите на екрані «Досягнення».Ось просте досягнення для отримання ґрунту:
Ось просте досягнення для отримання ґрунту:
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(wrapperLookup.getOrThrow(RegistryKeys.ITEM), Items.APPLE))
.criterion("ate_cooked_beef", ConsumeItemCriterion.Conditions.item(itemLookup, Items.COOKED_BEEF))
.build(consumer, FabricDocsReference.MOD_ID + ":apple_and_beef");
WARNING
У той час як datagen може бути на стороні клієнта, Criterion
s і Predicate
s знаходяться в основному вихідному наборі (обидві сторони), оскільки сервер повинен ініціювати та оцінювати їх.
criterion (у множині: criteria) — це те, що гравець може зробити (або що може статися з гравцем), що може бути зараховано для досягнення. У грі є багато критеріїв, які можна знайти в пакеті net.minecraft.advancement.criterion
. Як правило, вам знадобиться новий критерій, лише якщо ви запровадите в гру спеціальну механіку.
Conditions оцінюються за критеріями. Критерій зараховується, лише якщо виконуються всі відповідні умови. Умови зазвичай виражаються присудком.
Predicate – це те, що приймає значення та повертає boolean
. Наприклад, Predicate<Item>
може повернути true
, якщо предмет є діамантом, тоді як Predicate<LivingEntity>
може повернути true
, якщо сутність не є ворожою до селян.
По-перше, нам знадобиться нова механіка для впровадження. Скажімо гравцеві, який інструмент він використовував щоразу, коли ламав блок.
public class FabricDocsReferenceDatagenAdvancement 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(FabricDocsReference.MOD_ID + "/use_tool", new UseToolCriterion());
}
Щоб переконатися, що наші критерії ініціалізуються в потрібний час, додайте порожній метод init
:
// :::datagen-advancements:mod-criteria
public static final UseToolCriterion USE_TOOL = Criteria.register(FabricDocsReference.MOD_ID + "/use_tool", new UseToolCriterion());
// :::datagen-advancements:mod-criteria
// :::datagen-advancements:new-mod-criteria
public static final ParameterizedUseToolCriterion PARAMETERIZED_USE_TOOL = Criteria.register(FabricDocsReference.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, FabricDocsReference.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(FabricDocsReference.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, FabricDocsReference.MOD_ID + ":break_block_with_tool_five_times");
Запустіть datagen ще раз, і ви нарешті закінчили!