🇮🇹 Italiano (Italian)
🇮🇹 Italiano (Italian)
Aspetto
🇮🇹 Italiano (Italian)
🇮🇹 Italiano (Italian)
Aspetto
Questa pagina si applica alla versione:
1.21.4
Questa pagina si applica alla versione:
1.21.4
PREREQUISITI
Assicurati di aver prima completato il processo di configurazione della datagen.
Anzitutto, dobbiamo creare il nostro fornitore. Crea una classe che extends FabricAdvancementProvider
e compilane i metodi di base:
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) {
}
}
Per completare la configurazione, aggiungi questo fornitore alla tua DataGeneratorEntrypoint
nel metodo onInitializeDataGenerator
.
pack.addProvider(FabricDocsReferenceAdvancementProvider::new);
Un progresso è composto di alcune componenti diverse. Assieme ai requisiti, detti "criterio", potrebbe avere:
AdvancementDisplay
che dice al gioco come mostrare il progresso ai giocatori,AdvancementRequirements
, ovvero liste di liste di criteri, che richiedono che almeno un criterio di ogni sotto-lista sia soddisfatto,AdvancementRewards
, che il giocatore riceverà per aver completato il progresso,CriterionMerger
, che informa il progresso su come gestire criteri multipli, eAdvancement
genitore, che organizza la gerarchia che vedi nella schermata "Progressi".Ecco un semplice progresso per aver ottenuto un blocco di terra:
AdvancementEntry getDirt = Advancement.Builder.create()
.display(
Items.DIRT, // The display icon
Text.literal("Your First Dirt Block"), // The title
Text.literal("Now make a house from it"), // The description
Identifier.ofVanilla("textures/gui/advancements/backgrounds/adventure.png"), // Background image for the tab in the advancements page, if this is a root advancement (has no parent)
AdvancementFrame.TASK, // TASK, CHALLENGE, or GOAL
true, // Show the toast when completing it
true, // Announce it to chat
false // Hide it in the advancement tab until it's achieved
)
// "got_dirt" is the name referenced by other advancements when they want to have "requirements."
.criterion("got_dirt", InventoryChangedCriterion.Conditions.items(Items.DIRT))
// Give the advancement an id
.build(consumer, FabricDocsReference.MOD_ID + ":get_dirt");
WARNING
Nel costruire le voci del tuo progresso, ricorda che la funzione accetta l'Identifier
del progresso nel formato 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
}
Solo per capirne il funzionamento, aggiungiamo un altro progresso. Faremo pratica con l'aggiunta di ricompense, l'uso di criterio multiplo, e l'assegnazione di genitori:
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
Anche se la datagen può avvenire lato client, i Criterion
e i Predicate
sono nell'insieme di codice main (entrambi i lati), poiché il server ne ha bisogno per innescarli e valutarli.
Un criterio (in inglese criterion, plurale criteria) è qualcosa che un giocatore può fare (o che succede a un giocatore) e che può essere considerata per quanto riguarda un progresso. Nel gioco ci sono già vari criteri, che si possono trovare nel package net.minecraft.advancement.criterion
. In genere dovrai aggiungere un nuovo criterio solo se devi implementare una meccanica personalizzata nel gioco.
Le condizioni sono valutate dai criteri. Un criterio viene preso in considerazione solo se tutte le condizioni rilevanti sono soddisfatte. Le condizioni di solito si esprimono come predicati.
Un predicato è qualcosa che prende un valore e restituisce un boolean
. Per esempio, un Predicate<Item>
potrebbe restituire true
se l'oggetto è un diamante, mentre un Predicate<LivingEntity>
potrebbe restituire true
se l'entità non è ostile ai villici.
Anzitutto ci serve una meccanica da implementare. Informiamo il giocatore riguardo a quale utensile ha usato ogni volta che rompe un blocco.
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!"));
}
}));
}
}
Nota che questo è del codice molto brutto. La HashMap
non viene memorizzata persistentemente, quindi sarà resettata ad ogni riavvio del gioco. È solo per mostrare i Criterion
. Avvia il gioco e provalo!
Ora, creiamo il nostro criterio personalizzato, UseToolCriterion
. Avrà bisogno di una sua classe Conditions
, quindi creeremo entrambe insieme:
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;
}
}
}
Wow, questo è un sacco! Analizziamolo poco per volta.
UseToolCriterion
è un AbstractCriterion
, al quale si possono applicare delle Conditions
.Conditions
ha un attributo playerPredicate
. Tutte le Conditions
dovrebbero avere un predicato del giocatore (tecnicamente un LootContextPredicate
).Conditions
ha anche un CODEC
. Questo Codec
è semplicemente il codec per il suo unico attributo, playerPredicate
, con istruzioni aggiuntive per convertirlo tra di essi (xmap
).INFO
Per saperne di più sui codec, controlla la pagina Codec.
Ci serve un modo per controllare se le condizioni sono soddisfatte. Aggiungiamo un metodo ausiliare a Conditions
:
public boolean requirementsMet() {
return true; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}
Ora che abbiamo un criterio e le sue condizioni, ci serve un modo per innescarlo. Aggiungi un metodo d'innesco a UseToolCriterion
:
public void trigger(ServerPlayerEntity player) {
trigger(player, Conditions::requirementsMet);
}
Ci siamo quasi! Ora, ci serve un'istanza del nostro criterio con cui lavorare. Mettiamola in una nuova classe, chiamata ModCriteria
.
public class ModCriteria {
public static final UseToolCriterion USE_TOOL = Criteria.register(FabricDocsReference.MOD_ID + "/use_tool", new UseToolCriterion());
}
Per assicurarci che i nostri criteri siano inizializzati al tempo giusto, aggiungi un metodo vuoto 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
E chiamalo nell'initializer della tua mod:
ModCriteria.init();
Infine, dobbiamo innescare i nostri criteri. Aggiungi questo a dove inviamo un messaggio al giocatore nella classe main della mod.
ModCriteria.USE_TOOL.trigger(serverPlayer);
Il tuo criterio nuovo e luccicante è ora pronto per l'uso! Aggiungiamolo al nostro fornitore:
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");
Esegui l'operazione di datagen di nuovo, e avrai con te un nuovo progresso con cui giocare!
Tutto questo è bello e tutto, ma, e se volessimo solo concedere il progresso dopo aver fatto qualcosa 5 volte? E perché non concederne un altro dopo 10 volte? Per questo dovremo dare alla nostra condizione un parametro. Puoi attenerti a UseToolCriterion
, o andare avanti qui con un nuovo ParameterizedUseToolCriterion
. Nella pratica dovresti solo avere quello parametrizzato, ma li terremo entrambi per questo tutorial.
Lavoriamo dal basso verso l'alto. Dovremo verificare se i requisiti sono soddisfatti, quindi modifichiamo il nostro metodo Conditions#requirementsMet
:
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}
requiredTimes
non esiste, quindi rendilo un parametro di 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
}
}
Ora il nostro codec dà errore. Scriviamo un nuovo codec per le nuove modifiche:
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
}
}
Andando avanti, dobbiamo ora aggiustare il nostro metodo trigger
:
public void trigger(ServerPlayerEntity player, int totalTimes) {
trigger(player, conditions -> conditions.requirementsMet(totalTimes));
}
Se hai creato un nuov criterio, dobbiamo aggiungerlo a 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() {
}
E chiamarlo nella nostra classe principale, proprio dove c'è quello vecchio:
ModCriteria.PARAMETERIZED_USE_TOOL.trigger(serverPlayer, usedCount);
Aggiungi il progresso al tuo fornitore:
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");
Esegui nuovamente la datagen, e hai finalmente finito!