From 3aef5bc444c52cb2e398d26d4ec164463df7b402 Mon Sep 17 00:00:00 2001 From: thedarkcolour <30441001+thedarkcolour@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:34:51 -0700 Subject: [PATCH] Add native EMI support, fix Sieve rows glitching in JEI --- build.gradle | 4 +- changelog.md | 4 + gradle.properties | 2 +- .../93943142017732f21fbc4fa325d116c728b69767 | 4 +- .../resources/assets/exdeorum/lang/en_us.json | 11 + .../exdeorum/client/ClientHandler.java | 2 +- .../{event => client}/ClientsideCode.java | 17 +- .../exdeorum/compat/ClientXeiUtil.java | 265 +++++++++++++++++ ...edSieveRecipe.java => XeiSieveRecipe.java} | 21 +- .../exdeorum/compat/XeiUtil.java | 268 ++++++++++++++++++ .../compat/emi/BarrelCompostEmiRecipe.java | 74 +++++ .../compat/emi/BarrelMixingEmiRecipe.java | 100 +++++++ .../exdeorum/compat/emi/BlockEmiWidget.java | 109 +++++++ .../exdeorum/compat/emi/CrookEmiRecipe.java | 83 ++++++ .../compat/emi/CrucibleEmiRecipe.java | 62 ++++ .../compat/emi/CrucibleHeatEmiRecipe.java | 81 ++++++ .../exdeorum/compat/emi/EEmiRecipe.java | 41 +++ .../compat/emi/EmiOneToOneRecipe.java | 60 ++++ .../exdeorum/compat/emi/EmiUtil.java | 92 ++++++ .../compat/emi/ExDeorumEmiPlugin.java | 114 +++++++- .../exdeorum/compat/emi/HammerEmiRecipe.java | 44 +++ .../exdeorum/compat/emi/SieveEmiRecipe.java | 145 ++++++++++ .../compat/jei/BarrelCompostCategory.java | 23 +- .../compat/jei/BarrelMixingCategory.java | 15 +- .../exdeorum/compat/jei/ClientJeiUtil.java | 220 +------------- .../compat/jei/CompressedSieveCategory.java | 6 +- .../exdeorum/compat/jei/CrookCategory.java | 50 +--- .../exdeorum/compat/jei/CrookJeiRecipe.java | 18 +- .../jei/CrucibleHeatSourcesCategory.java | 11 +- .../compat/jei/ExDeorumJeiPlugin.java | 53 +--- .../exdeorum/compat/jei/OneToOneCategory.java | 6 +- .../exdeorum/compat/jei/SieveCategory.java | 121 ++------ .../thedarkcolour/exdeorum/data/English.java | 12 + .../exdeorum/data/TranslationKeys.java | 14 + .../exdeorum/event/EventHandler.java | 3 +- .../exdeorum/material/DefaultMaterials.java | 4 +- .../exdeorum/recipe/RecipeUtil.java | 1 + 37 files changed, 1685 insertions(+), 475 deletions(-) rename src/main/java/thedarkcolour/exdeorum/{event => client}/ClientsideCode.java (66%) create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/ClientXeiUtil.java rename src/main/java/thedarkcolour/exdeorum/compat/{GroupedSieveRecipe.java => XeiSieveRecipe.java} (86%) create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/XeiUtil.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelCompostEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/BarrelMixingEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/BlockEmiWidget.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/CrookEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/CrucibleHeatEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/EEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/EmiOneToOneRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/EmiUtil.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/HammerEmiRecipe.java create mode 100644 src/main/java/thedarkcolour/exdeorum/compat/emi/SieveEmiRecipe.java 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 recipeType) { - maxSieveRows = 1; + public static ImmutableList getAllRecipesGrouped(RecipeType 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 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;