RyanHub – file viewer
filename: common/src/main/java/rearth/oritech/api/recipe/util/MetalProcessingChainBuilder.java
branch: 1.21
back to repo
package rearth.oritech.api.recipe.util;

import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.Item;
import net.minecraft.item.ItemConvertible;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.registry.tag.TagKey;
import rearth.oritech.Oritech;
import rearth.oritech.api.recipe.AtomicForgeRecipeBuilder;
import rearth.oritech.api.recipe.CentrifugeFluidRecipeBuilder;
import rearth.oritech.api.recipe.CentrifugeRecipeBuilder;
import rearth.oritech.api.recipe.FoundryRecipeBuilder;
import rearth.oritech.api.recipe.GrinderRecipeBuilder;
import rearth.oritech.api.recipe.PulverizerRecipeBuilder;

import java.util.Arrays;
import java.util.List;

import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;

public class MetalProcessingChainBuilder {
    private String metalName;
    private String resourcePath = "";
    // ingredient should generally be used for recipe inputs and item for recipe output
    // wherever possible, use ConventionalItemTags (Fabric) or Tags.Items (Neoforge) for ingredients
    private Ingredient ore;
    private Ingredient rawOreIngredient;
    private Item rawOreItem;
    // should be a raw ore, secondary raw ore given when grinding ore blocks
    private Item rawOreByproduct;
    private Ingredient ingotIngredient;
    private Item ingotItem;
    private Ingredient nuggetIngredient;
    private Item nuggetItem;
    private Ingredient clumpIngredient;
    private Item clumpItem;
    private Item smallClumpItem;
    private Item dustItem;
    private Item smallDustItem;
    private Item centrifugeResult;
    private int centrifugeAmount;
    // usually a small dust (or nugget) given as a byproduct from the grinder or centrifuge
    private Item dustByproduct;
    private Item clumpByproduct;
    private int byproductAmount = 3;
    private Ingredient gemIngredient;
    private Item gemItem;
    private Ingredient gemCatalyst;
    private float timeMultiplier = 1f;
    // for compat use. no need to add vanilla processing for other mods' ores
    private boolean vanillaProcessing = false;

    private MetalProcessingChainBuilder(String metalName) {
        this.metalName = metalName;
    }

    public static MetalProcessingChainBuilder build(String metalName) {
        return new MetalProcessingChainBuilder(metalName);
    }

    public MetalProcessingChainBuilder resourcePath(String resourcePath) {
        this.resourcePath = resourcePath;
        return this;
    }

    public MetalProcessingChainBuilder ore(Ingredient ore) {
        this.ore = ore;
        return this;
    }

    public MetalProcessingChainBuilder ore(TagKey<Item> oreTag) {
        return ore(Ingredient.fromTag(oreTag));
    }

    public MetalProcessingChainBuilder ore(ItemConvertible ore) {
        return ore(Ingredient.ofItems(ore));
    }

    public MetalProcessingChainBuilder rawOre(Ingredient rawOreIngredient, Item rawOre) {
        this.rawOreIngredient = rawOreIngredient;
        this.rawOreItem = rawOre;
        return this;
    }

    public MetalProcessingChainBuilder rawOre(TagKey<Item> rawOreTag, Item rawOre) {
        return rawOre(Ingredient.fromTag(rawOreTag), rawOre);
    }

    public MetalProcessingChainBuilder rawOre(Item rawOre) {
        return rawOre(Ingredient.ofItems(rawOre), rawOre);
    }

    public MetalProcessingChainBuilder rawOreByproduct(Item byproduct) {
        this.rawOreByproduct = byproduct;
        return this;
    }

    public MetalProcessingChainBuilder ingot(Ingredient ingotIngredient, Item ingot) {
        this.ingotIngredient = ingotIngredient;
        this.ingotItem = ingot;
        return this;
    }

    public MetalProcessingChainBuilder ingot(TagKey<Item> ingotTag, Item ingot) {
        return ingot(Ingredient.fromTag(ingotTag), ingot);
    }

    public MetalProcessingChainBuilder ingot(Item ingot) {
        return ingot(Ingredient.ofItems(ingot), ingot);
    }

    public MetalProcessingChainBuilder nugget(Ingredient nuggetIngredient, Item nugget) {
        this.nuggetIngredient = nuggetIngredient;
        this.nuggetItem = nugget;
        return this;
    }

    public MetalProcessingChainBuilder nugget(TagKey<Item> nuggetTag, Item nugget) {
        return nugget(Ingredient.fromTag(nuggetTag), nugget);
    }

    public MetalProcessingChainBuilder nugget(Item nugget) {
        return nugget(Ingredient.ofItems(nugget), nugget);
    }

    public MetalProcessingChainBuilder clump(Ingredient clumpIngredient, Item clump) {
        this.clumpIngredient = clumpIngredient;
        this.clumpItem = clump;
        return this;
    }

    public MetalProcessingChainBuilder clump(TagKey<Item> clumpTag, Item clump) {
        return clump(Ingredient.fromTag(clumpTag), clump);
    }

    public MetalProcessingChainBuilder clump(Item clump) {
        return clump(Ingredient.ofItems(clump), clump);
    }

    public MetalProcessingChainBuilder smallClump(Item smallClump) {
        this.smallClumpItem = smallClump;
        return this;
    }

    public MetalProcessingChainBuilder centrifugeResult(Item result, int amount) {
        this.centrifugeResult = result;
        this.centrifugeAmount = amount;
        return this;
    }

    public MetalProcessingChainBuilder centrifugeResult(Item result) {
        return centrifugeResult(result, 1);
    }

    public MetalProcessingChainBuilder clumpByproduct(Item byproduct) {
        this.clumpByproduct = byproduct;
        return this;
    }

    public MetalProcessingChainBuilder dustByproduct(Item byproduct) {
        this.dustByproduct = byproduct;
        return this;
    }
    
    public MetalProcessingChainBuilder byproductAmount(int amount) {
        this.byproductAmount = amount;
        return this;
    }

    public MetalProcessingChainBuilder dust(Item dust) {
        this.dustItem = dust;
        return this;
    }

    public MetalProcessingChainBuilder smallDust(Item smallDust) {
        this.smallDustItem = smallDust;
        return this;
    }

    public MetalProcessingChainBuilder gem(Ingredient gemIngredient, Item gem) {
        this.gemIngredient = gemIngredient;
        this.gemItem = gem;
        return this;
    }

    public MetalProcessingChainBuilder gem(TagKey<Item> gemTag, Item gem) {
        return gem(Ingredient.fromTag(gemTag), gem);
    }

    public MetalProcessingChainBuilder gem(Item gem) {
        return gem(Ingredient.ofItems(gem), gem);
    }

    public MetalProcessingChainBuilder gemCatalyst(Ingredient gemCatalyst) {
        this.gemCatalyst = gemCatalyst;
        return this;
    }

    public MetalProcessingChainBuilder gemCatalyst(TagKey<Item> gemCatalyst) {
        return gemCatalyst(Ingredient.fromTag(gemCatalyst));
    }

    public MetalProcessingChainBuilder gemCatalyst(Item gemCatalyst) {
        return gemCatalyst(Ingredient.ofItems(gemCatalyst));
    }

    public MetalProcessingChainBuilder timeMultiplier(float timeMultiplier) {
        this.timeMultiplier = timeMultiplier;
        return this;
    }

    public MetalProcessingChainBuilder vanillaProcessing() {
        this.vanillaProcessing = true;
        return this;
    }

    private void validate(String path) throws IllegalStateException {
        if (ore == null)
            throw new IllegalStateException("ore is required for metal processing chain " + path);
        if (rawOreItem == null)
            throw new IllegalStateException("raw ore is required for metal processing chain " + path);
        if ((dustItem != null || vanillaProcessing == true) && ingotItem == null)
            throw new IllegalStateException("ingot is required if dust is provided or vanilla processing is required for metal processing chain " + path);
        if ((smallClumpItem != null || smallDustItem != null) && nuggetItem == null)
            throw new IllegalStateException("nugget item is required if small clump or small dust are provided for metal processing chain " + path);
        if (centrifugeResult != null && centrifugeAmount < 1)
            throw new IllegalStateException("centrifugeAmount must be >= 1 if centrifugeOutput is provided for metal processing chain " + path);
        if (clumpItem != null && (centrifugeResult == null && gemItem == null))
            throw new IllegalStateException("either centrifugeResult or gemItem is required if clump is provided for metal processing chain " + path);
    }

    public void export(RecipeExporter exporter) {
        validate(resourcePath + "ore/" + metalName);

        // ore block -> raw ores
        PulverizerRecipeBuilder.build().input(ore).result(rawOreItem, 2).timeMultiplier(timeMultiplier).export(exporter, resourcePath + "ore/" + metalName);
        var grinderOreRecipe = GrinderRecipeBuilder.build().input(ore).result(rawOreItem, 2).time(140).timeMultiplier(timeMultiplier);
        if (rawOreByproduct != null)
            grinderOreRecipe.result(rawOreByproduct);
        grinderOreRecipe.export(exporter, resourcePath + "ore/" + metalName);

        // raw ores -> dusts in pulverizer
        if (dustItem != null) {
            PulverizerRecipeBuilder.build()
                .input(rawOreIngredient)
                .result(dustItem)
                .result(firstNonNullOptional(smallDustItem, nuggetItem), 3)
                .timeMultiplier(timeMultiplier)
                .export(exporter, resourcePath + "raw/" + metalName);
        }

        // raw ores -> clumps (falling back to dusts) in grinder
        if (clumpItem != null || dustItem != null) {
            GrinderRecipeBuilder.build()
                .input(rawOreIngredient)
                .result(firstNonNull(clumpItem, dustItem))
                .result(firstNonNullOptional(smallClumpItem, smallDustItem, nuggetItem), 3)
                .result(Optional.fromNullable(clumpByproduct), byproductAmount)
                .time(140).timeMultiplier(timeMultiplier)
                .export(exporter, resourcePath + "raw/" + metalName);
        }

        // clump processing into gems in centrifuge
        if (clumpItem != null) {
            CentrifugeRecipeBuilder.build()
                .input(clumpIngredient)
                .result(firstNonNull(centrifugeResult, gemItem))
                .result(Optional.fromNullable(dustByproduct), byproductAmount)
                .timeMultiplier(timeMultiplier)
                .export(exporter, resourcePath + "clump/" + metalName);
            CentrifugeFluidRecipeBuilder.build()
                .input(clumpIngredient)
                .fluidInput(Fluids.WATER)
                .result(firstNonNull(centrifugeResult, gemItem), 2)
                .time(300).timeMultiplier(timeMultiplier)
                .export(exporter, resourcePath + "clump/" + metalName);
        }

        // gems to dust (doubling)
        if (gemIngredient != null) {
            // atomic forge: 1 gem -> 2 ingots
            AtomicForgeRecipeBuilder.build().input(gemIngredient).input(gemCatalyst).input(gemCatalyst).result(dustItem, 2).time(20).export(exporter, resourcePath + "dust/" + metalName);

            // foundry alternative: 2 gems -> 3 ingots
            FoundryRecipeBuilder.build().input(gemIngredient).input(gemIngredient).result(ingotItem, 3).export(exporter, resourcePath + "gem/" + metalName);
        }

        // ingots/nuggets to dust
        if (dustItem != null)
            RecipeHelpers.addDustRecipe(exporter, ingotIngredient, dustItem, resourcePath + "dust/" + metalName);
        if (smallDustItem != null)
            RecipeHelpers.addDustRecipe(exporter, nuggetIngredient, smallDustItem, resourcePath + "smalldust/" + metalName);
        
        // smelting/compacting
        // Using item instead of ingredient for recipe inputs, as that's what the offerSmelting/offerBlasting methods accept
        // This should be fine, because any mod that adds ores, dusts, etc. will provide their own smelting/blasting recipes
        if (vanillaProcessing) {
            if (dustItem != null) {
                RecipeProvider.offerSmelting(exporter, List.of(dustItem), RecipeCategory.MISC, ingotItem, 1f, 200, Oritech.MOD_ID);
                RecipeProvider.offerBlasting(exporter, List.of(dustItem), RecipeCategory.MISC, ingotItem, 1f, 100, Oritech.MOD_ID);
                RecipeProvider.offerCompactingRecipe(exporter, RecipeCategory.MISC, dustItem, smallDustItem);
            }
            if (smallDustItem != null) {
                RecipeProvider.offerSmelting(exporter, List.of(smallDustItem), RecipeCategory.MISC, nuggetItem, 0.5f, 50, Oritech.MOD_ID);
                RecipeProvider.offerBlasting(exporter, List.of(smallDustItem), RecipeCategory.MISC, nuggetItem, 0.5f, 25, Oritech.MOD_ID);
            }
            if (gemItem != null) {
                RecipeProvider.offerSmelting(exporter, List.of(gemItem), RecipeCategory.MISC, ingotItem, 1f, 200, Oritech.MOD_ID);
                RecipeProvider.offerBlasting(exporter, List.of(gemItem), RecipeCategory.MISC, ingotItem, 1f, 100, Oritech.MOD_ID);
            }
            if (clumpItem != null && smallClumpItem != null)
                RecipeProvider.offerCompactingRecipe(exporter, RecipeCategory.MISC, clumpItem, smallClumpItem);
            if (nuggetItem != null)
                RecipeProvider.offerCompactingRecipe(exporter, RecipeCategory.MISC, ingotItem, nuggetItem);
        }
    }

    private Item firstNonNull(Item... items) {
        return Iterables.find(Arrays.asList(items), Predicates.notNull());
    }

    private Optional<Item> firstNonNullOptional(Item... items) {
        return Iterables.tryFind(Arrays.asList(items), Predicates.notNull());
    }
}