diff --git a/build.gradle b/build.gradle index 67216cf33..2bad2c688 100644 --- a/build.gradle +++ b/build.gradle @@ -116,6 +116,14 @@ dependencies { deobfProvided 'curse.maven:blood-magic-224791:2822288' } + if (project.debug_extended_crafting.toBoolean()) { + deobfCompile 'curse.maven:cucumber-272335:2645867' + deobfCompile 'curse.maven:extended-crafting-nomifactory-edition-398267:3613140' + } else { + deobfProvided 'curse.maven:cucumber-272335:2645867' + deobfProvided 'curse.maven:extended-crafting-nomifactory-edition-398267:3613140' + } + if (project.debug_thermal.toBoolean()) { deobfCompile 'curse.maven:cofh_core-69162:2920433' deobfCompile 'curse.maven:cofh_world-271384:2920434' diff --git a/examples/extremecrafting.groovy b/examples/extremecrafting.groovy new file mode 100644 index 000000000..98fa4a976 --- /dev/null +++ b/examples/extremecrafting.groovy @@ -0,0 +1,145 @@ + +// Combination crafting + +// there are no combination recipes by default, and so none can be removed +//mods.extendedcrafting.combination.removeAll() +//mods.extendedcrafting.combination.removeByInput(item('minecraft:pumpkin')) +//mods.extendedcrafting.combination.removeByOutput(item('minecraft:gold_ingot')) + +mods.extendedcrafting.combination.recipeBuilder() + .cost(100) + .perTick(100) + .output(item('minecraft:diamond') * 2) + .input(item('minecraft:pumpkin')) + .pedestals(item('minecraft:pumpkin') * 8) + .register() + +mods.extendedcrafting.combinationcrafting.recipeBuilder() + .cost(10000) + .output(item('minecraft:gold_ingot') * 2) + .input(item('minecraft:pumpkin')) + .pedestals(item('minecraft:pumpkin'), item('minecraft:clay'), item('minecraft:clay'), item('minecraft:pumpkin')) + .register() + +// Compression crafting + +//mods.extendedcrafting.combination.removeAll() +//mods.extendedcrafting.combination.removeByInput(item('minecraft:gold_ingot')) +//mods.extendedcrafting.combination.removeByCatalyst(item('extendedcrafting:material:11')) +//mods.extendedcrafting.combination.removeByOutput(item('extendedcrafting:singularity:6')) + +mods.extendedcrafting.compression.recipeBuilder() + .input(item('minecraft:clay') * 10) + .output(item('minecraft:diamond') * 2) + .powerCost(1000) + .register() + +mods.extendedcrafting.compressioncrafting.recipeBuilder() + .input(item('minecraft:clay')) + .inputCount(100) + .output(item('minecraft:gold_ingot') * 7) + .catalyst(item('minecraft:diamond')) + .consumeCatalyst(true) + .powerCost(10000) + .powerRate(1000) + .register() + +// Ender crafting + +//mods.extendedcrafting.endercrafting.removeByOutput(item('extendedcrafting:material:40')) + +mods.extendedcrafting.endercrafting.shapelessBuilder() + .output(item('minecraft:clay') * 8) + .input(item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone')) + .register() + +mods.extendedcrafting.endercrafting.shapedBuilder() + .output(item('minecraft:stone')) + .matrix('BXX', + 'X B') + .key('B', item('minecraft:stone')) + .key('X', item('minecraft:gold_ingot')) + .time(1) + .mirrored() + .register() + +mods.extendedcrafting.endercrafting.shapelessBuilder() + .output(item('minecraft:clay') * 32) + .input(item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond')) + .time(1) + .register() + +mods.extendedcrafting.endercrafting.shapedBuilder() + .output(item('minecraft:diamond') * 32) + .matrix([[item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')]]) + .time(1) + .register() + +// Table crafting + +//mods.extendedcrafting.tablecrafting.removeByOutput(item('extendedcrafting:singularity_ultimate')) + +mods.extendedcrafting.tablecrafting.shapedBuilder() + .output(item('minecraft:stone') * 64) + .matrix( + 'DLLLLLDDD', + ' DNIGIND', + 'DDDNIGIND', + ' DLLLLLD') + .key('D', item('minecraft:diamond')) + .key('L', item('minecraft:redstone')) + .key('N', item('minecraft:stone')) + .key('I', item('minecraft:iron_ingot')) + .key('G', item('minecraft:gold_ingot')) + .tierUltimate() + .register() + +mods.extendedcrafting.tablecrafting.shapedBuilder() + .tierAdvanced() + .output(item('minecraft:stone') * 8) + .matrix('BXX') + .mirrored() + .key('B', item('minecraft:stone')) + .key('X', item('minecraft:gold_ingot')) + .register() + +mods.extendedcrafting.tablecrafting.shapedBuilder() + .tierAny() + .output(item('minecraft:diamond')) + .matrix('BXXXBX') + .mirrored() + .key('B', item('minecraft:stone')) + .key('X', item('minecraft:gold_ingot')) + .register() + +mods.extendedcrafting.tablecrafting.shapedBuilder() + .matrix([[item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')]]) + .output(item('minecraft:gold_ingot') * 64) + .tier(4) // while we only have a 7x7 of gold ingots in the recipe, specifically requiring tier 4 (ultimate) locks us in + .register() + +mods.extendedcrafting.tablecrafting.shapedBuilder() // can be crafted in any tier of table + .matrix([[item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),], + [item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),]]) + .output(item('minecraft:gold_ingot') * 64) + .register() + +mods.extendedcrafting.tablecrafting.shapelessBuilder() + .output(item('minecraft:stone') * 64) + .input(item('minecraft:stone'), // 26 stone = can be crafted in either elite or ultimate + item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'), + item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'), + item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'), + item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'), + item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone')) + .register() + diff --git a/gradle.properties b/gradle.properties index 83206caa5..03450aba7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,4 +32,5 @@ debug_ie = false debug_enderio = false debug_astral = false debug_blood_magic = false -debug_tinkers = false \ No newline at end of file +debug_tinkers = false +debug_extended_crafting = true diff --git a/src/main/java/com/cleanroommc/groovyscript/api/IIngredient.java b/src/main/java/com/cleanroommc/groovyscript/api/IIngredient.java index eb7684f20..550d116b1 100644 --- a/src/main/java/com/cleanroommc/groovyscript/api/IIngredient.java +++ b/src/main/java/com/cleanroommc/groovyscript/api/IIngredient.java @@ -35,6 +35,13 @@ default IIngredient or(IIngredient ingredient) { return orIngredient; } + @Override + default IIngredient withAmount(int amount) { + IIngredient iIngredientStack = exactCopy(); + iIngredientStack.setAmount(amount); + return iIngredientStack; + } + /** * An empty ingredient with stack size 0, that matches empty item stacks */ diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java index c071596af..06c50069c 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java @@ -6,6 +6,7 @@ import com.cleanroommc.groovyscript.compat.mods.bloodmagic.BloodMagic; import com.cleanroommc.groovyscript.compat.mods.draconicevolution.DraconicEvolution; import com.cleanroommc.groovyscript.compat.mods.enderio.EnderIO; +import com.cleanroommc.groovyscript.compat.mods.extendedcrafting.ExtendedCrafting; import com.cleanroommc.groovyscript.compat.mods.ic2.IC2; import com.cleanroommc.groovyscript.compat.mods.immersiveengineering.ImmersiveEngineering; import com.cleanroommc.groovyscript.compat.mods.jei.JustEnoughItems; @@ -45,6 +46,7 @@ public class ModSupport implements IDynamicGroovyProperty { public static final Container BLOOD_MAGIC = new Container<>("bloodmagic", "Blood Magic: Alchemical Wizardry", BloodMagic::new, "bm"); public static final Container IMMERSIVE_ENGINEERING = new Container<>("immersiveengineering", "Immersive Engineering", ImmersiveEngineering::new, "ie"); public static final Container INDUSTRIALCRAFT = new Container<>("ic2", "Industrial Craft 2", IC2::new, "industrialcraft"); + public static final Container EXTENDED_CRAFTING = new Container<>("extendedcrafting", "Extended Crafting", ExtendedCrafting::new); public static Collection> getAllContainers() { return new ObjectOpenHashSet<>(containers.values()); diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/AlchemyTable.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/AlchemyTable.java index 6b7f0b61a..b00141729 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/AlchemyTable.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/AlchemyTable.java @@ -17,7 +17,6 @@ import org.jetbrains.annotations.Nullable; import java.util.Collections; -import java.util.stream.Collectors; public class AlchemyTable extends VirtualizedRegistry { @@ -163,9 +162,7 @@ public void validate(GroovyLog.Msg msg) { @Override public @Nullable RecipeAlchemyTable register() { if (!validate()) return null; - NonNullList inputs = NonNullList.create(); - inputs.addAll(input.stream().map(IIngredient::toMcIngredient).collect(Collectors.toList())); - RecipeAlchemyTable recipe = ModSupport.BLOOD_MAGIC.get().alchemyTable.add(inputs, output.get(0), syphon, ticks, minimumTier); + RecipeAlchemyTable recipe = ModSupport.BLOOD_MAGIC.get().alchemyTable.add(IngredientHelper.toIngredientNonNullList(input), output.get(0), syphon, ticks, minimumTier); return recipe; } } diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/BloodAltar.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/BloodAltar.java index db91f571b..0110b2765 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/BloodAltar.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/BloodAltar.java @@ -14,11 +14,8 @@ import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; -import net.minecraft.util.NonNullList; import org.jetbrains.annotations.Nullable; -import java.util.stream.Collectors; - public class BloodAltar extends VirtualizedRegistry { public BloodAltar() { @@ -150,8 +147,6 @@ public void validate(GroovyLog.Msg msg) { @Override public @Nullable RecipeBloodAltar register() { if (!validate()) return null; - NonNullList inputs = NonNullList.create(); - inputs.addAll(input.stream().map(IIngredient::toMcIngredient).collect(Collectors.toList())); RecipeBloodAltar recipe = ModSupport.BLOOD_MAGIC.get().bloodAltar.add(input.get(0).toMcIngredient(), output.get(0), minimumTier, syphon, consumeRate, drainRate); return recipe; } diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/TartaricForge.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/TartaricForge.java index 8ca802c3f..fcb1b045d 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/TartaricForge.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/bloodmagic/TartaricForge.java @@ -17,7 +17,6 @@ import org.jetbrains.annotations.Nullable; import java.util.Collections; -import java.util.stream.Collectors; public class TartaricForge extends VirtualizedRegistry { @@ -159,9 +158,7 @@ public void validate(GroovyLog.Msg msg) { @Override public @Nullable RecipeTartaricForge register() { if (!validate()) return null; - NonNullList inputs = NonNullList.create(); - inputs.addAll(input.stream().map(IIngredient::toMcIngredient).collect(Collectors.toList())); - RecipeTartaricForge recipe = ModSupport.BLOOD_MAGIC.get().tartaricForge.add(inputs, output.get(0), minimumSouls, soulDrain); + RecipeTartaricForge recipe = ModSupport.BLOOD_MAGIC.get().tartaricForge.add(IngredientHelper.toIngredientNonNullList(input), output.get(0), minimumSouls, soulDrain); return recipe; } } diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/CombinationCrafting.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/CombinationCrafting.java new file mode 100644 index 000000000..f1622fa04 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/CombinationCrafting.java @@ -0,0 +1,166 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.config.ModConfig; +import com.blakebr0.extendedcrafting.crafting.CombinationRecipe; +import com.blakebr0.extendedcrafting.crafting.CombinationRecipeManager; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.util.NonNullList; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public class CombinationCrafting extends VirtualizedRegistry { + + public CombinationCrafting() { + super(VirtualizedRegistry.generateAliases("Combination")); + } + + @Override + public void onReload() { + removeScripted().forEach(recipe -> CombinationRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)); + CombinationRecipeManager.getInstance().getRecipes().addAll(restoreFromBackup()); + } + + public CombinationRecipe add(CombinationRecipe recipe) { + if (recipe != null) { + addScripted(recipe); + CombinationRecipeManager.getInstance().getRecipes().add(recipe); + } + return recipe; + } + + public CombinationRecipe add(ItemStack output, long cost, Ingredient input, NonNullList pedestals) { + return add(output, cost, ModConfig.confCraftingCoreRFRate, input, pedestals); + } + + public CombinationRecipe add(ItemStack output, long cost, int perTick, Ingredient input, NonNullList pedestals) { + return add(new CombinationRecipe(output, cost, perTick, input, pedestals)); + } + + public boolean removeByOutput(ItemStack output) { + return CombinationRecipeManager.getInstance().getRecipes().removeIf(r -> { + if (r.getOutput().equals(output)) { + addBackup(r); + return true; + } + return false; + }); + } + + public boolean removeByInput(ItemStack input) { + return CombinationRecipeManager.getInstance().getRecipes().removeIf(r -> { + if (r.getInput().equals(input)) { + addBackup(r); + return true; + } + return false; + }); + } + + public boolean removeByInput(IIngredient input) { + return removeByInput(IngredientHelper.toItemStack(input)); + } + + public boolean remove(CombinationRecipe recipe) { + if (CombinationRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)) { + addBackup(recipe); + return true; + } + return false; + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(CombinationRecipeManager.getInstance().getRecipes()).setRemover(this::remove); + } + + public void removeAll() { + CombinationRecipeManager.getInstance().getRecipes().forEach(this::addBackup); + CombinationRecipeManager.getInstance().getRecipes().clear(); + } + + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private final NonNullList pedestals = NonNullList.create(); + private long cost; + private int perTick = ModConfig.confCraftingCoreRFRate; + + public RecipeBuilder cost(long cost) { + this.cost = cost; + return this; + } + + public RecipeBuilder totalCost(long totalCost) { + return cost(totalCost); + } + + public RecipeBuilder perTick(int perTick) { + this.perTick = perTick; + return this; + } + + public RecipeBuilder costPerTick(int costPerTick) { + return perTick(costPerTick); + } + + public RecipeBuilder input(IIngredient ingredient) { + this.input.add(ingredient.withAmount(1)); + return this; + } + + public RecipeBuilder pedestals(IIngredient pedestals) { + for (int x = 0; x < pedestals.getAmount(); x++) { + this.pedestals.add(pedestals.withAmount(1)); + } + return this; + } + + public RecipeBuilder pedestals(Collection pedestals) { + for (IIngredient x : pedestals) { + this.pedestals(x); + } + return this; + } + + public RecipeBuilder pedestals(IIngredient... pedestals) { + for (IIngredient x : pedestals) { + this.pedestals(x); + } + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Extended Crafting Combination Crafting recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 1, 1, 1, 1); + validateFluids(msg); + msg.add(cost < 0, () -> "cost must not be negative"); + msg.add(perTick < 0, () -> "per tick must not be negative"); + } + + @Nullable + @Override + public CombinationRecipe register() { + if (!validate()) return null; + CombinationRecipe recipe = new CombinationRecipe(output.get(0), cost, perTick, input.get(0).toMcIngredient(), IngredientHelper.toIngredientNonNullList(pedestals)); + ModSupport.EXTENDED_CRAFTING.get().combinationCrafting.add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/CompressionCrafting.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/CompressionCrafting.java new file mode 100644 index 000000000..c21793a97 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/CompressionCrafting.java @@ -0,0 +1,161 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.config.ModConfig; +import com.blakebr0.extendedcrafting.crafting.CompressorRecipe; +import com.blakebr0.extendedcrafting.crafting.CompressorRecipeManager; +import com.blakebr0.extendedcrafting.item.ItemSingularity; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +public class CompressionCrafting extends VirtualizedRegistry { + + public CompressionCrafting() { + super(VirtualizedRegistry.generateAliases("Compression")); + } + + @Override + public void onReload() { + removeScripted().forEach(recipe -> CompressorRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)); + CompressorRecipeManager.getInstance().getRecipes().addAll(restoreFromBackup()); + } + + public CompressorRecipe add(CompressorRecipe recipe) { + if (recipe != null) { + addScripted(recipe); + CompressorRecipeManager.getInstance().getRecipes().add(recipe); + } + return recipe; + } + + public CompressorRecipe add(ItemStack output, IIngredient input, int inputCount, IIngredient catalyst, boolean consumeCatalyst, int powerCost) { + return add(output, input, inputCount, catalyst, consumeCatalyst, powerCost, ModConfig.confCompressorRFRate); + } + + public CompressorRecipe add(ItemStack output, IIngredient input, int inputCount, IIngredient catalyst, boolean consumeCatalyst, int powerCost, int powerRate) { + return add(new CompressorRecipe(output, input.toMcIngredient(), inputCount, catalyst.toMcIngredient(), consumeCatalyst, powerCost, powerRate)); + } + + public boolean removeByOutput(ItemStack output) { + return CompressorRecipeManager.getInstance().getRecipes().removeIf(r -> { + if (r.getOutput().equals(output)) { + addBackup(r); + return true; + } + return false; + }); + } + + public boolean removeByCatalyst(IIngredient catalyst) { + return CompressorRecipeManager.getInstance().getRecipes().removeIf(r -> { + if (r.getCatalyst().equals(catalyst.toMcIngredient())) { + addBackup(r); + return true; + } + return false; + }); + } + + public boolean removeByInput(IIngredient input) { + return CompressorRecipeManager.getInstance().getRecipes().removeIf(r -> { + if (r.getInput().equals(input.toMcIngredient())) { + addBackup(r); + return true; + } + return false; + }); + } + + public boolean remove(CompressorRecipe recipe) { + if (CompressorRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)) { + addBackup(recipe); + return true; + } + return false; + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(CompressorRecipeManager.getInstance().getRecipes()).setRemover(this::remove); + } + + public void removeAll() { + CompressorRecipeManager.getInstance().getRecipes().forEach(this::addBackup); + CompressorRecipeManager.getInstance().getRecipes().clear(); + } + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private IIngredient input; + private int inputCount; + private IIngredient catalyst = IngredientHelper.toIIngredient(ItemSingularity.getCatalystStack()); + private boolean consumeCatalyst = false; + private int powerCost; + private int powerRate = ModConfig.confCompressorRFRate; + + public RecipeBuilder input(IIngredient input) { + this.input = input.withAmount(1); + this.inputCount = input.getAmount(); + return this; + } + + public RecipeBuilder inputCount(int inputCount) { + this.inputCount = inputCount; + return this; + } + + public RecipeBuilder catalyst(IIngredient catalyst) { + this.catalyst = catalyst.withAmount(1); + return this; + } + + public RecipeBuilder consumeCatalyst(boolean consumeCatalyst) { + this.consumeCatalyst = consumeCatalyst; + return this; + } + + public RecipeBuilder powerCost(int powerCost) { + this.powerCost = powerCost; + return this; + } + + public RecipeBuilder powerRate(int powerRate) { + this.powerRate = powerRate; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Extended Crafting Compression Crafting recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg); + + msg.add(IngredientHelper.isEmpty(input), () -> "input must not be empty"); + msg.add(output.size() != 1, () -> getRequiredString(1, 1, "item output") + ", but found " + output.size()); + msg.add(IngredientHelper.isEmpty(catalyst), "catalyst must not be empty"); + msg.add(powerCost < 0, "power cost must not be negative"); + msg.add(powerRate < 0, "power rate must not be negative"); + } + + @Nullable + @Override + public CompressorRecipe register() { + if (!validate()) return null; + CompressorRecipe recipe = new CompressorRecipe(output.get(0), input.toMcIngredient(), inputCount, catalyst.toMcIngredient(), consumeCatalyst, powerCost, powerRate); + ModSupport.EXTENDED_CRAFTING.get().compressionCrafting.add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/EnderCrafting.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/EnderCrafting.java new file mode 100644 index 000000000..163bb2885 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/EnderCrafting.java @@ -0,0 +1,91 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.config.ModConfig; +import com.blakebr0.extendedcrafting.crafting.endercrafter.EnderCrafterRecipeManager; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; + +import java.util.List; + +public class EnderCrafting extends VirtualizedRegistry { + + public EnderCrafting() { + super(); + } + + public EnderRecipeBuilder.Shaped shapedBuilder() { + return new EnderRecipeBuilder.Shaped(); + } + + public EnderRecipeBuilder.Shapeless shapelessBuilder() { + return new EnderRecipeBuilder.Shapeless(); + } + + @Override + public void onReload() { + removeScripted().forEach(recipe -> EnderCrafterRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)); + EnderCrafterRecipeManager.getInstance().getRecipes().addAll(restoreFromBackup()); + } + + public IRecipe addShaped(ItemStack output, List> input) { + return addShaped(ModConfig.confEnderTimeRequired, output, input); + } + + public IRecipe addShaped(int time, ItemStack output, List> input) { + return shapedBuilder() + .matrix(input) + .time(time) + .output(output) + .register(); + } + + public IRecipe addShapeless(ItemStack output, List> input) { + return addShaped(ModConfig.confEnderTimeRequired, output, input); + } + + public IRecipe addShapeless(int time, ItemStack output, List input) { + return shapelessBuilder() + .input(input) + .time(time) + .output(output) + .register(); + } + + public IRecipe add(IRecipe recipe) { + if (recipe != null) { + addScripted(recipe); + EnderCrafterRecipeManager.getInstance().getRecipes().add(recipe); + } + return recipe; + } + + public boolean removeByOutput(ItemStack stack) { + return EnderCrafterRecipeManager.getInstance().getRecipes().removeIf(recipe -> { + if (recipe != null && recipe.getRecipeOutput().isItemEqual(stack)) { + addBackup(recipe); + return true; + } + return false; + }); + } + + public boolean remove(IRecipe recipe) { + if (EnderCrafterRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)) { + addBackup(recipe); + return true; + } + return false; + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(EnderCrafterRecipeManager.getInstance().getRecipes()).setRemover(this::remove); + } + + public void removeAll() { + EnderCrafterRecipeManager.getInstance().getRecipes().forEach(this::addBackup); + EnderCrafterRecipeManager.getInstance().getRecipes().clear(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/EnderRecipeBuilder.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/EnderRecipeBuilder.java new file mode 100644 index 000000000..37ff1d8b5 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/EnderRecipeBuilder.java @@ -0,0 +1,182 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.config.ModConfig; +import com.blakebr0.extendedcrafting.crafting.table.TableRecipeBase; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.compat.vanilla.CraftingRecipeBuilder; +import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; +import net.minecraft.item.crafting.IRecipe; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class EnderRecipeBuilder extends CraftingRecipeBuilder { + + protected int time = ModConfig.confEnderTimeRequired; + + public EnderRecipeBuilder() { + super(3, 3); + } + + public EnderRecipeBuilder time(int time) { + this.time = time; + return this; + } + + public EnderRecipeBuilder seconds(int seconds) { + this.time = seconds; + return this; + } + + public EnderRecipeBuilder ticks(int ticks) { + this.time = ticks * 20; + return this; + } + + @Override + public IRecipe register() { + return null; + } + + public static class Shaped extends EnderRecipeBuilder { + + private final Char2ObjectOpenHashMap keyMap = new Char2ObjectOpenHashMap<>(); + private final List errors = new ArrayList<>(); + protected boolean mirrored = false; + private String[] keyBasedMatrix; + private List> ingredientMatrix; + + public Shaped() { + keyMap.put(' ', IIngredient.EMPTY); + } + + public EnderRecipeBuilder.Shaped mirrored(boolean mirrored) { + this.mirrored = mirrored; + return this; + } + + public EnderRecipeBuilder.Shaped mirrored() { + return mirrored(true); + } + + public EnderRecipeBuilder.Shaped matrix(String... matrix) { + this.keyBasedMatrix = matrix; + return this; + } + + public EnderRecipeBuilder.Shaped shape(String... matrix) { + this.keyBasedMatrix = matrix; + return this; + } + + public EnderRecipeBuilder.Shaped row(String row) { + if (this.keyBasedMatrix == null) { + this.keyBasedMatrix = new String[]{row}; + } else { + this.keyBasedMatrix = ArrayUtils.add(this.keyBasedMatrix, row); + } + return this; + } + + public EnderRecipeBuilder.Shaped key(String c, IIngredient ingredient) { + if (c == null || c.length() != 1) { + errors.add("key must be a single char, but found '" + c + "'"); + return this; + } + this.keyMap.put(c.charAt(0), ingredient); + return this; + } + + public EnderRecipeBuilder.Shaped matrix(List> matrix) { + this.ingredientMatrix = matrix; + return this; + } + + public EnderRecipeBuilder.Shaped shape(List> matrix) { + this.ingredientMatrix = matrix; + return this; + } + + public boolean validate() { + GroovyLog.Msg msg = GroovyLog.msg("Error adding shaped Ender Crafting recipe").error(); + msg.add((keyBasedMatrix == null || keyBasedMatrix.length == 0) && (ingredientMatrix == null || ingredientMatrix.isEmpty()), () -> "No matrix was defined"); + msg.add(keyBasedMatrix != null && ingredientMatrix != null, () -> "A key based matrix AND a ingredient based matrix was defined. This is not allowed!"); + msg.add(IngredientHelper.isEmpty(this.output), () -> "Output must not be empty"); + msg.add(time < 0, "time must be a nonnegative integer, yet it was {}", time); + return !msg.postIfNotEmpty(); + } + + @Override + public IRecipe register() { + if (!validate()) return null; + GroovyLog.Msg msg = GroovyLog.msg("Error adding shaped Ender Crafting recipe").error(); + + ShapedTableRecipe recipe = null; + if (keyBasedMatrix != null) { + recipe = validateShape(msg, errors, keyBasedMatrix, keyMap, ((width1, height1, ingredients) -> ShapedTableRecipe.make(1, output, ingredients, width1, height1, mirrored, recipeFunction, recipeAction))); + } else if (ingredientMatrix != null) { + recipe = validateShape(msg, ingredientMatrix, ((width1, height1, ingredients) -> ShapedTableRecipe.make(1, output.copy(), ingredients, width1, height1, mirrored, recipeFunction, recipeAction))); + } + if (recipe == null) { + msg.add("The recipe could not be parsed!"); + } else { + recipe.enderCrafterRecipeTimeRequired = this.time; + } + if (msg.postIfNotEmpty()) return null; + + ModSupport.EXTENDED_CRAFTING.get().enderCrafting.add(recipe); + return recipe; + } + } + + public static class Shapeless extends EnderRecipeBuilder { + + private final List ingredients = new ArrayList<>(); + + public EnderRecipeBuilder.Shapeless input(IIngredient ingredient) { + ingredients.add(ingredient); + return this; + } + + public EnderRecipeBuilder.Shapeless input(IIngredient... ingredients) { + if (ingredients != null) { + for (IIngredient ingredient : ingredients) { + input(ingredient); + } + } + return this; + } + + public EnderRecipeBuilder.Shapeless input(Collection ingredients) { + if (ingredients != null && !ingredients.isEmpty()) { + for (IIngredient ingredient : ingredients) { + input(ingredient); + } + } + return this; + } + + public boolean validate() { + GroovyLog.Msg msg = GroovyLog.msg("Error adding shapeless Ender Crafting recipe").error(); + msg.add(IngredientHelper.isEmpty(this.output), () -> "Output must not be empty"); + msg.add(ingredients.isEmpty(), () -> "inputs must not be empty"); + msg.add(ingredients.size() > width * height, () -> "maximum inputs are " + (width * height) + " but found " + ingredients.size()); + msg.add(time < 0, "time must be a nonnegative integer, yet it was {}", time); + return !msg.postIfNotEmpty(); + } + + @Override + public IRecipe register() { + if (!validate()) return null; + ShapelessTableRecipe recipe = ShapelessTableRecipe.make(1, output.copy(), ingredients, recipeFunction, recipeAction); + ((TableRecipeBase) recipe).enderCrafterRecipeTimeRequired = this.time; + ModSupport.EXTENDED_CRAFTING.get().enderCrafting.add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ExtendedCrafting.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ExtendedCrafting.java new file mode 100644 index 000000000..b03dc358b --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ExtendedCrafting.java @@ -0,0 +1,18 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer; + +public class ExtendedCrafting extends ModPropertyContainer { + + public final TableCrafting tableCrafting = new TableCrafting(); + public final EnderCrafting enderCrafting = new EnderCrafting(); + public final CombinationCrafting combinationCrafting = new CombinationCrafting(); + public final CompressionCrafting compressionCrafting = new CompressionCrafting(); + + public ExtendedCrafting() { + addRegistry(tableCrafting); + addRegistry(enderCrafting); + addRegistry(combinationCrafting); + addRegistry(compressionCrafting); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ShapedTableRecipe.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ShapedTableRecipe.java new file mode 100644 index 000000000..7b8f20efe --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ShapedTableRecipe.java @@ -0,0 +1,45 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.crafting.table.TableRecipeShaped; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.vanilla.ShapedCraftingRecipe; +import groovy.lang.Closure; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ShapedTableRecipe extends TableRecipeShaped { + + private final ShapedCraftingRecipe groovyRecipe; + + public static ShapedTableRecipe make(int tier, ItemStack output, List input, int width, int height, boolean mirrored, @Nullable Closure recipeFunction, @Nullable Closure recipeAction) { + ShapedCraftingRecipe recipe = new ShapedCraftingRecipe(output, input, width, height, mirrored, recipeFunction, recipeAction); + return new ShapedTableRecipe(tier, recipe); + } + + public ShapedTableRecipe(int tier, ShapedCraftingRecipe groovyRecipe) { + super(tier, groovyRecipe.getRecipeOutput(), groovyRecipe.getRecipeWidth(), groovyRecipe.getRecipeHeight(), groovyRecipe.getIngredients()); + this.groovyRecipe = groovyRecipe; + setMirrored(this.groovyRecipe.isMirrored()); + } + + @Override + public @NotNull ItemStack getCraftingResult(@NotNull InventoryCrafting inv) { + return this.groovyRecipe.getCraftingResult(inv); + } + + @Override + public boolean matches(@NotNull InventoryCrafting inv, @NotNull World world) { + return (this.tier == 0 || this.tier == getTierFromGridSize(inv)) && this.groovyRecipe.matches(inv, world); + } + + @Override + public @NotNull NonNullList getRemainingItems(@NotNull InventoryCrafting inv) { + return this.groovyRecipe.getRemainingItems(inv); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ShapelessTableRecipe.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ShapelessTableRecipe.java new file mode 100644 index 000000000..6c3a906fa --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/ShapelessTableRecipe.java @@ -0,0 +1,44 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.crafting.table.TableRecipeShapeless; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.vanilla.ShapelessCraftingRecipe; +import groovy.lang.Closure; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ShapelessTableRecipe extends TableRecipeShapeless { + + private final ShapelessCraftingRecipe groovyRecipe; + + public static ShapelessTableRecipe make(int tier, ItemStack output, List input, @Nullable Closure recipeFunction, @Nullable Closure recipeAction) { + ShapelessCraftingRecipe recipe = new ShapelessCraftingRecipe(output, input, recipeFunction, recipeAction); + return new ShapelessTableRecipe(tier, recipe); + } + + public ShapelessTableRecipe(int tier, ShapelessCraftingRecipe groovyRecipe) { + super(tier, groovyRecipe.getRecipeOutput(), groovyRecipe.getIngredients()); + this.groovyRecipe = groovyRecipe; + } + + @Override + public @NotNull ItemStack getCraftingResult(@NotNull InventoryCrafting inv) { + return this.groovyRecipe.getCraftingResult(inv); + } + + @Override + public boolean matches(@NotNull InventoryCrafting inv, @NotNull World world) { + return (this.tier == 0 || this.tier == getTierFromSize(inv.getSizeInventory())) && this.groovyRecipe.matches(inv, world); + } + + @Override + public @NotNull NonNullList getRemainingItems(@NotNull InventoryCrafting inv) { + return this.groovyRecipe.getRemainingItems(inv); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/TableCrafting.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/TableCrafting.java new file mode 100644 index 000000000..bf7922334 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/TableCrafting.java @@ -0,0 +1,86 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.crafting.table.ITieredRecipe; +import com.blakebr0.extendedcrafting.crafting.table.TableRecipeManager; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public class TableCrafting extends VirtualizedRegistry { + + public TableRecipeBuilder.Shaped shapedBuilder() { + return new TableRecipeBuilder.Shaped(); + } + + public TableRecipeBuilder.Shapeless shapelessBuilder() { + return new TableRecipeBuilder.Shapeless(); + } + + @Override + public void onReload() { + removeScripted().forEach(recipe -> TableRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)); + TableRecipeManager.getInstance().getRecipes().addAll(restoreFromBackup()); + } + + public ITieredRecipe addShaped(ItemStack output, List> input) { + return addShaped(0, output, input); + } + + public ITieredRecipe addShaped(int tier, ItemStack output, List> input) { + return (ITieredRecipe) shapedBuilder() + .matrix(input) + .tier(tier) + .output(output) + .register(); + } + + public ITieredRecipe addShapeless(ItemStack output, List> input) { + return addShaped(0, output, input); + } + + public ITieredRecipe addShapeless(int tier, ItemStack output, List input) { + return (ITieredRecipe) shapelessBuilder() + .input(input) + .tier(tier) + .output(output) + .register(); + } + + public ITieredRecipe add(ITieredRecipe recipe) { + if (recipe != null) { + addScripted(recipe); + TableRecipeManager.getInstance().getRecipes().add(recipe); + } + return recipe; + } + + public boolean removeByOutput(ItemStack stack) { + return TableRecipeManager.getInstance().getRecipes().removeIf(recipe -> { + if (recipe != null && recipe.getRecipeOutput().isItemEqual(stack)) { + addBackup(recipe); + return true; + } + return false; + }); + } + + public boolean remove(ITieredRecipe recipe) { + if (TableRecipeManager.getInstance().getRecipes().removeIf(r -> r == recipe)) { + addBackup(recipe); + return true; + } + return false; + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(TableRecipeManager.getInstance().getRecipes()).setRemover(this::remove); + } + + public void removeAll() { + TableRecipeManager.getInstance().getRecipes().forEach(this::addBackup); + TableRecipeManager.getInstance().getRecipes().clear(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/TableRecipeBuilder.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/TableRecipeBuilder.java new file mode 100644 index 000000000..a60e17a05 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/extendedcrafting/TableRecipeBuilder.java @@ -0,0 +1,178 @@ +package com.cleanroommc.groovyscript.compat.mods.extendedcrafting; + +import com.blakebr0.extendedcrafting.crafting.table.TableRecipeShaped; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.compat.vanilla.CraftingRecipeBuilder; +import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; +import net.minecraft.item.crafting.IRecipe; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public abstract class TableRecipeBuilder extends CraftingRecipeBuilder { + + // 0 = any table it fits in, 1-4 specifically that tier of table + protected int tier = 0; + + public TableRecipeBuilder() { + super(9, 9); + } + + public TableRecipeBuilder tier(int tier) { + this.tier = tier; + int size = this.tier == 0 ? 9 : this.tier * 2 + 1; + this.width = size; + this.height = size; + return this; + } + + public TableRecipeBuilder tierAny() { + return tier(0); + } + + public TableRecipeBuilder tierBasic() { + return tier(1); + } + + public TableRecipeBuilder tierAdvanced() { + return tier(2); + } + + public TableRecipeBuilder tierElite() { + return tier(3); + } + + public TableRecipeBuilder tierUltimate() { + return tier(4); + } + + public static class Shaped extends TableRecipeBuilder { + + private final Char2ObjectOpenHashMap keyMap = new Char2ObjectOpenHashMap<>(); + private final List errors = new ArrayList<>(); + protected boolean mirrored = false; + private String[] keyBasedMatrix; + private List> ingredientMatrix; + + public Shaped() { + keyMap.put(' ', IIngredient.EMPTY); + } + + public TableRecipeBuilder.Shaped mirrored(boolean mirrored) { + this.mirrored = mirrored; + return this; + } + + public TableRecipeBuilder.Shaped mirrored() { + return mirrored(true); + } + + public TableRecipeBuilder.Shaped matrix(String... matrix) { + this.keyBasedMatrix = matrix; + return this; + } + + public TableRecipeBuilder.Shaped shape(String... matrix) { + this.keyBasedMatrix = matrix; + return this; + } + + public TableRecipeBuilder.Shaped row(String row) { + if (this.keyBasedMatrix == null) { + this.keyBasedMatrix = new String[]{row}; + } else { + this.keyBasedMatrix = ArrayUtils.add(this.keyBasedMatrix, row); + } + return this; + } + + public TableRecipeBuilder.Shaped key(String c, IIngredient ingredient) { + if (c == null || c.length() != 1) { + errors.add("key must be a single char, but found '" + c + "'"); + return this; + } + this.keyMap.put(c.charAt(0), ingredient); + return this; + } + + public TableRecipeBuilder.Shaped matrix(List> matrix) { + this.ingredientMatrix = matrix; + return this; + } + + public TableRecipeBuilder.Shaped shape(List> matrix) { + this.ingredientMatrix = matrix; + return this; + } + + @Override + public IRecipe register() { + GroovyLog.Msg msg = GroovyLog.msg("Error adding shaped Extended Crafting Table recipe").error() + .add((keyBasedMatrix == null || keyBasedMatrix.length == 0) && (ingredientMatrix == null || ingredientMatrix.isEmpty()), () -> "No matrix was defined") + .add(keyBasedMatrix != null && ingredientMatrix != null, () -> "A key based matrix AND a ingredient based matrix was defined. This is not allowed!"); + if (msg.postIfNotEmpty()) return null; + msg.add(IngredientHelper.isEmpty(this.output), () -> "Output must not be empty"); + TableRecipeShaped recipe = null; + if (keyBasedMatrix != null) { + recipe = validateShape(msg, errors, keyBasedMatrix, keyMap, ((width1, height1, ingredients) -> ShapedTableRecipe.make(tier, output, ingredients, width1, height1, mirrored, recipeFunction, recipeAction))); + } else if (ingredientMatrix != null) { + recipe = validateShape(msg, ingredientMatrix, ((width1, height1, ingredients) -> ShapedTableRecipe.make(tier, output.copy(), ingredients, width1, height1, mirrored, recipeFunction, recipeAction))); + } + if (msg.postIfNotEmpty()) return null; + if (recipe != null) { + ModSupport.EXTENDED_CRAFTING.get().tableCrafting.add(recipe); + } + return recipe; + } + } + + public static class Shapeless extends TableRecipeBuilder { + + private final List ingredients = new ArrayList<>(); + + public TableRecipeBuilder.Shapeless input(IIngredient ingredient) { + ingredients.add(ingredient); + return this; + } + + public TableRecipeBuilder.Shapeless input(IIngredient... ingredients) { + if (ingredients != null) { + for (IIngredient ingredient : ingredients) { + input(ingredient); + } + } + return this; + } + + public TableRecipeBuilder.Shapeless input(Collection ingredients) { + if (ingredients != null && !ingredients.isEmpty()) { + for (IIngredient ingredient : ingredients) { + input(ingredient); + } + } + return this; + } + + public boolean validate() { + GroovyLog.Msg msg = GroovyLog.msg("Error adding shapeless Extended Crafting Table recipe").error(); + msg.add(tier < 0 || tier > 4, () -> "tier must be between 0 and 4, was instead " + tier); + msg.add(IngredientHelper.isEmpty(this.output), () -> "Output must not be empty"); + msg.add(ingredients.isEmpty(), () -> "inputs must not be empty"); + msg.add(ingredients.size() > width * height, () -> "maximum inputs are " + (width * height) + " but found " + ingredients.size()); + return !msg.postIfNotEmpty(); + } + + @Override + public IRecipe register() { + if (!validate()) return null; + ShapelessTableRecipe recipe = ShapelessTableRecipe.make(tier, output.copy(), ingredients, recipeFunction, recipeAction); + ModSupport.EXTENDED_CRAFTING.get().tableCrafting.add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder.java b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder.java index e6c207e07..e6be8efb7 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder.java @@ -30,7 +30,7 @@ public abstract class CraftingRecipeBuilder { protected Closure recipeAction; protected byte replace = 0; - protected final int width, height; + protected int width, height; public CraftingRecipeBuilder(int width, int height) { this.width = width; diff --git a/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java index 8d5517373..8bbd7d91f 100644 --- a/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java +++ b/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java @@ -10,7 +10,9 @@ public class LateMixin implements ILateMixinLoader { - public static final List modMixins = ImmutableList.of("jei", "mekanism", "enderio", "thermalexpansion", "draconicevolution", "ic2_classic", "ic2_exp", "bloodmagic", "astralsorcery", "tconstruct", "tcomplement"); + public static final List modMixins = ImmutableList.of("jei", "mekanism", "enderio", "thermalexpansion", "draconicevolution", + "ic2_classic", "ic2_exp", "bloodmagic", "astralsorcery", "tconstruct", + "tcomplement", "extendedcrafting"); @Override public List getMixinConfigs() { diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/extendedcrafting/ItemRecipeMakerMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/extendedcrafting/ItemRecipeMakerMixin.java new file mode 100644 index 000000000..2a296e5f6 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/extendedcrafting/ItemRecipeMakerMixin.java @@ -0,0 +1,128 @@ +package com.cleanroommc.groovyscript.core.mixin.extendedcrafting; + +import com.blakebr0.extendedcrafting.config.ModConfig; +import com.blakebr0.extendedcrafting.item.ItemRecipeMaker; +import com.blakebr0.extendedcrafting.lib.IExtendedTable; +import com.blakebr0.extendedcrafting.tile.TileEnderCrafter; +import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import com.google.common.base.Joiner; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.util.ArrayList; +import java.util.List; + +@Mixin(value = ItemRecipeMaker.class, remap = false) +public abstract class ItemRecipeMakerMixin { + + @Shadow + protected abstract boolean isShapeless(ItemStack stack); + + @Inject(method = "setClipboard", at = @At("HEAD"), cancellable = true) + public void setClipboard(IExtendedTable table, ItemStack stack, CallbackInfoReturnable cir) { + if (Desktop.isDesktopSupported()) { + boolean ender = table instanceof TileEnderCrafter; + StringBuilder string = (new StringBuilder("mods.extendedcrafting.")).append(ender ? "EnderCrafting" : "TableCrafting"); + String inputs; + if (isShapeless(stack)) { + inputs = groovyscript$makeItemArrayShapeless(table); + string.append(".addShapeless("); + } else { + inputs = groovyscript$makeItemArrayShaped(table, !ender); + string.append(".addShaped("); + } + if (inputs == null) { + cir.setReturnValue(false); + return; + } + if (!ender) string.append("0, "); + string.append("OUTPUT, [").append(inputs).append("])"); + StringSelection stringSelection = new StringSelection(string.toString()); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + cir.setReturnValue(true); + return; + } + cir.setReturnValue(false); + } + + public String groovyscript$makeItemArrayShapeless(IExtendedTable table) { + StringBuilder builder = new StringBuilder(); + boolean isEmpty = true; + for (ItemStack stack : table.getMatrix()) { + if (!stack.isEmpty()) { + builder.append(groovyscript$makeItem(stack)) + .append(", "); + isEmpty = false; + } + } + if (isEmpty) return null; + return builder.delete(builder.length() - 2, builder.length()).toString(); + } + + public String groovyscript$makeItemArrayShaped(IExtendedTable table, boolean removeEmpties) { + List> matrix = new ArrayList<>(); + int row = 0; + int column = 0; + boolean rowEmpty = true, allEmpty = true; + matrix.add(new ArrayList<>()); + for (ItemStack stack : table.getMatrix()) { + String s = stack.isEmpty() ? null : groovyscript$makeItem(stack); + rowEmpty &= s == null; + allEmpty &= rowEmpty; + matrix.get(row).add(s); + if (++column == table.getLineSize()) { + if (rowEmpty && removeEmpties) { + matrix.remove(row); + row--; + } + row++; + column = 0; + rowEmpty = true; + matrix.add(new ArrayList<>()); + } + } + matrix.remove(row); + if (allEmpty || matrix.isEmpty()) return null; + if (removeEmpties) { + // remove empty columns + for (int col = 0; col < matrix.get(0).size(); col++) { + boolean isEmpty = true; + for (row = 0; row < matrix.size(); row++) { + if (matrix.get(row).get(col) != null) { + isEmpty = false; + break; + } + } + if (isEmpty) { + for (row = 0; row < matrix.size(); row++) { + matrix.get(row).remove(col); + } + col--; + } + } + } + return '\n' + Joiner.on(", \n").join(matrix) + '\n'; + } + + private static String groovyscript$makeItem(ItemStack stack) { + if (ModConfig.confRMOredict) { + int[] oreIds = OreDictionary.getOreIDs(stack); + if (oreIds.length > 0) { + return IngredientHelper.asGroovyCode(OreDictionary.getOreName(oreIds[0]), false); + } + } + if (ModConfig.confRMNBT) { + return IngredientHelper.asGroovyCode(stack, false, false); + } + return IngredientHelper.asGroovyCode(stack, false); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java index f97a3ef17..956473c1e 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java @@ -6,7 +6,9 @@ import net.minecraft.block.properties.IProperty; import net.minecraft.block.state.IBlockState; import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.NonNullList; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fluids.FluidStack; import org.jetbrains.annotations.Contract; @@ -51,6 +53,26 @@ public static IIngredient toIIngredient(FluidStack fluidStack) { return (IIngredient) fluidStack; } + @NotNull + public static NonNullList toNonNullList(IngredientList list) { + NonNullList ingredients = NonNullList.create(); + for (IIngredient i : list) { + if (i == null) ingredients.add(IIngredient.EMPTY); + else ingredients.add(i); + } + return ingredients; + } + + @NotNull + public static NonNullList toIngredientNonNullList(Collection list) { + NonNullList ingredients = NonNullList.create(); + for (IIngredient i : list) { + if (i == null) ingredients.add(Ingredient.EMPTY); + else ingredients.add(i.toMcIngredient()); + } + return ingredients; + } + public static boolean isEmpty(@Nullable IIngredient ingredient) { return ingredient == null || ingredient.getMatchingStacks().length == 0 || ingredient.getAmount() <= 0; } @@ -99,7 +121,7 @@ public static boolean isEmptyFluids(@Nullable Collection fluidStacks * Determines whether the list or all elements in the list are considered empty * * @param ingredients collection of ingredients - * @return true if the collection or the elements are empty + * @return true if the collection or all elements are empty */ public static boolean isEmpty(@Nullable Collection ingredients) { if (ingredients == null || ingredients.isEmpty()) @@ -109,6 +131,20 @@ public static boolean isEmpty(@Nullable Collection ingredients) { return true; } + /** + * Determines whether the list or all elements in the list are considered empty + * + * @param ingredients collection of ingredients + * @return true if the collection or one element is empty + */ + public static boolean isAnyEmpty(@Nullable Collection ingredients) { + if (ingredients == null || ingredients.isEmpty()) + return true; + for (IIngredient item : ingredients) + if (isEmpty(item)) return true; + return false; + } + /** * Determines whether the list or all elements in the array are considered empty * diff --git a/src/main/resources/mixin.groovyscript.extendedcrafting.json b/src/main/resources/mixin.groovyscript.extendedcrafting.json new file mode 100644 index 000000000..e00b6b2ff --- /dev/null +++ b/src/main/resources/mixin.groovyscript.extendedcrafting.json @@ -0,0 +1,10 @@ +{ + "package": "com.cleanroommc.groovyscript.core.mixin.extendedcrafting", + "refmap": "mixins.groovyscript.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "ItemRecipeMakerMixin" + ] +} \ No newline at end of file