diff --git a/src/main/java/com/simibubi/create/AllRecipeTypes.java b/src/main/java/com/simibubi/create/AllRecipeTypes.java index 481b2785a1..0e3596fe30 100644 --- a/src/main/java/com/simibubi/create/AllRecipeTypes.java +++ b/src/main/java/com/simibubi/create/AllRecipeTypes.java @@ -4,8 +4,6 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import com.simibubi.create.content.kinetics.deployer.ItemApplicationRecipeParams; - import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -20,6 +18,7 @@ import com.simibubi.create.content.kinetics.crusher.CrushingRecipe; import com.simibubi.create.content.kinetics.deployer.DeployerApplicationRecipe; import com.simibubi.create.content.kinetics.deployer.ItemApplicationRecipe; +import com.simibubi.create.content.kinetics.deployer.ItemApplicationRecipeParams; import com.simibubi.create.content.kinetics.deployer.ManualApplicationRecipe; import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe; import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe; @@ -60,9 +59,9 @@ public enum AllRecipeTypes implements IRecipeTypeInfo, StringRepresentable { CRUSHING(CrushingRecipe::new), CUTTING(CuttingRecipe::new), MILLING(MillingRecipe::new), - BASIN(BasinRecipe::new), - MIXING(MixingRecipe::new), - COMPACTING(CompactingRecipe::new), + BASIN(() -> new BasinRecipe.Serializer<>(BasinRecipe::new)), + MIXING(() -> new BasinRecipe.Serializer<>(MixingRecipe::new)), + COMPACTING(() -> new BasinRecipe.Serializer<>(CompactingRecipe::new)), PRESSING(PressingRecipe::new), SANDPAPER_POLISHING(SandPaperPolishingRecipe::new), SPLASHING(SplashingRecipe::new), @@ -79,8 +78,8 @@ public enum AllRecipeTypes implements IRecipeTypeInfo, StringRepresentable { ITEM_COPYING(() -> new SimpleCraftingRecipeSerializer<>(ItemCopyingRecipe::new), () -> RecipeType.CRAFTING, false); public static final Predicate> CAN_BE_AUTOMATED = r -> !r.id() - .getPath() - .endsWith("_manual_only"); + .getPath() + .endsWith("_manual_only"); public final ResourceLocation id; public final Supplier> serializerSupplier; diff --git a/src/main/java/com/simibubi/create/api/data/recipe/CompactingRecipeGen.java b/src/main/java/com/simibubi/create/api/data/recipe/CompactingRecipeGen.java index 4f15e37f9e..8a979c2756 100644 --- a/src/main/java/com/simibubi/create/api/data/recipe/CompactingRecipeGen.java +++ b/src/main/java/com/simibubi/create/api/data/recipe/CompactingRecipeGen.java @@ -3,11 +3,14 @@ import java.util.concurrent.CompletableFuture; import com.simibubi.create.AllRecipeTypes; - import com.simibubi.create.content.kinetics.mixer.CompactingRecipe; +import com.simibubi.create.content.processing.basin.BasinRecipe; +import com.simibubi.create.content.processing.basin.BasinRecipe.Builder; +import com.simibubi.create.content.processing.recipe.ProcessingRecipeParams; import net.minecraft.core.HolderLookup; import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; /** * The base class for Compacting recipe generation. @@ -16,7 +19,7 @@ * For an example of how you might do this, see Create's implementation: {@link com.simibubi.create.foundation.data.recipe.CreateCompactingRecipeGen}. * Needs to be added to a registered recipe provider to do anything, see {@link com.simibubi.create.foundation.data.recipe.CreateRecipeProvider} */ -public abstract class CompactingRecipeGen extends StandardProcessingRecipeGen { +public abstract class CompactingRecipeGen extends ProcessingRecipeGen> { public CompactingRecipeGen(PackOutput output, CompletableFuture registries, String defaultNamespace) { super(output, registries, defaultNamespace); @@ -27,4 +30,12 @@ protected AllRecipeTypes getRecipeType() { return AllRecipeTypes.COMPACTING; } + protected BasinRecipe.Serializer getSerializer() { + return getRecipeType().getSerializer(); + } + + @Override + protected Builder getBuilder(ResourceLocation id) { + return new BasinRecipe.Builder<>(getSerializer().factory(), id); + } } diff --git a/src/main/java/com/simibubi/create/api/data/recipe/MixingRecipeGen.java b/src/main/java/com/simibubi/create/api/data/recipe/MixingRecipeGen.java index 3bb479a9cb..808c472347 100644 --- a/src/main/java/com/simibubi/create/api/data/recipe/MixingRecipeGen.java +++ b/src/main/java/com/simibubi/create/api/data/recipe/MixingRecipeGen.java @@ -3,10 +3,14 @@ import java.util.concurrent.CompletableFuture; import com.simibubi.create.AllRecipeTypes; - import com.simibubi.create.content.kinetics.mixer.MixingRecipe; +import com.simibubi.create.content.processing.basin.BasinRecipe; +import com.simibubi.create.content.processing.basin.BasinRecipe.Builder; +import com.simibubi.create.content.processing.recipe.ProcessingRecipeParams; + import net.minecraft.core.HolderLookup; import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; /** * The base class for Mixing recipe generation. @@ -15,7 +19,7 @@ * For an example of how you might do this, see Create's implementation: {@link com.simibubi.create.foundation.data.recipe.CreateMixingRecipeGen}. * Needs to be added to a registered recipe provider to do anything, see {@link com.simibubi.create.foundation.data.recipe.CreateRecipeProvider} */ -public abstract class MixingRecipeGen extends StandardProcessingRecipeGen { +public abstract class MixingRecipeGen extends ProcessingRecipeGen> { public MixingRecipeGen(PackOutput output, CompletableFuture registries, String defaultNamespace) { super(output, registries, defaultNamespace); @@ -26,4 +30,12 @@ protected AllRecipeTypes getRecipeType() { return AllRecipeTypes.MIXING; } + protected BasinRecipe.Serializer getSerializer() { + return getRecipeType().getSerializer(); + } + + @Override + protected Builder getBuilder(ResourceLocation id) { + return new BasinRecipe.Builder<>(getSerializer().factory(), id); + } } diff --git a/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java index 65e88096f1..11bb31e1f0 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/BasinCategory.java @@ -9,7 +9,7 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.AllItems; -import com.simibubi.create.content.processing.basin.BasinRecipe; +import com.simibubi.create.content.processing.basin.AbstractBasinRecipe; import com.simibubi.create.content.processing.burner.BlazeBurnerBlock.HeatLevel; import com.simibubi.create.content.processing.recipe.HeatCondition; import com.simibubi.create.content.processing.recipe.ProcessingOutput; @@ -20,7 +20,6 @@ import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.ingredient.IRecipeSlotsView; -import mezz.jei.api.neoforge.NeoForgeTypes; import mezz.jei.api.recipe.IFocusGroup; import mezz.jei.api.recipe.RecipeIngredientRole; import net.createmod.catnip.data.Pair; @@ -32,17 +31,17 @@ import net.neoforged.neoforge.fluids.FluidStack; @ParametersAreNonnullByDefault -public class BasinCategory extends CreateRecipeCategory { +public class BasinCategory> extends CreateRecipeCategory { private final boolean needsHeating; - public BasinCategory(Info info, boolean needsHeating) { + public BasinCategory(Info info, boolean needsHeating) { super(info); this.needsHeating = needsHeating; } @Override - public void setRecipe(IRecipeLayoutBuilder builder, BasinRecipe recipe, IFocusGroup focuses) { + public void setRecipe(IRecipeLayoutBuilder builder, R recipe, IFocusGroup focuses) { List> condensedIngredients = ItemHelper.condenseIngredients(recipe.getIngredients()); int size = condensedIngredients.size() + recipe.getFluidIngredients().size(); @@ -58,9 +57,9 @@ public void setRecipe(IRecipeLayoutBuilder builder, BasinRecipe recipe, IFocusGr } builder - .addSlot(RecipeIngredientRole.INPUT, 17 + xOffset + (i % 3) * 19, 51 - (i / 3) * 19) - .setBackground(getRenderedSlot(), -1, -1) - .addItemStacks(stacks); + .addSlot(RecipeIngredientRole.INPUT, 17 + xOffset + (i % 3) * 19, 51 - (i / 3) * 19) + .setBackground(getRenderedSlot(), -1, -1) + .addItemStacks(stacks); i++; } for (FluidIngredient fluidIngredient : recipe.getFluidIngredients()) { @@ -78,10 +77,10 @@ public void setRecipe(IRecipeLayoutBuilder builder, BasinRecipe recipe, IFocusGr int yPosition = -19 * (i / 2) + 51; builder - .addSlot(RecipeIngredientRole.OUTPUT, xPosition, yPosition) - .setBackground(getRenderedSlot(result), -1, -1) - .addItemStack(result.getStack()) - .addRichTooltipCallback(addStochasticTooltip(result)); + .addSlot(RecipeIngredientRole.OUTPUT, xPosition, yPosition) + .setBackground(getRenderedSlot(result), -1, -1) + .addItemStack(result.getStack()) + .addRichTooltipCallback(addStochasticTooltip(result)); i++; } @@ -95,18 +94,18 @@ public void setRecipe(IRecipeLayoutBuilder builder, BasinRecipe recipe, IFocusGr HeatCondition requiredHeat = recipe.getRequiredHeat(); if (!requiredHeat.testBlazeBurner(HeatLevel.NONE)) { builder - .addSlot(RecipeIngredientRole.RENDER_ONLY, 134, 81) - .addItemStack(AllBlocks.BLAZE_BURNER.asStack()); + .addSlot(RecipeIngredientRole.RENDER_ONLY, 134, 81) + .addItemStack(AllBlocks.BLAZE_BURNER.asStack()); } if (!requiredHeat.testBlazeBurner(HeatLevel.KINDLED)) { builder - .addSlot(RecipeIngredientRole.CATALYST, 153, 81) - .addItemStack(AllItems.BLAZE_CAKE.asStack()); + .addSlot(RecipeIngredientRole.CATALYST, 153, 81) + .addItemStack(AllItems.BLAZE_CAKE.asStack()); } } @Override - public void draw(BasinRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) { + public void draw(R recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) { HeatCondition requiredHeat = recipe.getRequiredHeat(); boolean noHeat = requiredHeat == HeatCondition.NONE; @@ -125,7 +124,7 @@ public void draw(BasinRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphi AllGuiTextures heatBar = noHeat ? AllGuiTextures.JEI_NO_HEAT_BAR : AllGuiTextures.JEI_HEAT_BAR; heatBar.render(graphics, 4, 80); graphics.drawString(Minecraft.getInstance().font, CreateLang.translateDirect(requiredHeat.getTranslationKey()), 9, - 86, requiredHeat.getColor(), false); + 86, requiredHeat.getColor(), false); } } diff --git a/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java index 2c8c621fb4..849cdf5048 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/MixingCategory.java @@ -1,16 +1,17 @@ package com.simibubi.create.compat.jei.category; +import javax.annotation.ParametersAreNonnullByDefault; + import com.simibubi.create.compat.jei.category.animations.AnimatedBlazeBurner; import com.simibubi.create.compat.jei.category.animations.AnimatedMixer; import com.simibubi.create.content.processing.basin.BasinRecipe; import com.simibubi.create.content.processing.recipe.HeatCondition; + import mezz.jei.api.gui.ingredient.IRecipeSlotsView; import net.minecraft.client.gui.GuiGraphics; -import javax.annotation.ParametersAreNonnullByDefault; - @ParametersAreNonnullByDefault -public class MixingCategory extends BasinCategory { +public class MixingCategory extends BasinCategory { private final AnimatedMixer mixer = new AnimatedMixer(); private final AnimatedBlazeBurner heater = new AnimatedBlazeBurner(); diff --git a/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java index 1b207bf247..1a39b594ac 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/PackingCategory.java @@ -1,10 +1,13 @@ package com.simibubi.create.compat.jei.category; +import javax.annotation.ParametersAreNonnullByDefault; + import com.simibubi.create.compat.jei.category.animations.AnimatedBlazeBurner; import com.simibubi.create.compat.jei.category.animations.AnimatedPress; import com.simibubi.create.content.processing.basin.BasinRecipe; import com.simibubi.create.content.processing.recipe.HeatCondition; import com.simibubi.create.foundation.gui.AllGuiTextures; + import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.ingredient.IRecipeSlotsView; import mezz.jei.api.recipe.IFocusGroup; @@ -13,10 +16,8 @@ import net.minecraft.core.NonNullList; import net.minecraft.world.item.crafting.Ingredient; -import javax.annotation.ParametersAreNonnullByDefault; - @ParametersAreNonnullByDefault -public class PackingCategory extends BasinCategory { +public class PackingCategory extends BasinCategory { private final AnimatedPress press = new AnimatedPress(true); private final AnimatedBlazeBurner heater = new AnimatedBlazeBurner(); @@ -53,17 +54,17 @@ public void setRecipe(IRecipeLayoutBuilder builder, BasinRecipe recipe, IFocusGr while (i < size) { Ingredient ingredient = ingredients.get(i); builder - .addSlot(RecipeIngredientRole.INPUT, (rows == 2 ? 27 : 18) + (i % rows) * 19, 51 - (i / rows) * 19) - .setBackground(getRenderedSlot(), -1, -1) - .addIngredients(ingredient); + .addSlot(RecipeIngredientRole.INPUT, (rows == 2 ? 27 : 18) + (i % rows) * 19, 51 - (i / rows) * 19) + .setBackground(getRenderedSlot(), -1, -1) + .addIngredients(ingredient); i++; } builder - .addSlot(RecipeIngredientRole.OUTPUT, 142, 51) - .setBackground(getRenderedSlot(), -1, -1) - .addItemStack(getResultItem(recipe)); + .addSlot(RecipeIngredientRole.OUTPUT, 142, 51) + .setBackground(getRenderedSlot(), -1, -1) + .addItemStack(getResultItem(recipe)); } @Override diff --git a/src/main/java/com/simibubi/create/content/fluids/potion/PotionMixingRecipes.java b/src/main/java/com/simibubi/create/content/fluids/potion/PotionMixingRecipes.java index b38022e680..b1b275759c 100644 --- a/src/main/java/com/simibubi/create/content/fluids/potion/PotionMixingRecipes.java +++ b/src/main/java/com/simibubi/create/content/fluids/potion/PotionMixingRecipes.java @@ -10,8 +10,8 @@ import com.simibubi.create.Create; import com.simibubi.create.content.fluids.potion.PotionFluid.BottleType; import com.simibubi.create.content.kinetics.mixer.MixingRecipe; +import com.simibubi.create.content.processing.basin.BasinRecipe.Builder; import com.simibubi.create.content.processing.recipe.HeatCondition; -import com.simibubi.create.content.processing.recipe.StandardProcessingRecipe.Builder; import com.simibubi.create.foundation.fluid.FluidIngredient; import com.simibubi.create.foundation.mixin.accessor.PotionBrewingAccessor; @@ -127,7 +127,7 @@ private static List> createRecipesImpl(Level level) { for (ItemStack stack : supportedContainerStacks) { if (input.test(stack)) { ItemStack[] stacks = input.getItems(); - if (stacks.length == 0){ + if (stacks.length == 0) { continue; } FluidStack inputFluid = PotionFluidHandler.getFluidFromPotionItem(stacks[0]); @@ -148,11 +148,11 @@ private static List> createRecipesImpl(Level level) { private static RecipeHolder createRecipe(String id, Ingredient ingredient, FluidStack fromFluid, FluidStack toFluid) { ResourceLocation recipeId = Create.asResource(id); MixingRecipe recipe = new Builder<>(MixingRecipe::new, recipeId) - .require(ingredient) - .require(FluidIngredient.fromFluidStack(fromFluid)) - .output(toFluid) - .requiresHeat(HeatCondition.HEATED) - .build(); + .require(ingredient) + .require(FluidIngredient.fromFluidStack(fromFluid)) + .output(toFluid) + .requiresHeat(HeatCondition.HEATED) + .build(); return new RecipeHolder<>(recipeId, recipe); } diff --git a/src/main/java/com/simibubi/create/content/processing/basin/AbstractBasinRecipe.java b/src/main/java/com/simibubi/create/content/processing/basin/AbstractBasinRecipe.java new file mode 100644 index 0000000000..4bd4dd16b0 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/processing/basin/AbstractBasinRecipe.java @@ -0,0 +1,216 @@ +package com.simibubi.create.content.processing.basin; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import com.simibubi.create.content.processing.burner.BlazeBurnerBlock.HeatLevel; +import com.simibubi.create.content.processing.recipe.ProcessingRecipe; +import com.simibubi.create.content.processing.recipe.ProcessingRecipeParams; +import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour; +import com.simibubi.create.foundation.blockEntity.behaviour.fluid.SmartFluidTankBehaviour; +import com.simibubi.create.foundation.blockEntity.behaviour.fluid.SmartFluidTankBehaviour.TankSegment; +import com.simibubi.create.foundation.fluid.FluidIngredient; +import com.simibubi.create.foundation.recipe.DummyCraftingContainer; +import com.simibubi.create.foundation.recipe.IRecipeTypeInfo; + +import net.createmod.catnip.data.Iterate; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CraftingInput; +import net.minecraft.world.item.crafting.CraftingRecipe; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.level.Level; + +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import net.neoforged.neoforge.items.IItemHandler; + +public abstract class AbstractBasinRecipe

extends ProcessingRecipe { + + public static boolean match(BasinBlockEntity basin, Recipe recipe) { + FilteringBehaviour filter = basin.getFilter(); + if (filter == null) + return false; + + boolean filterTest = filter.test(recipe.getResultItem(basin.getLevel() + .registryAccess())); + if (recipe instanceof AbstractBasinRecipe basinRecipe) { + if (basinRecipe.getRollableResults() + .isEmpty() + && !basinRecipe.getFluidResults() + .isEmpty()) + filterTest = filter.test(basinRecipe.getFluidResults() + .getFirst()); + } + + if (!filterTest) + return false; + + return apply(basin, recipe, true); + } + + public static boolean apply(BasinBlockEntity basin, Recipe recipe) { + return apply(basin, recipe, false); + } + + private static boolean apply(BasinBlockEntity basin, Recipe recipe, boolean test) { + boolean isBasinRecipe = recipe instanceof AbstractBasinRecipe; + IItemHandler availableItems = basin.getLevel().getCapability(Capabilities.ItemHandler.BLOCK, basin.getBlockPos(), null); + IFluidHandler availableFluids = basin.getLevel().getCapability(Capabilities.FluidHandler.BLOCK, basin.getBlockPos(), null); + + if (availableItems == null || availableFluids == null) + return false; + + HeatLevel heat = basin.getHeatLevel(); + if (isBasinRecipe && !((AbstractBasinRecipe) recipe).getRequiredHeat() + .testBlazeBurner(heat)) + return false; + + List recipeOutputItems = new ArrayList<>(); + List recipeOutputFluids = new ArrayList<>(); + + List ingredients = new LinkedList<>(recipe.getIngredients()); + List fluidIngredients = + isBasinRecipe ? ((AbstractBasinRecipe) recipe).getFluidIngredients() : Collections.emptyList(); + + for (boolean simulate : Iterate.trueAndFalse) { + + if (!simulate && test) + return true; + + int[] extractedItemsFromSlot = new int[availableItems.getSlots()]; + int[] extractedFluidsFromTank = new int[availableFluids.getTanks()]; + + Ingredients: + for (Ingredient ingredient : ingredients) { + for (int slot = 0; slot < availableItems.getSlots(); slot++) { + if (simulate && availableItems.getStackInSlot(slot) + .getCount() <= extractedItemsFromSlot[slot]) + continue; + ItemStack extracted = availableItems.extractItem(slot, 1, true); + if (!ingredient.test(extracted)) + continue; + if (!simulate) + availableItems.extractItem(slot, 1, false); + extractedItemsFromSlot[slot]++; + continue Ingredients; + } + + // something wasn't found + return false; + } + + boolean fluidsAffected = false; + FluidIngredients: + for (FluidIngredient fluidIngredient : fluidIngredients) { + int amountRequired = fluidIngredient.getRequiredAmount(); + + for (int tank = 0; tank < availableFluids.getTanks(); tank++) { + FluidStack fluidStack = availableFluids.getFluidInTank(tank); + if (simulate && fluidStack.getAmount() <= extractedFluidsFromTank[tank]) + continue; + if (!fluidIngredient.test(fluidStack)) + continue; + int drainedAmount = Math.min(amountRequired, fluidStack.getAmount()); + if (!simulate) { + fluidStack.shrink(drainedAmount); + fluidsAffected = true; + } + amountRequired -= drainedAmount; + if (amountRequired != 0) + continue; + extractedFluidsFromTank[tank] += drainedAmount; + continue FluidIngredients; + } + + // something wasn't found + return false; + } + + if (fluidsAffected) { + basin.getBehaviour(SmartFluidTankBehaviour.INPUT) + .forEach(TankSegment::onFluidStackChanged); + basin.getBehaviour(SmartFluidTankBehaviour.OUTPUT) + .forEach(TankSegment::onFluidStackChanged); + } + + if (simulate) { + CraftingInput remainderInput = new DummyCraftingContainer(availableItems, extractedItemsFromSlot) + .asCraftInput(); + + if (recipe instanceof AbstractBasinRecipe basinRecipe) { + recipeOutputItems.addAll(basinRecipe.rollResults(basin.getLevel().random)); + + for (FluidStack fluidStack : basinRecipe.getFluidResults()) + if (!fluidStack.isEmpty()) + recipeOutputFluids.add(fluidStack); + for (ItemStack stack : basinRecipe.getRemainingItems(remainderInput)) + if (!stack.isEmpty()) + recipeOutputItems.add(stack); + + } else { + recipeOutputItems.add(recipe.getResultItem(basin.getLevel() + .registryAccess())); + + if (recipe instanceof CraftingRecipe craftingRecipe) { + for (ItemStack stack : craftingRecipe.getRemainingItems(remainderInput)) + if (!stack.isEmpty()) + recipeOutputItems.add(stack); + } + } + } + + if (!basin.acceptOutputs(recipeOutputItems, recipeOutputFluids, simulate)) + return false; + } + + return true; + } + + protected AbstractBasinRecipe(IRecipeTypeInfo type, P params) { + super(type, params); + } + + + @Override + protected int getMaxInputCount() { + return 64; + } + + @Override + protected int getMaxOutputCount() { + return 4; + } + + @Override + protected int getMaxFluidInputCount() { + return 2; + } + + @Override + protected int getMaxFluidOutputCount() { + return 2; + } + + @Override + protected boolean canRequireHeat() { + return true; + } + + @Override + protected boolean canSpecifyDuration() { + return true; + } + + @Override + public boolean matches(RecipeInput input, @NotNull Level worldIn) { + return false; + } + +} diff --git a/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java b/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java index fcfe10a0e7..67745f92f0 100644 --- a/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java +++ b/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java @@ -1,179 +1,28 @@ package com.simibubi.create.content.processing.basin; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import org.jetbrains.annotations.NotNull; - +import com.mojang.serialization.MapCodec; import com.simibubi.create.AllRecipeTypes; -import com.simibubi.create.content.processing.burner.BlazeBurnerBlock.HeatLevel; +import com.simibubi.create.content.processing.recipe.ProcessingRecipe; +import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder; import com.simibubi.create.content.processing.recipe.ProcessingRecipeParams; -import com.simibubi.create.content.processing.recipe.StandardProcessingRecipe; -import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour; -import com.simibubi.create.foundation.blockEntity.behaviour.fluid.SmartFluidTankBehaviour; -import com.simibubi.create.foundation.blockEntity.behaviour.fluid.SmartFluidTankBehaviour.TankSegment; -import com.simibubi.create.foundation.fluid.FluidIngredient; -import com.simibubi.create.foundation.recipe.DummyCraftingContainer; import com.simibubi.create.foundation.recipe.IRecipeTypeInfo; -import net.createmod.catnip.data.Iterate; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.client.Minecraft; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.CraftingInput; -import net.minecraft.world.item.crafting.CraftingRecipe; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.item.crafting.RecipeInput; -import net.minecraft.world.level.Level; - -import net.neoforged.neoforge.capabilities.Capabilities; -import net.neoforged.neoforge.fluids.FluidStack; -import net.neoforged.neoforge.fluids.capability.IFluidHandler; -import net.neoforged.neoforge.items.IItemHandler; - -public class BasinRecipe extends StandardProcessingRecipe { - - public static boolean match(BasinBlockEntity basin, Recipe recipe) { - FilteringBehaviour filter = basin.getFilter(); - if (filter == null) - return false; - - boolean filterTest = filter.test(recipe.getResultItem(basin.getLevel() - .registryAccess())); - if (recipe instanceof BasinRecipe basinRecipe) { - if (basinRecipe.getRollableResults() - .isEmpty() - && !basinRecipe.getFluidResults() - .isEmpty()) - filterTest = filter.test(basinRecipe.getFluidResults() - .get(0)); - } +import net.minecraft.world.item.crafting.RecipeSerializer; - if (!filterTest) - return false; +public class BasinRecipe extends AbstractBasinRecipe { - return apply(basin, recipe, true); - } - - public static boolean apply(BasinBlockEntity basin, Recipe recipe) { - return apply(basin, recipe, false); + protected BasinRecipe(IRecipeTypeInfo type, ProcessingRecipeParams params) { + super(type, params); } - private static boolean apply(BasinBlockEntity basin, Recipe recipe, boolean test) { - boolean isBasinRecipe = recipe instanceof BasinRecipe; - IItemHandler availableItems = basin.getLevel().getCapability(Capabilities.ItemHandler.BLOCK, basin.getBlockPos(), null); - IFluidHandler availableFluids = basin.getLevel().getCapability(Capabilities.FluidHandler.BLOCK, basin.getBlockPos(), null); - - if (availableItems == null || availableFluids == null) - return false; - - HeatLevel heat = basin.getHeatLevel(); - if (isBasinRecipe && !((BasinRecipe) recipe).getRequiredHeat() - .testBlazeBurner(heat)) - return false; - - List recipeOutputItems = new ArrayList<>(); - List recipeOutputFluids = new ArrayList<>(); - - List ingredients = new LinkedList<>(recipe.getIngredients()); - List fluidIngredients = - isBasinRecipe ? ((BasinRecipe) recipe).getFluidIngredients() : Collections.emptyList(); - - for (boolean simulate : Iterate.trueAndFalse) { - - if (!simulate && test) - return true; - - int[] extractedItemsFromSlot = new int[availableItems.getSlots()]; - int[] extractedFluidsFromTank = new int[availableFluids.getTanks()]; - - Ingredients: - for (Ingredient ingredient : ingredients) { - for (int slot = 0; slot < availableItems.getSlots(); slot++) { - if (simulate && availableItems.getStackInSlot(slot) - .getCount() <= extractedItemsFromSlot[slot]) - continue; - ItemStack extracted = availableItems.extractItem(slot, 1, true); - if (!ingredient.test(extracted)) - continue; - if (!simulate) - availableItems.extractItem(slot, 1, false); - extractedItemsFromSlot[slot]++; - continue Ingredients; - } - - // something wasn't found - return false; - } - - boolean fluidsAffected = false; - FluidIngredients: - for (FluidIngredient fluidIngredient : fluidIngredients) { - int amountRequired = fluidIngredient.getRequiredAmount(); - - for (int tank = 0; tank < availableFluids.getTanks(); tank++) { - FluidStack fluidStack = availableFluids.getFluidInTank(tank); - if (simulate && fluidStack.getAmount() <= extractedFluidsFromTank[tank]) - continue; - if (!fluidIngredient.test(fluidStack)) - continue; - int drainedAmount = Math.min(amountRequired, fluidStack.getAmount()); - if (!simulate) { - fluidStack.shrink(drainedAmount); - fluidsAffected = true; - } - amountRequired -= drainedAmount; - if (amountRequired != 0) - continue; - extractedFluidsFromTank[tank] += drainedAmount; - continue FluidIngredients; - } - - // something wasn't found - return false; - } - - if (fluidsAffected) { - basin.getBehaviour(SmartFluidTankBehaviour.INPUT) - .forEach(TankSegment::onFluidStackChanged); - basin.getBehaviour(SmartFluidTankBehaviour.OUTPUT) - .forEach(TankSegment::onFluidStackChanged); - } - - if (simulate) { - CraftingInput remainderInput = new DummyCraftingContainer(availableItems, extractedItemsFromSlot) - .asCraftInput(); - - if (recipe instanceof BasinRecipe basinRecipe) { - recipeOutputItems.addAll(basinRecipe.rollResults(basin.getLevel().random)); - - for (FluidStack fluidStack : basinRecipe.getFluidResults()) - if (!fluidStack.isEmpty()) - recipeOutputFluids.add(fluidStack); - for (ItemStack stack : basinRecipe.getRemainingItems(remainderInput)) - if (!stack.isEmpty()) - recipeOutputItems.add(stack); - - } else { - recipeOutputItems.add(recipe.getResultItem(basin.getLevel() - .registryAccess())); - - if (recipe instanceof CraftingRecipe craftingRecipe) { - for (ItemStack stack : craftingRecipe.getRemainingItems(remainderInput)) - if (!stack.isEmpty()) - recipeOutputItems.add(stack); - } - } - } - - if (!basin.acceptOutputs(recipeOutputItems, recipeOutputFluids, simulate)) - return false; - } - - return true; + public BasinRecipe(ProcessingRecipeParams params) { + super(AllRecipeTypes.BASIN, params); } public static RecipeHolder convertShapeless(RecipeHolder recipe) { @@ -184,47 +33,48 @@ public static RecipeHolder convertShapeless(RecipeHolder recipe) return new RecipeHolder<>(recipe.id(), basinRecipe); } - protected BasinRecipe(IRecipeTypeInfo type, ProcessingRecipeParams params) { - super(type, params); - } + public static class Builder extends ProcessingRecipeBuilder> { - public BasinRecipe(ProcessingRecipeParams params) { - this(AllRecipeTypes.BASIN, params); - } + public Builder(ProcessingRecipe.Factory factory, ResourceLocation recipeId) { + super(factory, recipeId); + } - @Override - protected int getMaxInputCount() { - return 64; - } + @Override + protected ProcessingRecipeParams createParams() { + return new ProcessingRecipeParams(); + } - @Override - protected int getMaxOutputCount() { - return 4; + @Override + public Builder self() { + return this; + } } - @Override - protected int getMaxFluidInputCount() { - return 2; - } + @MethodsReturnNonnullByDefault + public static class Serializer implements RecipeSerializer { + private final ProcessingRecipe.Factory factory; + private final MapCodec codec; + private final StreamCodec streamCodec; - @Override - protected int getMaxFluidOutputCount() { - return 2; - } + public Serializer(ProcessingRecipe.Factory factory) { + this.factory = factory; + this.codec = ProcessingRecipe.codec(factory, ProcessingRecipeParams.CODEC); + this.streamCodec = ProcessingRecipe.streamCodec(factory, ProcessingRecipeParams.STREAM_CODEC); + } - @Override - protected boolean canRequireHeat() { - return true; - } + @Override + public MapCodec codec() { + return codec; + } - @Override - protected boolean canSpecifyDuration() { - return true; - } + @Override + public StreamCodec streamCodec() { + return streamCodec; + } - @Override - public boolean matches(RecipeInput input, @NotNull Level worldIn) { - return false; + public ProcessingRecipe.Factory factory() { + return factory; + } } } diff --git a/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipeParams.java b/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipeParams.java index f51465f40c..77f145823b 100644 --- a/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipeParams.java +++ b/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipeParams.java @@ -30,7 +30,7 @@ public class ProcessingRecipeParams { protected int processingDuration; protected HeatCondition requiredHeat; - protected ProcessingRecipeParams() { + public ProcessingRecipeParams() { ingredients = NonNullList.create(); results = NonNullList.create(); fluidIngredients = NonNullList.create();