[Features] (Recipe-) Advancement API
This commit is contained in:
parent
e5da06a1e1
commit
18fffafb3a
10 changed files with 490 additions and 10 deletions
|
@ -0,0 +1,43 @@
|
|||
package org.betterx.bclib.api.v2.advancement;
|
||||
|
||||
import net.minecraft.advancements.DisplayInfo;
|
||||
import net.minecraft.advancements.FrameType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class Display {
|
||||
ItemStack icon;
|
||||
Component title;
|
||||
net.minecraft.network.chat.Component description;
|
||||
@Nullable ResourceLocation background;
|
||||
FrameType frame;
|
||||
boolean showToast;
|
||||
boolean announceChat;
|
||||
boolean hidden;
|
||||
|
||||
Display() {
|
||||
}
|
||||
|
||||
Display reset() {
|
||||
this.icon = null;
|
||||
this.title = null;
|
||||
this.description = null;
|
||||
frame = FrameType.TASK;
|
||||
background = null;
|
||||
showToast = true;
|
||||
announceChat = true;
|
||||
hidden = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
DisplayInfo build() {
|
||||
return new DisplayInfo(
|
||||
icon, title, description,
|
||||
background, frame, showToast, announceChat, hidden
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
package org.betterx.bclib.api.v2.advancement;
|
||||
|
||||
import net.minecraft.advancements.*;
|
||||
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
||||
import net.minecraft.advancements.critereon.RecipeUnlockedTrigger;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.recipes.RecipeBuilder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
public class AdvancementManager {
|
||||
private static final Map<ResourceLocation, Advancement.Builder> ADVANCEMENTS = new HashMap<>();
|
||||
|
||||
public static void register(ResourceLocation id, Advancement.Builder builder) {
|
||||
ADVANCEMENTS.put(id, builder);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void addAdvancements(Map<ResourceLocation, Advancement.Builder> map) {
|
||||
for (var entry : ADVANCEMENTS.entrySet()) {
|
||||
if (!map.containsKey(entry.getKey())) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class RewardsBuilder {
|
||||
private final Builder calle;
|
||||
private final AdvancementRewards.Builder builder = new AdvancementRewards.Builder();
|
||||
|
||||
private RewardsBuilder(Builder calle) {
|
||||
this.calle = calle;
|
||||
}
|
||||
|
||||
public RewardsBuilder addExperience(int i) {
|
||||
builder.addExperience(i);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public RewardsBuilder addLootTable(ResourceLocation resourceLocation) {
|
||||
builder.addLootTable(resourceLocation);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public RewardsBuilder addRecipe(ResourceLocation resourceLocation) {
|
||||
builder.addRecipe(resourceLocation);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public RewardsBuilder runs(ResourceLocation resourceLocation) {
|
||||
builder.runs(resourceLocation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder endReward() {
|
||||
calle.rewards(builder.build());
|
||||
return calle;
|
||||
}
|
||||
}
|
||||
|
||||
public enum AdvancementType {
|
||||
REGULAR,
|
||||
RECIPE_DECORATIONS,
|
||||
RECIPE_TOOL
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static final ThreadLocal<DisplayBuilder> DISPLAY_BUILDER = ThreadLocal.withInitial(DisplayBuilder::new);
|
||||
private static final ResourceLocation RECIPES_ROOT = RecipeBuilder.ROOT_RECIPE_ADVANCEMENT;
|
||||
|
||||
private final Advancement.Builder builder = Advancement.Builder.advancement();
|
||||
private final ResourceLocation id;
|
||||
private final AdvancementType type;
|
||||
private boolean canBuild = true;
|
||||
|
||||
private Builder(ResourceLocation id, AdvancementType type) {
|
||||
ResourceLocation ID;
|
||||
if (type == AdvancementType.RECIPE_DECORATIONS) {
|
||||
ID = new ResourceLocation(id.getNamespace(), "recipes/decorations/" + id.getPath());
|
||||
builder.parent(RECIPES_ROOT);
|
||||
} else if (type == AdvancementType.RECIPE_TOOL) {
|
||||
ID = new ResourceLocation(id.getNamespace(), "recipes/tools/" + id.getPath());
|
||||
builder.parent(RECIPES_ROOT);
|
||||
} else {
|
||||
ID = id;
|
||||
}
|
||||
this.id = ID;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static Builder create(ResourceLocation id) {
|
||||
return new Builder(id, AdvancementType.REGULAR);
|
||||
}
|
||||
|
||||
public static Builder create(ResourceLocation id, AdvancementType type) {
|
||||
return new Builder(id, type);
|
||||
}
|
||||
|
||||
public static Builder create(Item item) {
|
||||
return create(item, AdvancementType.REGULAR);
|
||||
}
|
||||
|
||||
public static Builder create(ItemStack item) {
|
||||
return create(item, AdvancementType.REGULAR);
|
||||
}
|
||||
|
||||
public static Builder create(ItemLike item, AdvancementType type) {
|
||||
return create(new ItemStack(item), type);
|
||||
}
|
||||
|
||||
public static Builder create(ItemStack item, AdvancementType type) {
|
||||
return create(item, type, (displayBuilder) -> {
|
||||
});
|
||||
}
|
||||
|
||||
public static Builder create(Item item, AdvancementType type, Consumer<DisplayBuilder> displayAdapter) {
|
||||
return create(new ItemStack(item), type, displayAdapter);
|
||||
}
|
||||
|
||||
public static Builder create(ItemStack item, AdvancementType type, Consumer<DisplayBuilder> displayAdapter) {
|
||||
var id = Registry.ITEM.getKey(item.getItem());
|
||||
boolean canBuild = true;
|
||||
if (id == null || item.is(Items.AIR)) {
|
||||
canBuild = false;
|
||||
id = Registry.ITEM.getDefaultKey();
|
||||
}
|
||||
|
||||
String baseName = "advancements." + id.getNamespace() + "." + id.getPath() + ".";
|
||||
Builder b = new Builder(id, type);
|
||||
var displayBuilder = b.startDisplay(
|
||||
item,
|
||||
Component.translatable(baseName + "title"),
|
||||
Component.translatable(baseName + "description")
|
||||
);
|
||||
if (displayAdapter != null) displayAdapter.accept(displayBuilder);
|
||||
b = displayBuilder.endDisplay();
|
||||
b.canBuild = canBuild;
|
||||
return b;
|
||||
}
|
||||
|
||||
public static <C extends Container, T extends Recipe<C>> Builder createRecipe(T recipe, AdvancementType type) {
|
||||
Item item = recipe.getResultItem().getItem();
|
||||
return create(item, type, displayBuilder -> displayBuilder.hideToast().hideFromChat())
|
||||
.awardRecipe(item)
|
||||
.addRecipeUnlockCriterion(
|
||||
"has_the_recipe",
|
||||
recipe
|
||||
);
|
||||
}
|
||||
|
||||
public Builder parent(Advancement advancement) {
|
||||
builder.parent(advancement);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder parent(ResourceLocation resourceLocation) {
|
||||
builder.parent(resourceLocation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder startDisplay(Item icon) {
|
||||
String baseName = "advancements." + id.getNamespace() + "." + id.getPath() + ".";
|
||||
return startDisplay(
|
||||
icon,
|
||||
Component.translatable(baseName + "title"),
|
||||
Component.translatable(baseName + "description")
|
||||
);
|
||||
}
|
||||
|
||||
public DisplayBuilder startDisplay(
|
||||
ItemLike icon,
|
||||
Component title,
|
||||
Component description
|
||||
) {
|
||||
return startDisplay(new ItemStack(icon), title, description);
|
||||
}
|
||||
|
||||
public DisplayBuilder startDisplay(
|
||||
ItemStack icon,
|
||||
Component title,
|
||||
Component description
|
||||
) {
|
||||
if (icon == null) {
|
||||
canBuild = false;
|
||||
} else {
|
||||
var id = Registry.ITEM.getKey(icon.getItem());
|
||||
if (id == null) {
|
||||
canBuild = false;
|
||||
}
|
||||
}
|
||||
DisplayBuilder dp = DISPLAY_BUILDER.get().reset(this);
|
||||
return dp.icon(icon).title(title).description(description);
|
||||
}
|
||||
|
||||
Builder display(DisplayInfo displayInfo) {
|
||||
builder.display(displayInfo);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder awardRecipe(ItemLike... items) {
|
||||
var rewardBuilder = startReward();
|
||||
for (ItemLike item : items) {
|
||||
var id = Registry.ITEM.getKey(item.asItem());
|
||||
if (id == null) continue;
|
||||
rewardBuilder.addRecipe(id);
|
||||
}
|
||||
return rewardBuilder.endReward();
|
||||
}
|
||||
|
||||
public RewardsBuilder startReward() {
|
||||
return new RewardsBuilder(this);
|
||||
}
|
||||
|
||||
public Builder rewards(AdvancementRewards advancementRewards) {
|
||||
builder.rewards(advancementRewards);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addCriterion(String string, CriterionTriggerInstance criterionTriggerInstance) {
|
||||
builder.addCriterion(string, new Criterion(criterionTriggerInstance));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addCriterion(String string, Criterion criterion) {
|
||||
builder.addCriterion(string, criterion);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <C extends Container, T extends Recipe<C>> Builder addRecipeUnlockCriterion(String name, T recipe) {
|
||||
return addCriterion(
|
||||
name,
|
||||
RecipeUnlockedTrigger.unlocked(recipe.getId())
|
||||
)
|
||||
.startReward()
|
||||
.addRecipe(recipe.getId())
|
||||
.endReward()
|
||||
.requirements(RequirementsStrategy.OR);
|
||||
}
|
||||
|
||||
public Builder addInventoryChangedCriterion(String name, ItemLike... items) {
|
||||
return addCriterion(
|
||||
name,
|
||||
InventoryChangeTrigger.TriggerInstance.hasItems(items)
|
||||
);
|
||||
}
|
||||
|
||||
public Builder requirements(RequirementsStrategy requirementsStrategy) {
|
||||
builder.requirements(requirementsStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder requirements(String[][] strings) {
|
||||
builder.requirements(strings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResourceLocation buildAndRegister() {
|
||||
AdvancementManager.register(id, this.builder);
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DisplayBuilder {
|
||||
Builder base;
|
||||
final Display display = new Display();
|
||||
|
||||
DisplayBuilder reset(Builder base) {
|
||||
this.base = base;
|
||||
this.display.reset();
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder background(ResourceLocation value) {
|
||||
display.background = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder icon(ItemLike value) {
|
||||
display.icon = new ItemStack(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder icon(ItemStack value) {
|
||||
display.icon = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder title(Component value) {
|
||||
display.title = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder description(Component value) {
|
||||
display.title = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder showToast() {
|
||||
display.showToast = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder hideToast() {
|
||||
display.showToast = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder hidden() {
|
||||
display.hidden = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder visible() {
|
||||
display.hidden = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder announceToChat() {
|
||||
display.announceChat = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder hideFromChat() {
|
||||
display.announceChat = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder frame(FrameType type) {
|
||||
display.frame = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DisplayBuilder challenge() {
|
||||
return frame(FrameType.CHALLENGE);
|
||||
}
|
||||
|
||||
public DisplayBuilder task() {
|
||||
return frame(FrameType.TASK);
|
||||
}
|
||||
|
||||
public DisplayBuilder goal() {
|
||||
return frame(FrameType.GOAL);
|
||||
}
|
||||
|
||||
public Builder endDisplay() {
|
||||
base.display(display.build());
|
||||
return base;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.betterx.bclib.mixin.common;
|
||||
|
||||
import org.betterx.bclib.api.v2.advancement.AdvancementManager;
|
||||
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.ServerAdvancementManager;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ServerAdvancementManager.class)
|
||||
public class ServerAdvancementManagerMixin {
|
||||
@ModifyArg(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/advancements/AdvancementList;add(Ljava/util/Map;)V"))
|
||||
public Map<ResourceLocation, Advancement.Builder> wunder_interceptApply(Map<ResourceLocation, Advancement.Builder> map) {
|
||||
AdvancementManager.addAdvancements(map);
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -249,9 +249,10 @@ public class AlloyingRecipe implements Recipe<Container>, UnknownReceipBookCateg
|
|||
BCLib.LOGGER.debug("Can't add Alloying recipe {}! Ingeredient or output not exists.", id);
|
||||
return;
|
||||
}
|
||||
BCLRecipeManager.addRecipe(
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(
|
||||
TYPE,
|
||||
new AlloyingRecipe(id, group, primaryInput, secondaryInput, output, experience, smeltTime)
|
||||
new AlloyingRecipe(id, group, primaryInput, secondaryInput, output, experience, smeltTime),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -350,9 +350,10 @@ public class AnvilRecipe implements Recipe<Container>, UnknownReceipBookCategory
|
|||
BCLib.LOGGER.debug("Can't add Anvil recipe {}! Ingeredient or output not exists.", id);
|
||||
return;
|
||||
}
|
||||
BCLRecipeManager.addRecipe(
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(
|
||||
TYPE,
|
||||
new AnvilRecipe(id, input, output, inputCount, toolLevel, anvilLevel, damage)
|
||||
new AnvilRecipe(id, input, output, inputCount, toolLevel, anvilLevel, damage),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package org.betterx.bclib.recipes;
|
||||
|
||||
import org.betterx.bclib.api.v2.advancement.AdvancementManager;
|
||||
import org.betterx.bclib.util.CollectionsUtil;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.TieredItem;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
|
@ -58,6 +63,48 @@ public class BCLRecipeManager {
|
|||
list.put(recipe.getId(), recipe);
|
||||
}
|
||||
|
||||
public static <C extends Container, T extends Recipe<C>> void addRecipeAndCreateAdvancement(
|
||||
RecipeType<T> type,
|
||||
T recipe
|
||||
) {
|
||||
addRecipe(type, recipe);
|
||||
registerAndCreateAdvancement(recipe, recipe.getResultItem().getItem() instanceof TieredItem);
|
||||
}
|
||||
|
||||
public static <C extends Container, T extends Recipe<C>> void addRecipeAndCreateAdvancement(
|
||||
RecipeType<T> type,
|
||||
T recipe,
|
||||
boolean isTool
|
||||
) {
|
||||
addRecipe(type, recipe);
|
||||
registerAndCreateAdvancement(recipe, isTool);
|
||||
}
|
||||
|
||||
public static <C extends Container, T extends Recipe<C>> ResourceLocation registerAndCreateAdvancement(
|
||||
T recipe,
|
||||
boolean isTool
|
||||
) {
|
||||
AdvancementManager.Builder b = AdvancementManager.Builder.createRecipe(
|
||||
recipe,
|
||||
isTool
|
||||
? AdvancementManager.AdvancementType.RECIPE_TOOL
|
||||
: AdvancementManager.AdvancementType.RECIPE_DECORATIONS
|
||||
);
|
||||
|
||||
int ct = 0;
|
||||
for (Ingredient ingredient : recipe.getIngredients()) {
|
||||
for (ItemStack stack : ingredient.getItems()) {
|
||||
if (stack.is(Items.AIR)) continue;
|
||||
|
||||
final String name = "has_" + ct++;
|
||||
Item item = stack.getItem();
|
||||
b.addInventoryChangedCriterion(name, item);
|
||||
}
|
||||
}
|
||||
|
||||
return b.buildAndRegister();
|
||||
}
|
||||
|
||||
public static <C extends Container, T extends Recipe<C>> T getRecipe(RecipeType<T> type, ResourceLocation id) {
|
||||
Map<ResourceLocation, T> map = BCLRecipeManager.<C, T>RECIPES().get(type);
|
||||
return map != null ? map.get(id) : null;
|
||||
|
|
|
@ -84,7 +84,7 @@ public class FurnaceRecipe {
|
|||
xp,
|
||||
time
|
||||
);
|
||||
BCLRecipeManager.addRecipe(RecipeType.SMELTING, recipe);
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(RecipeType.SMELTING, recipe, false);
|
||||
|
||||
if (blasting) {
|
||||
BlastingRecipe recipe2 = new BlastingRecipe(
|
||||
|
@ -95,7 +95,7 @@ public class FurnaceRecipe {
|
|||
xp,
|
||||
time / 2
|
||||
);
|
||||
BCLRecipeManager.addRecipe(RecipeType.BLASTING, recipe2);
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(RecipeType.BLASTING, recipe2, false);
|
||||
}
|
||||
|
||||
if (campfire) {
|
||||
|
@ -107,7 +107,7 @@ public class FurnaceRecipe {
|
|||
xp,
|
||||
time * 3
|
||||
);
|
||||
BCLRecipeManager.addRecipe(RecipeType.CAMPFIRE_COOKING, recipe2);
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(RecipeType.CAMPFIRE_COOKING, recipe2, false);
|
||||
}
|
||||
|
||||
if (smoker) {
|
||||
|
@ -119,7 +119,7 @@ public class FurnaceRecipe {
|
|||
xp,
|
||||
time / 2
|
||||
);
|
||||
BCLRecipeManager.addRecipe(RecipeType.SMOKING, recipe2);
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(RecipeType.SMOKING, recipe2, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,6 +145,6 @@ public class GridRecipe {
|
|||
result
|
||||
) : new ShapelessRecipe(id, group, result, materials);
|
||||
|
||||
BCLRecipeManager.addRecipe(type, recipe);
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(type, recipe);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,6 @@ public class SmithingTableRecipe {
|
|||
return;
|
||||
}
|
||||
|
||||
BCLRecipeManager.addRecipe(TYPE, new UpgradeRecipe(id, base, addition, result));
|
||||
BCLRecipeManager.addRecipeAndCreateAdvancement(TYPE, new UpgradeRecipe(id, base, addition, result));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"RecipeManagerAccessor",
|
||||
"RecipeManagerMixin",
|
||||
"RegistryAccessMixin",
|
||||
"ServerAdvancementManagerMixin",
|
||||
"ServerLevelMixin",
|
||||
"ShovelItemAccessor",
|
||||
"StructuresAccessor",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue