diff --git a/build.gradle b/build.gradle index c592037aa..76cdded7a 100644 --- a/build.gradle +++ b/build.gradle @@ -142,6 +142,11 @@ dependencies { if (project.debug_evilcraft.toBoolean()) { runtimeOnly rfg.deobf('curse.maven:evilcraft-74610:2811267') } + + compileOnly rfg.deobf('curse.maven:compact-machines-224218:2707509') + if (project.debug_compact_machines.toBoolean()) { + runtimeOnly rfg.deobf('curse.maven:compact-machines-224218:2707509') + } compileOnly rfg.deobf('curse.maven:actually-additions-228404:3117927') if (project.debug_actually_additions.toBoolean()) { diff --git a/examples/postInit/compactmachines.groovy b/examples/postInit/compactmachines.groovy new file mode 100644 index 000000000..e89f496da --- /dev/null +++ b/examples/postInit/compactmachines.groovy @@ -0,0 +1,51 @@ + +if (!isLoaded('compactmachines3')) return +println 'mod \'compactmachines3\' detected, running script' + +// Miniaturization: +// Consumes a 3d structure in-world based on keys when an item is thrown into the field. +mods.compactmachines.miniaturization.recipeBuilder() + .name('diamond_rectangle') // Optional, String + .input(item('minecraft:clay')) + .output(item('minecraft:clay')) + .symmetrical() // Indicates that the recipe does not have to test all 4 rotations to determine if the multiblock is valid + .ticks(10) // Alias: duration, default 100 + .shape([['www', 'www']]) // Shape is a List> + .key('w', blockstate('minecraft:diamond_block')) + // character, blockstate, nbt, metadata-sensitive (default true), display item + //.key('w', blockstate('minecraft:diamond_block'), null, true, item('minecraft:diamond') * 7) + .register() + +mods.compactmachines.miniaturization.recipeBuilder() + .name('groovy_rocket') // Optional, String + .input(item('minecraft:diamond')) + .output(item('minecraft:clay') * 64) + .symmetrical() + .ticks(5400) + // both ` ` and `_` are reserved for empty space, and cannot be used as keys + .key('a', blockstate('minecraft:stained_glass:0')) + .key('b', blockstate('minecraft:stained_glass:1')) + .key('c', blockstate('minecraft:stained_glass:2')) + .key('d', blockstate('minecraft:stained_glass:3')) + .key('e', blockstate('minecraft:diamond_block')) + .key('f', blockstate('minecraft:stained_glass:5')) + .key('g', blockstate('minecraft:stained_glass:6')) // Note: More than 6 keys results in incorrect displays in JEI + .layer(" ", " ", " a ", " aaa ", " a ", " ", " ") // layer adds String... to the shape structure + .layer(" ", " b ", " aaa ", " baaab ", " aaa ", " b ", " ") // adds layers in descending Y order + .layer(" ", " c ", " cac ", " caeac ", " cac ", " c ", " ") + .layer(" ", " a ", " aaa ", " aaeaa ", " aaa ", " a ", " ") + .layer(" ", " a ", " aaa ", " aaeaa ", " aaa ", " a ", " ") + .layer(" ", " a ", " aaa ", " aaeaa ", " aaa ", " a ", " ") + .layer(" ", " g ", " cac ", " caeac ", " cac ", " f ", " ") + .layer(" ", " a ", " aaa ", " aaeaa ", " aaa ", " a ", " ") + .layer(" ", " a ", " aaa ", " aaeaa ", " aaa ", " a ", " ") + .layer(" ", " a ", " aaa ", " aaeaa ", " aaa ", " a ", " ") + .layer(" ", " c ", " cac ", " caeac ", " cac ", " c ", " ") + .layer(" ", " a ", " aaa ", " aaaaa ", " aaa ", " a ", " ") + .layer(" a ", " ccc ", " cdddc ", "acdddca", " cdddc ", " ccc ", " a ") + .register() + +mods.compactmachines.miniaturization.removeByInput(item('minecraft:ender_pearl')) +mods.compactmachines.miniaturization.removeByCatalyst(item('minecraft:redstone')) +mods.compactmachines.miniaturization.removeByOutput(item('compactmachines3:machine:3')) +//mods.compactmachines.miniaturization.removeAll() diff --git a/gradle.properties b/gradle.properties index 3f6f623a6..a8eb2fc4c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,4 +34,5 @@ debug_blood_magic = false debug_tinkers = false debug_extended_crafting = false debug_botania = false -debug_forestry = false \ No newline at end of file +debug_forestry = false +debug_compact_machines = false \ No newline at end of file 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 6242b880c..eeab9034d 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java @@ -7,6 +7,7 @@ import com.cleanroommc.groovyscript.compat.mods.bloodmagic.BloodMagic; import com.cleanroommc.groovyscript.compat.mods.botania.Botania; import com.cleanroommc.groovyscript.compat.mods.chisel.Chisel; +import com.cleanroommc.groovyscript.compat.mods.compactmachines.CompactMachines; import com.cleanroommc.groovyscript.compat.mods.draconicevolution.DraconicEvolution; import com.cleanroommc.groovyscript.compat.mods.enderio.EnderIO; import com.cleanroommc.groovyscript.compat.mods.evilcraft.EvilCraft; @@ -59,6 +60,7 @@ public class ModSupport implements IDynamicGroovyProperty { 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 final Container FORESTRY = new Container<>("forestry", "Forestry", Forestry::new); + public static final Container COMPACT_MACHINES = new Container<>("compactmachines3", "Compact Machines 3", CompactMachines::new, "compactmachines"); public static Collection> getAllContainers() { return new ObjectOpenHashSet<>(containers.values()); diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/compactmachines/CompactMachines.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/compactmachines/CompactMachines.java new file mode 100644 index 000000000..13616b820 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/compactmachines/CompactMachines.java @@ -0,0 +1,13 @@ +package com.cleanroommc.groovyscript.compat.mods.compactmachines; + +import com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer; + +public class CompactMachines extends ModPropertyContainer { + + public final Miniaturization miniaturization = new Miniaturization(); + + public CompactMachines() { + addRegistry(miniaturization); + } + +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/compactmachines/Miniaturization.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/compactmachines/Miniaturization.java new file mode 100644 index 000000000..a9dbb3256 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/compactmachines/Miniaturization.java @@ -0,0 +1,227 @@ +package com.cleanroommc.groovyscript.compat.mods.compactmachines; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import org.dave.compactmachines3.miniaturization.MultiblockRecipes; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Miniaturization extends VirtualizedRegistry { + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(recipe -> MultiblockRecipes.getRecipes().removeIf(r -> r == recipe)); + restoreFromBackup().forEach(recipe -> MultiblockRecipes.getRecipes().add(recipe)); + } + + public void add(org.dave.compactmachines3.miniaturization.MultiblockRecipe recipe) { + addScripted(recipe); + MultiblockRecipes.getRecipes().add(recipe); + } + + public boolean remove(org.dave.compactmachines3.miniaturization.MultiblockRecipe recipe) { + addBackup(recipe); + return MultiblockRecipes.getRecipes().removeIf(r -> r == recipe); + } + + public void removeByInput(ItemStack input) { + for (org.dave.compactmachines3.miniaturization.MultiblockRecipe recipe : MultiblockRecipes.getRecipes().stream().filter(r -> r.getCatalystStack().isItemEqual(input)).collect(Collectors.toList())) { + addBackup(recipe); + MultiblockRecipes.getRecipes().removeIf(r -> r == recipe); + } + } + + public void removeByCatalyst(ItemStack catalyst) { + removeByInput(catalyst); + } + + public void removeByOutput(ItemStack output) { + for (org.dave.compactmachines3.miniaturization.MultiblockRecipe recipe : MultiblockRecipes.getRecipes().stream().filter(r -> r.getTargetStack().isItemEqual(output)).collect(Collectors.toList())) { + addBackup(recipe); + MultiblockRecipes.getRecipes().removeIf(r -> r == recipe); + } + } + + public void removeAll() { + MultiblockRecipes.getRecipes().forEach(this::addBackup); + MultiblockRecipes.getRecipes().clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(MultiblockRecipes.getRecipes()).setRemover(this::remove); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private final Char2ObjectOpenHashMap keyMap = new Char2ObjectOpenHashMap<>(); + private final List errors = new ArrayList<>(); + List> shape = new ArrayList<>(); + private boolean symmetrical; + private int ticks = 100; + + public RecipeBuilder shape(List> shape) { + this.shape = shape; + return this; + } + + public RecipeBuilder layer(List layer) { + this.shape.add(layer); + return this; + } + + public RecipeBuilder layer(String... layer) { + return layer(Arrays.asList(layer)); + } + + // groovy doesn't have char literals + public RecipeBuilder key(String c, IBlockState state, NBTTagCompound nbt, boolean ignoreMeta, ItemStack reference) { + if (c == null || c.length() != 1) { + errors.add("key must be a single char, but found '" + c + "'"); + return this; + } + if (c.equals("_") || c.equals(" ")) { + errors.add("key cannot be an underscore('_') or a space(' ')"); + return this; + } + this.keyMap.put(c.charAt(0), new ReferenceValues(state, nbt, ignoreMeta, reference)); + return this; + } + + public RecipeBuilder key(String c, IBlockState state, NBTTagCompound nbt, boolean ignoreMeta) { + return key(c, state, nbt, ignoreMeta, null); + } + + public RecipeBuilder key(String c, IBlockState state, NBTTagCompound nbt) { + return key(c, state, nbt, false, null); + } + + public RecipeBuilder key(String c, IBlockState state, boolean ignoreMeta) { + return key(c, state, null, ignoreMeta, null); + } + + public RecipeBuilder key(String c, IBlockState state) { + return key(c, state, null, false, null); + } + + public RecipeBuilder symmetrical(boolean symmetrical) { + this.symmetrical = symmetrical; + return this; + } + + public RecipeBuilder symmetrical() { + this.symmetrical = !symmetrical; + return this; + } + + public RecipeBuilder ticks(int ticks) { + this.ticks = ticks; + return this; + } + + public RecipeBuilder duration(int duration) { + return ticks(duration); + } + + @Override + public String getErrorMsg() { + return "Error adding Compact Machines Multiblock recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 1, 1, 1, 1); + validateFluids(msg); + for (String error : errors) { + msg.add(error); + } + String missingKeys = shape.stream().flatMap(l -> l.stream().flatMap(g -> Arrays.stream(g.split("")).map(q -> q.charAt(0)))) + .distinct() + .filter(x -> !(keyMap.containsKey(x) || x == ' ' || x == '_')) + .map(String::valueOf) + .collect(Collectors.joining()); + msg.add(!missingKeys.isEmpty(), "shape must contain only characters that are underscore('_'), space(' '), or declared via a key, but the following keys were not declared: {}", missingKeys); + } + + @Nullable + @Override + public org.dave.compactmachines3.miniaturization.MultiblockRecipe register() { + if (!validate()) return null; + + org.dave.compactmachines3.miniaturization.MultiblockRecipe recipe = new org.dave.compactmachines3.miniaturization.MultiblockRecipe( + name.toString(), + output.get(0), + input.get(0).getMatchingStacks()[0].getItem(), + input.get(0).getMatchingStacks()[0].getMetadata(), + input.get(0).getMatchingStacks()[0].getTagCompound(), + symmetrical, + ticks + ); + + String[][][] target = shape.stream().map(l -> l.stream().map(g -> g.replace(" ", "_").split("")) + .toArray(String[][]::new)).toArray(String[][][]::new); + + recipe.setPositionMap(target); + + keyMap.forEach((character, value) -> { + String ref = String.valueOf(character); + recipe.addBlockReference(ref, value.getState()); + if (value.getNbt() != null) recipe.addBlockVariation(ref, value.getNbt()); + recipe.setIgnoreMeta(ref, value.isIgnoreMeta()); + if (value.getReference() != null) recipe.setReferenceStack(ref, value.getReference()); + }); + + ModSupport.COMPACT_MACHINES.get().miniaturization.add(recipe); + return recipe; + } + + public static class ReferenceValues { + + private final IBlockState state; + private final NBTTagCompound nbt; + private final boolean ignoreMeta; + private final ItemStack reference; + + public ReferenceValues(IBlockState state, NBTTagCompound nbt, boolean ignoreMeta, ItemStack reference) { + this.state = state; + this.nbt = nbt; + this.ignoreMeta = ignoreMeta; + this.reference = reference; + } + + public IBlockState getState() { + return state; + } + + public NBTTagCompound getNbt() { + return nbt; + } + + public boolean isIgnoreMeta() { + return ignoreMeta; + } + + public ItemStack getReference() { + return reference; + } + + } + + } + +}