diff --git a/build.gradle b/build.gradle
index 196bbfda..948f9a32 100644
--- a/build.gradle
+++ b/build.gradle
@@ -132,14 +132,14 @@ dependencies {
implementation(fg.deobf("curse.maven:jade-324717:4986594"))
// JEI OPTIONAL
//compileOnly(fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}"))
- implementation(fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}"))
+ //implementation(fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}"))
// REI OPTIONAL
compileOnly(fg.deobf("me.shedaniel:RoughlyEnoughItems-forge:${rei_version}"))
compileOnly(fg.deobf("me.shedaniel.cloth:cloth-config-forge:${cloth_config_version}"))
compileOnly(fg.deobf("curse.maven:reipc-521393:4837449"))
// EMI OPTIONAL
compileOnly(fg.deobf("dev.emi:emi-forge:${emi_version}:api"))
- //runtimeOnly(fg.deobf("dev.emi:emi-forge:${emi_version}"))
+ runtimeOnly(fg.deobf("dev.emi:emi-forge:${emi_version}"))
// KubeJS OPTIONAL
implementation fg.deobf("dev.architectury:architectury-forge:${architectury_version}")
implementation fg.deobf("dev.latvian.mods:rhino-forge:${rhino_version}")
diff --git a/changelog.md b/changelog.md
index 1fcac52d..ecb6caf2 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,7 @@
+## Ex Deorum 1.41
+- Add native EMI support.
+- Fix bug where removing all Compressed Sieve recipes would break regular Sieve recipe display in JEI.
+
## Ex Deorum 1.40
- Buffed melt rate of water crucible. Should now be comparable to what it was in older versions.
- It is now possible to add custom Compressed Sieve block types. Their JSON schema is exactly the same as it is for regular sieve blocks.
diff --git a/gradle.properties b/gradle.properties
index fa386a4f..af7d1169 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,7 +5,7 @@ org.gradle.daemon=false
org.gradle.cache=true
mc_version=1.20.1
-forge_version=47.1.43
+forge_version=47.3.10
parchment_mappings=1.20.1-2023.06.26
jei_version=15.10.0.36
diff --git a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767
index 4e6941bf..11e4c8b1 100644
--- a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767
+++ b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767
@@ -1,2 +1,2 @@
-// 1.20.1 2024-06-08T15:07:39.710396 ModKit Language: en_us for mod 'exdeorum'
-dc59643415839f9885be6e0755e50db35134152b assets/exdeorum/lang/en_us.json
+// 1.20.1 2024-09-29T11:54:48.3155061 ModKit Language: en_us for mod 'exdeorum'
+4c047868de9d1dc6df275187edac9975b2b9ef75 assets/exdeorum/lang/en_us.json
diff --git a/src/generated/resources/assets/exdeorum/lang/en_us.json b/src/generated/resources/assets/exdeorum/lang/en_us.json
index aca450e2..ff72b942 100644
--- a/src/generated/resources/assets/exdeorum/lang/en_us.json
+++ b/src/generated/resources/assets/exdeorum/lang/en_us.json
@@ -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",
diff --git a/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java b/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java
index a5e27025..682c1f5b 100644
--- a/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java
+++ b/src/main/java/thedarkcolour/exdeorum/client/ClientHandler.java
@@ -81,7 +81,7 @@ public class ClientHandler {
fmlBus.addListener(ClientHandler::onScreenOpen);
fmlBus.addListener(ClientHandler::onTagsUpdated);
- if (ModList.get().isLoaded(ModIds.JEI)) {
+ if (ModList.get().isLoaded(ModIds.JEI) || ModList.get().isLoaded(ModIds.EMI)) {
modBus.addListener(ClientHandler::registerAdditionalModels);
}
}
diff --git a/src/main/java/thedarkcolour/exdeorum/event/ClientsideCode.java b/src/main/java/thedarkcolour/exdeorum/client/ClientsideCode.java
similarity index 66%
rename from src/main/java/thedarkcolour/exdeorum/event/ClientsideCode.java
rename to src/main/java/thedarkcolour/exdeorum/client/ClientsideCode.java
index fb490b75..135a6144 100644
--- a/src/main/java/thedarkcolour/exdeorum/event/ClientsideCode.java
+++ b/src/main/java/thedarkcolour/exdeorum/client/ClientsideCode.java
@@ -16,16 +16,27 @@
* along with this program. If not, see .
*/
-package thedarkcolour.exdeorum.event;
+package thedarkcolour.exdeorum.client;
import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.crafting.RecipeManager;
import org.jetbrains.annotations.Nullable;
// necessary to avoid EventBus loading LocalPlayer through its ASM transformations
-class ClientsideCode {
+public class ClientsideCode {
@Nullable
- static Player getLocalPlayer() {
+ public static Player getLocalPlayer() {
return Minecraft.getInstance().player;
}
+
+ @Nullable
+ public static RecipeManager getRecipeManager() {
+ ClientPacketListener connection = Minecraft.getInstance().getConnection();
+ if (connection != null) {
+ return connection.getRecipeManager();
+ }
+ return null;
+ }
}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/ClientXeiUtil.java b/src/main/java/thedarkcolour/exdeorum/compat/ClientXeiUtil.java
new file mode 100644
index 00000000..9a1a661b
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/ClientXeiUtil.java
@@ -0,0 +1,265 @@
+/*
+ * 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 .
+ */
+
+package thedarkcolour.exdeorum.compat;
+
+import com.mojang.blaze3d.platform.Lighting;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferBuilder;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.Tesselator;
+import com.mojang.math.Axis;
+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.LightTexture;
+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.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.minecraftforge.client.RenderTypeHelper;
+import net.minecraftforge.client.model.data.ModelData;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Matrix4f;
+import org.joml.Vector3f;
+import thedarkcolour.exdeorum.client.ClientHandler;
+import thedarkcolour.exdeorum.material.DefaultMaterials;
+import thedarkcolour.exdeorum.registry.EBlocks;
+
+// client-only logic shared between JEI and EMI
+public class ClientXeiUtil {
+ 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.mulPoseMatrix((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();
+ }
+
+ 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
+ renderItemAlternativeModel(guiGraphics, model, OAK_BARREL, xOffset, yOffset);
+ // From end of DrawableIngredient
+ RenderSystem.disableDepthTest();
+ }
+
+ // 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, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY);
+ }
+
+ buffers.endBatch();
+ } else {
+ RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
+ PoseStack modelView = RenderSystem.getModelViewStack();
+ Tesselator tesselator = Tesselator.getInstance();
+ BufferBuilder builder = tesselator.getBuilder();
+ renderType.setupRenderState();
+ modelView.pushPose();
+ modelView.mulPoseMatrix(poseStack.last().pose());
+ RenderSystem.applyModelViewMatrix();
+
+ builder.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;
+
+ if (builder.building()) {
+ tesselator.end();
+ }
+
+ renderType.clearRenderState();
+ modelView.popPose();
+ 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();
+ }
+
+ 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;
+ }
+
+ @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;
+ }
+ }
+
+ @FunctionalInterface
+ public interface RenderBlockFn {
+ void renderBlock(BlockState block, PoseStack poseStack, MultiBufferSource.BufferSource buffers);
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/GroupedSieveRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/XeiSieveRecipe.java
similarity index 86%
rename from src/main/java/thedarkcolour/exdeorum/compat/GroupedSieveRecipe.java
rename to src/main/java/thedarkcolour/exdeorum/compat/XeiSieveRecipe.java
index e5e43b6a..6bab08cd 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/GroupedSieveRecipe.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/XeiSieveRecipe.java
@@ -30,6 +30,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,12 +40,13 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-// Since no JEI code is used here, this can be reused for REI
-public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List results) {
- public static int maxSieveRows;
+// Since no JEI code is used here, this can be reused for EMI
+public record XeiSieveRecipe(Ingredient ingredient, ItemStack mesh, List results) {
+ public static final MutableInt SIEVE_ROWS = new MutableInt(0);
+ public static final MutableInt COMPRESSED_SIEVE_ROWS = new MutableInt(0);
- public static ImmutableList getAllRecipesGrouped(RecipeType extends SieveRecipe> recipeType) {
- maxSieveRows = 1;
+ public static ImmutableList getAllRecipesGrouped(RecipeType extends SieveRecipe> recipeType, MutableInt maxRows) {
+ int maxSieveRows = 1;
// copy the list so we can do removals
List recipes = new ArrayList<>(Objects.requireNonNull(Minecraft.getInstance().level).getRecipeManager().getAllRecipesFor(recipeType));
@@ -66,11 +68,11 @@ public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List jeiRecipes = new ImmutableList.Builder<>();
+ ImmutableList.Builder 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()) {
@@ -99,7 +101,7 @@ public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List.
+ */
+
+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.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.minecraftforge.fml.ModContainer;
+import net.minecraftforge.fml.ModList;
+import net.minecraftforge.forgespi.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.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 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();
+
+ 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 getBlockTooltip(List extraDetails, Block block) {
+ var modId = BuiltInRegistries.BLOCK.getKey(block).getNamespace();
+ var tooltip = new ArrayList();
+
+ tooltip.add(Component.translatable(block.getDescriptionId()));
+ tooltip.addAll(extraDetails);
+ tooltip.add(getModDisplayName(modId));
+
+ return tooltip;
+ }
+
+ public static ImmutableList getStateRequirements(BlockPredicate.@Nullable BlockStatePredicate predicate) {
+ ImmutableList.Builder requirements = ImmutableList.builder();
+ if (predicate != null) {
+ var json = predicate.properties().serializeToJson();
+ 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 getExtraDetails(BlockPredicate predicate) {
+ List 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 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 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 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();
+ 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());
+ }
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelCompostEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelCompostEmiRecipe.java
new file mode 100644
index 00000000..f902bcc6
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelCompostEmiRecipe.java
@@ -0,0 +1,74 @@
+/*
+ * 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 .
+ */
+
+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 thedarkcolour.exdeorum.data.TranslationKeys;
+import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
+
+import java.util.List;
+
+class BarrelCompostEmiRecipe extends EEmiRecipe {
+ private final List inputs;
+ private final int volume;
+
+ public BarrelCompostEmiRecipe(BarrelCompostRecipe recipe) {
+ super(recipe);
+
+ this.inputs = EmiUtil.inputs(recipe);
+ this.volume = recipe.getVolume();
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.BARREL_COMPOST;
+ }
+
+ @Override
+ public List getInputs() {
+ return this.inputs;
+ }
+
+ @Override
+ public List 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.get(0), 0, 0);
+
+ var volumeLabel = Component.translatable(TranslationKeys.BARREL_COMPOST_RECIPE_VOLUME, this.volume);
+ widgets.addText(volumeLabel, 24, 5, 0xff808080, false);
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelMixingEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelMixingEmiRecipe.java
new file mode 100644
index 00000000..3f25f3c3
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelMixingEmiRecipe.java
@@ -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 .
+ */
+
+package thedarkcolour.exdeorum.compat.emi;
+
+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.world.item.crafting.Recipe;
+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 inputs;
+ private final List outputs;
+
+ public BarrelMixingEmiRecipe(Recipe> recipe, EmiIngredient base, EmiIngredient additive, List outputs) {
+ super(recipe);
+
+ this.base = base;
+ this.additive = additive;
+
+ this.inputs = List.of(base, additive);
+ this.outputs = outputs;
+ }
+
+ @Override
+ public List getInputs() {
+ return this.inputs;
+ }
+
+ @Override
+ public List 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);
+ // todo first in 1.21
+ widgets.addSlot(this.outputs.get(0), 78 + 15, 0).recipeContext(this);
+ }
+
+ static class Items extends BarrelMixingEmiRecipe {
+ public Items(BarrelMixingRecipe recipe) {
+ super(recipe, EmiUtil.fluid(recipe.fluid, recipe.fluidAmount), EmiIngredient.of(recipe.ingredient), EmiUtil.outputs(recipe.result));
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.BARREL_MIXING;
+ }
+ }
+
+ static class Fluids extends BarrelMixingEmiRecipe {
+ public Fluids(BarrelFluidMixingRecipe recipe) {
+ super(recipe, EmiUtil.fluid(recipe.baseFluid, recipe.baseFluidAmount), EmiUtil.fluid(recipe.additiveFluid, 1000), EmiUtil.outputs(recipe.result));
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.BARREL_FLUID_MIXING;
+ }
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/BlockEmiWidget.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/BlockEmiWidget.java
new file mode 100644
index 00000000..db3baa4f
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/BlockEmiWidget.java
@@ -0,0 +1,109 @@
+/*
+ * 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 .
+ */
+
+package thedarkcolour.exdeorum.compat.emi;
+
+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 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 states;
+ private final List extraDetails;
+ private final int x;
+ private final int y;
+ private final float scale;
+ private final Bounds bounds;
+
+ public BlockEmiWidget(List states, List 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 getTooltip(int mouseX, int mouseY) {
+ int index = (int) (System.currentTimeMillis() / 1000 % this.states.size());
+ BlockState current = this.states.get(index);
+ List 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 states, Predicate 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 = EmiUtil.fluid(current.getFluidState().getType(), 1000);
+ }
+
+ if (stack.isEmpty()) {
+ return false;
+ } else {
+ SlotWidget internal = new SlotWidget(stack, 0, 0);
+ return call.test(internal);
+ }
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/CrookEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/CrookEmiRecipe.java
new file mode 100644
index 00000000..99402c6c
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/CrookEmiRecipe.java
@@ -0,0 +1,83 @@
+/*
+ * 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 .
+ */
+
+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.world.item.ItemStack;
+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 inputs;
+ private final List outputs;
+ private final List states;
+ private final BlockPredicate predicate;
+
+ public CrookEmiRecipe(CrookRecipe recipe) {
+ super(recipe);
+
+ this.inputs = EmiUtil.inputs(recipe.blockPredicate());
+ ItemStack result = new ItemStack(recipe.result(), 1);
+ result.setTag(recipe.getResultNbt());
+ this.outputs = ImmutableList.of(EmiStack.of(result));
+ this.states = XeiUtil.getStates(recipe.blockPredicate());
+ this.predicate = recipe.blockPredicate();
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.CROOK;
+ }
+
+ @Override
+ public List getInputs() {
+ return this.inputs;
+ }
+
+ @Override
+ public List 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.get(0), 79, 17).recipeContext(this);
+ widgets.add(new BlockEmiWidget(this.states, XeiUtil.getExtraDetails(this.predicate), 28, 18, 20f));
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleEmiRecipe.java
new file mode 100644
index 00000000..2f835818
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleEmiRecipe.java
@@ -0,0 +1,62 @@
+/*
+ * 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 .
+ */
+
+package thedarkcolour.exdeorum.compat.emi;
+
+import dev.emi.emi.api.recipe.EmiRecipeCategory;
+import dev.emi.emi.api.stack.EmiStack;
+import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
+
+import java.util.List;
+
+abstract class CrucibleEmiRecipe extends EmiOneToOneRecipe {
+ private final List outputs;
+
+ CrucibleEmiRecipe(CrucibleRecipe recipe) {
+ super(recipe);
+
+ this.outputs = EmiUtil.outputs(recipe.getResult());
+ }
+
+ @Override
+ public List getOutputs() {
+ return this.outputs;
+ }
+
+ static class Lava extends CrucibleEmiRecipe {
+ Lava(CrucibleRecipe recipe) {
+ super(recipe);
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.LAVA_CRUCIBLE;
+ }
+ }
+
+ static class Water extends CrucibleEmiRecipe {
+ Water(CrucibleRecipe recipe) {
+ super(recipe);
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.WATER_CRUCIBLE;
+ }
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleHeatEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleHeatEmiRecipe.java
new file mode 100644
index 00000000..ecee797c
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleHeatEmiRecipe.java
@@ -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 .
+ */
+
+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.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 inputs;
+ private final List states;
+ private final BlockPredicate predicate;
+ private final int heatValue;
+
+ public CrucibleHeatEmiRecipe(CrucibleHeatRecipe recipe) {
+ super(recipe);
+
+ 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 getInputs() {
+ return this.inputs;
+ }
+
+ @Override
+ public List 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));
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/EEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/EEmiRecipe.java
new file mode 100644
index 00000000..831a86da
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/EEmiRecipe.java
@@ -0,0 +1,41 @@
+/*
+ * 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 .
+ */
+
+package thedarkcolour.exdeorum.compat.emi;
+
+import dev.emi.emi.api.recipe.EmiRecipe;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.Recipe;
+import org.jetbrains.annotations.Nullable;
+
+abstract class EEmiRecipe implements EmiRecipe {
+ protected final ResourceLocation id;
+
+ EEmiRecipe(Recipe> recipe) {
+ this.id = recipe.getId();
+ }
+
+ EEmiRecipe(ResourceLocation id) {
+ this.id = id;
+ }
+
+ @Override
+ public @Nullable ResourceLocation getId() {
+ return this.id;
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/EmiOneToOneRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/EmiOneToOneRecipe.java
new file mode 100644
index 00000000..c0e9cc11
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/EmiOneToOneRecipe.java
@@ -0,0 +1,60 @@
+/*
+ * 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 .
+ */
+
+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 thedarkcolour.exdeorum.compat.XeiUtil;
+import thedarkcolour.exdeorum.recipe.SingleIngredientRecipe;
+
+import java.util.List;
+
+abstract class EmiOneToOneRecipe extends EEmiRecipe {
+ private final List inputs;
+
+ EmiOneToOneRecipe(SingleIngredientRecipe recipe) {
+ super(recipe);
+
+ this.inputs = EmiUtil.inputs(recipe);
+ }
+
+ @Override
+ public List 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);
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/EmiUtil.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/EmiUtil.java
new file mode 100644
index 00000000..02b47da9
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/EmiUtil.java
@@ -0,0 +1,92 @@
+/*
+ * 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 .
+ */
+
+package thedarkcolour.exdeorum.compat.emi;
+
+import com.google.common.collect.ImmutableList;
+import dev.emi.emi.api.EmiRegistry;
+import dev.emi.emi.api.recipe.EmiRecipe;
+import dev.emi.emi.api.stack.EmiIngredient;
+import dev.emi.emi.api.stack.EmiStack;
+import net.minecraft.world.Container;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.crafting.Recipe;
+import net.minecraft.world.item.crafting.RecipeType;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import thedarkcolour.exdeorum.recipe.BlockPredicate;
+import thedarkcolour.exdeorum.recipe.SingleIngredientRecipe;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+class EmiUtil {
+ // Returns a list with 1 element.
+ static List inputs(SingleIngredientRecipe recipe) {
+ return ImmutableList.of(EmiIngredient.of(recipe.getIngredient()));
+ }
+
+ static List 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 builder = ImmutableList.builder();
+ var items = new HashSet- ();
+
+ 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 > void addAll(EmiRegistry registry, Supplier> type, Function factory) {
+ for (var value : registry.getRecipeManager().byType(type.get()).values()) {
+ registry.addRecipe(factory.apply(value));
+ }
+ }
+
+ public static EmiStack fluid(Fluid fluid, int amount) {
+ return EmiStack.of(fluid, null, amount);
+ }
+
+ public static List outputs(Item result) {
+ return ImmutableList.of(EmiStack.of(result));
+ }
+
+ public static List outputs(FluidStack stack) {
+ return ImmutableList.of(fluid(stack.getFluid(), stack.getAmount()));
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/ExDeorumEmiPlugin.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/ExDeorumEmiPlugin.java
index c946d9de..49ddd7a3 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/emi/ExDeorumEmiPlugin.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/ExDeorumEmiPlugin.java
@@ -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.resources.ResourceLocation;
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.CompatHelper;
+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(new ResourceLocation(ExDeorum.ID, 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 : CompatHelper.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 : CompatHelper.getAvailableLavaCrucibles(true)) {
+ var stack = EmiStack.of(lavaCrucible);
+ registry.addWorkstation(LAVA_CRUCIBLE, stack);
+ registry.addWorkstation(CRUCIBLE_HEAT_SOURCES, stack);
+ }
+ for (var waterCrucible : CompatHelper.getAvailableWaterCrucibles(true)) {
+ registry.addWorkstation(WATER_CRUCIBLE, EmiStack.of(waterCrucible));
+ }
+ for (var sieve : CompatHelper.getAvailableSieves(true, true)) {
+ registry.addWorkstation(SIEVE, EmiStack.of(sieve));
+ }
+ for (var compressedSieve : CompatHelper.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 value : registry.getRecipeManager().byType(ERecipeTypes.CRUCIBLE_HEAT_SOURCE.get()).values()) {
+ if (value.blockPredicate() instanceof BlockPredicate.SingleBlockPredicate block && block.block() instanceof WallTorchBlock) {
+ continue;
+ }
+ registry.addRecipe(new CrucibleHeatEmiRecipe(value));
+ }
+
+ 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
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/HammerEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/HammerEmiRecipe.java
new file mode 100644
index 00000000..16ad81ed
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/HammerEmiRecipe.java
@@ -0,0 +1,44 @@
+package thedarkcolour.exdeorum.compat.emi;
+
+import dev.emi.emi.api.recipe.EmiRecipeCategory;
+import dev.emi.emi.api.stack.EmiStack;
+import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
+
+import java.util.List;
+
+abstract class HammerEmiRecipe extends EmiOneToOneRecipe {
+ private final List outputs;
+
+ HammerEmiRecipe(HammerRecipe recipe) {
+ super(recipe);
+
+ this.outputs = EmiUtil.outputs(recipe.result);
+ }
+
+ @Override
+ public List getOutputs() {
+ return this.outputs;
+ }
+
+ static class Hammer extends HammerEmiRecipe {
+ Hammer(HammerRecipe recipe) {
+ super(recipe);
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.HAMMER;
+ }
+ }
+
+ static class CompressedHammer extends HammerEmiRecipe {
+ CompressedHammer(HammerRecipe recipe) {
+ super(recipe);
+ }
+
+ @Override
+ public EmiRecipeCategory getCategory() {
+ return ExDeorumEmiPlugin.COMPRESSED_HAMMER;
+ }
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/emi/SieveEmiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/emi/SieveEmiRecipe.java
new file mode 100644
index 00000000..a344aa88
--- /dev/null
+++ b/src/main/java/thedarkcolour/exdeorum/compat/emi/SieveEmiRecipe.java
@@ -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 .
+ */
+
+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 inputs;
+ private final int rows;
+ private final List 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 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 new ResourceLocation(ExDeorum.ID, BuiltInRegistries.ITEM.getKey(mesh).getPath() + "_" + hashCode);
+ }
+
+ @Override
+ public List getInputs() {
+ return this.inputs;
+ }
+
+ @Override
+ public List 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 getTooltip(int mouseX, int mouseY) {
+ List list = super.getTooltip(mouseX, mouseY);
+ XeiUtil.addSieveDropTooltip(this.byHandOnly, this.amount, line -> list.add(ClientTooltipComponent.create(line.getVisualOrderText())));
+ return list;
+ }
+ }
+}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java
index 512644ce..87b2cfa7 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelCompostCategory.java
@@ -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,23 +29,19 @@ 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.compat.XeiUtil;
import thedarkcolour.exdeorum.data.TranslationKeys;
-import thedarkcolour.exdeorum.material.DefaultMaterials;
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
class BarrelCompostCategory implements IRecipeCategory {
- public static final int WIDTH = 120;
- public static final int HEIGHT = 18;
-
private final IDrawable background;
private final IDrawable slot;
private final IDrawable icon;
private final Component title;
public BarrelCompostCategory(IGuiHelper helper) {
- this.background = helper.createBlankDrawable(WIDTH, HEIGHT);
+ this.background = helper.createBlankDrawable(XeiUtil.BARREL_COMPOST_WIDTH, XeiUtil.BARREL_COMPOST_HEIGHT);
this.slot = helper.getSlotDrawable();
this.icon = new DrawableIcon();
this.title = Component.translatable(TranslationKeys.BARREL_COMPOST_CATEGORY_TITLE);
@@ -88,8 +83,6 @@ class BarrelCompostCategory implements IRecipeCategory {
}
private static class DrawableIcon implements IDrawable {
- private final ItemStack oakBarrel = new ItemStack(DefaultMaterials.OAK_BARREL.getItem());
-
@Override
public int getWidth() {
return 16;
@@ -102,15 +95,7 @@ class BarrelCompostCategory implements IRecipeCategory {
@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);
}
}
}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelMixingCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelMixingCategory.java
index 29182789..cc5b2dd1 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelMixingCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/BarrelMixingCategory.java
@@ -32,15 +32,14 @@ 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.compat.XeiUtil;
import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.material.DefaultMaterials;
import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe;
import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe;
-public abstract class BarrelMixingCategory implements IRecipeCategory {
- public static final int WIDTH = 120;
- public static final int HEIGHT = 18;
-
+abstract class BarrelMixingCategory implements IRecipeCategory {
private final IDrawable background;
private final IDrawable slot;
private final IDrawable plus;
@@ -49,7 +48,7 @@ public abstract class BarrelMixingCategory implements IRecipeCategory {
private final Component title;
public BarrelMixingCategory(IGuiHelper helper, IDrawable plus, IDrawable arrow, String titleKey, Item iconItem) {
- this.background = helper.createBlankDrawable(WIDTH, HEIGHT);
+ this.background = helper.createBlankDrawable(XeiUtil.BARREL_MIXING_WIDTH, XeiUtil.BARREL_MIXING_HEIGHT);
this.slot = helper.getSlotDrawable();
this.plus = plus;
this.arrow = arrow;
@@ -81,7 +80,7 @@ public abstract class BarrelMixingCategory implements IRecipeCategory {
this.slot.draw(graphics, 78, 0);
}
- public static class Items extends BarrelMixingCategory {
+ static class Items extends BarrelMixingCategory {
public Items(IGuiHelper helper, IDrawable plus, IDrawable arrow) {
super(helper, plus, arrow, TranslationKeys.BARREL_MIXING_CATEGORY_TITLE, DefaultMaterials.OAK_BARREL.getItem());
}
@@ -101,7 +100,7 @@ public abstract class BarrelMixingCategory implements IRecipeCategory {
}
}
- public static class Fluids extends BarrelMixingCategory {
+ static class Fluids extends BarrelMixingCategory {
private static final Component CONTENTS_ARE_CONSUMED_TOOLTIP = Component.translatable(TranslationKeys.BARREL_FLUID_MIXING_CONTENTS_ARE_CONSUMED).withStyle(ChatFormatting.RED);
public Fluids(IGuiHelper helper, IDrawable plus, IDrawable arrow) {
@@ -130,7 +129,7 @@ public abstract class BarrelMixingCategory implements IRecipeCategory {
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);
}
}
}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java
index 3fb88a84..086bfd79 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java
@@ -18,12 +18,7 @@
package thedarkcolour.exdeorum.compat.jei;
-import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.BufferBuilder;
-import com.mojang.blaze3d.vertex.PoseStack;
-import com.mojang.blaze3d.vertex.Tesselator;
-import com.mojang.math.Axis;
import me.shedaniel.rei.api.client.view.ViewSearchBuilder;
import me.shedaniel.rei.jeicompat.JEIPluginDetector;
import mezz.jei.api.ingredients.IIngredientRenderer;
@@ -34,165 +29,23 @@ 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.network.chat.MutableComponent;
import net.minecraft.world.entity.player.Player;
-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.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.ModList;
-import org.jetbrains.annotations.Nullable;
-import org.joml.Matrix4f;
-import org.joml.Vector3f;
+import thedarkcolour.exdeorum.compat.ClientXeiUtil;
import thedarkcolour.exdeorum.compat.ModIds;
-import thedarkcolour.exdeorum.data.TranslationKeys;
-import java.text.DecimalFormat;
import java.util.ArrayList;
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);
- PoseStack modelView = RenderSystem.getModelViewStack();
- Tesselator tesselator = Tesselator.getInstance();
- BufferBuilder builder = tesselator.getBuilder();
- renderType.setupRenderState();
- modelView.pushPose();
- modelView.mulPoseMatrix(poseStack.last().pose());
- RenderSystem.applyModelViewMatrix();
-
- builder.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;
-
- if (builder.building()) {
- tesselator.end();
- }
-
- renderType.clearRenderState();
- modelView.popPose();
- 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.mulPoseMatrix((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
public static void checkTypedIngredient(IIngredientManager manager, IIngredientType ingredientType, T uncheckedIngredient, Consumer> action) {
if ((uncheckedIngredient instanceof ItemStack stack && !stack.isEmpty()) || (uncheckedIngredient instanceof FluidStack fluidStack && !fluidStack.isEmpty())) {
@@ -217,75 +70,6 @@ class ClientJeiUtil {
}
}
- // 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);
- }
-
- @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;
- }
-
- @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;
- }
- }
-
enum AsteriskItemRenderer implements IIngredientRenderer {
INSTANCE;
@@ -293,7 +77,7 @@ class ClientJeiUtil {
public void render(GuiGraphics graphics, ItemStack ingredient) {
// From mezz.jei.library.render.ItemStackRenderer
RenderSystem.enableDepthTest();
- ClientJeiUtil.renderItemWithAsterisk(graphics, ingredient);
+ ClientXeiUtil.renderItemWithAsterisk(graphics, ingredient);
// From end of DrawableIngredient
RenderSystem.disableDepthTest();
}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CompressedSieveCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CompressedSieveCategory.java
index bd0611b6..e8db2abb 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CompressedSieveCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CompressedSieveCategory.java
@@ -21,17 +21,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.intValue());
}
@Override
- public RecipeType getRecipeType() {
+ public RecipeType getRecipeType() {
return ExDeorumJeiPlugin.COMPRESSED_SIEVE;
}
}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java
index b804e8cf..bb99ac6d 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java
@@ -24,7 +24,6 @@ import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IJeiHelpers;
-import mezz.jei.api.helpers.IModIdHelper;
import mezz.jei.api.recipe.IFocusFactory;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
@@ -32,25 +31,19 @@ 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.minecraftforge.client.RenderTypeHelper;
-import net.minecraftforge.client.model.data.ModelData;
+import thedarkcolour.exdeorum.compat.ClientXeiUtil;
+import thedarkcolour.exdeorum.compat.XeiUtil;
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 {
- private static final Component REQUIRES_CERTAIN_STATE = Component.translatable(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE).withStyle(ChatFormatting.GRAY);
+class CrookCategory implements IRecipeCategory {
private final IDrawable background;
private final IDrawable icon;
@@ -60,11 +53,10 @@ public class CrookCategory implements IRecipeCategory {
private final IFocusFactory focusFactory;
private final IIngredientManager ingredientManager;
- private final IModIdHelper modIdHelper;
private final CycleTimer timer = new CycleTimer(0);
- public CrookCategory(IJeiHelpers helpers, IDrawable arrow) {
+ CrookCategory(IJeiHelpers helpers, IDrawable arrow) {
var helper = helpers.getGuiHelper();
this.background = helper.createBlankDrawable(120, 48);
this.icon = helper.createDrawableItemStack(new ItemStack(EItems.CROOK.get()));
@@ -74,7 +66,6 @@ public class CrookCategory implements IRecipeCategory {
this.focusFactory = helpers.getFocusFactory();
this.ingredientManager = helpers.getIngredientManager();
- this.modIdHelper = helpers.getModIdHelper();
}
@Override
@@ -103,7 +94,7 @@ public class CrookCategory implements IRecipeCategory {
ItemStack result = new ItemStack(recipe.result, 1);
result.setTag(recipe.getResultNbt());
builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(result).addTooltipCallback((recipeSlotView, tooltip) -> {
- tooltip.add(ClientJeiUtil.formatChance(recipe.chance));
+ tooltip.add(XeiUtil.formatChance(recipe.chance));
});
}
@@ -115,37 +106,24 @@ public class CrookCategory implements IRecipeCategory {
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) -> {
- Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY);
- });
- }
+ ClientXeiUtil.renderBlock(graphics, state, 28, 18, 10, 20f);
}
@Override
public List getTooltipStrings(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();
- tooltip.add(Component.translatable(block.getDescriptionId()));
+
+ // todo replace with XeiUtil
+ ArrayList extraDetails = new ArrayList<>();
if (recipe instanceof CrookJeiRecipe.StatesRecipe statesRecipe && !statesRecipe.requirements.isEmpty()) {
- tooltip.add(REQUIRES_CERTAIN_STATE);
- tooltip.addAll(statesRecipe.requirements);
+ extraDetails.add(XeiUtil.REQUIRES_CERTAIN_STATE);
+ extraDetails.addAll(statesRecipe.requirements);
} else if (recipe instanceof CrookJeiRecipe.TagRecipe tagRecipe) {
- tooltip.add(Component.literal("#" + tagRecipe.tag.location()).withStyle(ChatFormatting.GRAY));
+ extraDetails.add(Component.literal("#" + tagRecipe.tag.location()).withStyle(ChatFormatting.GRAY));
}
- tooltip.add(Component.literal(this.modIdHelper.getFormattedModNameForModId(modId)));
- return tooltip;
+
+ return XeiUtil.getBlockTooltip(extraDetails, block);
}
return List.of();
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java
index ae8c55ee..3046577e 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java
@@ -19,10 +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.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
@@ -34,6 +32,7 @@ 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.crook.CrookRecipe;
@@ -41,7 +40,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-public sealed abstract class CrookJeiRecipe {
+sealed abstract class CrookJeiRecipe {
public final List states;
public Item result;
@Nullable
@@ -107,16 +106,7 @@ public sealed abstract class CrookJeiRecipe {
this.itemIngredients = itemIngredients.build();
- ImmutableList.Builder requirements = ImmutableList.builder();
- if (predicate != null) {
- var json = predicate.properties().serializeToJson();
- 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
@@ -140,7 +130,7 @@ public sealed abstract class CrookJeiRecipe {
private final ItemStack itemIngredient;
BlockRecipe(Block block, Item result, @Nullable CompoundTag resultNbt, float chance) {
- super(List.of(block.defaultBlockState()), result, resultNbt, chance);
+ super(ImmutableList.of(block.defaultBlockState()), result, resultNbt, chance);
var item = block.asItem();
if (item == Items.AIR) {
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java
index 1b4b25c3..7989b581 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java
@@ -33,20 +33,17 @@ 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.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraftforge.registries.ForgeRegistries;
+import thedarkcolour.exdeorum.compat.ClientXeiUtil;
import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.material.DefaultMaterials;
import java.util.List;
class CrucibleHeatSourcesCategory implements IRecipeCategory {
- public static final int WIDTH = 120;
- public static final int HEIGHT = 48;
-
private final IDrawable background;
private final IDrawable icon;
private final Component title;
@@ -57,7 +54,7 @@ class CrucibleHeatSourcesCategory implements IRecipeCategory {
- Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY);
- });
+ ClientXeiUtil.renderBlock(graphics, recipe.blockState(), 60, 24, 10, 20f);
}
@Override
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java
index fa11459c..82a32d6e 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java
@@ -18,7 +18,6 @@
package thedarkcolour.exdeorum.compat.jei;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.VanillaTypes;
@@ -38,10 +37,7 @@ import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Recipe;
-import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LiquidBlock;
-import net.minecraft.world.level.block.WallTorchBlock;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.registries.RegistryObject;
import net.minecraftforge.fml.ModList;
@@ -49,8 +45,9 @@ import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen;
import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen;
import thedarkcolour.exdeorum.compat.CompatHelper;
-import thedarkcolour.exdeorum.compat.GroupedSieveRecipe;
+import thedarkcolour.exdeorum.compat.XeiSieveRecipe;
import thedarkcolour.exdeorum.compat.ModIds;
+import thedarkcolour.exdeorum.compat.XeiUtil;
import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.item.WateringCanItem;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
@@ -81,16 +78,14 @@ public class ExDeorumJeiPlugin implements IModPlugin {
static final RecipeType LAVA_CRUCIBLE = recipeType("lava_crucible", CrucibleRecipe.class);
static final RecipeType WATER_CRUCIBLE = recipeType("water_crucible", CrucibleRecipe.class);
static final RecipeType CRUCIBLE_HEAT_SOURCES = recipeType("crucible_heat_sources", CrucibleHeatSourceRecipe.class);
- static final RecipeType SIEVE = recipeType("sieve", GroupedSieveRecipe.class);
- static final RecipeType COMPRESSED_SIEVE = recipeType("compressed_sieve", GroupedSieveRecipe.class);
+ static final RecipeType SIEVE = recipeType("sieve", XeiSieveRecipe.class);
+ static final RecipeType COMPRESSED_SIEVE = recipeType("compressed_sieve", XeiSieveRecipe.class);
static final RecipeType HAMMER = recipeType("hammer", HammerRecipe.class);
static final RecipeType COMPRESSED_HAMMER = recipeType("compressed_hammer", CompressedHammerRecipe.class);
static final RecipeType CROOK = recipeType("crook", CrookJeiRecipe.class);
private static RecipeType recipeType(String path, Class extends T> type) {
- // use alternative namespace so that EMI doesn't skip JEI compatibility
- String namespace = ModList.get().isLoaded(ModIds.EMI) ? ExDeorum.ID + "_" + ModIds.EMI : ExDeorum.ID;
- return RecipeType.create(namespace, path, type);
+ return RecipeType.create(ExDeorum.ID, path, type);
}
@Override
@@ -211,49 +206,31 @@ public class ExDeorumJeiPlugin implements IModPlugin {
crookRecipes.add(CrookJeiRecipe.create(recipe));
}
registration.addRecipes(CROOK, crookRecipes);
- 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);
}
private static void addCrucibleHeatSources(IRecipeRegistration registration) {
- var values = new Object2IntOpenHashMap();
- 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;
- }
- });
- }
- }
var fluidHelper = registration.getJeiHelpers().getPlatformFluidHelper();
var fluidIngredientType = fluidHelper.getFluidIngredientType();
var recipes = new ArrayList();
- for (var entry : values.object2IntEntrySet()) {
- if (entry.getKey() instanceof LiquidBlock liquid) {
- recipes.add(new CrucibleHeatSourceRecipe(entry.getIntValue(), entry.getKey().defaultBlockState(), fluidIngredientType, fluidHelper.create(liquid.getFluid(), 1000)));
+ XeiUtil.addCrucibleHeatRecipes((heat, state) -> {
+ if (state.getBlock() instanceof LiquidBlock liquid) {
+ recipes.add(new CrucibleHeatSourceRecipe(heat, state, fluidIngredientType, fluidHelper.create(liquid.getFluid(), 1000)));
} else {
- var itemForm = entry.getKey().asItem();
+ var itemForm = state.getBlock().asItem();
if (itemForm != Items.AIR) {
- recipes.add(new CrucibleHeatSourceRecipe(entry.getIntValue(), entry.getKey().defaultBlockState(), VanillaTypes.ITEM_STACK, new ItemStack(itemForm)));
+ recipes.add(new CrucibleHeatSourceRecipe(heat, state, VanillaTypes.ITEM_STACK, new ItemStack(itemForm)));
} else {
- recipes.add(new CrucibleHeatSourceRecipe(entry.getIntValue(), entry.getKey().defaultBlockState(), null, null));
+ recipes.add(new CrucibleHeatSourceRecipe(heat, state, null, null));
}
}
- }
+ });
+
registration.addRecipes(CRUCIBLE_HEAT_SOURCES, recipes);
}
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/OneToOneCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/OneToOneCategory.java
index cc5648b0..4e98fed6 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/OneToOneCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/OneToOneCategory.java
@@ -28,11 +28,9 @@ import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
+import thedarkcolour.exdeorum.compat.XeiUtil;
abstract class OneToOneCategory implements IRecipeCategory {
- public static final int WIDTH = 72;
- public static final int HEIGHT = 18;
-
private final IDrawable background;
private final IDrawable arrow;
private final IDrawable icon;
@@ -40,7 +38,7 @@ abstract class OneToOneCategory implements IRecipeCategory {
private final Component title;
public OneToOneCategory(IGuiHelper helper, IDrawable arrow, IDrawable icon, Component title) {
- this.background = helper.createBlankDrawable(WIDTH, HEIGHT);
+ this.background = helper.createBlankDrawable(XeiUtil.ONE_TO_ONE_WIDTH, XeiUtil.ONE_TO_ONE_HEIGHT);
this.arrow = arrow;
this.icon = icon;
this.slot = helper.getSlotDrawable();
diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java
index 1b0b9414..249c86ef 100644
--- a/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java
+++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java
@@ -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,53 +28,42 @@ 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.minecraftforge.common.util.Lazy;
-import thedarkcolour.exdeorum.compat.GroupedSieveRecipe;
+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 {
- private static final Component BY_HAND_ONLY_LABEL = Component.translatable(TranslationKeys.SIEVE_RECIPE_BY_HAND_ONLY).withStyle(ChatFormatting.RED);
-
- public static final int WIDTH = 162;
- public static final int ROW_START = 28;
-
- static {
- ClientJeiUtil.FORMATTER.setMinimumFractionDigits(0);
- ClientJeiUtil.FORMATTER.setMaximumFractionDigits(3);
- }
+class SieveCategory implements IRecipeCategory {
private final Lazy background;
private final IDrawable slot;
private final IDrawable row;
private final IDrawable icon;
private final Component title;
+ private final int rows;
- SieveCategory(IGuiHelper helper, ItemLike icon, Component title) {
- this.background = Lazy.of(() -> helper.createBlankDrawable(WIDTH, ROW_START + 18 * GroupedSieveRecipe.maxSieveRows));
+ // Common constructor
+ SieveCategory(IGuiHelper helper, ItemLike icon, Component title, int rows) {
+ this.background = Lazy.of(() -> helper.createBlankDrawable(XeiUtil.SIEVE_WIDTH, XeiUtil.SIEVE_ROW_START + XeiUtil.SIEVE_ROW_HEIGHT * rows));
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;
}
+ // Regular sieve
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.intValue());
}
@Override
- public RecipeType getRecipeType() {
+ public RecipeType getRecipeType() {
return ExDeorumJeiPlugin.SIEVE;
}
@@ -95,111 +83,34 @@ class SieveCategory implements IRecipeCategory {
}
@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());
for (int i = 0; i < recipe.results().size(); i++) {
var result = recipe.results().get(i);
- var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 1 + (i % 9) * 18, 1 + ROW_START + 18 * (i / 9)).addItemStack(result.item);
+ var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 1 + (i % 9) * 18, 1 + XeiUtil.SIEVE_ROW_START + 18 * (i / 9)).addItemStack(result.item);
addTooltips(slot, result.byHandOnly, result.provider);
}
}
public static void addTooltips(IRecipeSlotBuilder slot, boolean byHandOnly, NumberProvider provider) {
- var tooltipLines = new ImmutableList.Builder();
-
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);
+ 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 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 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++) {
+ for (int i = 0; i < this.rows; i++) {
this.row.draw(graphics, 0, 28 + i * 18);
}
}
diff --git a/src/main/java/thedarkcolour/exdeorum/data/English.java b/src/main/java/thedarkcolour/exdeorum/data/English.java
index 7bf38725..2c1d11be 100644
--- a/src/main/java/thedarkcolour/exdeorum/data/English.java
+++ b/src/main/java/thedarkcolour/exdeorum/data/English.java
@@ -106,6 +106,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");
diff --git a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java
index 9c63e5f6..d6bd5649 100644
--- a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java
+++ b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java
@@ -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[]{
diff --git a/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java b/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java
index be7aea33..0bdd9094 100644
--- a/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java
+++ b/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java
@@ -31,12 +31,10 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.util.Unit;
-import net.minecraft.world.entity.ai.behavior.GoAndGiveItemsToTarget;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.GameRules;
-import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.level.block.state.BlockState;
@@ -63,6 +61,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLEnvironment;
import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.helper.ItemHelper;
+import thedarkcolour.exdeorum.client.ClientsideCode;
import thedarkcolour.exdeorum.client.CompostColors;
import thedarkcolour.exdeorum.compat.ModIds;
import thedarkcolour.exdeorum.compat.top.ExDeorumTopCompat;
diff --git a/src/main/java/thedarkcolour/exdeorum/material/DefaultMaterials.java b/src/main/java/thedarkcolour/exdeorum/material/DefaultMaterials.java
index 1437e0fc..f913a91b 100644
--- a/src/main/java/thedarkcolour/exdeorum/material/DefaultMaterials.java
+++ b/src/main/java/thedarkcolour/exdeorum/material/DefaultMaterials.java
@@ -28,8 +28,8 @@ public class DefaultMaterials {
public static final MaterialRegistry BARRELS = new MaterialRegistry<>("barrel");
public static final MaterialRegistry SIEVES = new MaterialRegistry<>("sieve");
public static final MaterialRegistry COMPRESSED_SIEVES = new MaterialRegistry<>("compressed_sieve");
- public static final MaterialRegistry LAVA_CRUCIBLES = new MaterialRegistry<>("lava_crucible", "crucible");
- public static final MaterialRegistry WATER_CRUCIBLES = new MaterialRegistry<>("water_crucible", "crucible");
+ public static final MaterialRegistry LAVA_CRUCIBLES = new MaterialRegistry<>("lava_crucible", "crucible");
+ public static final MaterialRegistry WATER_CRUCIBLES = new MaterialRegistry<>("water_crucible", "crucible");
// Ex Deorum
public static final BarrelMaterial OAK_BARREL = addDefaultWoodBarrel("oak", SoundType.WOOD, false, MapColor.WOOD, ExDeorum.ID);
diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java b/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java
index 30f56a4c..efbe870a 100644
--- a/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java
+++ b/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java
@@ -55,6 +55,7 @@ import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.ExDeorum;
+import thedarkcolour.exdeorum.client.ClientsideCode;
import thedarkcolour.exdeorum.compat.PreferredOres;
import thedarkcolour.exdeorum.item.CompressedHammerItem;
import thedarkcolour.exdeorum.item.HammerItem;