diff --git a/dependencies.gradle b/dependencies.gradle index 73bb9dd77..ebffba5f4 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -88,6 +88,7 @@ final Map> mod_dependencies = [ 'lemonlib-306926:2639879' : [debug_arcane_world, debug_arcane_world_rotn], 'libnine-322344:3509087' : [debug_lazy_ae2], 'libvulpes-236541:3801015' : [debug_advanced_rocketry], + 'lightningcraft-237422:2872478' : [debug_lightningcraft], 'llibrary-243298:2504999' : [debug_ice_and_fire_old, debug_ice_and_fire_rotn], 'magneticraft-224808:3791484' : [debug_magneticraft], 'mantle-74924:2713386' : [debug_inspirations, debug_tinkers_construct], diff --git a/examples/postInit/generated/lightningcraft_generated.groovy b/examples/postInit/generated/lightningcraft_generated.groovy new file mode 100644 index 000000000..505ca90fb --- /dev/null +++ b/examples/postInit/generated/lightningcraft_generated.groovy @@ -0,0 +1,56 @@ + +// Auto generated groovyscript example file +// MODS_LOADED: lightningcraft + +log 'mod \'lightningcraft\' detected, running script' + +// Lightning Crusher: +// Consumes LE to convert 1 input itemstack into an output itemstack. + +mods.lightningcraft.crusher.removeByInput(item('minecraft:saddle')) +mods.lightningcraft.crusher.removeByOutput(item('minecraft:redstone')) +// mods.lightningcraft.crusher.removeAll() + +mods.lightningcraft.crusher.recipeBuilder() + .input(item('minecraft:diamond_block')) + .output(item('minecraft:nether_star')) + .register() + + +// Lightning Infusion Table: +// Consumes LE to convert up to 5 input itemstacks into an output itemstack. + +mods.lightningcraft.infusion.removeByOutput(item('minecraft:diamond')) +// mods.lightningcraft.infusion.removeAll() + +mods.lightningcraft.infusion.recipeBuilder() + .center(item('minecraft:clay')) + .input(item('minecraft:gold_ingot'), item('minecraft:gold_ingot'), item('minecraft:iron_ingot'), item('minecraft:iron_ingot')) + .output(item('minecraft:nether_star')) + .le(500) + .register() + +mods.lightningcraft.infusion.recipeBuilder() + .center(item('minecraft:clay')) + .input(item('minecraft:gold_ingot'), item('minecraft:potion').withNbt(['Potion': 'minecraft:leaping'])) + .output(item('minecraft:diamond_block')) + .le(200) + .register() + + +// Lightning Transformation: +// Converts any number of itemstacks on the ground into the output itemstack when the area is struck by lightning. + +mods.lightningcraft.transformation.removeByInput(item('minecraft:iron_ingot')) +mods.lightningcraft.transformation.removeByOutput(item('lightningcraft:material', 11)) +// mods.lightningcraft.transformation.removeAll() + +mods.lightningcraft.transformation.recipeBuilder() + .input(item('minecraft:clay')) + .output(item('minecraft:diamond')) + .register() + +mods.lightningcraft.transformation.recipeBuilder() + .input(item('minecraft:gold_ingot') * 3, item('minecraft:diamond_block')) + .output(item('minecraft:clay') * 16) + .register() diff --git a/gradle.properties b/gradle.properties index b78dc633e..5bd1de0d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -83,6 +83,7 @@ debug_inspirations = false debug_integrated_dynamics = false debug_lazy_ae2 = false +debug_lightningcraft = false debug_magneticraft = false debug_mekanism = false diff --git a/src/main/java/com/cleanroommc/groovyscript/api/documentation/annotations/Property.java b/src/main/java/com/cleanroommc/groovyscript/api/documentation/annotations/Property.java index d8b5720f6..29841cc3c 100644 --- a/src/main/java/com/cleanroommc/groovyscript/api/documentation/annotations/Property.java +++ b/src/main/java/com/cleanroommc/groovyscript/api/documentation/annotations/Property.java @@ -73,9 +73,8 @@ * For instance, "0" is the default value for {@code int}s, and so does not have to be declared. * * @return what the default value is, defaults to {@code false}, {@code 0}, {@code 0.0f}, {@code "null"}, etc depending on the property class. - * @see com.cleanroommc.groovyscript.documentation.Builder#defaultValueConverter + * @see com.cleanroommc.groovyscript.documentation.helper.descriptor.DescriptorHelper#defaultValueConverter */ - @SuppressWarnings("JavadocReference") String defaultValue() default ""; /** 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 e2ac33af1..3185fa53b 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java @@ -51,6 +51,7 @@ import com.cleanroommc.groovyscript.compat.mods.integrateddynamics.IntegratedDynamics; import com.cleanroommc.groovyscript.compat.mods.jei.JustEnoughItems; import com.cleanroommc.groovyscript.compat.mods.lazyae2.LazyAE2; +import com.cleanroommc.groovyscript.compat.mods.lightningcraft.LightningCraft; import com.cleanroommc.groovyscript.compat.mods.magneticraft.Magneticraft; import com.cleanroommc.groovyscript.compat.mods.mekanism.Mekanism; import com.cleanroommc.groovyscript.compat.mods.mysticalagriculture.MysticalAgriculture; @@ -142,10 +143,11 @@ public class ModSupport { public static final GroovyContainer INSPIRATIONS = new InternalModContainer<>("inspirations", "Inspirations", Inspirations::new); public static final GroovyContainer INTEGRATED_DYNAMICS = new InternalModContainer<>("integrateddynamics", "Integrated Dynamics", IntegratedDynamics::new, "id"); public static final GroovyContainer JEI = new InternalModContainer<>("jei", "Just Enough Items", JustEnoughItems::new, "hei"); + public static final GroovyContainer LAZYAE2 = new InternalModContainer<>("threng", "LazyAE2", LazyAE2::new, "lazyae2"); + public static final GroovyContainer LIGHTNING_CRAFT = new InternalModContainer<>("lightningcraft", "LightningCraft", LightningCraft::new); public static final GroovyContainer MAGNETICRAFT = new InternalModContainer<>("magneticraft", "Magneticraft", Magneticraft::new); public static final GroovyContainer MEKANISM = new InternalModContainer<>("mekanism", "Mekanism", Mekanism::new); public static final GroovyContainer MYSTICAL_AGRICULTURE = new InternalModContainer<>("mysticalagriculture", "Mystical Agriculture", MysticalAgriculture::new); - public static final GroovyContainer LAZYAE2 = new InternalModContainer<>("threng", "LazyAE2", LazyAE2::new, "lazyae2"); public static final GroovyContainer NATURES_AURA = new InternalModContainer<>("naturesaura", "Nature's Aura", NaturesAura::new); public static final GroovyContainer PNEUMATIC_CRAFT = new InternalModContainer<>("pneumaticcraft", "PneumaticCraft: Repressurized", PneumaticCraft::new); public static final GroovyContainer PRIMAL_TECH = new InternalModContainer<>("primal_tech", "Primal Tech", PrimalTech::new, "primaltech"); diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Crusher.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Crusher.java new file mode 100644 index 000000000..7500328e3 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Crusher.java @@ -0,0 +1,70 @@ +package com.cleanroommc.groovyscript.compat.mods.lightningcraft; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.api.documentation.annotations.*; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.StandardListRegistry; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import sblectric.lightningcraft.api.recipes.LightningCrusherRecipe; +import sblectric.lightningcraft.recipes.LightningCrusherRecipes; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@RegistryDescription +public class Crusher extends StandardListRegistry { + + @Override + public Collection getRecipes() { + return LightningCrusherRecipes.instance().getRecipeList(); + } + + @MethodDescription(example = @Example("item('minecraft:saddle')")) + public boolean removeByInput(IIngredient input) { + return getRecipes().removeIf(r -> r.getInput().stream().anyMatch(input) && doAddBackup(r)); + } + + @MethodDescription(example = @Example("item('minecraft:redstone')")) + public boolean removeByOutput(IIngredient output) { + return getRecipes().removeIf(r -> output.test(r.getOutput()) && doAddBackup(r)); + } + + @RecipeBuilderDescription(example = @Example(".input(item('minecraft:diamond_block')).output(item('minecraft:nether_star'))")) + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Property(property = "input", comp = @Comp(eq = 1)) + @Property(property = "output", comp = @Comp(eq = 1)) + public static class RecipeBuilder extends AbstractRecipeBuilder { + + @Override + public String getErrorMsg() { + return "Error adding LightningCraft Crusher recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 1, 1, 1, 1); + } + + @Override + protected int getMaxItemInput() { + return 1; + } + + @Override + @RecipeBuilderRegistrationMethod + public @Nullable LightningCrusherRecipe register() { + if (!validate()) return null; + List inputs = Arrays.asList(input.get(0).getMatchingStacks()); + LightningCrusherRecipe recipe = new LightningCrusherRecipe(output.get(0), inputs); + ModSupport.LIGHTNING_CRAFT.get().crusher.add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Infusion.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Infusion.java new file mode 100644 index 000000000..595a36e1e --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Infusion.java @@ -0,0 +1,131 @@ +package com.cleanroommc.groovyscript.compat.mods.lightningcraft; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.api.documentation.annotations.*; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.StandardListRegistry; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import sblectric.lightningcraft.api.recipes.LightningInfusionRecipe; +import sblectric.lightningcraft.recipes.LightningInfusionRecipes; + +import java.util.Collection; +import java.util.List; + +@RegistryDescription +public class Infusion extends StandardListRegistry { + + @Override + public Collection getRecipes() { + return LightningInfusionRecipes.instance().getRecipeList(); + } + + @MethodDescription(example = @Example("item('minecraft:diamond')")) + public boolean removeByOutput(IIngredient output) { + return getRecipes().removeIf(r -> output.test(r.getOutput()) && doAddBackup(r)); + } + + @RecipeBuilderDescription(example = { + @Example(".center(item('minecraft:clay')).input(item('minecraft:gold_ingot'), item('minecraft:gold_ingot'), item('minecraft:iron_ingot'), item('minecraft:iron_ingot')).output(item('minecraft:nether_star')).le(500)"), + @Example(".center(item('minecraft:clay')).input(item('minecraft:gold_ingot'), item('minecraft:potion').withNbt(['Potion': 'minecraft:leaping'])).output(item('minecraft:diamond_block')).le(200)"), + }) + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Property(property = "input", comp = @Comp(gte = 0, lte = 4)) + @Property(property = "output", comp = @Comp(eq = 1)) + public static class RecipeBuilder extends AbstractRecipeBuilder { + + @Property(comp = @Comp(not = "empty")) + private IIngredient center; + @Property(comp = @Comp(gte = 0)) + private int le; + @Property(defaultValue = "true if any input item has nbt data") + private boolean nbtSensitive = false; + + private boolean nbtSensitiveChanged = false; + + @GroovyBlacklist + private static boolean hasAnyNbt(ItemStack centerStack, List input) { + if (centerStack.hasTagCompound()) { + return true; + } + for (var stack : input) { + if (stack.hasTagCompound()) { + return true; + } + } + return false; + } + + @RecipeBuilderMethodDescription + public RecipeBuilder le(int le) { + this.le = le; + return this; + } + + @RecipeBuilderMethodDescription(field = "le") + public RecipeBuilder cost(int le) { + this.le = le; + return this; + } + + @RecipeBuilderMethodDescription + public RecipeBuilder nbtSensitive(boolean nbtSensitive) { + this.nbtSensitive = nbtSensitive; + nbtSensitiveChanged = true; + return this; + } + + @RecipeBuilderMethodDescription + public RecipeBuilder center(IIngredient center) { + this.center = center; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding LightningCraft Infusion Table recipe"; + } + + @Override + protected int getMaxItemInput() { + // recipes with more than 1 item in some slot don't get recognized + return 1; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 0, 4, 1, 1); + validateFluids(msg); + msg.add(IngredientHelper.isEmpty(center), "Center item must not be empty"); + msg.add(le < 0, "LE cost must be positive"); + for (IIngredient it : this.input) { + msg.add(IngredientHelper.isEmpty(it), "All inputs must not be empty"); + } + msg.add(IngredientHelper.overMaxSize(center, 1), "centerItem must have a stack size of 1"); + } + + @Override + @RecipeBuilderRegistrationMethod + public @Nullable LightningInfusionRecipe register() { + if (!validate()) return null; + + LightningInfusionRecipe recipe = null; + for (var centerStack : center.getMatchingStacks()) { + for (List cartesianProductItemStack : IngredientHelper.cartesianProductItemStacks(input)) { + recipe = new LightningInfusionRecipe(output.get(0), le, centerStack, cartesianProductItemStack.toArray()); + // check if any input items have NBT if NBT sensitive mode isn't manually set + if (nbtSensitiveChanged ? nbtSensitive : hasAnyNbt(centerStack, cartesianProductItemStack)) recipe.setNBTSensitive(); + ModSupport.LIGHTNING_CRAFT.get().infusion.add(recipe); + } + } + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/LightningCraft.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/LightningCraft.java new file mode 100644 index 000000000..51dcc8333 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/LightningCraft.java @@ -0,0 +1,10 @@ +package com.cleanroommc.groovyscript.compat.mods.lightningcraft; + +import com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer; + +public class LightningCraft extends GroovyPropertyContainer { + + public final Crusher crusher = new Crusher(); + public final Infusion infusion = new Infusion(); + public final Transformation transformation = new Transformation(); +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Transformation.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Transformation.java new file mode 100644 index 000000000..24d28e945 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/lightningcraft/Transformation.java @@ -0,0 +1,101 @@ +package com.cleanroommc.groovyscript.compat.mods.lightningcraft; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.api.documentation.annotations.*; +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.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; +import sblectric.lightningcraft.recipes.LightningTransformRecipes; + +import java.util.List; +import java.util.Map; + +@RegistryDescription(admonition = @Admonition(value = "groovyscript.wiki.lightningcraft.transformation.note", type = Admonition.Type.WARNING)) +public class Transformation extends VirtualizedRegistry, ItemStack>> { + + public Map, ItemStack> getEntries() { + return LightningTransformRecipes.instance().getRecipeList(); + } + + @Override + public void onReload() { + var instance = getEntries(); + var entries = instance.entrySet(); + removeScripted().forEach(entry -> entries.removeIf(entry.getKey()::equals)); + restoreFromBackup().forEach(x -> instance.put(x.getKey(), x.getValue())); + } + + public boolean add(Map.Entry, ItemStack> recipe) { + if (recipe == null) return false; + LightningTransformRecipes.instance().addRecipe(recipe.getValue(), recipe.getKey()); + doAddScripted(recipe); + return true; + } + + public boolean remove(Map.Entry, ItemStack> recipe) { + return recipe != null && getEntries().entrySet().removeIf(r -> r == recipe) && doAddBackup(recipe); + } + + @RecipeBuilderDescription(example = { + @Example(".input(item('minecraft:clay')).output(item('minecraft:diamond'))"), + @Example(".input(item('minecraft:gold_ingot') * 3, item('minecraft:diamond_block')).output(item('minecraft:clay') * 16)") + }) + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @MethodDescription(example = @Example("item('minecraft:iron_ingot')")) + public boolean removeByInput(IIngredient input) { + return getEntries().entrySet().removeIf(r -> r.getKey().stream().anyMatch(input) && doAddBackup(r)); + } + + @MethodDescription(example = @Example("item('lightningcraft:material', 11)")) + public boolean removeByOutput(IIngredient output) { + return getEntries().entrySet().removeIf(r -> output.test(r.getValue()) && doAddBackup(r)); + } + + @MethodDescription(priority = 2000, example = @Example(commented = true)) + public void removeAll() { + getEntries().entrySet().forEach(this::doAddBackup); + getEntries().clear(); + } + + @MethodDescription(type = MethodDescription.Type.QUERY) + public SimpleObjectStream, ItemStack>> streamRecipes() { + return new SimpleObjectStream<>(getEntries().entrySet()).setRemover(this::remove); + } + + @Property(property = "input", comp = @Comp(gte = 1)) + @Property(property = "output", comp = @Comp(eq = 1)) + public static class RecipeBuilder extends AbstractRecipeBuilder, ItemStack>> { + + @Override + public String getErrorMsg() { + return "Error adding LightningCraft Transformation recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 1, Integer.MAX_VALUE, 1, 1); + } + + @Override + @RecipeBuilderRegistrationMethod + public @Nullable Map.Entry, ItemStack> register() { + if (!validate()) return null; + var inputs = IngredientHelper.cartesianProductItemStacks(input); + Map.Entry, ItemStack> recipe = null; + for (List itemStacks : inputs) { + recipe = Pair.of(itemStacks, output.get(0)); + ModSupport.LIGHTNING_CRAFT.get().transformation.add(recipe); + } + return recipe; + } + } +} diff --git a/src/main/resources/assets/groovyscript/lang/en_us.lang b/src/main/resources/assets/groovyscript/lang/en_us.lang index b35ddcc8d..f35f02a82 100644 --- a/src/main/resources/assets/groovyscript/lang/en_us.lang +++ b/src/main/resources/assets/groovyscript/lang/en_us.lang @@ -2108,6 +2108,21 @@ groovyscript.wiki.threng.etcher.top.value=Sets the top slot of the recipe groovyscript.wiki.threng.etcher.bottom.value=Sets the bottom slot of the recipe +# LightningCraft +groovyscript.wiki.lightningcraft.crusher.title=Lightning Crusher +groovyscript.wiki.lightningcraft.crusher.description=Consumes LE to convert 1 input itemstack into an output itemstack + +groovyscript.wiki.lightningcraft.infusion.title=Lightning Infusion Table +groovyscript.wiki.lightningcraft.infusion.description=Consumes LE to convert up to 5 input itemstacks into an output itemstack +groovyscript.wiki.lightningcraft.infusion.center.value=Sets the center slot of the recipe +groovyscript.wiki.lightningcraft.infusion.le.value=Sets the LE cost for the recipe +groovyscript.wiki.lightningcraft.infusion.nbtSensitive.value=Sets whether the recipe inputs require a specific NBT tag + +groovyscript.wiki.lightningcraft.transformation.title=Lightning Transformation +groovyscript.wiki.lightningcraft.transformation.description=Converts any number of itemstacks on the ground into the output itemstack when the area is struck by lightning. +groovyscript.wiki.lightningcraft.transformation.note=THe items on the ground must match the stack size of the recipe exactly. + + # Magneticraft groovyscript.wiki.magneticraft.crushing_table.title=Crushing Table groovyscript.wiki.magneticraft.crushing_table.description=Converts an input itemstack into an output itemstack when placed on top of the Crushing Table and interacted with by a Hammer which has