package gregtech.api.util; import static gregtech.api.enums.Mods.GregTech; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import cpw.mods.fml.common.Loader; import gnu.trove.list.TIntList; import gnu.trove.list.array.TIntArrayList; import gregtech.api.interfaces.IRecipeMap; import gregtech.api.recipe.RecipeMetadataKey; /** * Define helpers useful in the creation of recipe maps. */ public class GT_RecipeMapUtil { public static final Function ALL_FAKE_RECIPE = r -> { r.mFakeRecipe = true; return r; }; private static final Map addonRecipeMaps = new HashMap<>(); private static final Multimap> delayedActions = ArrayListMultimap.create(); /** * Set of metadata that work as alias for special values. */ public static final Set> SPECIAL_VALUE_ALIASES = new HashSet<>(); public static T[] appendArray(T[] arr, T val) { T[] newArr = Arrays.copyOf(arr, arr.length + 1); newArr[arr.length] = val; return newArr; } public static GT_RecipeTemplate asTemplate(GT_Recipe r) { return asTemplate(r, false); } public static GT_RecipeTemplate asTemplate(GT_Recipe r, boolean includeTemplate) { return new GT_RecipeTemplate(r, includeTemplate); } public static List buildRecipeForMultiblock(GT_RecipeBuilder b) { return buildOrEmpty(convertCellToFluid(b, true)); } public static List buildRecipeForMultiblockNoCircuit(GT_RecipeBuilder b) { return buildOrEmpty(convertCellToFluid(b, false)); } public static GT_RecipeBuilder convertCellToFluid(GT_RecipeBuilder b, boolean removeIntegratedCircuit) { List itemInputs = new ArrayList<>(Arrays.asList(b.getItemInputsBasic())); List itemOutputs = new ArrayList<>(Arrays.asList(b.getItemOutputs())); List fluidInputs = new ArrayList<>(Arrays.asList(b.getFluidInputs())); List fluidOutputs = new ArrayList<>(Arrays.asList(b.getFluidOutputs())); TIntList chances = b.getChances() != null ? new TIntArrayList(b.getChances()) : null; cellToFluid(itemInputs, fluidInputs, removeIntegratedCircuit, null); cellToFluid(itemOutputs, fluidOutputs, removeIntegratedCircuit, chances); itemInputs.removeIf(Objects::isNull); if (chances == null) { itemOutputs.removeIf(Objects::isNull); } fluidInputs.removeIf(Objects::isNull); fluidOutputs.removeIf(Objects::isNull); b.itemInputs(itemInputs.toArray(new ItemStack[0])); b.itemOutputs(itemOutputs.toArray(new ItemStack[0]), chances != null ? chances.toArray() : null); b.fluidInputs(fluidInputs.toArray(new FluidStack[0])); b.fluidOutputs(fluidOutputs.toArray(new FluidStack[0])); return b; } private static void cellToFluid(List items, List fluids, boolean removeIntegratedCircuit, TIntList chances) { for (int i = items.size() - 1; i >= 0; i--) { ItemStack item = items.get(i); if (GT_Utility.getFluidForFilledItem(item, true) != null || GT_Utility.isCellEmpty(item) || (removeIntegratedCircuit && GT_Utility.isAnyIntegratedCircuit(item))) { fluids.add(GT_Utility.convertCellToFluid(item)); items.remove(i); if (chances != null) chances.removeAt(i); } } } public static List buildOrEmpty(GT_RecipeBuilder builder) { return builder.build() .map(Collections::singletonList) .orElse(Collections.emptyList()); } /** * Register a recipe map as part of your mod's public API under your modID and your given identifier. * * @param identifier map name * @param recipeMap the map to register * @param dependencies fully qualified identifier of dependent recipe maps. scheduler will only add recipes to one * of the dependent recipe maps and this recipe map concurrently, guaranteeing thread safety. * Currently unused, but you are advised to fill them, so that when The Day (tm) comes we don't * end up with a bunch of weird concurrency bugs. */ public static void registerRecipeMap(String identifier, IRecipeMap recipeMap, RecipeMapDependency... dependencies) { String modId = Loader.instance() .activeModContainer() .getModId(); if (GregTech.ID.equals(modId)) throw new IllegalStateException( "do not register recipe map under the name of gregtech! do it in your own preinit!"); String id = modId + "@" + identifier; addonRecipeMaps.put(id, recipeMap); for (Consumer action : delayedActions.get(id)) { action.accept(recipeMap); } } /** * Use this to register recipes for a recipe map in addon not present at compile time. *

* Do not use this for recipes maps already in {@link GT_RecipeConstants}. None of them will be available via this * interface! * * @param identifier recipe map id * @param registerAction DO NOT ADD RECIPES TO MAPS OTHER THAN THE ONE PASSED TO YOU. DO NOT DO ANYTHING OTHER THAN * ADDING RECIPES TO THIS R */ public static void registerRecipesFor(String modid, String identifier, Consumer registerAction) { String id = modid + "@" + identifier; IRecipeMap map = addonRecipeMaps.get(id); if (map == null) delayedActions.put(id, registerAction); else registerAction.accept(map); } public static final class GT_RecipeTemplate { private final GT_Recipe template; private final List derivatives = new ArrayList<>(); private GT_RecipeTemplate(GT_Recipe template, boolean includeTemplate) { this.template = template; if (includeTemplate) derivatives.add(template); } public GT_Recipe derive() { GT_Recipe derived = template.copyShallow(); derivatives.add(derived); return derived; } public List getAll() { // fix shallow references Set references = Collections.newSetFromMap(new IdentityHashMap<>()); for (GT_Recipe r : derivatives) { if (!references.add(r.mInputs)) r.mInputs = r.mInputs.clone(); if (!references.add(r.mOutputs)) r.mOutputs = r.mOutputs.clone(); if (!references.add(r.mFluidInputs)) r.mFluidInputs = r.mFluidInputs.clone(); if (!references.add(r.mFluidOutputs)) r.mFluidOutputs = r.mFluidOutputs.clone(); } return derivatives; } } public static final class RecipeMapDependency { private final IRecipeMap obj; private final String id; public RecipeMapDependency(IRecipeMap obj, String id) { this.obj = obj; this.id = id; } public static RecipeMapDependency create(String id) { return new RecipeMapDependency(null, id); } public static RecipeMapDependency create(IRecipeMap obj) { return new RecipeMapDependency(obj, null); } } }