RyanHub – file viewer
filename: common/src/main/java/rearth/oritech/init/recipes/OritechRecipe.java
branch: 1.21
back to repo
package rearth.oritech.init.recipes;

import dev.architectury.fluid.FluidStack;
import dev.architectury.platform.Platform;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType;
import net.minecraft.recipe.input.RecipeInput;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.util.SimpleCraftingInventory;

import java.util.List;

public class OritechRecipe implements Recipe<RecipeInput> {
    
    public static final int fluidDivider = Platform.isNeoForge() ? 81 : 1;  // dirty hack because bucket amounts are 81000 in neo, and 1000 in fabric, but datagen/recipes are on fabric
    
    protected final OritechRecipeType type;
    protected final List<Ingredient> inputs;
    protected final List<ItemStack> results;
    protected final FluidStack fluidInput;
    protected final FluidStack fluidOutput;
    protected final int time;

    public static final OritechRecipe DUMMY = new OritechRecipe(-1, DefaultedList.ofSize(1, Ingredient.ofStacks(Items.IRON_INGOT.getDefaultStack())), DefaultedList.ofSize(1, Items.IRON_BLOCK.getDefaultStack()), RecipeContent.PULVERIZER, FluidStack.empty(), FluidStack.empty());
    
    public OritechRecipe(int time, List<Ingredient> inputs, List<ItemStack> results, OritechRecipeType type, @Nullable FluidStack fluidInput, @Nullable FluidStack fluidOutput) {
        this.type = type;
        this.results = results;
        this.inputs = inputs;
        this.time = time;
        if (fluidInput == null) fluidInput = FluidStack.empty();
        this.fluidInput = fluidInput;
        if (fluidOutput == null) fluidOutput = FluidStack.empty();
        this.fluidOutput = fluidOutput;
    }
    
    public OritechRecipe(int time, List<Ingredient> inputs, List<ItemStack> results, OritechRecipeType type, Fluid inVariant, long inAmount, Fluid outVariant, long outAmount) {
        this(time, inputs, results, type, FluidStack.create(inVariant, inAmount / fluidDivider), FluidStack.create(outVariant, outAmount / fluidDivider));
    }
    
    @Override
    public boolean matches(RecipeInput input, World world) {
        
        if (world.isClient) return false;
        
        if (inputs.size() > 1) {
            return complexMatch(input);
        }
        
        if (input.getSize() < inputs.size()) return false;
        
        var ingredients = getInputs();
        for (int i = 0; i < ingredients.size(); i++) {
            var entry = ingredients.get(i);
            if (!entry.test(input.getStackInSlot(i))) {
                return false;
            }
        }
        
        return true;
    }
    
    private boolean complexMatch(RecipeInput input) {
        
        if (!(input instanceof SimpleCraftingInventory simpleInventory)) return false;
        
        // Input does not need to be in the correct slots / split into different slots.
        // We just check if we can remove all ingredients from the inventory, and fail is any input is not able to be removed.
        var copiedInv = simpleInventory.heldStacks.stream().map(ItemStack::copy).toArray(ItemStack[]::new);
        
        for (var ingredient : getInputs()) {
            
            var found = false;
            
            for (var heldStack : copiedInv) {
                if (ingredient.test(heldStack)) {
                    heldStack.decrement(1);
                    found = true;
                    break;
                }
            }
            
            if (!found) return false;
        }
        
        return true;
    }
    
    @Override
    public ItemStack craft(RecipeInput input, RegistryWrapper.WrapperLookup lookup) {
        return null;
    }
    
    @Override
    public boolean fits(int width, int height) {
        return true;
    }
    
    @Override
    public ItemStack getResult(RegistryWrapper.WrapperLookup registriesLookup) {
        return ItemStack.EMPTY;
    }

    @Override
    public RecipeSerializer<?> getSerializer() {
        return type;
    }
    
    @Override
    public boolean isIgnoredInRecipeBook() {
        return true;
    }
    
    @Override
    public RecipeType<?> getType() {
        return type;
    }
    
    @Override
    public String toString() {
        return "OritechRecipe{" +
                 "type=" + type +
                 ", inputs=" + inputs +
                 ", results=" + results +
                 ", fluidInput=" + fluidInput +
                 ", fluidOutput=" + fluidOutput +
                 ", time=" + time +
                 '}';
    }
    
    public int getTime() {
        return time;
    }

    public List<Ingredient> getInputs() {
        return inputs;
    }

    // do not use this one, use getInputs if applicable to avoid unnecessary copy
    @Override
    public DefaultedList<Ingredient> getIngredients() {
        return DefaultedList.copyOf(Ingredient.EMPTY, inputs.toArray(Ingredient[]::new));
    }

    public List<ItemStack> getResults() {
        return results;
    }

    public OritechRecipeType getOriType() {
        return type;
    }
    
    public @Nullable FluidStack getFluidInput() {
        return fluidInput;
    }
    
    public @Nullable FluidStack getFluidOutput() {
        return fluidOutput;
    }
}