[Feature] partial Integration with JEI

This commit is contained in:
Frank 2022-07-26 16:21:47 +02:00
parent 0c3a152caf
commit d734b8b140
7 changed files with 439 additions and 2 deletions

View file

@ -25,6 +25,8 @@ repositories {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
maven { url 'https://maven.terraformersmc.com/releases' } maven { url 'https://maven.terraformersmc.com/releases' }
maven { url "https://ladysnake.jfrog.io/artifactory/mods" } maven { url "https://ladysnake.jfrog.io/artifactory/mods" }
maven { url = "https://dvs1.progwml6.com/files/maven/" }
maven { url = "https://modmaven.dev" }
flatDir { flatDir {
dirs 'libs' dirs 'libs'
} }
@ -50,6 +52,12 @@ dependencies {
modCompileOnly "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}" modCompileOnly "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}"
modCompileOnly "me.shedaniel:RoughlyEnoughItems-api-fabric:${project.rei_version}" modCompileOnly "me.shedaniel:RoughlyEnoughItems-api-fabric:${project.rei_version}"
// compile against the JEI API but do not include it at runtime
modCompileOnlyApi "mezz.jei:jei-${project.minecraft_version}-common-api:${project.jei_version}"
modCompileOnlyApi "mezz.jei:jei-${project.minecraft_version}-fabric-api:${project.jei_version}"
// at runtime, use the full JEI jar for Fabric
modRuntimeOnly "mezz.jei:jei-${project.minecraft_version}-fabric:${project.jei_version}"
//needed for trinkets, otherwise BetterEnd would require users to install trinkets //needed for trinkets, otherwise BetterEnd would require users to install trinkets
modApi "dev.onyxstudios.cardinal-components-api:cardinal-components-base:${project.cca_version}" modApi "dev.onyxstudios.cardinal-components-api:cardinal-components-base:${project.cca_version}"
modCompileOnly "dev.emi:trinkets:${project.trinkets_version}" modCompileOnly "dev.emi:trinkets:${project.trinkets_version}"

View file

@ -16,5 +16,6 @@ archives_base_name=better-end
patchouli_version=1.19-73-FABRIC patchouli_version=1.19-73-FABRIC
bclib_version=2.0.17 bclib_version=2.0.17
rei_version=9.1.500 rei_version=9.1.500
jei_version=11.1.0.235
trinkets_version=3.4.0 trinkets_version=3.4.0
cca_version=5.0.0-beta.1 cca_version=5.0.0-beta.1

View file

@ -275,7 +275,7 @@ public class EndStoneSmelterBlockEntity extends BaseContainerBlockEntity impleme
} }
boolean accepted = blockEntity.canAcceptRecipeOutput(recipe); boolean accepted = blockEntity.canAcceptRecipeOutput(recipe);
if (!burning && accepted) { if (!burning && accepted) {
blockEntity.burnTime = blockEntity.getFuelTime(fuel); blockEntity.burnTime = EndStoneSmelterBlockEntity.getFuelTime(fuel);
blockEntity.fuelTime = blockEntity.burnTime; blockEntity.fuelTime = blockEntity.burnTime;
burning = blockEntity.isBurning(); burning = blockEntity.isBurning();
if (burning) { if (burning) {
@ -419,7 +419,7 @@ public class EndStoneSmelterBlockEntity extends BaseContainerBlockEntity impleme
return true; return true;
} }
protected int getFuelTime(ItemStack fuel) { public static int getFuelTime(ItemStack fuel) {
if (fuel.isEmpty()) { if (fuel.isEmpty()) {
return 0; return 0;
} }

View file

@ -0,0 +1,171 @@
package org.betterx.betterend.integration.jei;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.recipe.builders.AlloyingRecipe;
import org.betterx.betterend.registry.EndBlocks;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import mezz.jei.api.constants.ModIds;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.drawable.IDrawableAnimated;
import mezz.jei.api.gui.drawable.IDrawableStatic;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
public class JEIAlloyingCategory implements IRecipeCategory<AlloyingRecipe> {
public static final RecipeType TYPE = RecipeType.create(
BetterEnd.MOD_ID,
AlloyingRecipe.GROUP,
AlloyingRecipe.class
);
public static final String TEXTURE_GUI_PATH = "textures/gui/";
public static final String TEXTURE_GUI_VANILLA = TEXTURE_GUI_PATH + "gui_vanilla.png";
public static final ResourceLocation RECIPE_GUI_VANILLA = new ResourceLocation(ModIds.JEI_ID, TEXTURE_GUI_VANILLA);
public static final int width = 116;
public static final int height = 54;
protected final IDrawableStatic staticFlame;
protected final IDrawableStatic addonSlot;
protected final IDrawableAnimated animatedFlame;
private final IDrawable background;
private final IDrawable icon;
private final Component title;
private final LoadingCache<Integer, IDrawableAnimated> cachedArrows;
public JEIAlloyingCategory(IGuiHelper guiHelper) {
staticFlame = guiHelper.createDrawable(RECIPE_GUI_VANILLA, 82, 114, 14, 14);
animatedFlame = guiHelper.createAnimatedDrawable(staticFlame, 300, IDrawableAnimated.StartDirection.TOP, true);
background = guiHelper.createDrawable(RECIPE_GUI_VANILLA, 0, 114, 82, 54);
title = Component.translatable(EndBlocks.END_STONE_SMELTER.getDescriptionId());
icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(EndBlocks.END_STONE_SMELTER));
this.cachedArrows = CacheBuilder.newBuilder()
.maximumSize(25)
.build(new CacheLoader<>() {
@Override
public IDrawableAnimated load(Integer cookTime) {
return guiHelper.drawableBuilder(
RECIPE_GUI_VANILLA,
82,
128,
24,
17
)
.buildAnimated(
cookTime,
IDrawableAnimated.StartDirection.LEFT,
false
);
}
});
addonSlot = guiHelper.getSlotDrawable();
}
protected IDrawableAnimated getArrow(AlloyingRecipe recipe) {
int cookTime = recipe.getSmeltTime();
if (cookTime <= 0) {
cookTime = 0;
}
return this.cachedArrows.getUnchecked(cookTime);
}
@Override
public RecipeType<AlloyingRecipe> getRecipeType() {
return TYPE;
}
@Override
public Component getTitle() {
return title;
}
@Override
public IDrawable getBackground() {
return background;
}
@Override
public IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, AlloyingRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 1, 1)
.addIngredients(recipe.getIngredients().get(0));
if (recipe.getIngredients().size() > 1) {
builder.addSlot(RecipeIngredientRole.INPUT, 21, 1)
.addIngredients(recipe.getIngredients().get(1));
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 61, 19)
.addItemStack(recipe.getResultItem());
}
@Override
public boolean isHandled(AlloyingRecipe recipe) {
return !recipe.isSpecial();
}
@Override
public void draw(
AlloyingRecipe recipe,
IRecipeSlotsView recipeSlotsView,
PoseStack poseStack,
double mouseX,
double mouseY
) {
animatedFlame.draw(poseStack, 1, 20);
if (recipe.getIngredients().size() > 1) {
addonSlot.draw(poseStack, 20, 0);
}
IDrawableAnimated arrow = getArrow(recipe);
arrow.draw(poseStack, 24, 18);
drawExperience(recipe, poseStack, 0);
drawCookTime(recipe, poseStack, 45);
}
protected void drawExperience(AlloyingRecipe recipe, PoseStack poseStack, int y) {
float experience = recipe.getExperience();
if (experience > 0) {
Component experienceString = Component.translatable("gui.jei.category.smelting.experience", experience);
Minecraft minecraft = Minecraft.getInstance();
Font fontRenderer = minecraft.font;
int stringWidth = fontRenderer.width(experienceString);
fontRenderer.draw(poseStack, experienceString, background.getWidth() - stringWidth, y, 0xFF808080);
}
}
protected void drawCookTime(AlloyingRecipe recipe, PoseStack poseStack, int y) {
int cookTime = recipe.getSmeltTime();
if (cookTime > 0) {
int cookTimeSeconds = cookTime / 20;
Component timeString = Component.translatable("gui.jei.category.smelting.time.seconds", cookTimeSeconds);
Minecraft minecraft = Minecraft.getInstance();
net.minecraft.client.gui.Font fontRenderer = minecraft.font;
int stringWidth = fontRenderer.width(timeString);
fontRenderer.draw(poseStack, timeString, background.getWidth() - stringWidth, y, 0xFF808080);
}
}
}

View file

@ -0,0 +1,183 @@
package org.betterx.betterend.integration.jei;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.blocks.entities.EndStoneSmelterBlockEntity;
import org.betterx.betterend.recipe.builders.AlloyingRecipe;
import org.betterx.betterend.registry.EndBlocks;
import org.betterx.ui.layout.values.Rectangle;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.drawable.IDrawableAnimated;
import mezz.jei.api.gui.drawable.IDrawableStatic;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.api.recipe.vanilla.IJeiFuelingRecipe;
import mezz.jei.api.runtime.IIngredientManager;
import java.text.NumberFormat;
import java.util.Comparator;
import java.util.List;
import org.jetbrains.annotations.Unmodifiable;
public class JEIAlloyingFuelCategory implements IRecipeCategory<IJeiFuelingRecipe> {
public static final RecipeType FUEL_TYPE = RecipeType.create(
BetterEnd.MOD_ID,
AlloyingRecipe.GROUP + "_fuel",
IJeiFuelingRecipe.class
);
private final IDrawableStatic background;
private final IDrawable icon;
private final Component localizedName;
private final LoadingCache<Integer, IDrawableAnimated> cachedFlames;
private final Rectangle textArea;
public static List<IJeiFuelingRecipe> getFuelRecipes(IIngredientManager ingredientManager) {
return ingredientManager.getAllIngredients(VanillaTypes.ITEM_STACK).stream()
.<IJeiFuelingRecipe>mapMulti((stack, consumer) -> {
if (EndStoneSmelterBlockEntity.canUseAsFuel(stack)) {
final int time = EndStoneSmelterBlockEntity.getFuelTime(stack);
if (time > 0) {
final List<ItemStack> inputs = List.of(stack);
consumer.accept(new IJeiFuelingRecipe() {
@Override
public @Unmodifiable List<ItemStack> getInputs() {
return inputs;
}
@Override
public int getBurnTime() {
return time;
}
});
}
}
})
.sorted(Comparator.comparingInt(IJeiFuelingRecipe::getBurnTime))
.toList();
}
public JEIAlloyingFuelCategory(IGuiHelper guiHelper) {
// width of the recipe depends on the text, which is different in each language
Minecraft minecraft = Minecraft.getInstance();
Font fontRenderer = minecraft.font;
Component maxSmeltCountText = createSmeltCountText(10000000 * 200);
int maxStringWidth = fontRenderer.width(maxSmeltCountText.getString());
int backgroundHeight = 34;
int textPadding = 20;
background = guiHelper.drawableBuilder(JEIAlloyingCategory.RECIPE_GUI_VANILLA, 0, 134, 18, backgroundHeight)
.addPadding(0, 0, 0, textPadding + maxStringWidth)
.build();
textArea = new Rectangle(20, 0, textPadding + maxStringWidth, backgroundHeight);
icon = guiHelper.createDrawableIngredient(
VanillaTypes.ITEM_STACK,
new ItemStack(EndBlocks.END_STONE_SMELTER)
);
;
localizedName = Component.translatable("gui.jei.category.fuel");
this.cachedFlames = CacheBuilder.newBuilder()
.maximumSize(25)
.build(new CacheLoader<>() {
@Override
public IDrawableAnimated load(Integer burnTime) {
return guiHelper.drawableBuilder(
JEIAlloyingCategory.RECIPE_GUI_VANILLA,
82,
114,
14,
14
)
.buildAnimated(
burnTime,
IDrawableAnimated.StartDirection.TOP,
true
);
}
});
}
@Override
public IDrawable getBackground() {
return background;
}
@Override
public RecipeType<IJeiFuelingRecipe> getRecipeType() {
return FUEL_TYPE;
}
@Override
public Component getTitle() {
return localizedName;
}
@Override
public IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, IJeiFuelingRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 1, 17)
.addItemStacks(recipe.getInputs());
}
@Override
public void draw(
IJeiFuelingRecipe recipe,
IRecipeSlotsView recipeSlotsView,
PoseStack poseStack,
double mouseX,
double mouseY
) {
int burnTime = recipe.getBurnTime();
IDrawableAnimated flame = cachedFlames.getUnchecked(burnTime);
flame.draw(poseStack, 1, 0);
Minecraft minecraft = Minecraft.getInstance();
Font font = minecraft.font;
Component smeltCountText = createSmeltCountText(burnTime);
int width = font.width(smeltCountText);
int height = font.lineHeight;
font.draw(
poseStack,
smeltCountText,
this.textArea.left + (this.textArea.width - width) / 2,
this.textArea.top + (this.textArea.height - height) / 2 + 1,
0xFF808080
);
}
private static Component createSmeltCountText(int burnTime) {
if (burnTime == 200) {
return Component.translatable("gui.jei.category.fuel.smeltCount.single");
} else {
NumberFormat numberInstance = NumberFormat.getNumberInstance();
numberInstance.setMaximumFractionDigits(2);
String smeltCount = numberInstance.format(burnTime / 200f);
return Component.translatable("gui.jei.category.fuel.smeltCount", smeltCount);
}
}
}

