[Feature] EMI Integration for Anvil- and Alloying Recipes
This commit is contained in:
parent
a4205f3440
commit
bb15db429d
13 changed files with 492 additions and 9 deletions
|
@ -1,7 +1,15 @@
|
|||
package org.betterx.bclib.blocks;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.material.MaterialColor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LeveledAnvilBlock extends BaseAnvilBlock {
|
||||
protected final int level;
|
||||
|
||||
|
@ -10,6 +18,36 @@ public class LeveledAnvilBlock extends BaseAnvilBlock {
|
|||
this.level = level;
|
||||
}
|
||||
|
||||
public static int getAnvilCraftingLevel(Block anvil) {
|
||||
if (anvil instanceof LeveledAnvilBlock l) return l.getCraftingLevel();
|
||||
if (anvil == Blocks.ANVIL || anvil == Blocks.CHIPPED_ANVIL || anvil == Blocks.DAMAGED_ANVIL) return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean canHandle(Block anvil, int level) {
|
||||
return getAnvilCraftingLevel(anvil) >= level;
|
||||
}
|
||||
|
||||
public static List<Block> getAnvils() {
|
||||
return Registry.BLOCK
|
||||
.stream()
|
||||
.filter(b -> b instanceof LeveledAnvilBlock || b == Blocks.ANVIL)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public static List<FormattedCharSequence> getNamesForLevel(int level) {
|
||||
MutableComponent names = getAnvils()
|
||||
.stream()
|
||||
.filter(b -> canHandle(b, level))
|
||||
.map(Block::getName)
|
||||
.reduce(
|
||||
null,
|
||||
(p, c) -> p == null ? c : p.append(net.minecraft.network.chat.Component.literal(", ")).append(c)
|
||||
);
|
||||
if (names == null) return List.of();
|
||||
return Minecraft.getInstance().font.split(names, 200);
|
||||
}
|
||||
|
||||
public int getCraftingLevel() {
|
||||
return level;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package org.betterx.bclib.integration.emi;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
import dev.emi.emi.api.stack.ItemEmiStack;
|
||||
|
||||
public class AnvilEmiStack extends ItemEmiStack {
|
||||
public AnvilEmiStack(ItemLike itemLike) {
|
||||
super(new ItemStack(itemLike));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package org.betterx.bclib.integration.emi;
|
||||
|
||||
import org.betterx.bclib.recipes.AlloyingRecipe;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.recipe.EmiRecipe;
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.render.EmiTexture;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EMIAlloyingRecipe implements EmiRecipe {
|
||||
private final ResourceLocation id;
|
||||
private final List<EmiIngredient> input;
|
||||
private final List<EmiStack> output;
|
||||
|
||||
public EMIAlloyingRecipe(AlloyingRecipe recipe) {
|
||||
this.id = recipe.getId();
|
||||
this.input = List.of(
|
||||
EmiIngredient.of(recipe.getIngredients().get(0)),
|
||||
EmiIngredient.of(recipe.getIngredients().get(1))
|
||||
);
|
||||
this.output = List.of(EmiStack.of(recipe.getResultItem()));
|
||||
}
|
||||
|
||||
static void addAllRecipes(EmiRegistry emiRegistry, RecipeManager manager) {
|
||||
for (AlloyingRecipe recipe : manager.getAllRecipesFor(AlloyingRecipe.TYPE)) {
|
||||
emiRegistry.addRecipe(new EMIAlloyingRecipe(recipe));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return EMIPlugin.END_ALLOYING_CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return 76;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return 18;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
// Add an arrow texture to indicate processing
|
||||
widgets.addTexture(EmiTexture.EMPTY_ARROW, 46, 1);
|
||||
|
||||
// Adds an input slot on the left
|
||||
widgets.addSlot(input.get(0), 0, 0);
|
||||
widgets.addSlot(input.get(1), 20, 0);
|
||||
|
||||
// Adds an output slot on the right
|
||||
// Note that output slots need to call `recipeContext` to inform EMI about their recipe context
|
||||
// This includes being able to resolve recipe trees, favorite stacks with recipe context, and more
|
||||
widgets.addSlot(output.get(0), 58, 0).recipeContext(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.betterx.bclib.integration.emi;
|
||||
|
||||
import org.betterx.bclib.recipes.AnvilRecipe;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.recipe.EmiRecipe;
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.render.EmiTexture;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EMIAnvilRecipe implements EmiRecipe {
|
||||
private final ResourceLocation id;
|
||||
private final List<EmiIngredient> input;
|
||||
private final List<EmiStack> output;
|
||||
private final EmiRecipeCategory category;
|
||||
|
||||
public EMIAnvilRecipe(AnvilRecipe recipe, Item hammer) {
|
||||
this.id = new ResourceLocation(
|
||||
recipe.getId().getNamespace(),
|
||||
recipe.getId().getPath() + "_" + hammer.getDescriptionId()
|
||||
);
|
||||
this.input = List.of(
|
||||
EmiIngredient.of(recipe.getMainIngredient(), recipe.getInputCount()),
|
||||
EmiIngredient.of(Ingredient.of(hammer))
|
||||
);
|
||||
this.output = List.of(EmiStack.of(recipe.getResultItem()));
|
||||
this.category = EMIPlugin.getAnvilCategoryForLevel(recipe.getAnvilLevel());
|
||||
}
|
||||
|
||||
static void addAllRecipes(EmiRegistry emiRegistry, RecipeManager manager) {
|
||||
Iterable<Holder<Item>> hammers = AnvilRecipe.getAllHammers();
|
||||
for (AnvilRecipe recipe : manager.getAllRecipesFor(AnvilRecipe.TYPE)) {
|
||||
for (Holder<Item> hammer : hammers) {
|
||||
if (recipe.canUse(hammer.value()))
|
||||
emiRegistry.addRecipe(new EMIAnvilRecipe(recipe, hammer.value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return 76;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return 18;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgetHolder) {
|
||||
// Add an arrow texture to indicate processing
|
||||
widgetHolder.addTexture(EmiTexture.EMPTY_ARROW, 46, 1);
|
||||
|
||||
// Adds an input slot on the left
|
||||
widgetHolder.addSlot(input.get(0), 0, 0);
|
||||
widgetHolder.addSlot(input.get(1), 20, 0);
|
||||
|
||||
// Adds an output slot on the right
|
||||
// Note that output slots need to call `recipeContext` to inform EMI about their recipe context
|
||||
// This includes being able to resolve recipe trees, favorite stacks with recipe context, and more
|
||||
widgetHolder.addSlot(output.get(0), 58, 0).recipeContext(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package org.betterx.bclib.integration.emi;
|
||||
|
||||
import org.betterx.bclib.blocks.LeveledAnvilBlock;
|
||||
import org.betterx.bclib.util.RomanNumeral;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import dev.emi.emi.EmiPort;
|
||||
import dev.emi.emi.EmiUtil;
|
||||
import dev.emi.emi.api.recipe.EmiRecipe;
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.render.EmiRenderable;
|
||||
import dev.emi.emi.api.render.EmiTexture;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class EMIAnvilRecipeCategory extends EmiRecipeCategory {
|
||||
private final int anvilLevel;
|
||||
private final List<FormattedCharSequence> titleLines;
|
||||
|
||||
|
||||
public EMIAnvilRecipeCategory(ResourceLocation id, EmiRenderable icon, int anvilLevel) {
|
||||
super(id, icon);
|
||||
this.anvilLevel = anvilLevel;
|
||||
titleLines = LeveledAnvilBlock.getNamesForLevel(anvilLevel);
|
||||
}
|
||||
|
||||
public EMIAnvilRecipeCategory(ResourceLocation id, EmiRenderable icon, EmiRenderable simplified, int anvilLevel) {
|
||||
super(id, icon, simplified);
|
||||
this.anvilLevel = anvilLevel;
|
||||
titleLines = LeveledAnvilBlock.getNamesForLevel(anvilLevel);
|
||||
}
|
||||
|
||||
public EMIAnvilRecipeCategory(
|
||||
ResourceLocation id,
|
||||
EmiRenderable icon, EmiTexture simplified,
|
||||
Comparator<EmiRecipe> sorter,
|
||||
int anvilLevel
|
||||
) {
|
||||
super(id, icon, simplified, sorter);
|
||||
this.anvilLevel = anvilLevel;
|
||||
titleLines = LeveledAnvilBlock.getNamesForLevel(anvilLevel);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderSimplified(PoseStack stack, int x, int y, float delta) {
|
||||
super.renderSimplified(stack, x, y, delta);
|
||||
final Font font = Minecraft.getInstance().font;
|
||||
final String content = RomanNumeral.toRoman(anvilLevel);
|
||||
|
||||
MultiBufferSource.BufferSource bufferSource = MultiBufferSource
|
||||
.immediate(
|
||||
Tesselator.getInstance()
|
||||
.getBuilder()
|
||||
);
|
||||
font.drawInBatch(
|
||||
content,
|
||||
x + 19 - 2 - font.width(content), y + 6 + 3,
|
||||
0xFFFFFF,
|
||||
true,
|
||||
stack.last().pose(),
|
||||
bufferSource,
|
||||
false,
|
||||
0,
|
||||
0xF000F0
|
||||
);
|
||||
bufferSource.endBatch();
|
||||
}
|
||||
|
||||
public List<ClientTooltipComponent> getTooltip() {
|
||||
List<ClientTooltipComponent> list = Lists.newArrayList();
|
||||
if (titleLines.isEmpty()) {
|
||||
list.add(ClientTooltipComponent.create(EmiPort.ordered(EmiPort.translatable(EmiUtil.translateId(
|
||||
"emi.category.",
|
||||
this.getId()
|
||||
)))));
|
||||
} else {
|
||||
for (var line : titleLines)
|
||||
list.add(ClientTooltipComponent.create(line));
|
||||
}
|
||||
|
||||
|
||||
list.add(ClientTooltipComponent.create(EmiPort.ordered(EmiPort.literal(
|
||||
EmiUtil.getModName(this.getId()
|
||||
.getNamespace()),
|
||||
new ChatFormatting[]{ChatFormatting.BLUE, ChatFormatting.ITALIC}
|
||||
))));
|
||||
return list;
|
||||
}
|
||||
}
|
116
src/main/java/org/betterx/bclib/integration/emi/EMIPlugin.java
Normal file
116
src/main/java/org/betterx/bclib/integration/emi/EMIPlugin.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
package org.betterx.bclib.integration.emi;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.blocks.LeveledAnvilBlock;
|
||||
import org.betterx.bclib.interfaces.AlloyingRecipeWorkstation;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
import dev.emi.emi.api.EmiPlugin;
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.render.EmiTexture;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class EMIPlugin implements EmiPlugin {
|
||||
private static boolean didInit = false;
|
||||
private static int maxAnvilLevel = 1;
|
||||
public static final ResourceLocation MY_SPRITE_SHEET = BCLib.makeID(
|
||||
"textures/gui/widgets.png"
|
||||
);
|
||||
|
||||
public static EmiStack END_ALLOYING_WORKSTATION;
|
||||
public static EmiRecipeCategory END_ALLOYING_CATEGORY;
|
||||
|
||||
public static EmiRecipeCategory[] ANVIL_CATEGORIES;
|
||||
public static EmiStack[] ANVIL_WORKSTATIONS;
|
||||
|
||||
public static EmiTexture getSprite(int u, int v) {
|
||||
return new EmiTexture(MY_SPRITE_SHEET, u, v, 16, 16, 16, 16, 32, 32);
|
||||
}
|
||||
|
||||
public void lazyInit() {
|
||||
if (!didInit) {
|
||||
didInit = true;
|
||||
lazyInitAlloyingCategory();
|
||||
lazyInitAnvilCategories();
|
||||
}
|
||||
}
|
||||
|
||||
private void lazyInitAlloyingCategory() {
|
||||
var workstations = AlloyingRecipeWorkstation.getWorkstationIcon();
|
||||
if (!workstations.is(Blocks.BARRIER.asItem())) {
|
||||
END_ALLOYING_WORKSTATION = EmiStack.of(workstations);
|
||||
|
||||
END_ALLOYING_CATEGORY = new EmiRecipeCategory(
|
||||
BCLib.makeID("alloying"),
|
||||
END_ALLOYING_WORKSTATION,
|
||||
getSprite(16, 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void lazyInitAnvilCategories() {
|
||||
if (ANVIL_CATEGORIES == null) {
|
||||
maxAnvilLevel = Math.max(1, LeveledAnvilBlock
|
||||
.getAnvils()
|
||||
.stream()
|
||||
.map(LeveledAnvilBlock::getAnvilCraftingLevel)
|
||||
.reduce(0, Math::max)
|
||||
);
|
||||
|
||||
ANVIL_CATEGORIES = new EmiRecipeCategory[maxAnvilLevel + 1];
|
||||
ANVIL_WORKSTATIONS = new EmiStack[maxAnvilLevel + 1];
|
||||
|
||||
for (int anvilLevel = 0; anvilLevel <= maxAnvilLevel; anvilLevel++) {
|
||||
int finalAnvilLevel = anvilLevel;
|
||||
ANVIL_WORKSTATIONS[anvilLevel] = new AnvilEmiStack(LeveledAnvilBlock
|
||||
.getAnvils()
|
||||
.stream()
|
||||
.filter(b -> LeveledAnvilBlock.canHandle(b, finalAnvilLevel))
|
||||
.sorted(Comparator.comparingInt(LeveledAnvilBlock::getAnvilCraftingLevel))
|
||||
.findFirst().orElse(Blocks.BARRIER)
|
||||
);
|
||||
|
||||
ANVIL_CATEGORIES[anvilLevel] = new EMIAnvilRecipeCategory(
|
||||
BCLib.makeID("anvil_" + anvilLevel),
|
||||
ANVIL_WORKSTATIONS[anvilLevel],
|
||||
getSprite(0, 0),
|
||||
anvilLevel
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void register(EmiRegistry emiRegistry) {
|
||||
lazyInit();
|
||||
final RecipeManager manager = emiRegistry.getRecipeManager();
|
||||
|
||||
if (END_ALLOYING_CATEGORY != null && END_ALLOYING_WORKSTATION != null) {
|
||||
emiRegistry.addCategory(END_ALLOYING_CATEGORY);
|
||||
emiRegistry.addWorkstation(END_ALLOYING_CATEGORY, END_ALLOYING_WORKSTATION);
|
||||
|
||||
EMIAlloyingRecipe.addAllRecipes(emiRegistry, manager);
|
||||
}
|
||||
|
||||
if (ANVIL_CATEGORIES != null && ANVIL_WORKSTATIONS != null && ANVIL_CATEGORIES.length > 0) {
|
||||
for (int i = 0; i <= maxAnvilLevel; i++) {
|
||||
emiRegistry.addCategory(ANVIL_CATEGORIES[i]);
|
||||
emiRegistry.addWorkstation(ANVIL_CATEGORIES[i], ANVIL_WORKSTATIONS[i]);
|
||||
}
|
||||
EMIAnvilRecipe.addAllRecipes(emiRegistry, manager);
|
||||
}
|
||||
}
|
||||
|
||||
static EmiRecipeCategory getAnvilCategoryForLevel(int anvilLevel) {
|
||||
anvilLevel = Math.max(0, Math.min(ANVIL_CATEGORIES.length - 1, anvilLevel));
|
||||
return ANVIL_CATEGORIES[anvilLevel];
|
||||
}
|
||||
}
|
|
@ -6,8 +6,10 @@ import org.betterx.bclib.interfaces.UnknownReceipBookCategory;
|
|||
import org.betterx.bclib.util.ItemUtil;
|
||||
import org.betterx.bclib.util.RecipeHelper;
|
||||
import org.betterx.worlds.together.tag.v3.CommonItemTags;
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
@ -115,6 +117,12 @@ public class AnvilRecipe implements Recipe<Container>, UnknownReceipBookCategory
|
|||
return this.output.copy();
|
||||
}
|
||||
|
||||
public static Iterable<Holder<Item>> getAllHammers() {
|
||||
Registry<Item> registry = WorldBootstrap.getLastRegistryAccessOrElseBuiltin()
|
||||
.registryOrThrow(CommonItemTags.HAMMERS.registry());
|
||||
return registry.getTagOrEmpty(CommonItemTags.HAMMERS);
|
||||
}
|
||||
|
||||
public static int getHammerSlot(Container c) {
|
||||
ItemStack h = c.getItem(0);
|
||||
if (!h.isEmpty() && h.is(CommonItemTags.HAMMERS)) return 0;
|
||||
|
@ -182,18 +190,32 @@ public class AnvilRecipe implements Recipe<Container>, UnknownReceipBookCategory
|
|||
return this.inputCount;
|
||||
}
|
||||
|
||||
public Ingredient getMainIngredient() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
public int getAnvilLevel() {
|
||||
return this.anvilLevel;
|
||||
}
|
||||
|
||||
public boolean canUse(Item tool) {
|
||||
if (tool instanceof TieredItem ti) {
|
||||
return ti.getTier().getLevel() >= toolLevel;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isHammer(Item tool) {
|
||||
if (tool == null) return false;
|
||||
return tool.getDefaultInstance().is(CommonItemTags.HAMMERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonNullList<Ingredient> getIngredients() {
|
||||
NonNullList<Ingredient> defaultedList = NonNullList.create();
|
||||
defaultedList.add(Ingredient.of(Registry.ITEM.stream()
|
||||
.filter(item -> item.builtInRegistryHolder()
|
||||
.is(CommonItemTags.HAMMERS))
|
||||
.filter(hammer -> ((TieredItem) hammer).getTier()
|
||||
.getLevel() >= toolLevel)
|
||||
.filter(AnvilRecipe::isHammer)
|
||||
.filter(this::canUse)
|
||||
.map(ItemStack::new))
|
||||
);
|
||||
defaultedList.add(input);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue