Add Native EMI compat + fix JEI bug with removing compressed recipes
This commit is contained in:
parent
6454f127c2
commit
f6dad61a49
|
|
@ -131,13 +131,13 @@ dependencies {
|
|||
compileOnly("curse.maven:jade-324717:5109393")
|
||||
// JEI OPTIONAL
|
||||
compileOnly("mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}")
|
||||
runtimeOnly("mezz.jei:jei-${mc_version}-neoforge:${jei_version}")
|
||||
//runtimeOnly("mezz.jei:jei-${mc_version}-neoforge:${jei_version}")
|
||||
// REI OPTIONAL todo add
|
||||
compileOnly("me.shedaniel:RoughlyEnoughItems-forge:${rei_version}")
|
||||
compileOnly("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config_version}")
|
||||
// EMI OPTIONAL
|
||||
compileOnly("dev.emi:emi-neoforge:${emi_version}+${mc_version}:api")
|
||||
compileOnly("dev.emi:emi-neoforge:${emi_version}+${mc_version}")
|
||||
runtimeOnly("dev.emi:emi-neoforge:${emi_version}+${mc_version}")
|
||||
//implementation("curse.maven:reipc-521393:4837449")
|
||||
// KubeJS OPTIONAL
|
||||
implementation("dev.architectury:architectury-neoforge:${architectury_version}")
|
||||
|
|
@ -156,7 +156,7 @@ dependencies {
|
|||
|
||||
// testing
|
||||
//implementation("curse.maven:allthecompressed-514045:4938351")
|
||||
implementation("curse.maven:alltheores-405593:5500624")
|
||||
compileOnly("curse.maven:alltheores-405593:5500624")
|
||||
//implementation("curse.maven:inventorysorter-240633:4655091")
|
||||
//implementation("curse.maven:cyclic-239286:4994392")
|
||||
//implementation("curse.maven:flib-661261:4724762")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
## Ex Deorum 3.3
|
||||
- Now built against Minecraft 1.21.1
|
||||
- Add native EMI support.
|
||||
- Fix bug where removing all Compressed Sieve recipes would break regular Sieve recipe display in JEI.
|
||||
- Hack fix for random crashes with fluid transformation recipe cache
|
||||
|
||||
## Ex Deorum 3.2
|
||||
- Fix KubeJS plugin.
|
||||
|
|
|
|||
|
|
@ -179,6 +179,17 @@
|
|||
"config.jade.plugin_exdeorum.crucible": "Crucible",
|
||||
"config.jade.plugin_exdeorum.infested_leaves": "Infested Leaves",
|
||||
"config.jade.plugin_exdeorum.sieve": "Sieve",
|
||||
"emi.category.exdeorum.barrel_compost": "Barrel Compost",
|
||||
"emi.category.exdeorum.barrel_fluid_mixing": "Barrel Fluid Mixing",
|
||||
"emi.category.exdeorum.barrel_mixing": "Barrel Mixing",
|
||||
"emi.category.exdeorum.compressed_hammer": "Compressed Hammer",
|
||||
"emi.category.exdeorum.compressed_sieve": "Compressed Sieve",
|
||||
"emi.category.exdeorum.crook": "Crook",
|
||||
"emi.category.exdeorum.crucible_heat_sources": "Crucible Heat Source",
|
||||
"emi.category.exdeorum.hammer": "Hammer",
|
||||
"emi.category.exdeorum.lava_crucible": "Lava Crucible",
|
||||
"emi.category.exdeorum.sieve": "Sieve",
|
||||
"emi.category.exdeorum.water_crucible": "Water Crucible",
|
||||
"exdeorum.container.mechanical_hammer": "Mechanical Hammer",
|
||||
"exdeorum.container.mechanical_sieve": "Mechanical Sieve",
|
||||
"fluid_type.exdeorum.witch_water": "Witch Water",
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ public class BarrelBlockEntity extends ETankBlockEntity {
|
|||
if (recipe != null) {
|
||||
if (!simulate) {
|
||||
// Empty barrel
|
||||
this.tank.drain(recipe.fluidAmount, IFluidHandler.FluidAction.EXECUTE);
|
||||
this.tank.drain(recipe.fluid.amount(), IFluidHandler.FluidAction.EXECUTE);
|
||||
// Replace fluid with result
|
||||
setItem(recipe.result.copy());
|
||||
this.level.playSound(null, this.worldPosition, ESounds.BARREL_MIXING.get(), SoundSource.BLOCKS, 0.8f, 1.0f);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class ClientHandler {
|
|||
fmlBus.addListener(ClientHandler::onScreenOpen);
|
||||
fmlBus.addListener(ClientHandler::onRecipesUpdated);
|
||||
|
||||
if (ModList.get().isLoaded(ModIds.JEI)) {
|
||||
if (ModList.get().isLoaded(ModIds.JEI) || ModList.get().isLoaded(ModIds.EMI)) {
|
||||
modBus.addListener(ClientHandler::registerAdditionalModels);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ClientsideCode {
|
||||
@Nullable
|
||||
public static RecipeManager getRecipeManager() {
|
||||
ClientPacketListener connection = Minecraft.getInstance().getConnection();
|
||||
if (connection != null) {
|
||||
return connection.getRecipeManager();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
251
src/main/java/thedarkcolour/exdeorum/compat/ClientXeiUtil.java
Normal file
251
src/main/java/thedarkcolour/exdeorum/compat/ClientXeiUtil.java
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
package thedarkcolour.exdeorum.compat;
|
||||
|
||||
import com.mojang.blaze3d.platform.Lighting;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import com.mojang.math.Axis;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.neoforged.neoforge.client.RenderTypeHelper;
|
||||
import net.neoforged.neoforge.client.model.data.ModelData;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector3f;
|
||||
import thedarkcolour.exdeorum.client.ClientHandler;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
import thedarkcolour.exdeorum.registry.EBlocks;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
// client-only logic shared between JEI and EMI
|
||||
public class ClientXeiUtil {
|
||||
public static final DecimalFormat FORMATTER = new DecimalFormat();
|
||||
private static final ItemStack OAK_BARREL = new ItemStack(DefaultMaterials.OAK_BARREL.getItem());
|
||||
|
||||
private static final FluidState EMPTY = Fluids.EMPTY.defaultFluidState();
|
||||
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
|
||||
|
||||
// From https://github.com/The-Aether-Team/Nitrogen/blob/1.20.1-develop/src/main/java/com/aetherteam/nitrogen/integration/jei/BlockStateRenderer.java
|
||||
private static final Vector3f L1 = new Vector3f(0.4F, 0.0F, 1.0F).normalize();
|
||||
private static final Vector3f L2 = new Vector3f(-0.4F, 1.0F, -0.2F).normalize();
|
||||
|
||||
public static void renderItemAlternativeModel(GuiGraphics graphics, BakedModel model, ItemStack stack, int xOffset, int yOffset) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
var pose = graphics.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(8 + xOffset, 8 + yOffset, 150);
|
||||
|
||||
try {
|
||||
pose.mulPose(new Matrix4f().scaling(1.0F, -1.0F, 1.0F));
|
||||
pose.scale(16f, 16f, 16f);
|
||||
boolean flag = !model.usesBlockLight();
|
||||
if (flag) {
|
||||
Lighting.setupForFlatItems();
|
||||
}
|
||||
|
||||
mc.getItemRenderer().render(stack, ItemDisplayContext.GUI, false, pose, graphics.bufferSource(), 0xf000f0, OverlayTexture.NO_OVERLAY, model);
|
||||
graphics.flush();
|
||||
if (flag) {
|
||||
Lighting.setupFor3DItems();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item");
|
||||
CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered");
|
||||
crashreportcategory.setDetail("Item Type", () -> String.valueOf(stack.getItem()));
|
||||
crashreportcategory.setDetail("Registry Name", () -> BuiltInRegistries.ITEM.getKey(stack.getItem()).toString());
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
|
||||
pose.popPose();
|
||||
|
||||
// From end of ItemStackRenderer
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
|
||||
// https://github.com/way2muchnoise/JustEnoughResources/blob/89ee40ff068c8d6eb6ab103f76381445691cffc9/Common/src/main/java/jeresources/util/RenderHelper.java#L100
|
||||
public static void renderBlock(GuiGraphics guiGraphics, BlockState state, float x, float y, float z, float scale) {
|
||||
PoseStack poseStack = guiGraphics.pose();
|
||||
|
||||
poseStack.translate(x, y, z);
|
||||
poseStack.scale(-scale, -scale, -scale);
|
||||
poseStack.translate(-0.5F, -0.5F, 0);
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(-30F));
|
||||
poseStack.translate(0.5F, 0, -0.5F);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(45f));
|
||||
poseStack.translate(-0.5F, 0, 0.5F);
|
||||
|
||||
poseStack.pushPose();
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F);
|
||||
poseStack.translate(0, 0, -1);
|
||||
|
||||
FluidState fluidState = state.getFluidState();
|
||||
|
||||
if (fluidState.isEmpty()) {
|
||||
MultiBufferSource.BufferSource buffers = Minecraft.getInstance().renderBuffers().bufferSource();
|
||||
|
||||
RenderSystem.setupGui3DDiffuseLighting(L1, L2);
|
||||
if (state.is(EBlocks.INFESTED_LEAVES.get())) {
|
||||
var blockRenderer = Minecraft.getInstance().getBlockRenderer();
|
||||
var bakedmodel = blockRenderer.getBlockModel(state);
|
||||
|
||||
for (var renderType : bakedmodel.getRenderTypes(state, RandomSource.create(42), ModelData.EMPTY)) {
|
||||
blockRenderer.getModelRenderer().renderModel(poseStack.last(), buffers.getBuffer(RenderTypeHelper.getEntityRenderType(renderType, false)), state, bakedmodel, 1f, 1f, 1f, 15728880, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, renderType);
|
||||
}
|
||||
} else {
|
||||
Minecraft.getInstance().getBlockRenderer().renderSingleBlock(state, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, null);
|
||||
}
|
||||
|
||||
buffers.endBatch();
|
||||
} else {
|
||||
RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
||||
Matrix4fStack modelView = RenderSystem.getModelViewStack();
|
||||
Tesselator tesselator = Tesselator.getInstance();
|
||||
renderType.setupRenderState();
|
||||
modelView.pushMatrix();
|
||||
modelView.mul(poseStack.last().pose());
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
|
||||
BufferBuilder builder = tesselator.begin(renderType.mode(), renderType.format());
|
||||
|
||||
Dummy.tempState = state;
|
||||
Dummy.tempFluid = fluidState;
|
||||
Minecraft.getInstance().getBlockRenderer().renderLiquid(BlockPos.ZERO, Dummy.INSTANCE, builder, state, state.getFluidState());
|
||||
Dummy.tempFluid = EMPTY;
|
||||
Dummy.tempState = AIR;
|
||||
|
||||
MeshData build = builder.build();
|
||||
if (build != null) {
|
||||
BufferUploader.drawWithShader(build);
|
||||
}
|
||||
|
||||
renderType.clearRenderState();
|
||||
modelView.popMatrix();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
public static void renderItemWithAsterisk(GuiGraphics graphics, ItemStack stack) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
BakedModel model = mc.getItemRenderer().getModel(stack, mc.level, null, 0);
|
||||
renderItemAlternativeModel(graphics, model, stack, 0, 0);
|
||||
renderAsterisk(graphics, 0, 0);
|
||||
}
|
||||
|
||||
public static void renderAsterisk(GuiGraphics graphics, int xOffset, int yOffset) {
|
||||
graphics.pose().pushPose();
|
||||
graphics.pose().translate(0f, 0f, 200f);
|
||||
|
||||
var font = Minecraft.getInstance().font;
|
||||
// 0xff5555 is Minecraft's red text color.
|
||||
graphics.drawString(font, "*", xOffset + 19 - 2 - font.width("*"), yOffset + 12, 0xff5555, true);
|
||||
|
||||
graphics.pose().popPose();
|
||||
}
|
||||
|
||||
// Takes a decimal probability and returns a user-friendly percentage value
|
||||
public static Component formatChance(double probability) {
|
||||
var chance = FORMATTER.format(probability * 100);
|
||||
return Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY);
|
||||
}
|
||||
|
||||
public static void renderFilledCompostBarrel(GuiGraphics guiGraphics, int xOffset, int yOffset) {
|
||||
// From mezz.jei.library.render.ItemStackRenderer
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
var model = mc.getModelManager().getModel(ClientHandler.OAK_BARREL_COMPOSTING);
|
||||
// From GuiGraphics.renderFakeItem
|
||||
ClientXeiUtil.renderItemAlternativeModel(guiGraphics, model, OAK_BARREL, xOffset, yOffset);
|
||||
// From end of DrawableIngredient
|
||||
RenderSystem.disableDepthTest();
|
||||
}
|
||||
|
||||
public enum Dummy implements BlockAndTintGetter {
|
||||
INSTANCE;
|
||||
|
||||
private static BlockState tempState = AIR;
|
||||
private static FluidState tempFluid = EMPTY;
|
||||
|
||||
@Override
|
||||
public float getShade(Direction pDirection, boolean pShade) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine() {
|
||||
return Minecraft.getInstance().level.getLightEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos pBlockPos, ColorResolver pColorResolver) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightness(LightLayer pLightType, BlockPos pBlockPos) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawBrightness(BlockPos pBlockPos, int pAmount) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity getBlockEntity(BlockPos pPos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
return pos.equals(BlockPos.ZERO) ? tempState : AIR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
return pos.equals(BlockPos.ZERO) ? tempFluid : EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,10 +19,8 @@
|
|||
package thedarkcolour.exdeorum.compat;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
|
|
@ -36,11 +34,11 @@ import net.minecraft.world.level.Level;
|
|||
import net.neoforged.fml.ModList;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
import thedarkcolour.exdeorum.material.MaterialRegistry;
|
||||
import thedarkcolour.exdeorum.recipe.RecipeUtil;
|
||||
import thedarkcolour.exdeorum.registry.EItems;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
|
@ -82,7 +80,7 @@ public class CompatUtil {
|
|||
}
|
||||
|
||||
public static <C extends RecipeInput, R extends Recipe<C>, T> List<T> collectAllRecipes(RecipeType<R> recipeType, Function<R, T> mapper) {
|
||||
var byType = Objects.requireNonNull(Minecraft.getInstance().level).getRecipeManager().byType(recipeType);
|
||||
var byType = RecipeUtil.getRecipeManager().byType(recipeType);
|
||||
List<T> recipes = new ObjectArrayList<>(byType.size());
|
||||
for (RecipeHolder<R> value : byType) {
|
||||
recipes.add(mapper.apply(value.value()));
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import net.minecraft.world.item.crafting.Ingredient;
|
|||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
import thedarkcolour.exdeorum.recipe.RecipeUtil;
|
||||
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
|
||||
import thedarkcolour.exdeorum.registry.EItems;
|
||||
|
|
@ -39,11 +40,12 @@ import java.util.List;
|
|||
import java.util.function.Function;
|
||||
|
||||
// Since no JEI code is used here, this can be reused for REI
|
||||
public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Result> results) {
|
||||
public static int maxSieveRows;
|
||||
public record XeiSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Result> results) {
|
||||
public static final MutableInt SIEVE_ROWS = new MutableInt(0);
|
||||
public static final MutableInt COMPRESSED_SIEVE_ROWS = new MutableInt(0);
|
||||
|
||||
public static ImmutableList<GroupedSieveRecipe> getAllRecipesGrouped(RecipeType<? extends SieveRecipe> recipeType) {
|
||||
maxSieveRows = 1;
|
||||
public static ImmutableList<XeiSieveRecipe> getAllRecipesGrouped(RecipeType<? extends SieveRecipe> recipeType, MutableInt maxRows) {
|
||||
int maxSieveRows = 1;
|
||||
|
||||
var recipes = CompatUtil.collectAllRecipes(recipeType, Function.identity());
|
||||
Multimap<Ingredient, SieveRecipe> ingredientGrouper = ArrayListMultimap.create();
|
||||
|
|
@ -64,11 +66,11 @@ public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Res
|
|||
}
|
||||
}
|
||||
|
||||
ImmutableList.Builder<GroupedSieveRecipe> jeiRecipes = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<XeiSieveRecipe> jeiRecipes = new ImmutableList.Builder<>();
|
||||
// Sort based on expected count of result
|
||||
var resultSorter = Comparator.comparingDouble(Result::expectedCount).reversed();
|
||||
// Sort based on order of sieve tier
|
||||
var meshSorter = Comparator.comparingInt(GroupedSieveRecipe::meshOrder);
|
||||
var meshSorter = Comparator.comparingInt(XeiSieveRecipe::meshOrder);
|
||||
|
||||
// ingredients with common ingredients are grouped into lists (ex. dirt)
|
||||
for (var ingredient : ingredientGrouper.keySet()) {
|
||||
|
|
@ -97,7 +99,7 @@ public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Res
|
|||
|
||||
results.sort(resultSorter);
|
||||
|
||||
var jeiRecipe = new GroupedSieveRecipe(ingredient, new ItemStack(mesh), results);
|
||||
var jeiRecipe = new XeiSieveRecipe(ingredient, new ItemStack(mesh), results);
|
||||
jeiRecipes.add(jeiRecipe);
|
||||
|
||||
var rows = Mth.ceil((float) meshRecipes.size() / 9f);
|
||||
|
|
@ -106,6 +108,9 @@ public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Res
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxRows.setValue(maxSieveRows);
|
||||
|
||||
return jeiRecipes.build();
|
||||
}
|
||||
|
||||
270
src/main/java/thedarkcolour/exdeorum/compat/XeiUtil.java
Normal file
270
src/main/java/thedarkcolour/exdeorum/compat/XeiUtil.java
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.JsonObject;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.WallTorchBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.BinomialDistributionGenerator;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.neoforgespi.language.IModInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.loot.SummationGenerator;
|
||||
import thedarkcolour.exdeorum.recipe.BlockPredicate;
|
||||
import thedarkcolour.exdeorum.recipe.CodecUtil;
|
||||
import thedarkcolour.exdeorum.recipe.RecipeUtil;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// common logic shared between JEI, EMI, and REI (boo REI sucks)
|
||||
public class XeiUtil {
|
||||
// One To One (Hammer, Crucible)
|
||||
public static final int ONE_TO_ONE_WIDTH = 72;
|
||||
public static final int ONE_TO_ONE_HEIGHT = 18;
|
||||
|
||||
// Barrel mixing (Fluid/Item, Fluid/Fluid)
|
||||
public static final int BARREL_MIXING_WIDTH = 120;
|
||||
public static final int BARREL_MIXING_HEIGHT = 18;
|
||||
|
||||
// Barrel compost
|
||||
public static final int BARREL_COMPOST_WIDTH = 120;
|
||||
public static final int BARREL_COMPOST_HEIGHT = 18;
|
||||
|
||||
// Block predicate (Crucible Heat, Sieve)
|
||||
public static final Component REQUIRES_CERTAIN_STATE = Component.translatable(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE).withStyle(ChatFormatting.GRAY);
|
||||
|
||||
// Sieve
|
||||
public static final int SIEVE_WIDTH = 162;
|
||||
public static final int SIEVE_ROW_START = 28;
|
||||
public static final int SIEVE_ROW_HEIGHT = 18;
|
||||
public static final Component BY_HAND_ONLY_LABEL = Component.translatable(TranslationKeys.SIEVE_RECIPE_BY_HAND_ONLY).withStyle(ChatFormatting.RED);
|
||||
|
||||
public static final DecimalFormat FORMATTER = new DecimalFormat();
|
||||
|
||||
static {
|
||||
FORMATTER.setMinimumFractionDigits(0);
|
||||
FORMATTER.setMaximumFractionDigits(3);
|
||||
}
|
||||
|
||||
// Takes a decimal probability and returns a user-friendly percentage value
|
||||
public static Component formatChance(double probability) {
|
||||
var chance = FORMATTER.format(probability * 100);
|
||||
return Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY);
|
||||
}
|
||||
|
||||
public static List<BlockState> getStates(BlockPredicate predicate) {
|
||||
if (predicate instanceof BlockPredicate.BlockStatePredicate state) {
|
||||
return state.possibleStates()
|
||||
.filter(blockState -> !blockState.hasProperty(BlockStateProperties.WATERLOGGED) || !blockState.getValue(BlockStateProperties.WATERLOGGED))
|
||||
.toList();
|
||||
} else if (predicate instanceof BlockPredicate.SingleBlockPredicate block) {
|
||||
return ImmutableList.of(block.block().defaultBlockState());
|
||||
} else if (predicate instanceof BlockPredicate.TagPredicate tag) {
|
||||
var list = new ArrayList<BlockState>();
|
||||
|
||||
for (var holder : BuiltInRegistries.BLOCK.getTagOrEmpty(tag.tag())) {
|
||||
if (holder.isBound()) {
|
||||
list.add(holder.value().defaultBlockState());
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid Block Predicate");
|
||||
}
|
||||
|
||||
// Copied from mezz.jei.forge.platform.ModHelper and mezz.jei.library.ModIdHelper
|
||||
public static Component getModDisplayName(String modId) {
|
||||
String string = ModList.get().getModContainerById(modId)
|
||||
.map(ModContainer::getModInfo)
|
||||
.map(IModInfo::getDisplayName)
|
||||
.orElseGet(() -> StringUtils.capitalize(modId));
|
||||
|
||||
String withoutFormattingCodes = ChatFormatting.stripFormatting(string);
|
||||
return Component.literal((withoutFormattingCodes == null) ? "" : withoutFormattingCodes).withStyle(style -> style.withItalic(true).withColor(ChatFormatting.BLUE));
|
||||
}
|
||||
|
||||
public static List<Component> getBlockTooltip(List<Component> extraDetails, Block block) {
|
||||
var modId = BuiltInRegistries.BLOCK.getKey(block).getNamespace();
|
||||
var tooltip = new ArrayList<Component>();
|
||||
|
||||
tooltip.add(Component.translatable(block.getDescriptionId()));
|
||||
tooltip.addAll(extraDetails);
|
||||
tooltip.add(getModDisplayName(modId));
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public static ImmutableList<Component> getStateRequirements(BlockPredicate.@Nullable BlockStatePredicate predicate) {
|
||||
ImmutableList.Builder<Component> requirements = ImmutableList.builder();
|
||||
if (predicate != null) {
|
||||
var json = CodecUtil.encode(StatePropertiesPredicate.CODEC, predicate.properties());
|
||||
if (json instanceof JsonObject obj) {
|
||||
for (var entry : obj.entrySet()) {
|
||||
requirements.add(Component.literal(" " + entry.getKey() + "=" + entry.getValue().toString()).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
return requirements.build();
|
||||
}
|
||||
|
||||
public static List<Component> getExtraDetails(BlockPredicate predicate) {
|
||||
List<Component> extraDetails;
|
||||
if (predicate instanceof BlockPredicate.TagPredicate tag) {
|
||||
extraDetails = ImmutableList.of(Component.literal("#" + tag.tag().location()).withStyle(ChatFormatting.GRAY));
|
||||
} else if (predicate instanceof BlockPredicate.BlockStatePredicate state) {
|
||||
var requirements = getStateRequirements(state);
|
||||
extraDetails = new ArrayList<>(requirements.size() + 1);
|
||||
extraDetails.add(REQUIRES_CERTAIN_STATE);
|
||||
extraDetails.addAll(requirements);
|
||||
} else {
|
||||
extraDetails = List.of();
|
||||
}
|
||||
return extraDetails;
|
||||
}
|
||||
|
||||
public static void addSieveDropTooltip(boolean byHandOnly, NumberProvider provider, Consumer<Component> tooltipLines) {
|
||||
if (byHandOnly) {
|
||||
tooltipLines.accept(XeiUtil.BY_HAND_ONLY_LABEL);
|
||||
}
|
||||
|
||||
if (provider instanceof BinomialDistributionGenerator binomial) {
|
||||
if (binomial.n() instanceof ConstantValue constant && constant.value() == 1) {
|
||||
var chanceLabel = XeiUtil.formatChance(RecipeUtil.getExpectedValue(binomial.p()));
|
||||
tooltipLines.accept(chanceLabel);
|
||||
} else {
|
||||
addAvgOutput(tooltipLines, RecipeUtil.getExpectedValue(provider));
|
||||
}
|
||||
|
||||
addMinMaxes(tooltipLines, 0, getMax(binomial.n()));
|
||||
} else if (provider.getClass() != ConstantValue.class) {
|
||||
var val = RecipeUtil.getExpectedValue(provider);
|
||||
if (val != -1.0) {
|
||||
addAvgOutput(tooltipLines, val);
|
||||
|
||||
if (provider instanceof UniformGenerator || provider instanceof SummationGenerator) {
|
||||
addMinMaxes(tooltipLines, getMin(provider), getMax(provider));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double getMin(NumberProvider provider) {
|
||||
if (provider instanceof ConstantValue value) {
|
||||
return value.value();
|
||||
} else if (provider instanceof UniformGenerator uniform) {
|
||||
return getMin(uniform.min());
|
||||
} else if (provider instanceof BinomialDistributionGenerator) {
|
||||
return 0;
|
||||
} else if (provider instanceof SummationGenerator summation) {
|
||||
double sum = 0;
|
||||
|
||||
for (var child : summation.providers()) {
|
||||
sum += getMin(child);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static double getMax(NumberProvider provider) {
|
||||
if (provider instanceof ConstantValue value) {
|
||||
return value.value();
|
||||
} else if (provider instanceof UniformGenerator uniform) {
|
||||
return getMax(uniform.max());
|
||||
} else if (provider instanceof BinomialDistributionGenerator binomial) {
|
||||
return getMax(binomial.n());
|
||||
} else if (provider instanceof SummationGenerator summation) {
|
||||
double sum = 0;
|
||||
|
||||
for (var child : summation.providers()) {
|
||||
sum += getMax(child);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void addAvgOutput(Consumer<Component> tooltipLines, double avgValue) {
|
||||
String avgOutput = XeiUtil.FORMATTER.format(avgValue);
|
||||
tooltipLines.accept(Component.translatable(TranslationKeys.SIEVE_RECIPE_AVERAGE_OUTPUT, avgOutput).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
|
||||
// when the player holds shift, they can see the min/max amounts of a drop
|
||||
private static void addMinMaxes(Consumer<Component> tooltipLines, double min, double max) {
|
||||
String minFormatted = XeiUtil.FORMATTER.format(min);
|
||||
String maxFormatted = XeiUtil.FORMATTER.format(max);
|
||||
|
||||
tooltipLines.accept(Component.translatable(TranslationKeys.SIEVE_RECIPE_MIN_OUTPUT, minFormatted).withStyle(ChatFormatting.GRAY));
|
||||
tooltipLines.accept(Component.translatable(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, maxFormatted).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
|
||||
public interface HeatRecipeAcceptor {
|
||||
void accept(int heat, BlockState state);
|
||||
}
|
||||
|
||||
public static void addCrucibleHeatRecipes(HeatRecipeAcceptor acceptor) {
|
||||
var values = new Object2IntOpenHashMap<Block>();
|
||||
for (var entry : RecipeUtil.getHeatSources()) {
|
||||
var state = entry.getKey();
|
||||
var block = state.getBlock();
|
||||
|
||||
if (block instanceof WallTorchBlock) continue;
|
||||
|
||||
if (block != Blocks.AIR) {
|
||||
final int newValue = entry.getIntValue();
|
||||
|
||||
values.computeInt(block, (key, value) -> {
|
||||
if (value != null) {
|
||||
return Math.max(value, newValue);
|
||||
} else {
|
||||
return newValue == 0 ? null : newValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (var entry : values.object2IntEntrySet()) {
|
||||
acceptor.accept(entry.getIntValue(), entry.getKey().defaultBlockState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class BarrelCompostEmiRecipe extends EEmiRecipe {
|
||||
private final List<EmiIngredient> inputs;
|
||||
private final int volume;
|
||||
|
||||
public BarrelCompostEmiRecipe(BarrelCompostRecipe recipe, ResourceLocation id) {
|
||||
super(id);
|
||||
|
||||
this.inputs = EmiUtil.inputs(recipe);
|
||||
this.volume = recipe.getVolume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.BARREL_COMPOST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return 120;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return 18;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
widgets.addSlot(this.inputs.getFirst(), 0, 0);
|
||||
|
||||
var volumeLabel = Component.translatable(TranslationKeys.BARREL_COMPOST_RECIPE_VOLUME, this.volume);
|
||||
widgets.addText(volumeLabel, 24, 5, 0xff808080, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.neoforge.NeoForgeEmiIngredient;
|
||||
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 net.minecraft.resources.ResourceLocation;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class BarrelMixingEmiRecipe extends EEmiRecipe {
|
||||
private final EmiIngredient base;
|
||||
private final EmiIngredient additive;
|
||||
private final List<EmiIngredient> inputs;
|
||||
private final List<EmiStack> outputs;
|
||||
|
||||
public BarrelMixingEmiRecipe(EmiIngredient base, EmiIngredient additive, List<EmiStack> outputs, ResourceLocation id) {
|
||||
super(id);
|
||||
|
||||
this.base = base;
|
||||
this.additive = additive;
|
||||
|
||||
this.inputs = List.of(base, additive);
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return XeiUtil.BARREL_MIXING_WIDTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return XeiUtil.BARREL_MIXING_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
widgets.addSlot(this.base, 0, 0);
|
||||
widgets.addTexture(EmiTexture.PLUS, 22, 2);
|
||||
widgets.addSlot(this.additive, 39, 0);
|
||||
widgets.addTexture(EmiTexture.EMPTY_ARROW, 63, 1);
|
||||
widgets.addSlot(this.outputs.getFirst(), 78 + 15, 0).recipeContext(this);
|
||||
}
|
||||
|
||||
static class Items extends BarrelMixingEmiRecipe {
|
||||
public Items(BarrelMixingRecipe recipe, ResourceLocation id) {
|
||||
super(NeoForgeEmiIngredient.of(recipe.fluid), EmiIngredient.of(recipe.ingredient), EmiUtil.outputs(recipe.result), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.BARREL_MIXING;
|
||||
}
|
||||
}
|
||||
|
||||
static class Fluids extends BarrelMixingEmiRecipe {
|
||||
public Fluids(BarrelFluidMixingRecipe recipe, ResourceLocation id) {
|
||||
super(NeoForgeEmiIngredient.of(recipe.baseFluid()), NeoForgeEmiIngredient.of(recipe.additiveFluid()), EmiUtil.outputs(recipe.result()), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.BARREL_FLUID_MIXING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.neoforge.NeoForgeEmiStack;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.Bounds;
|
||||
import dev.emi.emi.api.widget.SlotWidget;
|
||||
import dev.emi.emi.api.widget.Widget;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.neoforged.neoforge.fluids.FluidStack;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BlockEmiWidget extends Widget {
|
||||
private final List<BlockState> states;
|
||||
private final List<Component> extraDetails;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final float scale;
|
||||
private final Bounds bounds;
|
||||
|
||||
public BlockEmiWidget(List<BlockState> states, List<Component> extraDetails, int x, int y, float scale) {
|
||||
this.states = states;
|
||||
this.extraDetails = extraDetails;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.scale = scale;
|
||||
|
||||
int size = (int) (1.5f * scale);
|
||||
this.bounds = new Bounds(x - size / 2, y - size / 4, size, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bounds getBounds() {
|
||||
return this.bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics draw, int mouseX, int mouseY, float delta) {
|
||||
draw.pose().pushPose();
|
||||
|
||||
int index = (int) (System.currentTimeMillis() / 1000 % this.states.size());
|
||||
BlockState current = this.states.get(index);
|
||||
|
||||
ClientXeiUtil.renderBlock(draw, current, this.x, this.y, 0, this.scale);
|
||||
|
||||
draw.pose().popPose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTooltipComponent> getTooltip(int mouseX, int mouseY) {
|
||||
int index = (int) (System.currentTimeMillis() / 1000 % this.states.size());
|
||||
BlockState current = this.states.get(index);
|
||||
List<Component> tooltip = XeiUtil.getBlockTooltip(this.extraDetails, current.getBlock());
|
||||
|
||||
return tooltip.stream()
|
||||
.map(component -> ClientTooltipComponent.create(component.getVisualOrderText()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(int mouseX, int mouseY, int button) {
|
||||
return slotCall(this.states, widget -> widget.mouseClicked(1, 1, button));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
return slotCall(this.states, widget -> widget.keyPressed(keyCode, scanCode, modifiers));
|
||||
}
|
||||
|
||||
// Hack to use internal EMI behavior of switching to different recipe view
|
||||
private static boolean slotCall(List<BlockState> states, Predicate<SlotWidget> call) {
|
||||
int index = (int) (System.currentTimeMillis() / 1000 % states.size());
|
||||
BlockState current = states.get(index);
|
||||
EmiStack stack = EmiStack.of(current.getBlock());
|
||||
|
||||
if (stack.isEmpty() && !current.getFluidState().isEmpty()) {
|
||||
stack = NeoForgeEmiStack.of(new FluidStack(current.getFluidState().getType(), 1000));
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
SlotWidget internal = new SlotWidget(stack, 0, 0);
|
||||
return call.test(internal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
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 net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
import thedarkcolour.exdeorum.recipe.BlockPredicate;
|
||||
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class CrookEmiRecipe extends EEmiRecipe {
|
||||
private final List<EmiIngredient> inputs;
|
||||
private final List<EmiStack> outputs;
|
||||
private final List<BlockState> states;
|
||||
private final BlockPredicate predicate;
|
||||
|
||||
public CrookEmiRecipe(CrookRecipe recipe, ResourceLocation id) {
|
||||
super(id);
|
||||
|
||||
this.inputs = EmiUtil.inputs(recipe.blockPredicate());
|
||||
this.outputs = ImmutableList.of(EmiStack.of(recipe.result()));
|
||||
this.states = XeiUtil.getStates(recipe.blockPredicate());
|
||||
this.predicate = recipe.blockPredicate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.CROOK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return 120;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
widgets.addTexture(EmiTexture.EMPTY_ARROW, 50, 18);
|
||||
widgets.addSlot(this.outputs.getFirst(), 79, 17).recipeContext(this);
|
||||
widgets.add(new BlockEmiWidget(this.states, XeiUtil.getExtraDetails(this.predicate), 28, 18, 20f));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class CrucibleEmiRecipe extends EmiOneToOneRecipe {
|
||||
private final List<EmiStack> outputs;
|
||||
|
||||
CrucibleEmiRecipe(CrucibleRecipe recipe, ResourceLocation id) {
|
||||
super(recipe, id);
|
||||
|
||||
this.outputs = EmiUtil.outputs(recipe.getResult());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
static class Lava extends CrucibleEmiRecipe {
|
||||
Lava(CrucibleRecipe recipe, ResourceLocation id) {
|
||||
super(recipe, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.LAVA_CRUCIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
static class Water extends CrucibleEmiRecipe {
|
||||
Water(CrucibleRecipe recipe, ResourceLocation id) {
|
||||
super(recipe, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.WATER_CRUCIBLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.TextWidget;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.recipe.BlockPredicate;
|
||||
import thedarkcolour.exdeorum.recipe.crucible.CrucibleHeatRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class CrucibleHeatEmiRecipe extends EEmiRecipe {
|
||||
private final List<EmiIngredient> inputs;
|
||||
private final List<BlockState> states;
|
||||
private final BlockPredicate predicate;
|
||||
private final int heatValue;
|
||||
|
||||
public CrucibleHeatEmiRecipe(CrucibleHeatRecipe recipe, ResourceLocation id) {
|
||||
super(id);
|
||||
|
||||
this.inputs = EmiUtil.inputs(recipe.blockPredicate());
|
||||
this.states = XeiUtil.getStates(recipe.blockPredicate());
|
||||
this.predicate = recipe.blockPredicate();
|
||||
this.heatValue = recipe.heatValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.CRUCIBLE_HEAT_SOURCES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return 120;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
widgets.addText(Component.translatable(TranslationKeys.CRUCIBLE_HEAT_SOURCE_CATEGORY_MULTIPLIER, this.heatValue), 60, 5, 0xff808080, false)
|
||||
.horizontalAlign(TextWidget.Alignment.CENTER);
|
||||
widgets.add(new BlockEmiWidget(this.states, XeiUtil.getExtraDetails(this.predicate), 60, 24, 20f));
|
||||
}
|
||||
}
|
||||
|
|
@ -16,16 +16,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.event;
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import dev.emi.emi.api.recipe.EmiRecipe;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// necessary to avoid EventBus loading LocalPlayer through its ASM transformations
|
||||
class ClientsideCode {
|
||||
@Nullable
|
||||
static Player getLocalPlayer() {
|
||||
return Minecraft.getInstance().player;
|
||||
abstract class EEmiRecipe implements EmiRecipe {
|
||||
protected final ResourceLocation id;
|
||||
|
||||
EEmiRecipe(ResourceLocation id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getId() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.render.EmiTexture;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
import thedarkcolour.exdeorum.recipe.SingleIngredientRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class EmiOneToOneRecipe extends EEmiRecipe {
|
||||
private final List<EmiIngredient> inputs;
|
||||
|
||||
EmiOneToOneRecipe(SingleIngredientRecipe recipe, ResourceLocation id) {
|
||||
super(id);
|
||||
|
||||
this.inputs = EmiUtil.inputs(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return XeiUtil.ONE_TO_ONE_WIDTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return XeiUtil.ONE_TO_ONE_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
// todo replace with first in 1.21
|
||||
widgets.addSlot(this.inputs.get(0), 0, 0);
|
||||
widgets.addTexture(EmiTexture.EMPTY_ARROW, 24, 1);
|
||||
widgets.addSlot(getOutputs().get(0), 54, 0).recipeContext(this);
|
||||
}
|
||||
}
|
||||
90
src/main/java/thedarkcolour/exdeorum/compat/emi/EmiUtil.java
Normal file
90
src/main/java/thedarkcolour/exdeorum/compat/emi/EmiUtil.java
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.neoforge.NeoForgeEmiStack;
|
||||
import dev.emi.emi.api.recipe.EmiRecipe;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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.item.crafting.RecipeInput;
|
||||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
import net.neoforged.neoforge.fluids.FluidStack;
|
||||
import thedarkcolour.exdeorum.recipe.BlockPredicate;
|
||||
import thedarkcolour.exdeorum.recipe.SingleIngredientRecipe;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class EmiUtil {
|
||||
// Returns a list with 1 element.
|
||||
static List<EmiIngredient> inputs(SingleIngredientRecipe recipe) {
|
||||
return ImmutableList.of(EmiIngredient.of(recipe.ingredient));
|
||||
}
|
||||
|
||||
static List<EmiIngredient> inputs(BlockPredicate predicate) {
|
||||
if (predicate instanceof BlockPredicate.SingleBlockPredicate block) {
|
||||
Item item = block.block().asItem();
|
||||
|
||||
if (item != Items.AIR) {
|
||||
return ImmutableList.of(EmiStack.of(item));
|
||||
}
|
||||
} else {
|
||||
ImmutableList.Builder<EmiIngredient> builder = ImmutableList.builder();
|
||||
var items = new HashSet<Item>();
|
||||
|
||||
predicate.possibleStates().forEach(state -> {
|
||||
Item item = state.getBlock().asItem();
|
||||
|
||||
if (item != Items.AIR) {
|
||||
if (items.add(item)) {
|
||||
builder.add(EmiStack.of(item));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Need to wrap list in an ingredient to get OR behavior instead of AND behavior
|
||||
return ImmutableList.of(EmiIngredient.of(builder.build()));
|
||||
}
|
||||
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public static <C extends RecipeInput, R extends Recipe<C>> void addAll(EmiRegistry registry, Supplier<RecipeType<R>> type, BiFunction<R, ResourceLocation, ? extends EmiRecipe> factory) {
|
||||
for (var holder : registry.getRecipeManager().byType(type.get())) {
|
||||
registry.addRecipe(factory.apply(holder.value(), holder.id()));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<EmiStack> outputs(ItemStack result) {
|
||||
return ImmutableList.of(EmiStack.of(result));
|
||||
}
|
||||
|
||||
public static List<EmiStack> outputs(FluidStack stack) {
|
||||
return ImmutableList.of(NeoForgeEmiStack.of(new FluidStack(stack.getFluid(), stack.getAmount())));
|
||||
}
|
||||
}
|
||||
|
|
@ -22,17 +22,129 @@ import dev.emi.emi.api.EmiEntrypoint;
|
|||
import dev.emi.emi.api.EmiInitRegistry;
|
||||
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.EmiRenderable;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.WallTorchBlock;
|
||||
import thedarkcolour.exdeorum.ExDeorum;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.compat.CompatUtil;
|
||||
import thedarkcolour.exdeorum.compat.XeiSieveRecipe;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
import thedarkcolour.exdeorum.recipe.BlockPredicate;
|
||||
import thedarkcolour.exdeorum.registry.EItems;
|
||||
import thedarkcolour.exdeorum.registry.ERecipeTypes;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@EmiEntrypoint
|
||||
public class ExDeorumEmiPlugin implements EmiPlugin {
|
||||
static final EmiRecipeCategory BARREL_COMPOST = emiCategory("barrel_compost", (graphics, x, y, partialTick) -> ClientXeiUtil.renderFilledCompostBarrel(graphics, x, y));
|
||||
static final EmiRecipeCategory BARREL_MIXING = emiCategory("barrel_mixing", EmiStack.of(DefaultMaterials.OAK_BARREL));
|
||||
static final EmiRecipeCategory BARREL_FLUID_MIXING = emiCategory("barrel_fluid_mixing", EmiStack.of(DefaultMaterials.STONE_BARREL));
|
||||
static final EmiRecipeCategory LAVA_CRUCIBLE = emiCategory("lava_crucible", EmiStack.of(DefaultMaterials.PORCELAIN_CRUCIBLE));
|
||||
static final EmiRecipeCategory WATER_CRUCIBLE = emiCategory("water_crucible", EmiStack.of(DefaultMaterials.OAK_CRUCIBLE));
|
||||
static final EmiRecipeCategory CRUCIBLE_HEAT_SOURCES = emiCategory("crucible_heat_sources", EmiStack.of(DefaultMaterials.PORCELAIN_CRUCIBLE));
|
||||
static final EmiRecipeCategory SIEVE = emiCategory("sieve", EmiStack.of(DefaultMaterials.OAK_SIEVE));
|
||||
static final EmiRecipeCategory COMPRESSED_SIEVE = emiCategory("compressed_sieve", EmiStack.of(DefaultMaterials.OAK_COMPRESSED_SIEVE));
|
||||
static final EmiRecipeCategory HAMMER = emiCategory("hammer", EmiStack.of(EItems.DIAMOND_HAMMER.get()));
|
||||
static final EmiRecipeCategory COMPRESSED_HAMMER = emiCategory("compressed_hammer", EmiStack.of(EItems.COMPRESSED_DIAMOND_HAMMER.get()));
|
||||
static final EmiRecipeCategory CROOK = emiCategory("crook", EmiStack.of(EItems.CROOK.get()));
|
||||
|
||||
private static EmiRecipeCategory emiCategory(String name, EmiRenderable icon) {
|
||||
return new EmiRecipeCategory(ExDeorum.loc(name), icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(EmiRegistry emiRegistry) {
|
||||
public void register(EmiRegistry registry) {
|
||||
addCategories(registry);
|
||||
addWorkstations(registry);
|
||||
addRecipes(registry);
|
||||
}
|
||||
|
||||
private static void addCategories(EmiRegistry registry) {
|
||||
registry.addCategory(BARREL_COMPOST);
|
||||
registry.addCategory(BARREL_MIXING);
|
||||
registry.addCategory(BARREL_FLUID_MIXING);
|
||||
registry.addCategory(LAVA_CRUCIBLE);
|
||||
registry.addCategory(WATER_CRUCIBLE);
|
||||
registry.addCategory(CRUCIBLE_HEAT_SOURCES);
|
||||
registry.addCategory(SIEVE);
|
||||
registry.addCategory(COMPRESSED_SIEVE);
|
||||
registry.addCategory(HAMMER);
|
||||
registry.addCategory(COMPRESSED_HAMMER);
|
||||
registry.addCategory(CROOK);
|
||||
}
|
||||
|
||||
private static void addWorkstations(EmiRegistry registry) {
|
||||
for (var barrel : CompatUtil.getAvailableBarrels(true)) {
|
||||
var stack = EmiStack.of(barrel);
|
||||
registry.addWorkstation(BARREL_COMPOST, stack);
|
||||
registry.addWorkstation(BARREL_MIXING, stack);
|
||||
registry.addWorkstation(BARREL_FLUID_MIXING, stack);
|
||||
}
|
||||
for (var lavaCrucible : CompatUtil.getAvailableLavaCrucibles(true)) {
|
||||
var stack = EmiStack.of(lavaCrucible);
|
||||
registry.addWorkstation(LAVA_CRUCIBLE, stack);
|
||||
registry.addWorkstation(CRUCIBLE_HEAT_SOURCES, stack);
|
||||
}
|
||||
for (var waterCrucible : CompatUtil.getAvailableWaterCrucibles(true)) {
|
||||
registry.addWorkstation(WATER_CRUCIBLE, EmiStack.of(waterCrucible));
|
||||
}
|
||||
for (var sieve : CompatUtil.getAvailableSieves(true, true)) {
|
||||
registry.addWorkstation(SIEVE, EmiStack.of(sieve));
|
||||
}
|
||||
for (var compressedSieve : CompatUtil.getAvailableCompressedSieves(true)) {
|
||||
registry.addWorkstation(COMPRESSED_SIEVE, EmiStack.of(compressedSieve));
|
||||
}
|
||||
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.WOODEN_HAMMER.get()));
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.STONE_HAMMER.get()));
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.GOLDEN_HAMMER.get()));
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.IRON_HAMMER.get()));
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.DIAMOND_HAMMER.get()));
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.NETHERITE_HAMMER.get()));
|
||||
registry.addWorkstation(HAMMER, EmiStack.of(EItems.MECHANICAL_HAMMER.get()));
|
||||
|
||||
registry.addWorkstation(COMPRESSED_HAMMER, EmiStack.of(EItems.COMPRESSED_WOODEN_HAMMER.get()));
|
||||
registry.addWorkstation(COMPRESSED_HAMMER, EmiStack.of(EItems.COMPRESSED_STONE_HAMMER.get()));
|
||||
registry.addWorkstation(COMPRESSED_HAMMER, EmiStack.of(EItems.COMPRESSED_GOLDEN_HAMMER.get()));
|
||||
registry.addWorkstation(COMPRESSED_HAMMER, EmiStack.of(EItems.COMPRESSED_IRON_HAMMER.get()));
|
||||
registry.addWorkstation(COMPRESSED_HAMMER, EmiStack.of(EItems.COMPRESSED_DIAMOND_HAMMER.get()));
|
||||
registry.addWorkstation(COMPRESSED_HAMMER, EmiStack.of(EItems.COMPRESSED_NETHERITE_HAMMER.get()));
|
||||
|
||||
registry.addWorkstation(CROOK, EmiStack.of(EItems.CROOK.get()));
|
||||
registry.addWorkstation(CROOK, EmiStack.of(EItems.BONE_CROOK.get()));
|
||||
}
|
||||
|
||||
private static void addRecipes(EmiRegistry registry) {
|
||||
EmiUtil.addAll(registry, ERecipeTypes.BARREL_COMPOST, BarrelCompostEmiRecipe::new);
|
||||
EmiUtil.addAll(registry, ERecipeTypes.BARREL_MIXING, BarrelMixingEmiRecipe.Items::new);
|
||||
EmiUtil.addAll(registry, ERecipeTypes.BARREL_FLUID_MIXING, BarrelMixingEmiRecipe.Fluids::new);
|
||||
EmiUtil.addAll(registry, ERecipeTypes.LAVA_CRUCIBLE, CrucibleEmiRecipe.Lava::new);
|
||||
EmiUtil.addAll(registry, ERecipeTypes.WATER_CRUCIBLE, CrucibleEmiRecipe.Water::new);
|
||||
|
||||
for (var holder : registry.getRecipeManager().byType(ERecipeTypes.CRUCIBLE_HEAT_SOURCE.get())) {
|
||||
var value = holder.value();
|
||||
if (value.blockPredicate() instanceof BlockPredicate.SingleBlockPredicate block && block.block() instanceof WallTorchBlock) {
|
||||
continue;
|
||||
}
|
||||
registry.addRecipe(new CrucibleHeatEmiRecipe(value, holder.id()));
|
||||
}
|
||||
|
||||
for (XeiSieveRecipe recipe : XeiSieveRecipe.getAllRecipesGrouped(ERecipeTypes.SIEVE.get(), XeiSieveRecipe.SIEVE_ROWS)) {
|
||||
registry.addRecipe(new SieveEmiRecipe.Sieve(recipe));
|
||||
}
|
||||
for (XeiSieveRecipe recipe : XeiSieveRecipe.getAllRecipesGrouped(ERecipeTypes.COMPRESSED_SIEVE.get(), XeiSieveRecipe.COMPRESSED_SIEVE_ROWS)) {
|
||||
registry.addRecipe(new SieveEmiRecipe.CompressedSieve(recipe));
|
||||
}
|
||||
|
||||
EmiUtil.addAll(registry, ERecipeTypes.HAMMER, HammerEmiRecipe.Hammer::new);
|
||||
EmiUtil.addAll(registry, ERecipeTypes.COMPRESSED_HAMMER, HammerEmiRecipe.CompressedHammer::new);
|
||||
EmiUtil.addAll(registry, ERecipeTypes.CROOK, CrookEmiRecipe::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -50,8 +162,6 @@ public class ExDeorumEmiPlugin implements EmiPlugin {
|
|||
toRemoveItems.add(itemLike.asItem());
|
||||
}
|
||||
|
||||
registry.disableStacks(stack -> {
|
||||
return toRemoveItems.contains(stack.getItemStack().getItem());
|
||||
});
|
||||
registry.disableStacks(stack -> toRemoveItems.contains(stack.getItemStack().getItem()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class HammerEmiRecipe extends EmiOneToOneRecipe {
|
||||
private final List<EmiStack> outputs;
|
||||
|
||||
HammerEmiRecipe(HammerRecipe recipe, ResourceLocation id) {
|
||||
super(recipe, id);
|
||||
|
||||
this.outputs = EmiUtil.outputs(recipe.result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
static class Hammer extends HammerEmiRecipe {
|
||||
Hammer(HammerRecipe recipe, ResourceLocation id) {
|
||||
super(recipe, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.HAMMER;
|
||||
}
|
||||
}
|
||||
|
||||
static class CompressedHammer extends HammerEmiRecipe {
|
||||
CompressedHammer(HammerRecipe recipe, ResourceLocation id) {
|
||||
super(recipe, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.COMPRESSED_HAMMER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Ex Deorum
|
||||
* Copyright (c) 2024 thedarkcolour
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package thedarkcolour.exdeorum.compat.emi;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dev.emi.emi.api.recipe.EmiRecipeCategory;
|
||||
import dev.emi.emi.api.stack.EmiIngredient;
|
||||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import dev.emi.emi.api.widget.SlotWidget;
|
||||
import dev.emi.emi.api.widget.WidgetHolder;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
|
||||
import thedarkcolour.exdeorum.ExDeorum;
|
||||
import thedarkcolour.exdeorum.compat.XeiSieveRecipe;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
abstract class SieveEmiRecipe extends EEmiRecipe {
|
||||
private final List<EmiIngredient> inputs;
|
||||
private final int rows;
|
||||
private final List<EmiStack> outputs;
|
||||
private final XeiSieveRecipe recipe;
|
||||
|
||||
SieveEmiRecipe(XeiSieveRecipe recipe, int rows) {
|
||||
super(determineId(recipe));
|
||||
this.inputs = ImmutableList.of(EmiIngredient.of(recipe.ingredient()), EmiStack.of(recipe.mesh()));
|
||||
this.rows = rows;
|
||||
this.recipe = recipe;
|
||||
|
||||
ImmutableList.Builder<EmiStack> outputs = ImmutableList.builderWithExpectedSize(recipe.results().size());
|
||||
for (XeiSieveRecipe.Result result : recipe.results()) {
|
||||
outputs.add(EmiStack.of(result.item));
|
||||
}
|
||||
this.outputs = outputs.build();
|
||||
}
|
||||
|
||||
private static ResourceLocation determineId(XeiSieveRecipe recipe) {
|
||||
Item mesh = recipe.mesh().getItem();
|
||||
int hashCode = Arrays.hashCode(Arrays.stream(recipe.ingredient().getItems())
|
||||
.map(ItemStack::getItem)
|
||||
.toArray());
|
||||
|
||||
return ExDeorum.loc(BuiltInRegistries.ITEM.getKey(mesh).getPath() + "_" + hashCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiIngredient> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmiStack> getOutputs() {
|
||||
return this.outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayWidth() {
|
||||
return XeiUtil.SIEVE_WIDTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayHeight() {
|
||||
return XeiUtil.SIEVE_ROW_START + XeiUtil.SIEVE_ROW_HEIGHT * this.rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets(WidgetHolder widgets) {
|
||||
widgets.addSlot(this.inputs.get(0), 58, 1);
|
||||
widgets.addSlot(this.inputs.get(1), 86, 1);
|
||||
|
||||
for (int i = 0; i < this.rows * 9; i++) {
|
||||
int x = (i % 9) * 18;
|
||||
int y = XeiUtil.SIEVE_ROW_START + 18 * (i / 9);
|
||||
|
||||
if (i < this.outputs.size()) {
|
||||
XeiSieveRecipe.Result result = this.recipe.results().get(i);
|
||||
widgets.add(new SieveResultWidget(this.outputs.get(i), x, y, result.byHandOnly, result.provider)).recipeContext(this);
|
||||
} else {
|
||||
widgets.addSlot(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Sieve extends SieveEmiRecipe {
|
||||
Sieve(XeiSieveRecipe recipe) {
|
||||
super(recipe, XeiSieveRecipe.SIEVE_ROWS.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.SIEVE;
|
||||
}
|
||||
}
|
||||
|
||||
static class CompressedSieve extends SieveEmiRecipe {
|
||||
CompressedSieve(XeiSieveRecipe recipe) {
|
||||
super(recipe, XeiSieveRecipe.COMPRESSED_SIEVE_ROWS.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmiRecipeCategory getCategory() {
|
||||
return ExDeorumEmiPlugin.COMPRESSED_SIEVE;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SieveResultWidget extends SlotWidget {
|
||||
private final boolean byHandOnly;
|
||||
private final NumberProvider amount;
|
||||
|
||||
public SieveResultWidget(EmiIngredient stack, int x, int y, boolean byHandOnly, NumberProvider amount) {
|
||||
super(stack, x, y);
|
||||
this.byHandOnly = byHandOnly;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientTooltipComponent> getTooltip(int mouseX, int mouseY) {
|
||||
List<ClientTooltipComponent> list = super.getTooltip(mouseX, mouseY);
|
||||
XeiUtil.addSieveDropTooltip(this.byHandOnly, this.amount, line -> list.add(ClientTooltipComponent.create(line.getVisualOrderText())));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package thedarkcolour.exdeorum.compat.jei;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
|
||||
import mezz.jei.api.gui.drawable.IDrawable;
|
||||
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
|
||||
|
|
@ -30,10 +29,8 @@ import mezz.jei.api.recipe.category.IRecipeCategory;
|
|||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import thedarkcolour.exdeorum.client.ClientHandler;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
|
||||
|
||||
class BarrelCompostCategory implements IRecipeCategory<BarrelCompostRecipe> {
|
||||
|
|
@ -88,8 +85,6 @@ class BarrelCompostCategory implements IRecipeCategory<BarrelCompostRecipe> {
|
|||
}
|
||||
|
||||
private static class DrawableIcon implements IDrawable {
|
||||
private final ItemStack oakBarrel = new ItemStack(DefaultMaterials.OAK_BARREL.getItem());
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return 16;
|
||||
|
|
@ -102,15 +97,7 @@ class BarrelCompostCategory implements IRecipeCategory<BarrelCompostRecipe> {
|
|||
|
||||
@Override
|
||||
public void draw(GuiGraphics guiGraphics, int xOffset, int yOffset) {
|
||||
// From mezz.jei.library.render.ItemStackRenderer
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
var model = mc.getModelManager().getModel(ClientHandler.OAK_BARREL_COMPOSTING);
|
||||
// From GuiGraphics.renderFakeItem
|
||||
ClientJeiUtil.renderItemAlternativeModel(guiGraphics, model, this.oakBarrel, xOffset, yOffset);
|
||||
// From end of DrawableIngredient
|
||||
RenderSystem.disableDepthTest();
|
||||
ClientXeiUtil.renderFilledCompostBarrel(guiGraphics, xOffset, yOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import net.minecraft.client.gui.GuiGraphics;
|
|||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe;
|
||||
|
|
@ -127,7 +128,7 @@ public abstract class BarrelMixingCategory<T> implements IRecipeCategory<T> {
|
|||
super.draw(recipe, recipeSlotsView, graphics, mouseX, mouseY);
|
||||
|
||||
if (recipe.consumesAdditive()) {
|
||||
ClientJeiUtil.renderAsterisk(graphics, 18 + 3 + 3 + 8, 0);
|
||||
ClientXeiUtil.renderAsterisk(graphics, 18 + 3 + 3 + 8, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@
|
|||
|
||||
package thedarkcolour.exdeorum.compat.jei;
|
||||
|
||||
import com.mojang.blaze3d.platform.Lighting;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import com.mojang.math.Axis;
|
||||
import mezz.jei.api.ingredients.IIngredientRenderer;
|
||||
import mezz.jei.api.ingredients.IIngredientType;
|
||||
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||
|
|
@ -29,166 +26,21 @@ import mezz.jei.api.recipe.IFocusFactory;
|
|||
import mezz.jei.api.recipe.RecipeIngredientRole;
|
||||
import mezz.jei.api.runtime.IIngredientManager;
|
||||
import mezz.jei.api.runtime.IRecipesGui;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.neoforge.fluids.FluidStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector3f;
|
||||
import thedarkcolour.exdeorum.compat.ModIds;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class ClientJeiUtil {
|
||||
private static final FluidState EMPTY = Fluids.EMPTY.defaultFluidState();
|
||||
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
|
||||
|
||||
// From https://github.com/The-Aether-Team/Nitrogen/blob/1.20.1-develop/src/main/java/com/aetherteam/nitrogen/integration/jei/BlockStateRenderer.java
|
||||
private static final Vector3f L1 = new Vector3f(0.4F, 0.0F, 1.0F).normalize();
|
||||
private static final Vector3f L2 = new Vector3f(-0.4F, 1.0F, -0.2F).normalize();
|
||||
static final DecimalFormat FORMATTER = new DecimalFormat();
|
||||
|
||||
// https://github.com/way2muchnoise/JustEnoughResources/blob/89ee40ff068c8d6eb6ab103f76381445691cffc9/Common/src/main/java/jeresources/util/RenderHelper.java#L100
|
||||
static void renderBlock(GuiGraphics guiGraphics, BlockState block, float x, float y, float z, float scale, RenderBlockFn renderFunction) {
|
||||
PoseStack poseStack = guiGraphics.pose();
|
||||
|
||||
poseStack.translate(x, y, z);
|
||||
poseStack.scale(-scale, -scale, -scale);
|
||||
poseStack.translate(-0.5F, -0.5F, 0);
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(-30F));
|
||||
poseStack.translate(0.5F, 0, -0.5F);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(45f));
|
||||
poseStack.translate(-0.5F, 0, 0.5F);
|
||||
|
||||
poseStack.pushPose();
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 1F);
|
||||
poseStack.translate(0, 0, -1);
|
||||
|
||||
FluidState fluidState = block.getFluidState();
|
||||
|
||||
if (fluidState.isEmpty()) {
|
||||
MultiBufferSource.BufferSource buffers = Minecraft.getInstance().renderBuffers().bufferSource();
|
||||
|
||||
RenderSystem.setupGui3DDiffuseLighting(L1, L2);
|
||||
renderFunction.renderBlock(block, poseStack, buffers);
|
||||
|
||||
buffers.endBatch();
|
||||
} else {
|
||||
RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
||||
Matrix4fStack modelView = RenderSystem.getModelViewStack();
|
||||
Tesselator tesselator = Tesselator.getInstance();
|
||||
renderType.setupRenderState();
|
||||
modelView.pushMatrix();
|
||||
modelView.mul(poseStack.last().pose());
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
|
||||
BufferBuilder builder = tesselator.begin(renderType.mode(), renderType.format());
|
||||
|
||||
Dummy.tempState = block;
|
||||
Dummy.tempFluid = fluidState;
|
||||
Minecraft.getInstance().getBlockRenderer().renderLiquid(BlockPos.ZERO, Dummy.INSTANCE, builder, block, block.getFluidState());
|
||||
Dummy.tempFluid = EMPTY;
|
||||
Dummy.tempState = AIR;
|
||||
|
||||
MeshData build = builder.build();
|
||||
if (build != null) {
|
||||
BufferUploader.drawWithShader(build);
|
||||
}
|
||||
|
||||
renderType.clearRenderState();
|
||||
modelView.popMatrix();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
static void renderItemWithAsterisk(GuiGraphics graphics, ItemStack stack) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
BakedModel model = mc.getItemRenderer().getModel(stack, mc.level, null, 0);
|
||||
renderItemAlternativeModel(graphics, model, stack, 0, 0);
|
||||
renderAsterisk(graphics, 0, 0);
|
||||
}
|
||||
|
||||
static void renderAsterisk(GuiGraphics graphics, int xOffset, int yOffset) {
|
||||
graphics.pose().pushPose();
|
||||
graphics.pose().translate(0f, 0f, 200f);
|
||||
|
||||
var font = Minecraft.getInstance().font;
|
||||
// 0xff5555 is Minecraft's red text color.
|
||||
graphics.drawString(font, "*", xOffset + 19 - 2 - font.width("*"), yOffset + 12, 0xff5555, true);
|
||||
|
||||
graphics.pose().popPose();
|
||||
}
|
||||
|
||||
static void renderItemAlternativeModel(GuiGraphics graphics, BakedModel model, ItemStack stack, int xOffset, int yOffset) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
var pose = graphics.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(8 + xOffset, 8 + yOffset, 150);
|
||||
|
||||
try {
|
||||
pose.mulPose(new Matrix4f().scaling(1.0F, -1.0F, 1.0F));
|
||||
pose.scale(16f, 16f, 16f);
|
||||
boolean flag = !model.usesBlockLight();
|
||||
if (flag) {
|
||||
Lighting.setupForFlatItems();
|
||||
}
|
||||
|
||||
mc.getItemRenderer().render(stack, ItemDisplayContext.GUI, false, pose, graphics.bufferSource(), 0xf000f0, OverlayTexture.NO_OVERLAY, model);
|
||||
graphics.flush();
|
||||
if (flag) {
|
||||
Lighting.setupFor3DItems();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item");
|
||||
CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered");
|
||||
crashreportcategory.setDetail("Item Type", () -> {
|
||||
return String.valueOf(stack.getItem());
|
||||
});
|
||||
crashreportcategory.setDetail("Registry Name", () -> BuiltInRegistries.ITEM.getKey(stack.getItem()).toString());
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
|
||||
pose.popPose();
|
||||
|
||||
// From end of ItemStackRenderer
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
|
||||
// Required due to broken JEI implementation in REI plugin compatibility
|
||||
static <T> void checkTypedIngredient(IIngredientManager manager, IIngredientType<T> ingredientType, @Nullable T uncheckedIngredient, Consumer<ITypedIngredient<T>> action) {
|
||||
if ((uncheckedIngredient instanceof ItemStack stack && !stack.isEmpty()) || (uncheckedIngredient instanceof FluidStack fluidStack && !fluidStack.isEmpty())) {
|
||||
|
|
@ -199,9 +51,6 @@ class ClientJeiUtil {
|
|||
static <T> void showRecipes(IFocusFactory focusFactory, ITypedIngredient<T> ingredient) {
|
||||
if (Minecraft.getInstance().screen instanceof IRecipesGui recipesGui) {
|
||||
recipesGui.show(focusFactory.createFocus(RecipeIngredientRole.OUTPUT, ingredient));
|
||||
} else if (ModList.get().isLoaded(ModIds.REI_PC)) {
|
||||
// todo fix when REIPC is on 1.21
|
||||
//ViewSearchBuilder.builder().addRecipesFor(JEIPluginDetector.unwrapStack(ingredient)).open();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,79 +58,6 @@ class ClientJeiUtil {
|
|||
if (Minecraft.getInstance().screen instanceof IRecipesGui recipesGui) {
|
||||
// input + catalyst
|
||||
recipesGui.show(List.of(focusFactory.createFocus(RecipeIngredientRole.INPUT, ingredient), focusFactory.createFocus(RecipeIngredientRole.CATALYST, ingredient)));
|
||||
} else if (ModList.get().isLoaded(ModIds.REI_PC)) {
|
||||
// todo fix when REIPC is on 1.21
|
||||
//ViewSearchBuilder.builder().addUsagesFor(JEIPluginDetector.unwrapStack(ingredient)).open();
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a decimal probability and returns a user-friendly percentage value
|
||||
static Component formatChance(double probability) {
|
||||
var chance = FORMATTER.format(probability * 100);
|
||||
return Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface RenderBlockFn {
|
||||
void renderBlock(BlockState block, PoseStack poseStack, MultiBufferSource.BufferSource buffers);
|
||||
}
|
||||
|
||||
private enum Dummy implements BlockAndTintGetter {
|
||||
INSTANCE;
|
||||
|
||||
private static BlockState tempState = AIR;
|
||||
private static FluidState tempFluid = EMPTY;
|
||||
|
||||
@Override
|
||||
public float getShade(Direction pDirection, boolean pShade) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine() {
|
||||
return Minecraft.getInstance().level.getLightEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos pBlockPos, ColorResolver pColorResolver) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightness(LightLayer pLightType, BlockPos pBlockPos) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawBrightness(BlockPos pBlockPos, int pAmount) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity getBlockEntity(BlockPos pPos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
return pos.equals(BlockPos.ZERO) ? tempState : AIR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
return pos.equals(BlockPos.ZERO) ? tempFluid : EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +69,7 @@ class ClientJeiUtil {
|
|||
if (ingredient != null) {
|
||||
// From mezz.jei.library.render.ItemStackRenderer
|
||||
RenderSystem.enableDepthTest();
|
||||
ClientJeiUtil.renderItemWithAsterisk(graphics, ingredient);
|
||||
ClientXeiUtil.renderItemWithAsterisk(graphics, ingredient);
|
||||
// From end of DrawableIngredient
|
||||
RenderSystem.disableDepthTest();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@ package thedarkcolour.exdeorum.compat.jei;
|
|||
import mezz.jei.api.helpers.IGuiHelper;
|
||||
import mezz.jei.api.recipe.RecipeType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import thedarkcolour.exdeorum.compat.GroupedSieveRecipe;
|
||||
import thedarkcolour.exdeorum.compat.XeiSieveRecipe;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
|
||||
class CompressedSieveCategory extends SieveCategory {
|
||||
CompressedSieveCategory(IGuiHelper helper) {
|
||||
super(helper, DefaultMaterials.OAK_COMPRESSED_SIEVE, Component.translatable(TranslationKeys.COMPRESSED_SIEVE_CATEGORY_TITLE));
|
||||
super(helper, DefaultMaterials.OAK_COMPRESSED_SIEVE, Component.translatable(TranslationKeys.COMPRESSED_SIEVE_CATEGORY_TITLE), XeiSieveRecipe.COMPRESSED_SIEVE_ROWS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeType<GroupedSieveRecipe> getRecipeType() {
|
||||
public RecipeType<XeiSieveRecipe> getRecipeType() {
|
||||
return ExDeorumJeiPlugin.COMPRESSED_SIEVE;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ package thedarkcolour.exdeorum.compat.jei;
|
|||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import mezz.jei.api.constants.VanillaTypes;
|
||||
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
|
||||
import mezz.jei.api.gui.builder.ITooltipBuilder;
|
||||
import mezz.jei.api.gui.drawable.IDrawable;
|
||||
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
|
||||
import mezz.jei.api.helpers.IJeiHelpers;
|
||||
|
|
@ -32,23 +33,15 @@ import mezz.jei.api.recipe.RecipeType;
|
|||
import mezz.jei.api.recipe.category.IRecipeCategory;
|
||||
import mezz.jei.api.runtime.IIngredientManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.neoforged.neoforge.client.RenderTypeHelper;
|
||||
import net.neoforged.neoforge.client.model.data.ModelData;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.registry.EBlocks;
|
||||
import thedarkcolour.exdeorum.registry.EItems;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CrookCategory implements IRecipeCategory<CrookJeiRecipe> {
|
||||
private static final Component REQUIRES_CERTAIN_STATE = Component.translatable(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE).withStyle(ChatFormatting.GRAY);
|
||||
|
||||
|
|
@ -100,8 +93,8 @@ public class CrookCategory implements IRecipeCategory<CrookJeiRecipe> {
|
|||
@Override
|
||||
public void setRecipe(IRecipeLayoutBuilder builder, CrookJeiRecipe recipe, IFocusGroup focuses) {
|
||||
recipe.addIngredients(builder);
|
||||
builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(recipe.result).addTooltipCallback((recipeSlotView, tooltip) -> {
|
||||
tooltip.add(ClientJeiUtil.formatChance(recipe.chance));
|
||||
builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(recipe.result).addRichTooltipCallback((recipeSlotView, tooltip) -> {
|
||||
tooltip.add(ClientXeiUtil.formatChance(recipe.chance));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -113,29 +106,15 @@ public class CrookCategory implements IRecipeCategory<CrookJeiRecipe> {
|
|||
this.slot.draw(graphics, 79, 17);
|
||||
|
||||
BlockState state = this.timer.getCycledItem(recipe.states);
|
||||
if (state.is(EBlocks.INFESTED_LEAVES.get())) {
|
||||
ClientJeiUtil.renderBlock(graphics, state, 28, 18, 10, 20f, (block, poseStack, buffers) -> {
|
||||
var blockRenderer = Minecraft.getInstance().getBlockRenderer();
|
||||
var bakedmodel = blockRenderer.getBlockModel(state);
|
||||
|
||||
for (var renderType : bakedmodel.getRenderTypes(state, RandomSource.create(42), ModelData.EMPTY)) {
|
||||
blockRenderer.getModelRenderer().renderModel(poseStack.last(), buffers.getBuffer(RenderTypeHelper.getEntityRenderType(renderType, false)), state, bakedmodel, 1f, 1f, 1f, 15728880, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, renderType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ClientJeiUtil.renderBlock(graphics, state, 28, 18, 10, 20f, (block, poseStack, buffers) -> {
|
||||
//noinspection DataFlowIssue
|
||||
Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, null);
|
||||
});
|
||||
}
|
||||
ClientXeiUtil.renderBlock(graphics, state, 28, 18, 10, 20f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Component> getTooltipStrings(CrookJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, double mouseX, double mouseY) {
|
||||
public void getTooltip(ITooltipBuilder tooltip, CrookJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, double mouseX, double mouseY) {
|
||||
if (12 < mouseX && mouseX < 44 && 10 < mouseY && mouseY < 42) {
|
||||
var block = this.timer.getCycledItem(recipe.states).getBlock();
|
||||
var modId = BuiltInRegistries.BLOCK.getKey(block).getNamespace();
|
||||
var tooltip = new ArrayList<Component>();
|
||||
|
||||
tooltip.add(Component.translatable(block.getDescriptionId()));
|
||||
if (recipe instanceof CrookJeiRecipe.StatesRecipe statesRecipe && !statesRecipe.requirements.isEmpty()) {
|
||||
tooltip.add(REQUIRES_CERTAIN_STATE);
|
||||
|
|
@ -144,10 +123,7 @@ public class CrookCategory implements IRecipeCategory<CrookJeiRecipe> {
|
|||
tooltip.add(Component.literal("#" + tagRecipe.tag.location()).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
tooltip.add(Component.literal(this.modIdHelper.getFormattedModNameForModId(modId)));
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,11 +19,8 @@
|
|||
package thedarkcolour.exdeorum.compat.jei;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.JsonObject;
|
||||
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
|
||||
import mezz.jei.api.recipe.RecipeIngredientRole;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.tags.TagKey;
|
||||
|
|
@ -33,8 +30,8 @@ import net.minecraft.world.level.block.Block;
|
|||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
import thedarkcolour.exdeorum.recipe.BlockPredicate;
|
||||
import thedarkcolour.exdeorum.recipe.CodecUtil;
|
||||
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -101,16 +98,7 @@ public sealed abstract class CrookJeiRecipe {
|
|||
|
||||
this.itemIngredients = itemIngredients.build();
|
||||
|
||||
ImmutableList.Builder<Component> requirements = ImmutableList.builder();
|
||||
if (predicate != null) {
|
||||
var json = CodecUtil.encode(StatePropertiesPredicate.CODEC, predicate.properties());
|
||||
if (json instanceof JsonObject obj) {
|
||||
for (var entry : obj.entrySet()) {
|
||||
requirements.add(Component.literal(" " + entry.getKey() + "=" + entry.getValue().toString()).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.requirements = requirements.build();
|
||||
this.requirements = XeiUtil.getStateRequirements(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -134,7 +122,7 @@ public sealed abstract class CrookJeiRecipe {
|
|||
private final ItemStack itemIngredient;
|
||||
|
||||
BlockRecipe(Block block, ItemStack result, float chance) {
|
||||
super(List.of(block.defaultBlockState()), result, chance);
|
||||
super(ImmutableList.of(block.defaultBlockState()), result, chance);
|
||||
|
||||
var item = block.asItem();
|
||||
if (item == Items.AIR) {
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ import mezz.jei.api.recipe.category.IRecipeCategory;
|
|||
import mezz.jei.api.runtime.IIngredientManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
|
||||
|
|
@ -103,10 +103,7 @@ class CrucibleHeatSourcesCategory implements IRecipeCategory<CrucibleHeatSourceR
|
|||
|
||||
graphics.drawString(font, volumeLabel, 60 - font.width(volumeLabel) / 2, 5, 0xff808080, false);
|
||||
|
||||
ClientJeiUtil.renderBlock(graphics, recipe.blockState(), 60, 24, 10, 20F, (block, poseStack, buffers) -> {
|
||||
//noinspection deprecation
|
||||
Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY);
|
||||
});
|
||||
ClientXeiUtil.renderBlock(graphics, recipe.blockState(), 60, 24, 10, 20F);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ import thedarkcolour.exdeorum.ExDeorum;
|
|||
import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen;
|
||||
import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen;
|
||||
import thedarkcolour.exdeorum.compat.CompatUtil;
|
||||
import thedarkcolour.exdeorum.compat.GroupedSieveRecipe;
|
||||
import thedarkcolour.exdeorum.compat.ModIds;
|
||||
import thedarkcolour.exdeorum.compat.XeiSieveRecipe;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.item.WateringCanItem;
|
||||
import thedarkcolour.exdeorum.recipe.RecipeUtil;
|
||||
|
|
@ -81,8 +81,8 @@ public class ExDeorumJeiPlugin implements IModPlugin {
|
|||
static final RecipeType<CrucibleRecipe> LAVA_CRUCIBLE = recipeType("lava_crucible", CrucibleRecipe.class);
|
||||
static final RecipeType<CrucibleRecipe> WATER_CRUCIBLE = recipeType("water_crucible", CrucibleRecipe.class);
|
||||
static final RecipeType<CrucibleHeatSourceRecipe> CRUCIBLE_HEAT_SOURCES = recipeType("crucible_heat_sources", CrucibleHeatSourceRecipe.class);
|
||||
static final RecipeType<GroupedSieveRecipe> SIEVE = recipeType("sieve", GroupedSieveRecipe.class);
|
||||
static final RecipeType<GroupedSieveRecipe> COMPRESSED_SIEVE = recipeType("compressed_sieve", GroupedSieveRecipe.class);
|
||||
static final RecipeType<XeiSieveRecipe> SIEVE = recipeType("sieve", XeiSieveRecipe.class);
|
||||
static final RecipeType<XeiSieveRecipe> COMPRESSED_SIEVE = recipeType("compressed_sieve", XeiSieveRecipe.class);
|
||||
static final RecipeType<HammerRecipe> HAMMER = recipeType("hammer", HammerRecipe.class);
|
||||
static final RecipeType<HammerRecipe> COMPRESSED_HAMMER = recipeType("compressed_hammer", CompressedHammerRecipe.class);
|
||||
static final RecipeType<CrookJeiRecipe> CROOK = recipeType("crook", CrookJeiRecipe.class);
|
||||
|
|
@ -198,7 +198,8 @@ public class ExDeorumJeiPlugin implements IModPlugin {
|
|||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_ZINC)) toRemove.add(new ItemStack(EItems.ZINC_ORE_CHUNK.get()));
|
||||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_IRIDIUM)) toRemove.add(new ItemStack(EItems.IRIDIUM_ORE_CHUNK.get()));
|
||||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_THORIUM)) toRemove.add(new ItemStack(EItems.THORIUM_ORE_CHUNK.get()));
|
||||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_MAGNESIUM)) toRemove.add(new ItemStack(EItems.MAGNESIUM_ORE_CHUNK.get()));
|
||||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_MAGNESIUM))
|
||||
toRemove.add(new ItemStack(EItems.MAGNESIUM_ORE_CHUNK.get()));
|
||||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_LITHIUM)) toRemove.add(new ItemStack(EItems.LITHIUM_ORE_CHUNK.get()));
|
||||
if (RecipeUtil.isTagEmpty(EItemTags.ORES_BORON)) toRemove.add(new ItemStack(EItems.BORON_ORE_CHUNK.get()));
|
||||
|
||||
|
|
@ -215,8 +216,8 @@ public class ExDeorumJeiPlugin implements IModPlugin {
|
|||
//noinspection rawtypes,unchecked
|
||||
addRecipes(registration, COMPRESSED_HAMMER, ((DeferredHolder) ERecipeTypes.COMPRESSED_HAMMER));
|
||||
registration.addRecipes(CROOK, CompatUtil.collectAllRecipes(ERecipeTypes.CROOK.get(), CrookJeiRecipe::create));
|
||||
registration.addRecipes(SIEVE, GroupedSieveRecipe.getAllRecipesGrouped(ERecipeTypes.SIEVE.get()));
|
||||
registration.addRecipes(COMPRESSED_SIEVE, GroupedSieveRecipe.getAllRecipesGrouped(ERecipeTypes.COMPRESSED_SIEVE.get()));
|
||||
registration.addRecipes(SIEVE, XeiSieveRecipe.getAllRecipesGrouped(ERecipeTypes.SIEVE.get(), XeiSieveRecipe.SIEVE_ROWS));
|
||||
registration.addRecipes(COMPRESSED_SIEVE, XeiSieveRecipe.getAllRecipesGrouped(ERecipeTypes.COMPRESSED_SIEVE.get(), XeiSieveRecipe.COMPRESSED_SIEVE_ROWS));
|
||||
|
||||
addCrucibleHeatSources(registration);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package thedarkcolour.exdeorum.compat.jei;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import mezz.jei.api.constants.VanillaTypes;
|
||||
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
|
||||
import mezz.jei.api.gui.builder.IRecipeSlotBuilder;
|
||||
|
|
@ -29,31 +28,26 @@ 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 net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.BinomialDistributionGenerator;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
|
||||
import net.neoforged.neoforge.common.util.Lazy;
|
||||
import thedarkcolour.exdeorum.compat.GroupedSieveRecipe;
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
import thedarkcolour.exdeorum.compat.ClientXeiUtil;
|
||||
import thedarkcolour.exdeorum.compat.XeiSieveRecipe;
|
||||
import thedarkcolour.exdeorum.compat.XeiUtil;
|
||||
import thedarkcolour.exdeorum.data.TranslationKeys;
|
||||
import thedarkcolour.exdeorum.loot.SummationGenerator;
|
||||
import thedarkcolour.exdeorum.material.DefaultMaterials;
|
||||
import thedarkcolour.exdeorum.recipe.RecipeUtil;
|
||||
|
||||
class SieveCategory implements IRecipeCategory<GroupedSieveRecipe> {
|
||||
private static final Component BY_HAND_ONLY_LABEL = Component.translatable(TranslationKeys.SIEVE_RECIPE_BY_HAND_ONLY).withStyle(ChatFormatting.RED);
|
||||
|
||||
class SieveCategory implements IRecipeCategory<XeiSieveRecipe> {
|
||||
public static final int WIDTH = 162;
|
||||
public static final int ROW_START = 28;
|
||||
|
||||
static {
|
||||
ClientJeiUtil.FORMATTER.setMinimumFractionDigits(0);
|
||||
ClientJeiUtil.FORMATTER.setMaximumFractionDigits(3);
|
||||
ClientXeiUtil.FORMATTER.setMinimumFractionDigits(0);
|
||||
ClientXeiUtil.FORMATTER.setMaximumFractionDigits(3);
|
||||
}
|
||||
|
||||
private final Lazy<IDrawable> background;
|
||||
|
|
@ -61,21 +55,23 @@ class SieveCategory implements IRecipeCategory<GroupedSieveRecipe> {
|
|||
private final IDrawable row;
|
||||
private final IDrawable icon;
|
||||
private final Component title;
|
||||
private final MutableInt rows;
|
||||
|
||||
SieveCategory(IGuiHelper helper, ItemLike icon, Component title) {
|
||||
this.background = Lazy.of(() -> helper.createBlankDrawable(WIDTH, ROW_START + 18 * GroupedSieveRecipe.maxSieveRows));
|
||||
SieveCategory(IGuiHelper helper, ItemLike icon, Component title, MutableInt rows) {
|
||||
this.background = Lazy.of(() -> helper.createBlankDrawable(XeiUtil.SIEVE_WIDTH, XeiUtil.SIEVE_ROW_START + XeiUtil.SIEVE_ROW_HEIGHT * rows.intValue()));
|
||||
this.slot = helper.getSlotDrawable();
|
||||
this.row = helper.createDrawable(ExDeorumJeiPlugin.EX_DEORUM_JEI_TEXTURE, 0, 0, 162, 18);
|
||||
this.icon = helper.createDrawableItemStack(new ItemStack(icon));
|
||||
this.title = title;
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
SieveCategory(IGuiHelper helper) {
|
||||
this(helper, DefaultMaterials.OAK_SIEVE, Component.translatable(TranslationKeys.SIEVE_CATEGORY_TITLE));
|
||||
this(helper, DefaultMaterials.OAK_SIEVE, Component.translatable(TranslationKeys.SIEVE_CATEGORY_TITLE), XeiSieveRecipe.SIEVE_ROWS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeType<GroupedSieveRecipe> getRecipeType() {
|
||||
public RecipeType<XeiSieveRecipe> getRecipeType() {
|
||||
return ExDeorumJeiPlugin.SIEVE;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +91,7 @@ class SieveCategory implements IRecipeCategory<GroupedSieveRecipe> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(IRecipeLayoutBuilder builder, GroupedSieveRecipe recipe, IFocusGroup focuses) {
|
||||
public void setRecipe(IRecipeLayoutBuilder builder, XeiSieveRecipe recipe, IFocusGroup focuses) {
|
||||
builder.addSlot(RecipeIngredientRole.INPUT, 59, 1).addIngredients(recipe.ingredient());
|
||||
builder.addSlot(RecipeIngredientRole.CATALYST, 87, 1).addItemStack(recipe.mesh());
|
||||
|
||||
|
|
@ -108,98 +104,22 @@ class SieveCategory implements IRecipeCategory<GroupedSieveRecipe> {
|
|||
}
|
||||
|
||||
public static void addTooltips(IRecipeSlotBuilder slot, boolean byHandOnly, NumberProvider provider) {
|
||||
var tooltipLines = new ImmutableList.Builder<Component>();
|
||||
|
||||
if (byHandOnly) {
|
||||
tooltipLines.add(BY_HAND_ONLY_LABEL);
|
||||
slot.setCustomRenderer(VanillaTypes.ITEM_STACK, ClientJeiUtil.AsteriskItemRenderer.INSTANCE);
|
||||
}
|
||||
if (provider instanceof BinomialDistributionGenerator binomial) {
|
||||
if (binomial.n() instanceof ConstantValue constant && constant.value() == 1) {
|
||||
var chanceLabel = ClientJeiUtil.formatChance(RecipeUtil.getExpectedValue(binomial.p()));
|
||||
tooltipLines.add(chanceLabel);
|
||||
} else {
|
||||
addAvgOutput(tooltipLines, RecipeUtil.getExpectedValue(provider));
|
||||
}
|
||||
|
||||
addMinMaxes(tooltipLines, 0, getMax(binomial.n()));
|
||||
} else if (provider.getClass() != ConstantValue.class) {
|
||||
var val = RecipeUtil.getExpectedValue(provider);
|
||||
if (val != -1.0) {
|
||||
addAvgOutput(tooltipLines, val);
|
||||
|
||||
if (provider instanceof UniformGenerator || provider instanceof SummationGenerator) {
|
||||
addMinMaxes(tooltipLines, getMin(provider), getMax(provider));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tooltipLinesList = tooltipLines.build();
|
||||
slot.addTooltipCallback((slotView, tooltip) -> {
|
||||
tooltip.addAll(tooltipLinesList);
|
||||
slot.addRichTooltipCallback((slotView, tooltip) -> {
|
||||
XeiUtil.addSieveDropTooltip(byHandOnly, provider, tooltip::add);
|
||||
});
|
||||
}
|
||||
|
||||
private static double getMin(NumberProvider provider) {
|
||||
if (provider instanceof ConstantValue value) {
|
||||
return value.value();
|
||||
} else if (provider instanceof UniformGenerator uniform) {
|
||||
return getMin(uniform.min());
|
||||
} else if (provider instanceof BinomialDistributionGenerator) {
|
||||
return 0;
|
||||
} else if (provider instanceof SummationGenerator summation) {
|
||||
double sum = 0;
|
||||
|
||||
for (var child : summation.providers()) {
|
||||
sum += getMin(child);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static double getMax(NumberProvider provider) {
|
||||
if (provider instanceof ConstantValue value) {
|
||||
return value.value();
|
||||
} else if (provider instanceof UniformGenerator uniform) {
|
||||
return getMax(uniform.max());
|
||||
} else if (provider instanceof BinomialDistributionGenerator binomial) {
|
||||
return getMax(binomial.n());
|
||||
} else if (provider instanceof SummationGenerator summation) {
|
||||
double sum = 0;
|
||||
|
||||
for (var child : summation.providers()) {
|
||||
sum += getMax(child);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void addAvgOutput(ImmutableList.Builder<Component> tooltipLines, double avgValue) {
|
||||
String avgOutput = ClientJeiUtil.FORMATTER.format(avgValue);
|
||||
tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_AVERAGE_OUTPUT, avgOutput).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
|
||||
// when the player holds shift, they can see the min/max amounts of a drop
|
||||
private static void addMinMaxes(ImmutableList.Builder<Component> tooltipLines, double min, double max) {
|
||||
String minFormatted = ClientJeiUtil.FORMATTER.format(min);
|
||||
String maxFormatted = ClientJeiUtil.FORMATTER.format(max);
|
||||
|
||||
tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_MIN_OUTPUT, minFormatted).withStyle(ChatFormatting.GRAY));
|
||||
tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, maxFormatted).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GroupedSieveRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) {
|
||||
public void draw(XeiSieveRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) {
|
||||
this.slot.draw(graphics, 58, 0);
|
||||
this.slot.draw(graphics, 86, 0);
|
||||
|
||||
for (int i = 0; i < GroupedSieveRecipe.maxSieveRows; i++) {
|
||||
int rows = this.rows.intValue();
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
this.row.draw(graphics, 0, 28 + i * 18);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,18 @@ class English {
|
|||
english.add(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, "Max: %s");
|
||||
english.add(TranslationKeys.SIEVE_RECIPE_BY_HAND_ONLY, "Does not drop from Mechanical Sieve");
|
||||
|
||||
english.add(TranslationKeys.EMI_BARREL_COMPOST_CATEGORY_TITLE, "Barrel Compost");
|
||||
english.add(TranslationKeys.EMI_BARREL_MIXING_CATEGORY_TITLE, "Barrel Mixing");
|
||||
english.add(TranslationKeys.EMI_BARREL_FLUID_MIXING_CATEGORY_TITLE, "Barrel Fluid Mixing");
|
||||
english.add(TranslationKeys.EMI_LAVA_CRUCIBLE_CATEGORY_TITLE, "Lava Crucible");
|
||||
english.add(TranslationKeys.EMI_WATER_CRUCIBLE_CATEGORY_TITLE, "Water Crucible");
|
||||
english.add(TranslationKeys.EMI_CRUCIBLE_HEAT_SOURCES_CATEGORY_TITLE, "Crucible Heat Source");
|
||||
english.add(TranslationKeys.EMI_SIEVE_CATEGORY_TITLE, "Sieve");
|
||||
english.add(TranslationKeys.EMI_COMPRESSED_SIEVE_CATEGORY_TITLE, "Compressed Sieve");
|
||||
english.add(TranslationKeys.EMI_HAMMER_CATEGORY_TITLE, "Hammer");
|
||||
english.add(TranslationKeys.EMI_COMPRESSED_HAMMER_CATEGORY_TITLE, "Compressed Hammer");
|
||||
english.add(TranslationKeys.EMI_CROOK_CATEGORY_TITLE, "Crook");
|
||||
|
||||
english.add(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE, "Mechanical Sieve");
|
||||
english.add(TranslationKeys.REDSTONE_CONTROL_MODES[RedstoneControlWidget.REDSTONE_MODE_IGNORED], "Always");
|
||||
english.add(TranslationKeys.REDSTONE_CONTROL_MODES[RedstoneControlWidget.REDSTONE_MODE_UNPOWERED], "Unpowered");
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
package thedarkcolour.exdeorum.data;
|
||||
|
||||
import thedarkcolour.exdeorum.ExDeorum;
|
||||
import thedarkcolour.exdeorum.compat.ModIds;
|
||||
|
||||
public class TranslationKeys {
|
||||
// Misc
|
||||
|
|
@ -84,6 +85,19 @@ public class TranslationKeys {
|
|||
public static final String SIEVE_RECIPE_MAX_OUTPUT = "gui." + ExDeorum.ID + ".category.sieve.max_output";
|
||||
public static final String SIEVE_RECIPE_BY_HAND_ONLY = "gui." + ExDeorum.ID + ".category.sieve.by_hand_only";
|
||||
|
||||
// EMI recipe categories
|
||||
public static final String EMI_BARREL_COMPOST_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".barrel_compost";
|
||||
public static final String EMI_BARREL_MIXING_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".barrel_mixing";
|
||||
public static final String EMI_BARREL_FLUID_MIXING_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".barrel_fluid_mixing";
|
||||
public static final String EMI_LAVA_CRUCIBLE_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".lava_crucible";
|
||||
public static final String EMI_WATER_CRUCIBLE_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".water_crucible";
|
||||
public static final String EMI_CRUCIBLE_HEAT_SOURCES_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".crucible_heat_sources";
|
||||
public static final String EMI_SIEVE_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".sieve";
|
||||
public static final String EMI_COMPRESSED_SIEVE_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".compressed_sieve";
|
||||
public static final String EMI_HAMMER_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".hammer";
|
||||
public static final String EMI_COMPRESSED_HAMMER_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".compressed_hammer";
|
||||
public static final String EMI_CROOK_CATEGORY_TITLE = ModIds.EMI + ".category." + ExDeorum.ID + ".crook";
|
||||
|
||||
// Screens
|
||||
public static final String MECHANICAL_SIEVE_SCREEN_TITLE = ExDeorum.ID + ".container.mechanical_sieve";
|
||||
public static final String[] REDSTONE_CONTROL_MODES = new String[] {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import net.minecraft.core.registries.BuiltInRegistries;
|
|||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
|
@ -43,9 +44,13 @@ import net.minecraft.world.level.material.Fluids;
|
|||
import net.minecraft.world.level.storage.loot.LootContext;
|
||||
import net.minecraft.world.level.storage.loot.LootParams;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.*;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.fml.loading.FMLEnvironment;
|
||||
import net.neoforged.neoforge.fluids.FluidStack;
|
||||
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import thedarkcolour.exdeorum.compat.PreferredOres;
|
||||
import thedarkcolour.exdeorum.client.ClientsideCode;
|
||||
import thedarkcolour.exdeorum.loot.SummationGenerator;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
|
||||
import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe;
|
||||
|
|
@ -404,4 +409,14 @@ public final class RecipeUtil {
|
|||
public static boolean isValidResourceLocation(String string) {
|
||||
return ResourceLocation.tryParse(string) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* From Forestry: Community Edition
|
||||
* @return The global registry manager. {@code null} on server when there is no server, or when there is no world (on client).
|
||||
*/
|
||||
@Nullable
|
||||
public static RecipeManager getRecipeManager() {
|
||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||
return server == null ? (FMLEnvironment.dist == Dist.CLIENT ? ClientsideCode.getRecipeManager() : null) : server.getRecipeManager();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,13 +45,11 @@ public class BarrelMixingRecipe extends SingleIngredientRecipe {
|
|||
public static final StreamCodec<RegistryFriendlyByteBuf, BarrelMixingRecipe> STREAM_CODEC = StreamCodec.of(BarrelMixingRecipe::toNetwork, BarrelMixingRecipe::fromNetwork);
|
||||
|
||||
public final SizedFluidIngredient fluid;
|
||||
public final int fluidAmount;
|
||||
public final ItemStack result;
|
||||
|
||||
public BarrelMixingRecipe(Ingredient ingredient, SizedFluidIngredient fluid, ItemStack result) {
|
||||
super(ingredient);
|
||||
this.fluid = fluid;
|
||||
this.fluidAmount = fluid.amount();
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +69,7 @@ public class BarrelMixingRecipe extends SingleIngredientRecipe {
|
|||
}
|
||||
|
||||
public boolean matches(ItemStack item, FluidStack fluid) {
|
||||
return this.ingredient.test(item) && this.fluid.test(fluid) && fluid.getAmount() >= this.fluidAmount;
|
||||
return this.ingredient.test(item) && this.fluid.test(fluid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user