View file

@ -0,0 +1,71 @@
package org.betterx.betterend.integration.jei;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.recipe.builders.AlloyingRecipe;
import org.betterx.betterend.registry.EndBlocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.helpers.IJeiHelpers;
import mezz.jei.api.registration.IRecipeCatalystRegistration;
import mezz.jei.api.registration.IRecipeCategoryRegistration;
import mezz.jei.api.registration.IRecipeRegistration;
import mezz.jei.api.registration.IRecipeTransferRegistration;
import mezz.jei.api.runtime.IIngredientManager;
public class JEIPlugin implements IModPlugin {
public final static ResourceLocation PLUGIN_ID = BetterEnd.makeID("jei_plugin");
@Override
public ResourceLocation getPluginUid() {
return PLUGIN_ID;
}
@Override
public void registerCategories(IRecipeCategoryRegistration registration) {
IModPlugin.super.registerCategories(registration);
IJeiHelpers jeiHelpers = registration.getJeiHelpers();
IGuiHelper guiHelper = jeiHelpers.getGuiHelper();
registration.addRecipeCategories(new JEIAlloyingCategory(guiHelper));
registration.addRecipeCategories(new JEIAlloyingFuelCategory(guiHelper));
}
@Override
public void registerRecipes(IRecipeRegistration registration) {
Minecraft minecraft = Minecraft.getInstance();
ClientLevel world = minecraft.level;
var recipeManager = world.getRecipeManager();
IIngredientManager ingredientManager = registration.getIngredientManager();
IModPlugin.super.registerRecipes(registration);
registration.addRecipes(JEIAlloyingCategory.TYPE, recipeManager.getAllRecipesFor(AlloyingRecipe.TYPE));
registration.addRecipes(
JEIAlloyingFuelCategory.FUEL_TYPE,
JEIAlloyingFuelCategory.getFuelRecipes(ingredientManager)
);
}
@Override
public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) {
IModPlugin.super.registerRecipeCatalysts(registration);
registration.addRecipeCatalyst(
new ItemStack(EndBlocks.END_STONE_SMELTER),
JEIAlloyingCategory.TYPE,
JEIAlloyingFuelCategory.FUEL_TYPE
);
}
@Override
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
IModPlugin.super.registerRecipeTransferHandlers(registration);
}
}

View file

@ -30,6 +30,9 @@
], ],
"rei_client": [ "rei_client": [
"org.betterx.betterend.integration.rei.REIPlugin" "org.betterx.betterend.integration.rei.REIPlugin"
],
"jei_mod_plugin": [
"org.betterx.betterend.integration.jei.JEIPlugin"
] ]
}, },
"accessWidener": "betterend.accesswidener", "accessWidener": "betterend.accesswidener",