diff --git a/build.gradle b/build.gradle index 2bad2c688..e0693f8ac 100644 --- a/build.gradle +++ b/build.gradle @@ -166,7 +166,7 @@ dependencies { deobfProvided 'curse.maven:industrialcraft_classic-242942:3093607' } - if (project.debug_astral.toBoolean() || project.debug_thaum.toBoolean()) { + if (project.debug_astral.toBoolean() || project.debug_thaum.toBoolean() || project.debug_botania.toBoolean()) { runtime 'curse.maven:baubles-227083:2518667' } @@ -200,6 +200,12 @@ dependencies { deobfProvided 'curse.maven:constructs-armory-287683:3174535' deobfProvided 'curse.maven:tinkers-complement-272671:2843439' } + + if (project.debug_botania.toBoolean()) { + deobfCompile 'curse.maven:botania-225643:3330934' + } else { + deobfProvided 'curse.maven:botania-225643:3330934' + } } sourceSets { diff --git a/gradle.properties b/gradle.properties index 965c01376..5f662f2b4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,3 +34,4 @@ debug_astral = false debug_blood_magic = false debug_tinkers = false debug_extended_crafting = false +debug_botania = false 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 06c50069c..205d1abf6 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java @@ -4,6 +4,7 @@ import com.cleanroommc.groovyscript.api.IDynamicGroovyProperty; import com.cleanroommc.groovyscript.compat.mods.astralsorcery.AstralSorcery; import com.cleanroommc.groovyscript.compat.mods.bloodmagic.BloodMagic; +import com.cleanroommc.groovyscript.compat.mods.botania.Botania; import com.cleanroommc.groovyscript.compat.mods.draconicevolution.DraconicEvolution; import com.cleanroommc.groovyscript.compat.mods.enderio.EnderIO; import com.cleanroommc.groovyscript.compat.mods.extendedcrafting.ExtendedCrafting; @@ -38,6 +39,7 @@ public class ModSupport implements IDynamicGroovyProperty { public static final Container ENDER_IO = new Container<>("enderio", "Ender IO", EnderIO::new, "eio"); public static final Container JEI = new Container<>("jei", "Just Enough Items", JustEnoughItems::new, "hei"); public static final Container THAUMCRAFT = new Container<>("thaumcraft", "Thaumcraft", Thaumcraft::new, "tc", "thaum"); + public static final Container BOTANIA = new Container<>("botania", "Botania", Botania::new); public static final Container MEKANISM = new Container<>("mekanism", "Mekanism", Mekanism::new); public static final Container THERMAL_EXPANSION = new Container<>("thermalexpansion", "Thermal Expansion", ThermalExpansion::new, "te", "thermal"); public static final Container TINKERS_CONSTRUCT = new Container<>("tconstruct", "Tinkers' Construct", TinkersConstruct::new, "ticon", "tinkersconstruct"); diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Apothecary.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Apothecary.java new file mode 100644 index 000000000..d60440263 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Apothecary.java @@ -0,0 +1,113 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.recipe.RecipePetals; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Apothecary extends VirtualizedRegistry { + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.petalRecipes::remove); + BotaniaAPI.petalRecipes.addAll(restoreFromBackup()); + } + + public RecipePetals add(ItemStack output, IIngredient... inputs) { + RecipePetals recipe = new RecipePetals(output, Arrays.stream(inputs).map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).toArray()); + add(recipe); + return recipe; + } + + public void add(RecipePetals recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.petalRecipes.add(recipe); + } + + public boolean remove(RecipePetals recipe) { + if (recipe == null) return false; + addBackup(recipe); + return BotaniaAPI.petalRecipes.remove(recipe); + } + + public boolean removeByOutput(IIngredient output) { + if (BotaniaAPI.petalRecipes.removeIf(recipe -> { + boolean found = output.test(recipe.getOutput()); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Apothecary recipe") + .add("could not find recipe with output {}", output) + .error() + .post(); + return false; + } + + public boolean removeByInput(IIngredient... inputs) { + List converted = Arrays.stream(inputs).map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).collect(Collectors.toList()); + if (BotaniaAPI.petalRecipes.removeIf(recipe -> { + boolean found = converted.stream().allMatch(o -> recipe.getInputs().stream().anyMatch(i -> o instanceof String ? o.equals(i) : ItemStack.areItemStacksEqual((ItemStack) i, (ItemStack) o))); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Apothecary recipe") + .add("could not find recipe with inputs {}", converted) + .error() + .post(); + return false; + } + + public boolean removeByInputs(IIngredient... inputs) { + return removeByInput(inputs); + } + + public void removeAll() { + BotaniaAPI.petalRecipes.forEach(this::addBackup); + BotaniaAPI.petalRecipes.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(BotaniaAPI.petalRecipes).setRemover(this::remove); + } + + public class RecipeBuilder extends AbstractRecipeBuilder { + + @Override + public String getErrorMsg() { + return "Error adding Botania Apothecary recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg, 0, 0, 0, 0); + validateItems(msg, 1, 20, 1, 1); + } + + @Override + public @Nullable RecipePetals register() { + if (!validate()) return null; + RecipePetals recipe = new RecipePetals(output.get(0), input.stream().map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).toArray()); + add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Botania.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Botania.java new file mode 100644 index 000000000..c591f526b --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Botania.java @@ -0,0 +1,56 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.brackets.BracketHandlerManager; +import com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.lexicon.LexiconCategory; +import vazkii.botania.api.lexicon.LexiconEntry; + +public class Botania extends ModPropertyContainer { + + public final ElvenTrade elvenTrade = new ElvenTrade(); + public final ManaInfusion manaInfusion = new ManaInfusion(); + public final PureDaisy pureDaisy = new PureDaisy(); + public final Apothecary apothecary = new Apothecary(); + public final Orechid orechid = new Orechid(); + public final OrechidIgnem orechidIgnem = new OrechidIgnem(); + public final RuneAltar runeAltar = new RuneAltar(); + public final Brew brew = new Brew(); + public final Lexicon lexicon = new Lexicon(); + public final Knowledge knowledge = new Knowledge(); + public final Magnet magnet = new Magnet(); + public final Flowers flowers = new Flowers(); + + public Botania() { + addRegistry(elvenTrade); + addRegistry(manaInfusion); + addRegistry(pureDaisy); + addRegistry(apothecary); + addRegistry(orechid); + addRegistry(orechidIgnem); + addRegistry(runeAltar); + addRegistry(brew); + addRegistry(lexicon.category); + addRegistry(lexicon.entry); + addRegistry(lexicon.page); + addRegistry(knowledge); + addRegistry(magnet); + } + + public static LexiconCategory getCategory(String name) { + for (LexiconCategory category : BotaniaAPI.getAllCategories()) + if (category.getUnlocalizedName().equals(name)) return category; + return null; + } + + public static LexiconEntry getEntry(String name) { + for (LexiconEntry entry : BotaniaAPI.getAllEntries()) + if (entry.getUnlocalizedName().equals(name)) return entry; + return null; + } + + @Override + public void initialize() { + BracketHandlerManager.registerBracketHandler("brew", s -> BotaniaAPI.brewMap.getOrDefault(s, BotaniaAPI.fallbackBrew)); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Brew.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Brew.java new file mode 100644 index 000000000..4d8384d5c --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Brew.java @@ -0,0 +1,205 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import net.minecraft.potion.PotionEffect; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.recipe.RecipeBrew; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Brew extends VirtualizedRegistry { + + public BrewBuilder brewBuilder() { + return new BrewBuilder(); + } + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.brewRecipes::remove); + BotaniaAPI.brewRecipes.addAll(restoreFromBackup()); + } + + public void add(RecipeBrew recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.brewRecipes.add(recipe); + } + + public boolean remove(RecipeBrew recipe) { + if (recipe == null) return false; + addBackup(recipe); + return BotaniaAPI.brewRecipes.remove(recipe); + } + + public boolean removeByOutput(String brew) { + if (BotaniaAPI.brewRecipes.removeIf(recipe -> { + boolean found = recipe.getBrew().getKey().equals(brew); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Brew recipe") + .add("could not find recipe with input {}", brew) + .error() + .post(); + return false; + } + + public boolean removeByOutput(vazkii.botania.api.brew.Brew brew) { + return removeByOutput(brew.getKey()); + } + + public boolean removeByInput(IIngredient... inputs) { + List converted = Arrays.stream(inputs).map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).collect(Collectors.toList()); + if (BotaniaAPI.brewRecipes.removeIf(recipe -> { + boolean found = converted.stream().allMatch(o -> recipe.getInputs().stream().anyMatch(i -> (i instanceof String || o instanceof String) ? i.equals(o) : ItemStack.areItemStacksEqual((ItemStack) i, (ItemStack) o))); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Brew recipe") + .add("could not find recipe with inputs {}", converted) + .error() + .post(); + return false; + } + + public boolean removeByInputs(IIngredient... inputs) { + return removeByInput(inputs); + } + + public void removeAll() { + BotaniaAPI.brewRecipes.forEach(this::addBackup); + BotaniaAPI.brewRecipes.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(BotaniaAPI.brewRecipes).setRemover(this::remove); + } + + public class RecipeBuilder extends AbstractRecipeBuilder { + + protected vazkii.botania.api.brew.Brew brew; + + public RecipeBuilder output(vazkii.botania.api.brew.Brew brew) { + this.brew = brew; + return this; + } + + public RecipeBuilder brew(vazkii.botania.api.brew.Brew brew) { + return output(brew); + } + + @Override + public String getErrorMsg() { + return "Error adding Botania Brew recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg, 0, 0, 0, 0); + validateItems(msg, 1, 20, 0, 0); + msg.add(brew == null, "Expected a valid output brew, got " + brew); + } + + @Override + public @Nullable RecipeBrew register() { + if (!validate()) return null; + RecipeBrew recipe = new RecipeBrew(brew, input.stream().map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).toArray()); + add(recipe); + return recipe; + } + } + + public SimpleObjectStream streamBrews() { + return new SimpleObjectStream<>(BotaniaAPI.brewMap.values()); + } + + public static class BrewBuilder extends AbstractRecipeBuilder { + + protected String key; + protected String name; + protected int color = 0xFFFFFF; + protected int cost; + protected boolean canInfuseIncense = true; + protected boolean canInfuseBloodPendant = true; + protected List effects = new ArrayList<>(); + + public BrewBuilder key(String key) { + this.key = key; + return this; + } + + public BrewBuilder name(String name) { + this.name = name; + return this; + } + + public BrewBuilder color(int color) { + this.color = color; + return this; + } + + public BrewBuilder cost(int cost) { + this.cost = cost; + return this; + } + + public BrewBuilder noIncenseInfusion() { + this.canInfuseIncense = false; + return this; + } + + public BrewBuilder noBloodPendantInfusion() { + this.canInfuseBloodPendant = false; + return this; + } + + public BrewBuilder effect(PotionEffect effect) { + this.effects.add(effect); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Botania Brew"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 0, 0, 0, 0); + validateFluids(msg, 0, 0, 0, 0); + msg.add(key == null, "must have a unique key for brew, got " + key); + msg.add(cost < 1, "cost must be at least 1, got " + cost); + msg.add(effects.size() < 1, "must have at least 1 potion effect, got " + effects.size()); + } + + @Nullable + @Override + public vazkii.botania.api.brew.Brew register() { + if (!validate()) return null; + if (name == null) name = key; + vazkii.botania.api.brew.Brew brew = new vazkii.botania.api.brew.Brew(key, name, color, cost, effects.toArray(new PotionEffect[0])); + if (!canInfuseBloodPendant) brew.setNotBloodPendantInfusable(); + if (!canInfuseIncense) brew.setNotIncenseInfusable(); + BotaniaAPI.registerBrew(brew); + return brew; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/ElvenTrade.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/ElvenTrade.java new file mode 100644 index 000000000..f8b53baff --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/ElvenTrade.java @@ -0,0 +1,117 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.recipe.RecipeElvenTrade; + +import java.util.Arrays; +import java.util.List; + +public class ElvenTrade extends VirtualizedRegistry { + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.elvenTradeRecipes::remove); + BotaniaAPI.elvenTradeRecipes.addAll(restoreFromBackup()); + } + + protected Object[] convertIngredients(IIngredient[] inputs) { + return Arrays.stream(inputs).map(input -> input instanceof OreDictIngredient ? ((OreDictIngredient) input).getOreDict() : input.getMatchingStacks()[0]).toArray(); + } + + public RecipeElvenTrade add(ItemStack[] outputs, IIngredient[] inputs) { + RecipeElvenTrade recipe = new RecipeElvenTrade(outputs, convertIngredients(inputs)); + add(recipe); + return recipe; + } + + public RecipeElvenTrade add(ItemStack output, IIngredient[] inputs) { + return add(new ItemStack[]{output}, inputs); + } + + public void add(RecipeElvenTrade recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.elvenTradeRecipes.add(recipe); + } + + public boolean remove(RecipeElvenTrade recipe) { + if (recipe == null) return false; + addBackup(recipe); + return BotaniaAPI.elvenTradeRecipes.remove(recipe); + } + + public boolean removeByOutputs(ItemStack... outputs) { + if (BotaniaAPI.elvenTradeRecipes.removeIf(recipe -> { + boolean found = Arrays.stream(outputs).allMatch(output -> recipe.getOutputs().stream().anyMatch(o -> ItemStack.areItemStacksEqual(o, output))); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Elven Trade recipe") + .add("could not find recipe with outputs {}", outputs) + .error() + .post(); + return false; + } + + public boolean removeByInputs(IIngredient... inputs) { + List converted = Arrays.asList(convertIngredients(inputs)); + List list = Arrays.asList(inputs); + if (BotaniaAPI.elvenTradeRecipes.removeIf(recipe -> { + boolean found = recipe.getInputs().stream().allMatch(input -> input instanceof String ? converted.contains(input) : list.stream().anyMatch(i -> i.test((ItemStack) input))); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Elven Trade recipe") + .add("could not find recipe with inputs {}", converted) + .error() + .post(); + return false; + } + + public void removeAll() { + BotaniaAPI.elvenTradeRecipes.forEach(this::addBackup); + BotaniaAPI.elvenTradeRecipes.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(BotaniaAPI.elvenTradeRecipes).setRemover(this::remove); + } + + public class RecipeBuilder extends AbstractRecipeBuilder { + + @Override + public String getErrorMsg() { + return "Error adding Botania Elven Trade recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg, 0, 0, 0, 0); + validateItems(msg, 1, 99, 1, 99); + } + + @Override + public @Nullable RecipeElvenTrade register() { + if (!validate()) return null; + RecipeElvenTrade recipe = new RecipeElvenTrade(output.toArray(new ItemStack[0]), convertIngredients(input.toArray(new IIngredient[0]))); + add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Flowers.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Flowers.java new file mode 100644 index 000000000..5842e587d --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Flowers.java @@ -0,0 +1,21 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.subtile.SubTileEntity; +import vazkii.botania.api.subtile.signature.BasicSignature; + +public class Flowers { + + public void registerFlower(String name, Class clazz) { + BotaniaAPI.registerSubTile(name, clazz); + BotaniaAPI.registerSubTileSignature(clazz, new BasicSignature(name)); + BotaniaAPI.addSubTileToCreativeMenu(name); + } + + public void registerFlowerWithMini(String name, Class clazz, Class miniClazz) { + registerFlower(name, clazz); + BotaniaAPI.registerMiniSubTile(name + "Chibi", miniClazz, name); + BotaniaAPI.registerSubTileSignature(miniClazz, new BasicSignature(name + "Chibi")); + BotaniaAPI.addSubTileToCreativeMenu(name); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Knowledge.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Knowledge.java new file mode 100644 index 000000000..e623d90ba --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Knowledge.java @@ -0,0 +1,38 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.util.text.TextFormatting; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.lexicon.KnowledgeType; + +import javax.annotation.Nullable; + +public class Knowledge extends VirtualizedRegistry { + + @Override + public void onReload() { + removeScripted().forEach(type -> BotaniaAPI.knowledgeTypes.remove(type.id, type)); + restoreFromBackup().forEach(type -> BotaniaAPI.knowledgeTypes.put(type.id, type)); + } + + public KnowledgeType add(String id, @Nullable TextFormatting formatting, boolean autoUnlock) { + KnowledgeType type = new KnowledgeType(id, formatting != null ? formatting : TextFormatting.RESET, autoUnlock); + add(type); + return type; + } + + public KnowledgeType add(String id, @Nullable TextFormatting formatting) { + return add(id, formatting, false); + } + + public void add(KnowledgeType type) { + if (type == null) return; + addScripted(type); + BotaniaAPI.knowledgeTypes.put(type.id, type); + } + + public SimpleObjectStream streamKnowledgeTypes() { + return new SimpleObjectStream<>(BotaniaAPI.knowledgeTypes.values()); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Lexicon.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Lexicon.java new file mode 100644 index 000000000..e46bed8c0 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Lexicon.java @@ -0,0 +1,302 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.botania.recipe.PageChange; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.EntityEntry; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.lexicon.KnowledgeType; +import vazkii.botania.api.lexicon.LexiconCategory; +import vazkii.botania.api.lexicon.LexiconEntry; +import vazkii.botania.api.lexicon.LexiconPage; +import vazkii.botania.api.recipe.*; +import vazkii.botania.common.lexicon.page.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Lexicon { + public final Category category = new Category(); + public final Entry entry = new Entry(); + public final Page page = new Page(); + + public static class Category extends VirtualizedRegistry { + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.getAllCategories()::remove); + BotaniaAPI.getAllCategories().addAll(restoreFromBackup()); + } + + public LexiconCategory add(String name, ResourceLocation icon, int priority) { + LexiconCategory category = new LexiconCategory(name); + category.setIcon(icon); + category.setPriority(priority); + add(category); + return category; + } + + public LexiconCategory add(String name, ResourceLocation icon) { + return add(name, icon, 5); + } + + public void add(LexiconCategory category) { + if (category == null) return; + addScripted(category); + BotaniaAPI.addCategory(category); + } + + public boolean remove(LexiconCategory category) { + if (category == null) return false; + addBackup(category); + BotaniaAPI.getAllCategories().remove(category); + return true; + } + + public boolean removeCategory(String name) { + LexiconCategory category = Botania.getCategory(name); + if (category != null) return remove(category); + return false; + } + + public SimpleObjectStream streamCategories() { + return new SimpleObjectStream<>(BotaniaAPI.getAllCategories()).setRemover(this::remove); + } + } + + public static class Page extends VirtualizedRegistry { + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(change -> change.parent.pages.remove(change.index)); + restoreFromBackup().forEach(change -> change.parent.pages.add(change.index, change.page)); + } + + public void add(LexiconEntry entry, LexiconPage page, int index) { + if (page == null || entry == null) return; + if (entry.pages.contains(page)) return; + PageChange change = new PageChange(page, entry, index); + addScripted(change); + entry.pages.add(index, page); + } + + public boolean remove(LexiconEntry entry, LexiconPage page) { + if (page == null || entry == null) return false; + if (!entry.pages.contains(page)) return false; + int index = entry.pages.indexOf(page); + PageChange change = new PageChange(page, entry, index); + addBackup(change); + entry.pages.remove(index); + return true; + } + + public boolean remove(LexiconEntry entry, int index) { + if (entry == null) return false; + if (entry.pages.get(index) == null) return false; + LexiconPage page = entry.pages.get(index); + return remove(entry, page); + } + + public void removeAll(LexiconEntry entry) { + for (int i = 0; i < entry.pages.size(); i++) + addBackup(new PageChange(entry.pages.get(i), entry, i)); + entry.pages.clear(); + } + + public SimpleObjectStream streamPages(LexiconEntry entry) { + return new SimpleObjectStream<>(entry.pages).setRemover(page -> remove(entry, page)); + } + + public PageText createTextPage(String name) { + return new PageText(name); + } + + public PageLoreText createLoreTextPage(String name) { + return new PageLoreText(name); + } + + public PageImage createImagePage(String name, String image) { + return new PageImage(name, image); + } + + public PageEntity createEntityPage(String name, int size, String entity) { + return new PageEntity(name, entity, size); + } + + public PageEntity createEntityPage(String name, int size, EntityEntry entity) { + return createEntityPage(name, size, Objects.requireNonNull(entity.getRegistryName()).toString()); + } + + public PageCraftingRecipe createCraftingPage(String name, String... recipes) { + return new PageCraftingRecipe(name, Arrays.stream(recipes).map(ResourceLocation::new).collect(Collectors.toList())); + } + + public PageBrew createBrewingPage(String name, String bottomText, RecipeBrew recipe) { + return new PageBrew(recipe, name, bottomText); + } + + public PageManaInfusionRecipe createInfusionPage(String name, RecipeManaInfusion... recipes) { + return new PageManaInfusionRecipe(name, Arrays.asList(recipes)); + } + + public PageRuneRecipe createRunePage(String name, RecipeRuneAltar... recipes) { + return new PageRuneRecipe(name, Arrays.asList(recipes)); + } + + public PagePetalRecipe createPetalPage(String name, RecipePetals... recipes) { + return new PagePetalRecipe<>(name, Arrays.asList(recipes)); + } + + public PageElvenRecipe createElvenTradePage(String name, RecipeElvenTrade... recipes) { + return new PageElvenRecipe(name, Arrays.asList(recipes)); + } + } + + public static class Entry extends VirtualizedRegistry { + + public EntryBuilder entryBuilder() { + return new EntryBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.getAllEntries()::remove); + restoreFromBackup().forEach(entry -> { + BotaniaAPI.getAllEntries().add(entry); + entry.category.entries.add(entry); + }); + } + + public LexiconEntry add(String name, LexiconCategory category) { + LexiconEntry entry = new LexiconEntry(name, category); + add(entry); + return entry; + } + + public LexiconEntry add(String name, String category) { + return add(name, Botania.getCategory(category)); + } + + public void add(LexiconEntry entry) { + if (entry == null) return; + addScripted(entry); + BotaniaAPI.addEntry(entry, entry.category); + } + + public boolean remove(LexiconEntry entry) { + if (entry == null) return false; + addBackup(entry); + BotaniaAPI.getAllEntries().remove(entry); + entry.category.entries.remove(entry); + return true; + } + + public boolean removeEntry(String name) { + LexiconEntry entry = Botania.getEntry(name); + if (entry != null) return remove(entry); + return false; + } + + public void setKnowledgeType(String entry, KnowledgeType type) { + Objects.requireNonNull(Botania.getEntry(entry)).setKnowledgeType(type); + } + + public void setKnowledgeType(String entry, String type) { + setKnowledgeType(entry, BotaniaAPI.knowledgeTypes.get(type)); + } + + public SimpleObjectStream streamEntries() { + return new SimpleObjectStream<>(BotaniaAPI.getAllEntries()).setRemover(this::remove); + } + + public class EntryBuilder extends AbstractRecipeBuilder { + + protected String name; + protected LexiconCategory category; + protected KnowledgeType type = BotaniaAPI.basicKnowledge; + protected ItemStack icon = ItemStack.EMPTY; + protected final List pages = new ArrayList<>(); + protected final List extraRecipes = new ArrayList<>(); + protected boolean priority = false; + + public EntryBuilder isPriority() { + this.priority = true; + return this; + } + + public EntryBuilder icon(IIngredient icon) { + this.icon = icon.getMatchingStacks()[0]; + return this; + } + + public EntryBuilder name(String name) { + this.name = name; + return this; + } + + public EntryBuilder category(LexiconCategory category) { + this.category = category; + return this; + } + + public EntryBuilder category(String categoryName) { + return category(Botania.getCategory(categoryName)); + } + + public EntryBuilder knowledgeType(KnowledgeType type) { + this.type = type; + return this; + } + + public EntryBuilder page(LexiconPage page) { + this.pages.add(page); + return this; + } + + public EntryBuilder extraRecipe(IIngredient stack) { + this.extraRecipes.add(stack.getMatchingStacks()[0]); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Botania Lexicon Entry"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg, 0, 0, 0, 0); + validateItems(msg, 0, 0, 0, 0); + msg.add(name == null, "expected a valid name, got " + name); + msg.add(pages.size() < 1, "entry must have at least 1 page, got " + pages.size()); + msg.add(category == null, "expected a valid category, got " + category); + } + + @Override + public @Nullable LexiconEntry register() { + if (!validate()) return null; + LexiconEntry entry = new LexiconEntry(name, category); + if (priority) entry.setPriority(); + entry.setKnowledgeType(type); + entry.setIcon(icon); + pages.forEach(entry::addPage); + extraRecipes.forEach(entry::addExtraDisplayedRecipe); + add(entry); + return entry; + } + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Magnet.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Magnet.java new file mode 100644 index 000000000..4b98b3d97 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Magnet.java @@ -0,0 +1,74 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.botania.recipe.MagnetSubject; +import com.cleanroommc.groovyscript.core.mixin.botania.BotaniaAPIAccessor; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import vazkii.botania.api.BotaniaAPI; + +public class Magnet extends VirtualizedRegistry { + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(m -> BotaniaAPI.magnetBlacklist.remove(m.getMagnetKey())); + restoreFromBackup().forEach(m -> BotaniaAPI.magnetBlacklist.add(m.getMagnetKey())); + } + + public boolean isInBlacklist(IIngredient item) { + return BotaniaAPI.isItemBlacklistedFromMagnet(item.getMatchingStacks()[0]); + } + + public boolean isInBlacklist(Block block, int meta) { + return BotaniaAPI.isBlockBlacklistedFromMagnet(block, meta); + } + + public boolean isInBlacklist(IBlockState state) { + return BotaniaAPI.isBlockBlacklistedFromMagnet(state); + } + + public void addToBlacklist(IIngredient item) { + if (item == null) return; + MagnetSubject subject = new MagnetSubject(item); + addScripted(subject); + BotaniaAPI.blacklistItemFromMagnet(item.getMatchingStacks()[0]); + } + + public void addToBlacklist(Block block, int meta) { + if (block == null) return; + MagnetSubject subject = new MagnetSubject(block, meta); + addScripted(subject); + BotaniaAPI.blacklistBlockFromMagnet(block, meta); + } + + public void addToBlacklist(IBlockState state) { + addToBlacklist(state.getBlock(), state.getBlock().getMetaFromState(state)); + } + + public boolean removeFromBlacklist(IIngredient item) { + if (item == null) return false; + String key = BotaniaAPIAccessor.invokeGetMagnetKey(item.getMatchingStacks()[0]); + if (!BotaniaAPI.magnetBlacklist.contains(key)) return false; + MagnetSubject subject = new MagnetSubject(item); + addBackup(subject); + BotaniaAPI.magnetBlacklist.remove(key); + return true; + } + + public boolean removeFromBlacklist(Block block, int meta) { + if (block == null) return false; + String key = BotaniaAPIAccessor.invokeGetMagnetKey(block, meta); + if (!BotaniaAPI.magnetBlacklist.contains(key)) return false; + MagnetSubject subject = new MagnetSubject(block, meta); + addBackup(subject); + BotaniaAPI.magnetBlacklist.remove(key); + return true; + } + + public boolean removeFromBlacklist(IBlockState state) { + return removeFromBlacklist(state.getBlock(), state.getBlock().getMetaFromState(state)); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/ManaInfusion.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/ManaInfusion.java new file mode 100644 index 000000000..605db69f4 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/ManaInfusion.java @@ -0,0 +1,128 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.recipe.RecipeManaInfusion; + +public class ManaInfusion extends VirtualizedRegistry { + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.manaInfusionRecipes::remove); + BotaniaAPI.manaInfusionRecipes.addAll(restoreFromBackup()); + } + + public RecipeManaInfusion add(ItemStack output, IIngredient input, int mana) { + RecipeManaInfusion recipe = new RecipeManaInfusion(output, input instanceof OreDictIngredient ? ((OreDictIngredient) input).getOreDict() : input.getMatchingStacks()[0], mana); + add(recipe); + return recipe; + } + + public void add(RecipeManaInfusion recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.manaInfusionRecipes.add(recipe); + } + + public boolean remove(RecipeManaInfusion recipe) { + if (recipe == null) return false; + addBackup(recipe); + return BotaniaAPI.manaInfusionRecipes.remove(recipe); + } + + public boolean removeByOutput(ItemStack output) { + if (BotaniaAPI.manaInfusionRecipes.removeIf(recipe -> { + boolean found = ItemStack.areItemStacksEqual(recipe.getOutput(), output); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Mana Infusion recipe") + .add("could not find recipe with output {}", output) + .error() + .post(); + return false; + } + + public boolean removeByInput(IIngredient input) { + if (BotaniaAPI.manaInfusionRecipes.removeIf(recipe -> { + boolean found = recipe.getInput() instanceof ItemStack ? input.test((ItemStack) recipe.getInput()) : (input instanceof OreDictIngredient && ((OreDictIngredient) input).getOreDict().equals(recipe.getInput())); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Mana Infusion recipe") + .add("could not find recipe with input {}", input) + .error() + .post(); + return false; + } + + public void removeAll() { + BotaniaAPI.manaInfusionRecipes.forEach(this::addBackup); + BotaniaAPI.manaInfusionRecipes.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(BotaniaAPI.manaInfusionRecipes).setRemover(this::remove); + } + + public class RecipeBuilder extends AbstractRecipeBuilder { + + protected int mana = 100; + protected IBlockState catalyst; + + public RecipeBuilder mana(int amount) { + this.mana = amount; + return this; + } + + public RecipeBuilder catalyst(IBlockState block) { + this.catalyst = block; + return this; + } + + public RecipeBuilder useAlchemy() { + return catalyst(RecipeManaInfusion.alchemyState); + } + + public RecipeBuilder useConjuration() { + return catalyst(RecipeManaInfusion.conjurationState); + } + + @Override + public String getErrorMsg() { + return "Error adding Botania Mana Infusion recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg, 0, 0, 0, 0); + validateItems(msg, 1, 1, 1, 1); + msg.add(mana < 1, "Mana amount must be at least 1, got " + mana); + } + + @Override + public @Nullable RecipeManaInfusion register() { + if (!validate()) return null; + RecipeManaInfusion recipe = new RecipeManaInfusion(output.get(0), input.get(0) instanceof OreDictIngredient ? ((OreDictIngredient) input.get(0)).getOreDict() : input.get(0).getMatchingStacks()[0], mana); + if (catalyst != null) recipe.setCatalyst(catalyst); + add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Orechid.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Orechid.java new file mode 100644 index 000000000..765e40359 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/Orechid.java @@ -0,0 +1,81 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.botania.recipe.OrechidRecipe; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import vazkii.botania.api.BotaniaAPI; + +import java.util.ArrayList; +import java.util.List; + +public class Orechid extends VirtualizedRegistry { + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(recipe -> BotaniaAPI.oreWeights.remove(recipe.output)); + restoreFromBackup().forEach(recipe -> BotaniaAPI.oreWeights.put(recipe.output, recipe.weight)); + } + + protected List getAllRecipes() { + List recipes = new ArrayList<>(BotaniaAPI.oreWeights.size()); + BotaniaAPI.oreWeights.forEach((ore, weight) -> recipes.add(new OrechidRecipe(ore, weight))); + return recipes; + } + + public OrechidRecipe add(String output, int weight) { + OrechidRecipe recipe = new OrechidRecipe(output, weight); + add(recipe); + return recipe; + } + + public OrechidRecipe add(OreDictIngredient output, int weight) { + return add(output.getOreDict(), weight); + } + + public void add(OrechidRecipe recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.oreWeights.put(recipe.output, recipe.weight); + } + + public boolean remove(OrechidRecipe recipe) { + if (recipe == null) return false; + if (BotaniaAPI.oreWeights.containsKey(recipe.output)) { + addBackup(recipe); + BotaniaAPI.oreWeights.remove(recipe.output); + return true; + } + return false; + } + + public boolean removeByOutput(String output) { + if (BotaniaAPI.oreWeights.containsKey(output)) { + addBackup(new OrechidRecipe(output, BotaniaAPI.getOreWeight(output))); + BotaniaAPI.oreWeights.remove(output); + return true; + } + + GroovyLog.msg("Error removing Botania Orechid recipe") + .add("could not find recipe for oredict {}", output) + .error() + .post(); + return false; + } + + public boolean removeByOutput(OreDictIngredient output) { + return removeByOutput(output.getOreDict()); + } + + public void removeAll() { + getAllRecipes().forEach(this::addBackup); + BotaniaAPI.oreWeights.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(getAllRecipes(), false).setRemover(this::remove); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/OrechidIgnem.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/OrechidIgnem.java new file mode 100644 index 000000000..c5accd124 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/OrechidIgnem.java @@ -0,0 +1,66 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.botania.recipe.OrechidRecipe; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import vazkii.botania.api.BotaniaAPI; + +import java.util.ArrayList; +import java.util.List; + +public class OrechidIgnem extends Orechid { + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(recipe -> BotaniaAPI.oreWeightsNether.remove(recipe.output)); + restoreFromBackup().forEach(recipe -> BotaniaAPI.oreWeightsNether.put(recipe.output, recipe.weight)); + } + + @Override + protected List getAllRecipes() { + List list = new ArrayList<>(BotaniaAPI.oreWeightsNether.size()); + BotaniaAPI.oreWeightsNether.forEach((ore, weight) -> list.add(new OrechidRecipe(ore, weight))); + return list; + } + + @Override + public void add(OrechidRecipe recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.oreWeightsNether.put(recipe.output, recipe.weight); + } + + @Override + public boolean remove(OrechidRecipe recipe) { + if (recipe == null) return false; + if (BotaniaAPI.oreWeightsNether.containsKey(recipe.output)) { + addBackup(recipe); + BotaniaAPI.oreWeightsNether.remove(recipe.output); + return true; + } + return false; + } + + @Override + public boolean removeByOutput(String output) { + if (BotaniaAPI.oreWeightsNether.containsKey(output)) { + addBackup(new OrechidRecipe(output, BotaniaAPI.getOreWeight(output))); + BotaniaAPI.oreWeightsNether.remove(output); + return true; + } + + GroovyLog.msg("Error removing Botania OrechidIgnem recipe") + .add("could not find recipe for oredict {}", output) + .error() + .post(); + return false; + } + + @Override + public void removeAll() { + getAllRecipes().forEach(this::addBackup); + BotaniaAPI.oreWeightsNether.clear(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/PureDaisy.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/PureDaisy.java new file mode 100644 index 000000000..3db5c93b4 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/PureDaisy.java @@ -0,0 +1,158 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.recipe.RecipePureDaisy; + +public class PureDaisy extends VirtualizedRegistry { + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.pureDaisyRecipes::remove); + BotaniaAPI.pureDaisyRecipes.addAll(restoreFromBackup()); + } + + public RecipePureDaisy add(IBlockState output, IBlockState input, int time) { + RecipePureDaisy recipe = new RecipePureDaisy(input, output, time); + add(recipe); + return recipe; + } + + public RecipePureDaisy add(IBlockState output, IBlockState input) { + return add(output, input, RecipePureDaisy.DEFAULT_TIME); + } + + public void add(RecipePureDaisy recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.pureDaisyRecipes.add(recipe); + } + + public boolean remove(RecipePureDaisy recipe) { + if (recipe == null) return false; + addBackup(recipe); + return BotaniaAPI.pureDaisyRecipes.remove(recipe); + } + + public boolean removeByOutput(IBlockState output) { + if (BotaniaAPI.pureDaisyRecipes.removeIf(recipe -> { + boolean found = recipe.getOutputState().equals(output); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Pure Daisy recipe") + .add("could not find recipe with output {}", output) + .error() + .post(); + return false; + } + + public boolean removeByInput(String input) { + if (BotaniaAPI.pureDaisyRecipes.removeIf(recipe -> { + boolean found = recipe.getInput() instanceof String && recipe.getInput().equals(input); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Pure Daisy recipe") + .add("could not find recipe with input {}", input) + .error() + .post(); + return false; + } + + public boolean removeByInput(IBlockState input) { + if (BotaniaAPI.pureDaisyRecipes.removeIf(recipe -> { + boolean found = (recipe.getInput() instanceof IBlockState && recipe.getInput().equals(input)) || (recipe.getInput() instanceof Block && recipe.getInput() == input.getBlock()); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Pure Daisy recipe") + .add("could not find recipe with input {}", input) + .error() + .post(); + return false; + } + + public boolean removeByInput(Block input) { + return removeByInput(input.getDefaultState()); + } + + public void removeAll() { + BotaniaAPI.pureDaisyRecipes.forEach(this::addBackup); + BotaniaAPI.pureDaisyRecipes.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(BotaniaAPI.pureDaisyRecipes).setRemover(this::remove); + } + + public class RecipeBuilder extends AbstractRecipeBuilder { + + protected int time = RecipePureDaisy.DEFAULT_TIME; + protected IBlockState output; + protected Object input; + + public RecipeBuilder time(int amount) { + this.time = amount; + return this; + } + + public RecipeBuilder output(IBlockState output) { + this.output = output; + return this; + } + + public RecipeBuilder input(IBlockState input) { + this.input = input; + return this; + } + + public RecipeBuilder input(Block input) { + return input(input.getDefaultState()); + } + + public RecipeBuilder input(String input) { + this.input = input; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Botania Pure Daisy recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 0, 0, 0, 0); + validateFluids(msg, 0, 0, 0, 0); + msg.add(time < 0, "time must be at least 1, got " + time); + msg.add(output != null, "expected IBlockState output, got " + output); + msg.add(input != null, "expected IBlockState or String input, got " + input); + } + + @Override + public @Nullable RecipePureDaisy register() { + if (!validate()) return null; + RecipePureDaisy recipe = new RecipePureDaisy(input, output, time); + add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/RuneAltar.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/RuneAltar.java new file mode 100644 index 000000000..3c245dffd --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/RuneAltar.java @@ -0,0 +1,121 @@ +package com.cleanroommc.groovyscript.compat.mods.botania; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.recipe.RecipeRuneAltar; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class RuneAltar extends VirtualizedRegistry { + + public RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + @GroovyBlacklist + public void onReload() { + removeScripted().forEach(BotaniaAPI.runeAltarRecipes::remove); + BotaniaAPI.runeAltarRecipes.addAll(restoreFromBackup()); + } + + public RecipeRuneAltar add(ItemStack output, int mana, IIngredient... inputs) { + RecipeRuneAltar recipe = new RecipeRuneAltar(output, mana, Arrays.stream(inputs).map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).toArray()); + add(recipe); + return recipe; + } + + public void add(RecipeRuneAltar recipe) { + if (recipe == null) return; + addScripted(recipe); + BotaniaAPI.runeAltarRecipes.add(recipe); + } + + public boolean remove(RecipeRuneAltar recipe) { + if (recipe == null) return false; + addBackup(recipe); + return BotaniaAPI.runeAltarRecipes.remove(recipe); + } + + public boolean removeByOutput(IIngredient output) { + if (BotaniaAPI.runeAltarRecipes.removeIf(recipe -> { + boolean found = output.test(recipe.getOutput()); + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Rune Altar recipe") + .add("could not find recipe with output {}", output) + .error() + .post(); + return false; + } + + public boolean removeByInput(IIngredient... inputs) { + List converted = Arrays.stream(inputs).map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).collect(Collectors.toList()); + if (BotaniaAPI.runeAltarRecipes.removeIf(recipe -> { + boolean found = converted.stream().allMatch(o -> recipe.getInputs().stream().anyMatch(i -> (i instanceof String || o instanceof String)? i.equals(o) : ItemStack.areItemStacksEqual((ItemStack) i, (ItemStack) o)));; + if (found) addBackup(recipe); + return found; + })) return true; + + GroovyLog.msg("Error removing Botania Rune Altar recipe") + .add("could not find recipe with inputs {}", converted) + .error() + .post(); + return false; + } + + public boolean removeByInputs(IIngredient... inputs) { + return removeByInput(inputs); + } + + public void removeAll() { + BotaniaAPI.runeAltarRecipes.forEach(this::addBackup); + BotaniaAPI.runeAltarRecipes.clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(BotaniaAPI.runeAltarRecipes).setRemover(this::remove); + } + + public class RecipeBuilder extends AbstractRecipeBuilder { + + protected int mana; + + public RecipeBuilder mana(int amount) { + this.mana = amount; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Botania Rune Altar recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateFluids(msg, 0, 0, 0, 0); + validateItems(msg, 1, 20, 1, 1); + msg.add(mana < 1, "mana must be at least 1, got " + mana); + } + + @Override + public @Nullable RecipeRuneAltar register() { + if (!validate()) return null; + RecipeRuneAltar recipe = new RecipeRuneAltar(output.get(0), mana, input.stream().map(i -> i instanceof OreDictIngredient ? ((OreDictIngredient) i).getOreDict() : i.getMatchingStacks()[0]).toArray()); + add(recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/MagnetSubject.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/MagnetSubject.java new file mode 100644 index 000000000..472294e8d --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/MagnetSubject.java @@ -0,0 +1,31 @@ +package com.cleanroommc.groovyscript.compat.mods.botania.recipe; + +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.core.mixin.botania.BotaniaAPIAccessor; +import net.minecraft.block.Block; + +public class MagnetSubject { + public final IIngredient item; + public final Block block; + public final int meta; + + public MagnetSubject(IIngredient item) { + this.item = item; + this.meta = 0; + this.block = null; + } + + public MagnetSubject(Block block, int meta) { + this.meta = meta; + this.block = block; + this.item = null; + } + + public boolean isBlock() { + return block != null; + } + + public String getMagnetKey() { + return isBlock() ? BotaniaAPIAccessor.invokeGetMagnetKey(block, meta) : BotaniaAPIAccessor.invokeGetMagnetKey(item.getMatchingStacks()[0]); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/OrechidRecipe.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/OrechidRecipe.java new file mode 100644 index 000000000..d3bf89482 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/OrechidRecipe.java @@ -0,0 +1,12 @@ +package com.cleanroommc.groovyscript.compat.mods.botania.recipe; + +public class OrechidRecipe { + + public final int weight; + public final String output; + + public OrechidRecipe(String output, int weight) { + this.weight = weight; + this.output = output; + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/PageChange.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/PageChange.java new file mode 100644 index 000000000..990747176 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/botania/recipe/PageChange.java @@ -0,0 +1,16 @@ +package com.cleanroommc.groovyscript.compat.mods.botania.recipe; + +import vazkii.botania.api.lexicon.LexiconEntry; +import vazkii.botania.api.lexicon.LexiconPage; + +public class PageChange { + public LexiconPage page; + public LexiconEntry parent; + public int index; + + public PageChange(LexiconPage page, LexiconEntry parent, int index) { + this.page = page; + this.parent = parent; + this.index = index; + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java index 8bbd7d91f..8e98391a6 100644 --- a/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java +++ b/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java @@ -12,7 +12,7 @@ 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", "extendedcrafting"); + "tcomplement", "extendedcrafting", "botania"); @Override public List getMixinConfigs() { diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/botania/BotaniaAPIAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/botania/BotaniaAPIAccessor.java new file mode 100644 index 000000000..e78f7bc0c --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/botania/BotaniaAPIAccessor.java @@ -0,0 +1,21 @@ +package com.cleanroommc.groovyscript.core.mixin.botania; + +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; +import vazkii.botania.api.BotaniaAPI; + +@Mixin(value = BotaniaAPI.class, remap = false) +public interface BotaniaAPIAccessor { + + @Invoker + static String invokeGetMagnetKey(ItemStack stack) { + throw new AssertionError(); + } + + @Invoker + static String invokeGetMagnetKey(Block block, int meta) { + throw new AssertionError(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/botania/PageCraftingRecipeMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/botania/PageCraftingRecipeMixin.java new file mode 100644 index 000000000..c38199264 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/botania/PageCraftingRecipeMixin.java @@ -0,0 +1,90 @@ +package com.cleanroommc.groovyscript.core.mixin.botania; + +import com.cleanroommc.groovyscript.compat.vanilla.ShapedCraftingRecipe; +import com.cleanroommc.groovyscript.compat.vanilla.ShapelessCraftingRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import vazkii.botania.api.internal.IGuiLexiconEntry; +import vazkii.botania.common.lexicon.page.PageCraftingRecipe; +import vazkii.botania.common.lexicon.page.PageRecipe; + +@Mixin(value = PageCraftingRecipe.class, remap = false) +public abstract class PageCraftingRecipeMixin extends PageRecipe { + @Shadow + boolean shapelessRecipe; + @Shadow + boolean oreDictRecipe; + @Shadow + int ticksElapsed; + + public PageCraftingRecipeMixin(String unlocalizedName) { + super(unlocalizedName); + } + + /** + * @author Turing6 + * @reason Groovyscript's recipe classes are ignored during ingredient render + */ + @SideOnly(Side.CLIENT) + @Overwrite + public void renderCraftingRecipe(IGuiLexiconEntry gui, IRecipe recipe) { + if (recipe != null) { + int x; + int y; + int index; + if (!(recipe instanceof ShapedRecipes) && !(recipe instanceof ShapedOreRecipe) && !(recipe instanceof ShapedCraftingRecipe)) { + if (recipe instanceof ShapelessRecipes || recipe instanceof ShapelessOreRecipe || recipe instanceof ShapelessCraftingRecipe) { + this.shapelessRecipe = true; + this.oreDictRecipe = recipe instanceof ShapelessOreRecipe; + + drawGrid: + for(y = 0; y < 3; ++y) { + for(x = 0; x < 3; ++x) { + index = y * 3 + x; + if (index >= recipe.getIngredients().size()) { + break drawGrid; + } + + Ingredient input = recipe.getIngredients().get(index); + if (input != Ingredient.EMPTY) { + ItemStack[] stacks = input.getMatchingStacks(); + this.renderItemAtGridPos(gui, 1 + x, 1 + y, stacks[this.ticksElapsed / 40 % stacks.length], true); + } + } + } + } + } else { + this.oreDictRecipe = recipe instanceof ShapedOreRecipe; + int width = oreDictRecipe + ? ReflectionHelper.getPrivateValue(ShapedOreRecipe.class, (ShapedOreRecipe) recipe, "width") + : recipe instanceof ShapedCraftingRecipe ? ((ShapedCraftingRecipe) recipe).getRecipeWidth() : ((ShapedRecipes) recipe).getWidth(); + int height = oreDictRecipe + ? ReflectionHelper.getPrivateValue(ShapedOreRecipe.class, (ShapedOreRecipe) recipe, "height") + : recipe instanceof ShapedCraftingRecipe ? ((ShapedCraftingRecipe) recipe).getRecipeHeight() : ((ShapedRecipes) recipe).getHeight(); + + for(index = 0; index < height; ++index) { + for(x = 0; x < width; ++x) { + Ingredient input = recipe.getIngredients().get(index * width + x); + ItemStack[] stacks = input.getMatchingStacks(); + if (stacks.length > 0) { + this.renderItemAtGridPos(gui, 1 + x, 1 + index, stacks[this.ticksElapsed / 40 % stacks.length], true); + } + } + } + } + + this.renderItemAtGridPos(gui, 2, 0, recipe.getRecipeOutput(), false); + } + } +} diff --git a/src/main/resources/mixin.groovyscript.botania.json b/src/main/resources/mixin.groovyscript.botania.json new file mode 100644 index 000000000..26eca1b73 --- /dev/null +++ b/src/main/resources/mixin.groovyscript.botania.json @@ -0,0 +1,11 @@ +{ + "package": "com.cleanroommc.groovyscript.core.mixin.botania", + "refmap": "mixins.groovyscript.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "BotaniaAPIAccessor", + "PageCraftingRecipeMixin" + ] +} \ No newline at end of file