package gregtech.api.recipe; import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import gregtech.api.util.GTRecipe; import gregtech.api.util.MethodsReturnNonnullByDefault; // spotless:off spotless likes formatting @code to @code /** * Helper class for searching recipe. Retrieve instance with {@link RecipeMap#findRecipeQuery}. *

* It features fluent API, so you can find recipes like this: * *

 * {@code
 *     GTRecipe recipe = recipeMap.findRecipeQuery()
 *         .items(inputItems)
 *         .fluids(inputFluids)
 *         .find();
 * }
 * 
*/ // spotless:on @SuppressWarnings("unused") @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public final class FindRecipeQuery { private static final Predicate ALWAYS = r -> true; private final RecipeMap recipeMap; @Nullable private ItemStack[] items; @Nullable private FluidStack[] fluids; @Nullable private ItemStack specialSlot; private Predicate filter = ALWAYS; private long voltage = Integer.MAX_VALUE; @Nullable private GTRecipe cachedRecipe; private boolean notUnificated; private boolean dontCheckStackSizes; private boolean forCollisionCheck; FindRecipeQuery(RecipeMap recipeMap) { this.recipeMap = recipeMap; } // region executors /** * @return The first matched recipe, or null if not found. */ @Nullable public GTRecipe find() { return findAll().findFirst() .orElse(null); } /** * @return All the matched recipes in the form of Stream. */ public Stream findAll() { if (items == null) { items = new ItemStack[0]; } if (fluids == null) { fluids = new FluidStack[0]; } return recipeMap.getBackend() .matchRecipeStream( items, fluids, specialSlot, cachedRecipe, notUnificated, dontCheckStackSizes, forCollisionCheck) .filter(recipe -> voltage * recipeMap.getAmperage() >= recipe.mEUt && filter.test(recipe)); } /** * Checks if given inputs conflict with already registered recipes. * * @return True if collision is found. */ public boolean checkCollision() { dontCheckStackSizes = true; forCollisionCheck = true; return findAll().findAny() .isPresent(); } // endregion // region setters /** * @param items Item inputs. */ public FindRecipeQuery items(@Nullable ItemStack... items) { this.items = items; return this; } /** * @param fluids Fluid inputs. */ public FindRecipeQuery fluids(@Nullable FluidStack... fluids) { this.fluids = fluids; return this; } /** * @param specialSlot Content of the special slot. Normal recipemaps don't need this, but some do. * Set {@link RecipeMapBuilder#specialSlotSensitive} to make it actually functional. * Alternatively overriding {@link RecipeMapBackend#filterFindRecipe} will also work. */ public FindRecipeQuery specialSlot(@Nullable ItemStack specialSlot) { this.specialSlot = specialSlot; return this; } /** * @param filter Matched recipe will be tested by this function. If it returns false, the query will attempt to * find next recipe. */ public FindRecipeQuery filter(Predicate filter) { this.filter = filter; return this; } /** * @param voltage Recipes that exceed this voltage won't match. It will be automatically multiplied by amperage * of the recipemap. By default, voltage is set to max Integer, meaning no voltage check. */ public FindRecipeQuery voltage(long voltage) { this.voltage = voltage; return this; } /** * @param cachedRecipe If this is not null, the query tests it before all other recipes. */ public FindRecipeQuery cachedRecipe(@Nullable GTRecipe cachedRecipe) { this.cachedRecipe = cachedRecipe; return this; } /** * @param notUnificated If this is set to true, item inputs will be unificated. */ public FindRecipeQuery notUnificated(boolean notUnificated) { this.notUnificated = notUnificated; return this; } /** * @param dontCheckStackSizes If this is set to true, the query won't check item count and fluid amount * for the matched recipe. */ public FindRecipeQuery dontCheckStackSizes(boolean dontCheckStackSizes) { this.dontCheckStackSizes = dontCheckStackSizes; return this; } // endregion }