diff --git a/build.gradle b/build.gradle index e0693f8ac..c200f7f8d 100644 --- a/build.gradle +++ b/build.gradle @@ -116,6 +116,18 @@ dependencies { deobfProvided 'curse.maven:blood-magic-224791:2822288' } + if (project.debug_roots.toBoolean()) { + deobfCompile 'curse.maven:patchouli-306770:3162874' + deobfCompile 'curse.maven:mystical_lib-277064:3483816' + deobfCompile 'curse.maven:mystical_world-282940:3460961' + deobfCompile 'curse.maven:roots-246183:3905074' + } else { + deobfProvided 'curse.maven:patchouli-306770:3162874' + deobfProvided 'curse.maven:mystical_lib-277064:3483816' + deobfProvided 'curse.maven:mystical_world-282940:3460961' + deobfProvided 'curse.maven:roots-246183:3905074' + } + if (project.debug_extended_crafting.toBoolean()) { deobfCompile 'curse.maven:cucumber-272335:2645867' deobfCompile 'curse.maven:extended-crafting-nomifactory-edition-398267:3613140' diff --git a/examples/roots.groovy b/examples/roots.groovy new file mode 100644 index 000000000..27fd5ba57 --- /dev/null +++ b/examples/roots.groovy @@ -0,0 +1,459 @@ + +// Bracket Handlers + +// Cost Bracket Handler +cost('no_cost') +cost('additional_cost') +cost('all_cost_multiplier') +cost('specific_cost_adjustment') +cost('specific_cost_multiplier') + +// Herb Bracket Handler +herb('spirit_herb') +herb('baffle_cap') +herb('moonglow_leaf') +herb('pereskia') +herb('terra_moss') +herb('wildroot') +herb('wildewheet') +herb('infernal_bulb') +herb('dewgonia') +herb('stalicripe') +herb('cloud_berry') + +// Spell Bracket Handler +spell('roots:spell_geas') +spell('spell_geas') // Automatically adds `roots:` +spell('geas') // Automatically adds `roots:spell_` + +// Modifier Bracket Handler +modifier('roots:extended_geas') + +// Ritual Bracket Handler +ritual('ritual_summon_creatures') +ritual('summon_creatures') // Automatically prefixes `ritual_` + + +// Animal Harvest: +// Animal Harvest is a ritual that drops items from nearby mobs based on that mob's loottable without harming the mob. Only applies to allowed mobs. +mods.roots.animalharvest.recipeBuilder() + .name('wither_skeleton_harvest') // Optional, either a ResourceLocation or a String + .entity(entity('minecraft:wither_skeleton')) // Target Entity must extend EntityLivingBase + .register() + +mods.roots.animalharvest.recipeBuilder() + .entity(entity('minecraft:enderman')) + .register() + +mods.roots.animalharvest.removeByName(resource('roots:chicken')) +mods.roots.animalharvest.removeByEntity(entity('minecraft:pig')) +//mods.roots.animalharvest.removeAll() + + +// Animal Harvest Fish: +// Animal Harvest Fish is another effect of the Animal Harvest ritual that applies if there are water source blocks within the ritual range. +mods.roots.animalharvestfish.recipeBuilder() + .name('clay_fish') // Optional, either a ResourceLocation or a String + .weight(50) + .output(item('minecraft:clay')) + .register() + +mods.roots.animalharvestfish.recipeBuilder() + .weight(13) + .fish(item('minecraft:gold_ingot')) // fish is an alternative to output + .register() + +mods.roots.animalharvestfish.removeByName(resource('roots:cod')) +mods.roots.animalharvestfish.removeByOutput(item('minecraft:fish:1')) +mods.roots.animalharvestfish.removeByFish(item('minecraft:fish:2')) +//mods.roots.animalharvestfish.removeAll() + + +// Bark Carving: +// Bark Carving is a special set of alternate drops for blocks when broken with an item containing the tool type 'knife'. Amount dropped is up to 2 + fortune/looting level higher than the set amount. +// bark or barkcarving +mods.roots.bark.recipeBuilder() // also accessible via BarkCarving + .name('gold_bark') // Optional, either a ResourceLocation or a String + .input(item('minecraft:clay')) // An item that would be dropped by a block when broken + .output(item('minecraft:gold_ingot')) + .register() + +mods.roots.bark.recipeBuilder() + .blockstate(blockstate('minecraft:gold_block')) + .output(item('minecraft:diamond')) + .register() + +mods.roots.bark.recipeBuilder() + .input(blockstate('minecraft:diamond_block')) + .output(item('minecraft:clay') * 10) + .register() + +mods.roots.bark.removeByName(resource('roots:wildwood')) +mods.roots.bark.removeByInput(item('minecraft:log')) +mods.roots.bark.removeByBlock(item('minecraft:log:1')) +mods.roots.bark.removeByOutput(item('roots:bark_dark_oak')) +//mods.roots.bark.removeAll() + + +// Chrysopoeia: +// Chrysopoeia is a spell that transmutes items held in the main hand. +mods.roots.chrysopoeia.recipeBuilder() + .name('clay_transmute') // Optional, either a ResourceLocation or a String + .input(item('minecraft:gold_ingot')) + .output(item('minecraft:clay')) + .register() + +mods.roots.chrysopoeia.recipeBuilder() + .input(item('minecraft:diamond') * 3) + .output(item('minecraft:gold_ingot') * 3) + .register() + +mods.roots.chrysopoeia.removeByName(resource('roots:gold_from_silver')) +mods.roots.chrysopoeia.removeByInput(item('minecraft:rotten_flesh')) +mods.roots.chrysopoeia.removeByOutput(item('minecraft:iron_nugget')) +//mods.roots.chrysopoeia.removeAll() + + +// Fey Crafter: +// The Fey Crafter is a crafting mechanism that requires an activated Grove Stone nearby to take 5 item inputs and return an item output. +mods.roots.feycrafter.recipeBuilder() + .name('clay_craft') // Optional, either a ResourceLocation or a String + .input(item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone')) // Must be exactly 5 + .output(item('minecraft:clay')) + .xp(100) // Optional, int + .register() + +mods.roots.feycrafter.removeByName(resource('roots:unending_bowl')) // WARNING: When reloading recipes with the Fey Crafter, you may encounter a ConcurrentModificationException! +mods.roots.feycrafter.removeByOutput(item('minecraft:gravel')) +//mods.roots.feycrafter.removeAll() + + +// Flower Generation: +// When running the Flower Growth Ritual, allowed flowers will generate in the area. Additionally, using the spell Growth Infusion's Floral Reproduction modifier will duplicate the flower. +mods.roots.flowergeneration.recipeBuilder() + .name('clay_flower') // Optional, either a ResourceLocation or a String + .flower(blockstate('minecraft:clay')) + .register() + +mods.roots.flowergeneration.removeByName(resource('roots:dandelion')) +//mods.roots.flowergeneration.removeByFlower(block('minecraft:red_flower')) // Removes by all blockstates of the block +mods.roots.flowergeneration.removeByFlower(block('minecraft:red_flower'), 1) +mods.roots.flowergeneration.removeByFlower(blockstate('minecraft:red_flower:2')) +mods.roots.flowergeneration.removeByFlower(item('minecraft:red_flower:3')) +//mods.roots.flowergeneration.removeAll() + + +// Life Essence: +// When shift right clicking a mob in the Life Essence Pool with Runic Shears, it will drop a Life-Essence, which allows that mob to be spawned via the Creature Summoning ritual. +mods.roots.lifeessence.add(entity('minecraft:wither_skeleton')) + +mods.roots.lifeessence.remove(entity('minecraft:sheep')) +//mods.roots.lifeessence.removeAll() + + +// Mortar And Pestle: +// When right clicking a mortar containing the input items with a pestle, it will display a few colored sparkles, consume the inputs, and drop the item output. +mods.roots.mortar.recipeBuilder() // also accessible via MortarAndPestle + .name('clay_mortar') // Optional, either a ResourceLocation or a String + .input(item('minecraft:stone'),item('minecraft:gold_ingot'),item('minecraft:stone'),item('minecraft:gold_ingot'),item('minecraft:stone')) // Between 1 and 5 + .generate(false) // Optional, when inputs = 3 and generate isnt disabled, creates a recipe for each amount of items + .output(item('minecraft:clay')) + .color(1, 0, 0.1, 1, 0, 0.1) // Optional, sets color as red1, green1, blue1, red2, green2, blue2. All values must be a float between 0 and 1 + .register() + +mods.roots.mortarandpestle.recipeBuilder() + .input(item('minecraft:clay')) // With generate being true and only 1 input, this will generate a recipe for each amount of inputs to 5. Without this, only 1 clay could be converted at a time + .output(item('minecraft:diamond')) + .color(0, 0, 0.1) // Optional, sets the color as red, green, blue. All values must be a float between 0 and 1 + .register() + +mods.roots.mortar.recipeBuilder() + .input(item('minecraft:diamond'), item('minecraft:diamond')) + .output(item('minecraft:gold_ingot') * 16) + .red(0) // Optional, sets red1 and red2. All values must be a float between 0 and 1 + .green1(0.5) // Optional. The value must be a float between 0 and 1 + .green2(1) // Optional. The value must be a float between 0 and 1 + .register() + +mods.roots.mortar.removeByName(resource('roots:wheat_flour')) // Many Mortar recipes are generated and have the resource location of [base]_x, where x is the output. They can all be removed by running removeByName(base) +mods.roots.mortar.removeByOutput(item('minecraft:string')) +//mods.roots.mortar.removeAll() + + +// Moss: +// Moss indicates a pair of items that can right click the input with a knife to turn it into the output and give a Terra Moss and right click the output with moss spores to turn it into the input. +mods.roots.moss.recipeBuilder() + .input(item('minecraft:gold_block')) + .output(item('minecraft:clay')) + .register() + +mods.roots.moss.add(item('minecraft:stained_glass:3'), item('minecraft:stained_glass:4')) + +mods.roots.moss.remove(item('minecraft:cobblestone')) +//mods.roots.moss.removeAll() + + +// Pacifist: +// Pacifist is a list of entities which killing will give the player the advancement 'Untrue Pacifist'. +mods.roots.pacifist.recipeBuilder() + .name('wither_skeleton_pacifist') // Optional, either a ResourceLocation or a String + .entity(entity('minecraft:wither_skeleton')) + .register() + +mods.roots.pacifist.removeByName(resource('minecraft:chicken')) +mods.roots.pacifist.removeByEntity(entity('minecraft:cow')) +//mods.roots.pacifist.removeAll() + + +// Pyre: +// Converts 5 input items into the ouput after a period of time when the Pyre is lit on fire. +mods.roots.pyre.recipeBuilder() + .name('clay_from_fire') // Optional, either a ResourceLocation or a String + .input(item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone'),item('minecraft:stone')) + .output(item('minecraft:clay')) + .xp(5) // Optional, XP given when the recipe finishes in levels. Default 0 + .time(1) // Optional, time in ticks for the recipe to procress. Default 200 + .register() + +mods.roots.pyre.recipeBuilder() + .input(item('minecraft:gold_ingot'),item('minecraft:clay'),item('minecraft:clay'),item('minecraft:stone'),item('minecraft:stone')) + .output(item('minecraft:diamond') * 32) + .levels(5) // Optional, XP given when the recipe finishes in levels. Default 0 + .burnTime(1000) // Optional, time in ticks for the recipe to procress. Default 200 + .register() + +mods.roots.pyre.removeByName(resource('roots:infernal_bulb')) +mods.roots.pyre.removeByOutput(item('minecraft:gravel')) +//mods.roots.pyre.removeAll() + + +// Runic Shear Block: +// Right clicking a Runic Shear on a block to convert it into a replacement block and drop items +mods.roots.runicshearblock.recipeBuilder() + .name('clay_from_runic_diamond') // Optional, either a ResourceLocation or a String + .state(blockstate('minecraft:diamond_block')) // Either an IBlockState or BlockStatePredicate + .replacementState(blockstate('minecraft:air')) // NOTE: Not displayed in JEI + .output(item('minecraft:clay') * 64) + .displayItem(item('minecraft:diamond') * 9) // Optional, represents the state. Otherwise, is the first itemstack found from the given state + .register() + +mods.roots.runicshearblock.recipeBuilder() + .state(mods.roots.predicates.stateBuilder().blockstate(blockstate('minecraft:yellow_flower:type=dandelion')).properties('type').register()) + .replacementState(blockstate('minecraft:red_flower:type=poppy')) + .output(item('minecraft:gold_ingot')) + .register() + +mods.roots.runicshearblock.removeByName(resource('roots:wildewheet')) +mods.roots.runicshearblock.removeByOutput(item('roots:spirit_herb')) +mods.roots.runicshearblock.removeByState(blockstate('minecraft:beetroots:age=3')) +//mods.roots.runicshearblock.removeAll() + + +// Runic Shear Entity: +// Right clicking a Runic Shear on an entity. The entity will have a cooldown, preventing spamming. +// === WARNING: Not Reloadable === +mods.roots.runicshearentity.recipeBuilder() + .name('clay_from_wither_skeletons') // Optional, either a ResourceLocation or a String + .entity(entity('minecraft:wither_skeleton')) + .output(item('minecraft:clay')) + .cooldown(1000) // Optional, time in ticks between harvesting. Default of 0 + .register() + +mods.roots.runicshearentity.recipeBuilder() + .name('creeper_at_the_last_moment') // Optional, either a ResourceLocation or a String + .entity(entity('minecraft:creeper')) + .output(item('minecraft:diamond'), item('minecraft:nether_star')) // WARNING: JEI will not display this properly. Add a tooltip to the first item informing about all options. + .functionMap({ entityLivingBase -> + // If the creeper has ignited (been right clicked with Flint and Steel), it will drop a Nether Star. Otherwise, its just dirt. + if (entityLivingBase.hasIgnited()) return item('minecraft:nether_star') + return item('minecraft:dirt') + }) + .register() + +mods.roots.runicshearentity.recipeBuilder() + .entity(entity('minecraft:witch')) + .output(item('minecraft:clay')) + .register() + +mods.roots.runicshearentity.removeByName(resource('roots:slime_strange_ooze')) +mods.roots.runicshearentity.removeByOutput(item('roots:fey_leather')) +mods.roots.runicshearentity.removeByEntity(entity('minecraft:chicken')) +//mods.roots.runicshearentity.removeAll() + + +// Summon Creature: +// When running a Summon Creature Ritual, the input items placed on Catalyst Plate will summon the target entity +mods.roots.summoncreature.recipeBuilder() + .name('wither_skeleton_from_clay') // Optional, either a ResourceLocation or a String + .input(item('minecraft:clay'), item('minecraft:clay'), item('minecraft:clay'), item('minecraft:clay'), item('minecraft:clay')) // Between 1 and 10 + .entity(entity('minecraft:wither_skeleton')) + .register() + +mods.roots.summoncreature.removeByName(resource('roots:cow')) +mods.roots.summoncreature.removeByEntity(entity('minecraft:chicken')) +//mods.roots.summoncreature.removeAll() + + +// Transmutation: +// When running the Transmutation, convert nearby blocks that match a set of conditions into either a block or items. +mods.roots.transmutation.recipeBuilder() + .name('clay_duping') // Optional, either a ResourceLocation or a String + .start(blockstate('minecraft:clay')) // Either an IBlockState or BlockStatePredicate + .output(item('minecraft:clay_ball') * 30) // Either a state or output must be defined + .condition(mods.roots.predicates.stateBuilder().blockstate(blockstate('minecraft:gold_block')).below().register()) // Optional, must be a WorldBlockStatePredicate (BlockStateAbove, or BlockStateBelow) + .register() + +mods.roots.transmutation.recipeBuilder() + .start(mods.roots.predicates.stateBuilder().blockstate(blockstate('minecraft:yellow_flower:type=dandelion')).properties('type').register()) // Either an IBlockState or BlockStatePredicate + .state(blockstate('minecraft:gold_block')) + .condition(mods.roots.predicates.above(mods.roots.predicates.LEAVES)) + .register() + +mods.roots.transmutation.recipeBuilder() + .start(blockstate('minecraft:diamond_block')) + .state(blockstate('minecraft:gold_block')) + .register() + +mods.roots.transmutation.removeByName(resource('roots:redstone_block_to_glowstone')) +mods.roots.transmutation.removeByOutput(item('minecraft:dye:3')) +mods.roots.transmutation.removeByOutput(blockstate('minecraft:log:variant=jungle')) +//mods.roots.transmutation.removeAll() + + +// Predicates: +// Predicates are used in Transmution and RunicShearBlock. They either match all blockstates of a block, or all blockstates that have the given properties that match the input blockstate. +// When used in Transmutation, they may require a direction (above or below) to be set. +mods.roots.predicates.stateBuilder() + .blockstate(blockstate('minecraft:red_flower')) // Because this is has no 'properties' set, any blockstate of the base block will work. + .register() + +mods.roots.predicates.stateBuilder() + .block(block('minecraft:red_flower')) // This is identical to the prior predicate. + .register() + +mods.roots.predicates.stateBuilder() + .blockstate(blockstate('minecraft:red_flower:type=poppy')) + .properties('type') // Optional, contains a String..., String[], or List. Controls what properties are matched against with the provided blockstate + .register() + +mods.roots.predicates.stateBuilder() // Matches any log on the z-axis, as 'properties' only checks 'axis'. Matches above the target in Transmutation. + .blockstate(blockstate('minecraft:log:axis=z:variant=oak')) + .properties('axis') + .above() // Optional, used for Transmution to indicate the required direction relative to the primary block. + .register() + +mods.roots.predicates.stateBuilder() // Matches any log, regardless of axis or variant, below the target in Transmutation. + .blockstate(blockstate('minecraft:log')) + .below() // Optional, used for Transmution to indicate the required direction relative to the primary block. + .register() + +// A few constant values are provided: +mods.roots.predicates.ANY // Matches everything +mods.roots.predicates.TRUE // Matches everything +mods.roots.predicates.LAVA // Matches a Lava source block. Required to render properly in JEI +mods.roots.predicates.WATER // Matches a Water source block. Required to render properly in JEI +mods.roots.predicates.LEAVES // Matches any leaf block + +// Converts a BlockStatePredicate into a WorldBlockStatePredicate (BlockStateAbove, or BlockStateBelow) +mods.roots.predicates.above(mods.roots.predicates.LAVA) +mods.roots.predicates.below(mods.roots.predicates.LAVA) + +mods.roots.predicates.create(blockstate('minecraft:red_flower')) +mods.roots.predicates.create(blockstate('minecraft:log:axis=z:variant=oak'), 'axis', 'variant') + + + +// Rituals: +// Set the Pyre Ritual recipe and control all stats. Dump the modifiable stats into `roots.log` by running `/roots rituals`. +// WARNING: When reloading scripts, changes made are never undone. +mods.roots.rituals.recipeBuilder() + .ritual(ritual('ritual_healing_aura')) + .input(item('minecraft:clay'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot'),item('minecraft:gold_ingot')) // Exactly 5 input items + .register() + +mods.roots.rituals.ritual(ritual('ritual_summon_creatures')) + .recipe(item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:clay'),item('minecraft:clay')) + .setDuration(10) + .setProperty('tries', 10) + .setProperty('radius_z', 1) + .setProperty('radius_y', 10) + .setProperty('glow_duration', 100) + .setProperty('interval', 1) + .setProperty('radius_x', 1) + +mods.roots.rituals.ritual(ritual('ritual_spreading_forest')) + .setDisabled() + +//mods.roots.rituals.disableAll() + +// Cost: +// A helper method to generate modifier costs for spells. Output should be used in a setModifierCost of a spell. +def exampleCost = mods.roots.spells.costBuilder() + .cost(cost('additional_cost'), herb('spirit_herb'), 0.1) + .cost(cost('all_cost_multiplier'), null, -0.125) + .register() + +// Spells: +// Controls the recipe for the given spell, the sound, all properties, the base cost, and each modifier's cost. +// WARNING: When reloading scripts, changes made are never undone. +mods.roots.spells.recipeBuilder() + .spell(spell('spell_fey_light')) + .input(item('minecraft:clay'),item('minecraft:diamond'),item('minecraft:gold_ingot')) // Up to 5 input items, but < 5 generates an error in the Patchouli book + .register() + +mods.roots.spells.spell(spell('spell_acid_cloud')) + .recipe(item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:diamond'),item('minecraft:clay'),item('minecraft:clay')) + .setSound(5) // Change the volume of the sound played + .setCooldown(10) + .setDamage(10) + .setProperty('regeneration', 10) // An example list of the modifiable properties + .setProperty('radius_general', 10) + .setProperty('damage_count', 10) + .setProperty('poison_amplification', 10) + .setProperty('healing', 10) + .setProperty('slow_duration', 10) + .setProperty('weakness_duration', 10) + .setProperty('regeneration_amplifier', 10) + .setProperty('weakness_amplifier', 10) + .setProperty('radius_boost', 10) + .setProperty('healing_count', 10) + .setProperty('night_modifier_high', 0.6) + .setProperty('fire_duration', 10) + .setProperty('undead_damage', 10) + .setProperty('slow_amplifier', 10) + .setProperty('underwater_boost', 10) + .setProperty('night_modifier_low', 0.05) + .clearCost() // clearCost or clearSpellCost + .addCost(herb('baffle_cap'), 1.0) // addCost or addSpellCost + .addSpellCost(herb('dewgonia'), 0.25) + .setModifierCost(modifier('roots:moonfall'), exampleCost) + .setModifierCost(modifier('roots:radius_boost'), exampleCost) + .setModifierCost(modifier('roots:peaceful_cloud'), exampleCost) + .setModifierCost(modifier('roots:weakening_cloud'), exampleCost) + .setModifierCost(modifier('roots:moonfall'), exampleCost) + .setModifierCost(modifier('roots:unholy_vanquisher'), exampleCost) + .setModifierCost(modifier('roots:healing_cloud'), exampleCost) + .setModifierCost(modifier('roots:increased_speed'), exampleCost) + .setModifierCost(modifier('roots:fire_cloud'), exampleCost) + .setModifierCost(modifier('roots:slowing'), exampleCost) + .setModifierCost(modifier('roots:underwater_increase'), exampleCost) + +mods.roots.spells.spell(spell('spell_harvest')) + .disableSound() + .setSpellCost(herb('baffle_cap'), 0.01) // setCost or setSpellCost + +mods.roots.spells.spell(spell('spell_aqua_bubble')) + .addSpellCost(herb('baffle_cap'), 0.01) + .setSound(0.1) + +mods.roots.spells.spell(spell('spell_geas')) + .setDisabled() + +//mods.roots.spells.disableAll() + +// Modifiers +mods.roots.modifiers.disable(spell('spell_geas')) // Disable all Modifiers for the spell Geas +mods.roots.modifiers.enable(modifier('roots:extended_geas')) // Reenable these three modifiers +mods.roots.modifiers.enable(modifier('roots:animal_savior')) +mods.roots.modifiers.enable(modifier('roots:weakened_response')) +//mods.roots.modifiers.disableAll() diff --git a/examples/thaumcraft.groovy b/examples/thaumcraft.groovy index a1e171cfa..9fd24f9ea 100644 --- a/examples/thaumcraft.groovy +++ b/examples/thaumcraft.groovy @@ -1,5 +1,3 @@ -import net.minecraft.util.ResourceLocation - mods.thaumcraft.Crucible.removeByOutput(item('minecraft:gunpowder')) mods.thaumcraft.Crucible.recipeBuilder() @@ -52,7 +50,7 @@ mods.thaumcraft.ArcaneWorkbench.shapedBuilder() // .chatColor(14013676) // .component(aspect('cognito')) // .component(aspect('perditio')) -// .image(new ResourceLocation('thaumcraft', 'textures/aspects/humor.png')) +// .image(resource('thaumcraft:textures/aspects/humor.png')) // .register() mods.thaumcraft.AspectHelper.aspectBuilder() @@ -122,12 +120,12 @@ mods.thaumcraft.SmeltingBonus.removeByOutput(item('minecraft:gold_nugget')) // .formulaAspect(aspect('ignis') * 5) // .formulaAspect(aspect('terra') * 5) // .formulaAspect(aspect('aqua') * 5) -// .icon(new ResourceLocation('thaumcraft', 'textures/aspects/humor.png')) -// .background(new ResourceLocation('thaumcraft', 'textures/gui/gui_research_back_1.jpg')) -// .background2(new ResourceLocation('thaumcraft', 'textures/gui/gui_research_back_over.png')) +// .icon(resource('thaumcraft:textures/aspects/humor.png')) +// .background(resource('thaumcraft:textures/gui/gui_research_back_1.jpg')) +// .background2(resource('thaumcraft:textures/gui/gui_research_back_over.png')) // .register() // -//mods.thaumcraft.Research.addResearchLocation(new ResourceLocation('thaumcraft', 'research/new.json')) +//mods.thaumcraft.Research.addResearchLocation(resource('thaumcraft:research/new.json')) mods.thaumcraft.Research.addScannable('KNOWLEDGETYPEHUMOR', item('minecraft:pumpkin')) diff --git a/gradle.properties b/gradle.properties index 5f662f2b4..dfca65ff3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,6 +31,7 @@ debug_de = false debug_ie = false debug_enderio = false debug_astral = false +debug_roots = false debug_blood_magic = false debug_tinkers = false debug_extended_crafting = false diff --git a/src/main/java/com/cleanroommc/groovyscript/brackets/BracketHandlerManager.java b/src/main/java/com/cleanroommc/groovyscript/brackets/BracketHandlerManager.java index a18955b14..1212a250f 100644 --- a/src/main/java/com/cleanroommc/groovyscript/brackets/BracketHandlerManager.java +++ b/src/main/java/com/cleanroommc/groovyscript/brackets/BracketHandlerManager.java @@ -57,6 +57,7 @@ public static BracketHandler getBracketHandler(String key) { } public static void init() { + registerBracketHandler("resource", ResourceLocationBracketHandler.INSTANCE); registerBracketHandler("ore", s -> s.contains("*") ? OreDictWildcardIngredient.of(s) : new OreDictIngredient(s)); registerBracketHandler("item", ItemBracketHandler.INSTANCE, () -> ItemStack.EMPTY); registerBracketHandler("liquid", BracketHandlerManager::parseFluidStack); diff --git a/src/main/java/com/cleanroommc/groovyscript/brackets/ResourceLocationBracketHandler.java b/src/main/java/com/cleanroommc/groovyscript/brackets/ResourceLocationBracketHandler.java new file mode 100644 index 000000000..1a74eab34 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/brackets/ResourceLocationBracketHandler.java @@ -0,0 +1,36 @@ +package com.cleanroommc.groovyscript.brackets; + +import com.cleanroommc.groovyscript.api.IBracketHandler; +import net.minecraft.util.ResourceLocation; + +import static com.cleanroommc.groovyscript.brackets.BracketHandlerManager.SPLITTER; + +public class ResourceLocationBracketHandler implements IBracketHandler { + + public static final ResourceLocationBracketHandler INSTANCE = new ResourceLocationBracketHandler(); + + private ResourceLocationBracketHandler() { + } + + @Override + public ResourceLocation parse(String mainArg, Object[] args) { + String[] parts = mainArg.split(SPLITTER); + if (parts.length > 1) { + return new ResourceLocation(parts[0], parts[1]); + } + + if (args.length > 1 || (args.length == 1 && !(args[0] instanceof String))) { + throw new IllegalArgumentException("Arguments not valid for bracket handler. Use 'resource(String)' or 'resource(String mod, String path)'"); + } + return new ResourceLocation(mainArg, (String) args[0]); + } + + @Override + public ResourceLocation parse(String arg) { + String[] parts = arg.split(SPLITTER); + if (parts.length < 2) { + return null; + } + return new ResourceLocation(parts[0], parts[1]); + } +} 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 205d1abf6..b1a4e2849 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/ModSupport.java @@ -12,6 +12,7 @@ import com.cleanroommc.groovyscript.compat.mods.immersiveengineering.ImmersiveEngineering; import com.cleanroommc.groovyscript.compat.mods.jei.JustEnoughItems; import com.cleanroommc.groovyscript.compat.mods.mekanism.Mekanism; +import com.cleanroommc.groovyscript.compat.mods.roots.Roots; import com.cleanroommc.groovyscript.compat.mods.tcomplement.TinkersComplement; import com.cleanroommc.groovyscript.compat.mods.thaumcraft.Thaumcraft; import com.cleanroommc.groovyscript.compat.mods.thermalexpansion.ThermalExpansion; @@ -45,6 +46,7 @@ public class ModSupport implements IDynamicGroovyProperty { public static final Container TINKERS_CONSTRUCT = new Container<>("tconstruct", "Tinkers' Construct", TinkersConstruct::new, "ticon", "tinkersconstruct"); public static final Container TINKERS_COMPLEMENT = new Container<>("tcomplement", "Tinkers Complement", TinkersComplement::new, "tcomp", "tinkerscomplement"); public static final Container DRACONIC_EVO = new Container<>("draconicevolution", "Draconic Evolution", DraconicEvolution::new, "de"); + public static final Container ROOTS = new Container<>("roots", "Roots 3", Roots::new); public static final Container BLOOD_MAGIC = new Container<>("bloodmagic", "Blood Magic: Alchemical Wizardry", BloodMagic::new, "bm"); public static final Container IMMERSIVE_ENGINEERING = new Container<>("immersiveengineering", "Immersive Engineering", ImmersiveEngineering::new, "ie"); public static final Container INDUSTRIALCRAFT = new Container<>("ic2", "Industrial Craft 2", IC2::new, "industrialcraft"); diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/AnimalHarvest.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/AnimalHarvest.java new file mode 100644 index 000000000..a8149c0ee --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/AnimalHarvest.java @@ -0,0 +1,115 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.AnimalHarvestRecipe; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.EntityEntry; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.getAnimalHarvestRecipes; +import static epicsquid.roots.init.ModRecipes.removeAnimalHarvestRecipe; + +public class AnimalHarvest extends VirtualizedRegistry> { + + public AnimalHarvest() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> removeAnimalHarvestRecipe(pair.getKey())); + restoreFromBackup().forEach(pair -> getAnimalHarvestRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(AnimalHarvestRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, AnimalHarvestRecipe recipe) { + getAnimalHarvestRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(AnimalHarvestRecipe recipe) { + for (Map.Entry entry : getAnimalHarvestRecipes().entrySet()) { + if (entry.getValue().matches(recipe.getHarvestClass())) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + AnimalHarvestRecipe recipe = getAnimalHarvestRecipes().get(name); + if (recipe == null) return false; + removeAnimalHarvestRecipe(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByEntity(EntityEntry entity) { + for (Map.Entry x : getAnimalHarvestRecipes().entrySet()) { + if (x.getValue().matches(entity.getEntityClass())) { + getAnimalHarvestRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + getAnimalHarvestRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getAnimalHarvestRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getAnimalHarvestRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private Class entity; + + public RecipeBuilder entity(EntityEntry entity) { + this.entity = (Class) entity.getEntityClass(); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Animal Harvest recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_animal_harvest_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg); + validateFluids(msg); + msg.add(entity == null, "entity must be defined and extended EntityLivingBase, instead it was {}", entity); + } + + @Override + public @Nullable AnimalHarvestRecipe register() { + if (!validate()) return null; + AnimalHarvestRecipe recipe = new AnimalHarvestRecipe(name, entity); + ModSupport.ROOTS.get().animalHarvest.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/AnimalHarvestFish.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/AnimalHarvestFish.java new file mode 100644 index 000000000..01ef5be9b --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/AnimalHarvestFish.java @@ -0,0 +1,124 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.AnimalHarvestFishRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.getAnimalHarvestFishRecipes; +import static epicsquid.roots.init.ModRecipes.removeAnimalHarvestFishRecipe; + + +public class AnimalHarvestFish extends VirtualizedRegistry> { + + public AnimalHarvestFish() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> removeAnimalHarvestFishRecipe(pair.getKey())); + restoreFromBackup().forEach(pair -> getAnimalHarvestFishRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(AnimalHarvestFishRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, AnimalHarvestFishRecipe recipe) { + getAnimalHarvestFishRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipeByOutput(ItemStack output) { + for (Map.Entry entry : getAnimalHarvestFishRecipes().entrySet()) { + if (ItemStack.areItemsEqual(entry.getValue().getItemStack(), output)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + AnimalHarvestFishRecipe recipe = getAnimalHarvestFishRecipes().get(name); + if (recipe == null) return false; + removeAnimalHarvestFishRecipe(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByOutput(ItemStack output) { + for (Map.Entry x : getAnimalHarvestFishRecipes().entrySet()) { + if (ItemStack.areItemsEqual(x.getValue().getItemStack(), output)) { + getAnimalHarvestFishRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public boolean removeByFish(ItemStack fish) { + return removeByOutput(fish); + } + + public void removeAll() { + getAnimalHarvestFishRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getAnimalHarvestFishRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getAnimalHarvestFishRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private int weight; + + public RecipeBuilder weight(int weight) { + this.weight = weight; + return this; + } + + public RecipeBuilder fish(ItemStack fish) { + this.output.add(fish); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Animal Harvest Fish recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_animal_harvest_fish_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 0, 0, 1, 1); + validateFluids(msg); + msg.add(weight <= 0, "weight must be a nonnegative integer greater than 0, instead it was {}", weight); + } + + @Override + public @Nullable AnimalHarvestFishRecipe register() { + if (!validate()) return null; + AnimalHarvestFishRecipe recipe = new AnimalHarvestFishRecipe(name, output.get(0), weight); + ModSupport.ROOTS.get().animalHarvestFish.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/BarkCarving.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/BarkCarving.java new file mode 100644 index 000000000..84fd1456b --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/BarkCarving.java @@ -0,0 +1,151 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +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 epicsquid.roots.recipe.BarkRecipe; +import net.minecraft.block.BlockPlanks; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.*; + +public class BarkCarving extends VirtualizedRegistry> { + + public BarkCarving() { + super(VirtualizedRegistry.generateAliases("Bark")); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> getBarkRecipeMap().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> getBarkRecipeMap().put(pair.getKey(), pair.getValue())); + } + + public void add(BarkRecipe recipe) { + add(recipe.getName(), recipe); + } + + public void add(ResourceLocation name, BarkRecipe recipe) { + getBarkRecipeMap().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(BarkRecipe recipe) { + return getBarkRecipeByName(recipe.getName()) == null ? null : recipe.getName(); + } + + public ResourceLocation findRecipeByInput(BlockPlanks.EnumType input) { + for (BarkRecipe entry : getBarkRecipes()) { + if (entry.getType().equals(input)) return entry.getName(); + } + return null; + } + + public ResourceLocation findRecipeByInput(ItemStack input) { + for (BarkRecipe entry : getBarkRecipes()) { + if (ItemStack.areItemsEqual(entry.getBlockStack(), input)) return entry.getName(); + } + return null; + } + + public ResourceLocation findRecipeByOutput(ItemStack output) { + for (BarkRecipe entry : getBarkRecipes()) { + if (ItemStack.areItemsEqual(entry.getItem(), output)) return entry.getName(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + BarkRecipe recipe = getBarkRecipeByName(name); + if (recipe == null) return false; + removeBarkRecipe(recipe.getBlockStack()); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByInput(ItemStack input) { + for (Map.Entry x : getBarkRecipeMap().entrySet()) { + if (ItemStack.areItemsEqual(x.getValue().getBlockStack(), input)) { + getBarkRecipeMap().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public boolean removeByBlock(ItemStack block) { + return removeByInput(block); + } + + public boolean removeByOutput(ItemStack output) { + for (Map.Entry x : getBarkRecipeMap().entrySet()) { + if (ItemStack.areItemsEqual(x.getValue().getItem(), output)) { + getBarkRecipeMap().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + getBarkRecipeMap().forEach((key, value) -> addBackup(Pair.of(key, value))); + getBarkRecipeMap().clear(); + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(getBarkRecipes()) + .setRemover(r -> this.removeByName(r.getName())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + public RecipeBuilder blockstate(IBlockState blockstate) { + return this.input(blockstate); + } + + public RecipeBuilder input(IBlockState blockstate) { + this.input.add(IngredientHelper.toIIngredient(new ItemStack(blockstate.getBlock(), 1, blockstate.getBlock().damageDropped(blockstate)))); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Pyre recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_bark_carving_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 1, 1, 1, 1); + validateFluids(msg); + } + + @Override + public @Nullable BarkRecipe register() { + if (!validate()) return null; + BarkRecipe recipe; + recipe = new BarkRecipe(name, output.get(0), input.get(0).toMcIngredient().getMatchingStacks()[0]); + ModSupport.ROOTS.get().barkCarving.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Chrysopoeia.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Chrysopoeia.java new file mode 100644 index 000000000..9764db89d --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Chrysopoeia.java @@ -0,0 +1,147 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.core.mixin.roots.ModRecipesAccessor; +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 epicsquid.roots.recipe.ChrysopoeiaRecipe; +import epicsquid.roots.util.IngredientWithStack; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class Chrysopoeia extends VirtualizedRegistry> { + + public Chrysopoeia() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> ModRecipesAccessor.getChrysopoeiaRecipes().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> ModRecipesAccessor.getChrysopoeiaRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(ChrysopoeiaRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, ChrysopoeiaRecipe recipe) { + ModRecipesAccessor.getChrysopoeiaRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(ChrysopoeiaRecipe recipe) { + for (Map.Entry entry : ModRecipesAccessor.getChrysopoeiaRecipes().entrySet()) { + if (entry.getValue().matches(recipe.getOutput())) return entry.getKey(); + } + return null; + } + + public ResourceLocation findRecipeByOutput(ItemStack output) { + for (Map.Entry entry : ModRecipesAccessor.getChrysopoeiaRecipes().entrySet()) { + if (ItemStack.areItemsEqual(entry.getValue().getOutput(), output)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + ChrysopoeiaRecipe recipe = ModRecipesAccessor.getChrysopoeiaRecipes().get(name); + if (recipe == null) return false; + ModRecipesAccessor.getChrysopoeiaRecipes().remove(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByOutput(ItemStack output) { + for (Map.Entry entry : ModRecipesAccessor.getChrysopoeiaRecipes().entrySet()) { + if (ItemStack.areItemsEqual(entry.getValue().getOutput(), output)) { + ModRecipesAccessor.getChrysopoeiaRecipes().remove(entry.getKey()); + addBackup(Pair.of(entry.getKey(), entry.getValue())); + return true; + } + } + return false; + } + + public boolean removeByInput(ItemStack output) { + for (Map.Entry entry : ModRecipesAccessor.getChrysopoeiaRecipes().entrySet()) { + if (entry.getValue().getIngredient().getIngredient().test(output)) { + ModRecipesAccessor.getChrysopoeiaRecipes().remove(entry.getKey()); + addBackup(Pair.of(entry.getKey(), entry.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + ModRecipesAccessor.getChrysopoeiaRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + ModRecipesAccessor.getChrysopoeiaRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(ModRecipesAccessor.getChrysopoeiaRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + +// overload, byproductChance, and byproduct are all unused +// private float overload = 0.0F; +// private float byproductChance = 0.0F; +// private ItemStack byproduct = ItemStack.EMPTY; + +// public RecipeBuilder overload(float overload) { +// this.overload = overload; +// return this; +// } + +// public RecipeBuilder byproductChance(float byproductChance) { +// this.byproductChance = byproductChance; +// return this; +// } + +// public RecipeBuilder byproduct(IIngredient byproduct) { +// this.byproduct = IngredientHelper.toItemStack(byproduct); +// return this; +// } + + @Override + public String getErrorMsg() { + return "Error adding Roots Chrysopoeia conversion recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_chrysopoeia_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 1, 1, 1, 1); + validateFluids(msg); +// msg.add(overload < 0, "overload must be a nonnegative float, yet it was {}", overload); +// msg.add(byproductChance < 0, "byproductChance must be a nonnegative float, yet it was {}", byproductChance); + } + + @Override + public @Nullable ChrysopoeiaRecipe register() { + if (!validate()) return null; + ChrysopoeiaRecipe recipe = new ChrysopoeiaRecipe(new IngredientWithStack(IngredientHelper.toItemStack(input.get(0))), output.get(0)/*, byproduct, overload, byproductChance*/); + recipe.setRegistryName(name); + ModSupport.ROOTS.get().chrysopoeia.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/FeyCrafter.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/FeyCrafter.java new file mode 100644 index 000000000..e5b1c71c2 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/FeyCrafter.java @@ -0,0 +1,121 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.FeyCraftingRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.*; + +public class FeyCrafter extends VirtualizedRegistry> { + + public FeyCrafter() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> removeFeyCraftingRecipe(pair.getKey())); + restoreFromBackup().forEach(pair -> addFeyCraftingRecipe(pair.getKey(), pair.getValue())); + } + + public void add(FeyCraftingRecipe recipe) { + add(recipe.getName().contains(":") ? new ResourceLocation(recipe.getName()) : new ResourceLocation("roots", recipe.getName()), recipe); + } + + public void add(ResourceLocation name, FeyCraftingRecipe recipe) { + addFeyCraftingRecipe(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(FeyCraftingRecipe recipe) { + for (Map.Entry entry : getFeyCraftingRecipes().entrySet()) { + if (entry.getValue().matches(recipe.getRecipe())) return entry.getKey(); + } + return null; + } + + public ResourceLocation findRecipeByOutput(ItemStack output) { + for (Map.Entry entry : getFeyCraftingRecipes().entrySet()) { + if (ItemStack.areItemsEqual(entry.getValue().getResult(), output)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + FeyCraftingRecipe recipe = getFeyCraftingRecipe(name); + if (recipe == null) return false; + removeFeyCraftingRecipe(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByOutput(ItemStack output) { + for (Map.Entry x : getFeyCraftingRecipes().entrySet()) { + if (ItemStack.areItemsEqual(x.getValue().getResult(), output)) { + getFeyCraftingRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + getFeyCraftingRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getFeyCraftingRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getFeyCraftingRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private int xp = 0; + + public RecipeBuilder xp(int xp) { + this.xp = xp; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Fey Crafter recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_fey_crafter_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 5, 5, 1, 1); + validateFluids(msg); + msg.add(xp < 0, "xp must be a nonnegative integer, yet it was {}", xp); + } + + @Override + public @Nullable FeyCraftingRecipe register() { + if (!validate()) return null; + FeyCraftingRecipe recipe = new FeyCraftingRecipe(output.get(0), xp); + input.forEach(i -> recipe.addIngredient(i.toMcIngredient())); + ModSupport.ROOTS.get().feyCrafter.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/FlowerGeneration.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/FlowerGeneration.java new file mode 100644 index 000000000..6b85317b6 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/FlowerGeneration.java @@ -0,0 +1,163 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.FlowerRecipe; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.getFlowerRecipes; + + +public class FlowerGeneration extends VirtualizedRegistry> { + + public FlowerGeneration() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> getFlowerRecipes().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> getFlowerRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(FlowerRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, FlowerRecipe recipe) { + getFlowerRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(FlowerRecipe recipe) { + for (Map.Entry entry : getFlowerRecipes().entrySet()) { + if (entry.getValue().equals(recipe)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + FlowerRecipe recipe = getFlowerRecipes().get(name); + if (recipe == null) return false; + getFlowerRecipes().remove(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByFlower(IBlockState flower) { + for (Map.Entry x : getFlowerRecipes().entrySet()) { + if (x.getValue().getFlower() == flower) { + getFlowerRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public boolean removeByFlower(Block flower, int meta) { + return removeByFlower(flower.getStateFromMeta(meta)); + } + + public boolean removeByFlower(Block flower) { + boolean found = false; + for (IBlockState state : flower.getBlockState().getValidStates()) { + if (removeByFlower(state)) found = true; + } + return found; + } + + public boolean removeByFlower(ItemStack output) { + return removeByFlower(((ItemBlock) output.getItem()).getBlock().getStateFromMeta(output.getMetadata())); + } + + public void removeAll() { + getFlowerRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getFlowerRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getFlowerRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private IBlockState flower; + private final List allowedSoils = new ArrayList<>(); + + public RecipeBuilder flower(IBlockState flower) { + this.flower = flower; + return this; + } + + public RecipeBuilder flower(Block flower, int meta) { + this.flower = flower.getStateFromMeta(meta); + return this; + } + + /* + public RecipeBuilder allowedSoils(IIngredient allowedSoils) { + this.allowedSoils.add(allowedSoils.toMcIngredient()); + return this; + } + + public RecipeBuilder allowedSoils(IIngredient... allowedSoilss) { + for (IIngredient allowedSoils : allowedSoilss) { + allowedSoils(allowedSoils); + } + return this; + } + + public RecipeBuilder allowedSoils(Collection allowedSoilss) { + for (IIngredient allowedSoils : allowedSoilss) { + allowedSoils(allowedSoils); + } + return this; + } + */ + + @Override + public String getErrorMsg() { + return "Error adding Roots Flower Generation recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_flower_generation_recipe_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg); + validateFluids(msg); + msg.add(flower == null, "flower must be defined"); + } + + @Override + public @Nullable FlowerRecipe register() { + if (!validate()) return null; + FlowerRecipe recipe = new FlowerRecipe(name, flower, allowedSoils); + ModSupport.ROOTS.get().flowerGeneration.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/LifeEssence.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/LifeEssence.java new file mode 100644 index 000000000..a94bf7270 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/LifeEssence.java @@ -0,0 +1,57 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import net.minecraft.entity.EntityLivingBase; +import net.minecraftforge.fml.common.registry.EntityEntry; + +import static epicsquid.roots.init.ModRecipes.getLifeEssenceList; + +public class LifeEssence extends VirtualizedRegistry> { + + public LifeEssence() { + super(); + } + + @Override + public void onReload() { + removeScripted().forEach(getLifeEssenceList()::remove); + restoreFromBackup().forEach(getLifeEssenceList()::add); + } + + public void add(Class clazz) { + getLifeEssenceList().add(clazz); + addScripted(clazz); + } + + public void add(EntityLivingBase entity) { + add(entity.getClass()); + } + + public void add(EntityEntry entity) { + add((Class) entity.getEntityClass()); + } + + public boolean remove(Class clazz) { + if (!getLifeEssenceList().remove(clazz)) return false; + addBackup(clazz); + return true; + } + + public boolean remove(EntityLivingBase entity) { + return remove(entity.getClass()); + } + + public boolean remove(EntityEntry entity) { + return remove((Class) entity.getEntityClass()); + } + + public void removeAll() { + getLifeEssenceList().forEach(this::addBackup); + getLifeEssenceList().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getLifeEssenceList()).setRemover(this::remove); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Modifiers.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Modifiers.java new file mode 100644 index 000000000..ba81cea0b --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Modifiers.java @@ -0,0 +1,109 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.core.mixin.roots.ModifierRegistryAccessor; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.modifiers.Modifier; +import epicsquid.roots.modifiers.ModifierRegistry; +import epicsquid.roots.spell.SpellBase; +import net.minecraft.util.ResourceLocation; + +public class Modifiers extends VirtualizedRegistry { + + public Modifiers() { + super(); + } + + @Override + public void onReload() { + removeScripted().forEach(ModifierRegistryAccessor.getDisabledModifiers()::remove); + restoreFromBackup().forEach(ModifierRegistryAccessor.getDisabledModifiers()::add); + } + + public boolean disable(String name) { + return disable(name.contains(":") ? new ResourceLocation(name) : new ResourceLocation("roots", name)); + } + + public boolean disable(ResourceLocation rl) { + Modifier modifier = ModifierRegistry.get(rl); + if (modifier == null) { + GroovyLog.msg("Error disabling modifier {}", rl).error().post(); + } else { + ModifierRegistry.disable(modifier); + addScripted(modifier.getRegistryName()); + return true; + } + return false; + } + + public boolean disable(Modifier modifier) { + if (ModifierRegistry.get(modifier) == null) { + GroovyLog.msg("Error disabling modifier {}", modifier).error().post(); + return false; + } + ModifierRegistry.disable(modifier); + addScripted(modifier.getRegistryName()); + return true; + } + + public boolean disable(SpellBase spell) { + for (Modifier mod : spell.getModifiers()) { + ModifierRegistry.disable(mod); + addScripted(mod.getRegistryName()); + } + return true; + } + + public boolean enable(String name) { + return enable(name.contains(":") ? new ResourceLocation(name) : new ResourceLocation("roots", name)); + } + + public boolean enable(ResourceLocation rl) { + Modifier modifier = ModifierRegistry.get(rl); + if (modifier == null) { + GroovyLog.msg("Error enabling modifier {}", rl).error().post(); + } else { + ModifierRegistryAccessor.getDisabledModifiers().remove(rl); + addBackup(rl); + return true; + } + return false; + } + + public boolean enable(Modifier modifier) { + if (ModifierRegistry.get(modifier) == null) { + GroovyLog.msg("Error enabling modifier {}", modifier).error().post(); + return false; + } + ModifierRegistryAccessor.getDisabledModifiers().remove(modifier.getRegistryName()); + addScripted(modifier.getRegistryName()); + return true; + } + + public boolean enable(SpellBase spell) { + for (Modifier mod : spell.getModifiers()) { + ModifierRegistryAccessor.getDisabledModifiers().remove(mod.getRegistryName()); + addBackup(mod.getRegistryName()); + } + return true; + } + + public void disableAll() { + for (Modifier mod : ModifierRegistry.getModifiers()) { + ModifierRegistry.disable(mod); + addScripted(mod.getRegistryName()); + } + } + + public void enableAll() { + for (Modifier mod : ModifierRegistry.getModifiers()) { + ModifierRegistryAccessor.getDisabledModifiers().remove(mod.getRegistryName()); + addBackup(mod.getRegistryName()); + } + } + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(ModifierRegistryAccessor.getDisabledModifiers()).setRemover(this::disable); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Mortar.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Mortar.java new file mode 100644 index 000000000..fe77aecf0 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Mortar.java @@ -0,0 +1,248 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.core.mixin.roots.ModRecipesAccessor; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.recipe.MortarRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import static epicsquid.roots.init.ModRecipes.addMortarRecipe; +import static epicsquid.roots.init.ModRecipes.getMortarRecipes; + +public class Mortar extends VirtualizedRegistry> { + + public Mortar() { + super(VirtualizedRegistry.generateAliases("MortarAndPestle")); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> ModRecipesAccessor.getMortarRecipes().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> addMortarRecipe(pair.getValue())); + } + + public void add(MortarRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, MortarRecipe recipe) { + addMortarRecipe(recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(MortarRecipe recipe) { + for (MortarRecipe entry : getMortarRecipes()) { + if (entry.matches(recipe.getRecipe())) return entry.getRegistryName(); + } + return null; + } + + public ResourceLocation findRecipeByOutput(ItemStack output) { + for (MortarRecipe entry : getMortarRecipes()) { + if (ItemStack.areItemsEqual(entry.getResult(), output)) return entry.getRegistryName(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + return ModRecipesAccessor.getMortarRecipes().entrySet().removeIf(x -> { + // Some Mortar recipe names are generated via [base]_x. If we are removing eg "wheat_flour" we should detect and remove all 5 variants + if (x.getKey().equals(name) || x.getKey().toString().startsWith(name.toString())) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public boolean removeByOutput(ItemStack output) { + return ModRecipesAccessor.getMortarRecipes().entrySet().removeIf(x -> { + if (ItemStack.areItemsEqual(x.getValue().getResult(), output)) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public void removeAll() { + ModRecipesAccessor.getMortarRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + ModRecipesAccessor.getMortarRecipes().clear(); + } + + + public SimpleObjectStream streamRecipes() { + return new SimpleObjectStream<>(getMortarRecipes()) + .setRemover(r -> this.removeByName(r.getRegistryName())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private float red1 = 1.0F; + private float green1 = 1.0F; + private float blue1 = 1.0F; + private float red2 = 1.0F; + private float green2 = 1.0F; + private float blue2 = 1.0F; + private boolean generate = true; + + public RecipeBuilder red1(float red1) { + this.red1 = red1; + return this; + } + + public RecipeBuilder green1(float green1) { + this.green1 = green1; + return this; + } + + public RecipeBuilder blue1(float blue1) { + this.blue1 = blue1; + return this; + } + + public RecipeBuilder red2(float red2) { + this.red2 = red2; + return this; + } + + public RecipeBuilder green2(float green2) { + this.green2 = green2; + return this; + } + + public RecipeBuilder blue2(float blue2) { + this.blue2 = blue2; + return this; + } + + public RecipeBuilder red(float red1, float red2) { + this.red1 = red1; + this.red2 = red2; + return this; + } + + public RecipeBuilder red(float red) { + this.red1 = red; + this.red2 = red; + return this; + } + + public RecipeBuilder green(float green1, float green2) { + this.green1 = green1; + this.green2 = green2; + return this; + } + + public RecipeBuilder green(float green) { + this.green1 = green; + this.green2 = green; + return this; + } + + public RecipeBuilder blue(float blue1, float blue2) { + this.blue1 = blue1; + this.blue2 = blue2; + return this; + } + + public RecipeBuilder blue(float blue) { + this.blue1 = blue; + this.blue2 = blue; + return this; + } + + public RecipeBuilder color(float red1, float green1, float blue1, float red2, float green2, float blue2) { + this.red1 = red1; + this.red2 = red2; + this.green1 = green1; + this.green2 = green2; + this.blue1 = blue1; + this.blue2 = blue2; + return this; + } + + public RecipeBuilder color(float red, float green, float blue) { + this.red1 = red; + this.red2 = red; + this.green1 = green; + this.green2 = green; + this.blue1 = blue; + this.blue2 = blue; + return this; + } + + public RecipeBuilder generate(boolean generate) { + this.generate = generate; + return this; + } + + public RecipeBuilder generate() { + this.generate = !this.generate; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Pyre recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_mortar_recipe_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 1, 5, 1, 1); + validateFluids(msg); + msg.add(red1 < 0 || red1 > 1, "red1 must be a float between 0 and 1, yet it was {}", red1); + msg.add(green1 < 0 || green1 > 1, "green1 must be a float between 0 and 1, yet it was {}", green1); + msg.add(blue1 < 0 || blue1 > 1, "blue1 must be a float between 0 and 1, yet it was {}", blue1); + msg.add(red2 < 0 || red2 > 1, "red2 must be a float between 0 and 1, yet it was {}", red2); + msg.add(green2 < 0 || green2 > 1, "green2 must be a float between 0 and 1, yet it was {}", green2); + msg.add(blue2 < 0 || blue2 > 1, "blue2 must be a float between 0 and 1, yet it was {}", blue2); + } + + @Override + public @Nullable MortarRecipe register() { + if (!validate()) return null; + + if (input.size() == 1 && generate) { + List ingredients = new ArrayList<>(); + int count = output.get(0).getCount(); + + for (int i = 1; i <= 5; i++ ) { + ingredients.add(input.get(0).toMcIngredient()); + ItemStack copy = output.get(0).copy(); + copy.setCount(i * count); + MortarRecipe recipe = new MortarRecipe(copy, ingredients.toArray(new Ingredient[0]), red1, green1, blue1, red2, green2, blue2); + recipe.setRegistryName(new ResourceLocation(name.toString() + "_" + i)); + ModSupport.ROOTS.get().mortar.add(recipe.getRegistryName(), recipe); + } + return null; + } + + MortarRecipe recipe = new MortarRecipe(output.get(0), input.stream().map(IIngredient::toMcIngredient).toArray(Ingredient[]::new), red1, red2, green1, green2, blue1, blue2); + recipe.setRegistryName(name); + ModSupport.ROOTS.get().mortar.add(name, recipe); + return recipe; + + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Moss.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Moss.java new file mode 100644 index 000000000..c58feab79 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Moss.java @@ -0,0 +1,124 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.core.mixin.roots.MossConfigAccessor; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.mysticallib.util.ConfigUtil; +import net.minecraft.block.Block; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import static epicsquid.roots.config.MossConfig.MossyCobblestones; + +public class Moss extends VirtualizedRegistry> { + + public Moss() { + super(); + // Roots adds the default ingredients after we have to, but only does so if mossyCobblestones is null. + // So we define it here to prevent NPEs and make sure we still respect the config. + // Probably a better way to do this, but it works. + if (MossConfigAccessor.getMossyCobblestones() == null) { + MossConfigAccessor.setMossyCobblestones(ConfigUtil.parseMap(new HashMap<>(), ConfigUtil::parseItemStack, ConfigUtil::parseItemStack, ",", MossyCobblestones)); + } + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> MossConfigAccessor.getMossyCobblestones().remove(pair.getKey(), pair.getValue())); + restoreFromBackup().forEach(pair -> MossConfigAccessor.getMossyCobblestones().put(pair.getKey(), pair.getValue())); + Moss.reload(); + } + + public static void reload() { + MossConfigAccessor.getMossyBlocks().clear(); + MossConfigAccessor.getMossyStates().clear(); + + for (Map.Entry entry : MossConfigAccessor.getMossyCobblestones().entrySet()) { + ItemStack in = entry.getKey(); + ItemStack out = entry.getValue(); + + if (!(in.getItem() instanceof ItemBlock) || !(out.getItem() instanceof ItemBlock)) continue; + + Block blockIn = ((ItemBlock) in.getItem()).getBlock(); + Block blockOut = ((ItemBlock) out.getItem()).getBlock(); + + if (in.getMetadata() == 0 && out.getMetadata() == 0) { + MossConfigAccessor.getMossyBlocks().put(blockIn, blockOut); + } else { + MossConfigAccessor.getMossyStates().put(blockIn.getStateFromMeta(in.getMetadata()), blockOut.getStateFromMeta(out.getMetadata())); + } + } + + } + + public void add(ItemStack in, ItemStack out) { + MossConfigAccessor.getMossyCobblestones().put(in, out); + addScripted(Pair.of(in, out)); + Moss.reload(); + } + + public boolean remove(ItemStack in, ItemStack out) { + if (MossConfigAccessor.getMossyCobblestones().remove(in, out)) { + addBackup(Pair.of(in, out)); + Moss.reload(); + return true; + } + return false; + } + + public boolean remove(ItemStack in) { + ItemStack out = MossConfigAccessor.getMossyCobblestones().remove(in); + if (out != null) { + addBackup(Pair.of(in, out)); + Moss.reload(); + return true; + } + return false; + } + + public void removeAll() { + MossConfigAccessor.getMossyCobblestones().forEach((in, out) -> addBackup(Pair.of(in, out))); + MossConfigAccessor.getMossyCobblestones().clear(); + Moss.reload(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(MossConfigAccessor.getMossyCobblestones().entrySet()).setRemover(r -> this.remove(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder> { + + @Override + public String getErrorMsg() { + return "Error adding Roots Moss conversion"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 1, 1, 1, 1); + validateFluids(msg); + msg.add(!(input.get(0).getMatchingStacks()[0].getItem() instanceof ItemBlock), "input must be an instance of ItemBlock"); + msg.add(!(output.get(0).getItem() instanceof ItemBlock), "output must be an instance of ItemBlock"); + } + + @Override + public @Nullable Pair register() { + if (!validate()) return null; + ModSupport.ROOTS.get().moss.add(input.get(0).getMatchingStacks()[0], output.get(0)); + Moss.reload(); + return Pair.of(input.get(0).getMatchingStacks()[0], output.get(0)); + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Pacifist.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Pacifist.java new file mode 100644 index 000000000..f6b9221b7 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Pacifist.java @@ -0,0 +1,119 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.core.mixin.roots.PacifistEntryAccessor; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.recipe.PacifistEntry; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.EntityEntry; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.getPacifistEntities; + +public class Pacifist extends VirtualizedRegistry> { + + public Pacifist() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> getPacifistEntities().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> getPacifistEntities().put(pair.getKey(), pair.getValue())); + } + + public void add(PacifistEntry recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, PacifistEntry recipe) { + getPacifistEntities().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(PacifistEntry recipe) { + for (Map.Entry entry : getPacifistEntities().entrySet()) { + if (entry.getValue().equals(recipe)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + PacifistEntry recipe = getPacifistEntities().get(name); + if (recipe == null) return false; + getPacifistEntities().remove(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByEntity(EntityEntry entity) { + return removeByClass(entity.getEntityClass()); + } + + public boolean removeByClass(Class clazz) { + return getPacifistEntities().entrySet().removeIf(x -> { + if (x.getValue().getEntityClass().equals(clazz)) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public void removeAll() { + getPacifistEntities().forEach((key, value) -> addBackup(Pair.of(key, value))); + getPacifistEntities().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getPacifistEntities().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private Class entity; + + public RecipeBuilder entity(EntityEntry entity) { + this.entity = entity.getEntityClass(); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Runic Shear Entity recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_pacifist_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg); + validateFluids(msg); + msg.add(entity == null, "entity must be defined"); + } + + @Override + public @Nullable PacifistEntry register() { + if (!validate()) return null; + PacifistEntry recipe = new PacifistEntry(entity, name.toString()); + ((PacifistEntryAccessor) recipe).setName(name); + ModSupport.ROOTS.get().pacifist.add(recipe.getRegistryName(), recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Predicates.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Predicates.java new file mode 100644 index 000000000..3bec5c17c --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Predicates.java @@ -0,0 +1,126 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.recipe.transmutation.*; +import net.minecraft.block.Block; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Collectors; + +public class Predicates extends VirtualizedRegistry { + + public WorldBlockStatePredicate ANY = WorldBlockStatePredicate.TRUE; + public WorldBlockStatePredicate TRUE = WorldBlockStatePredicate.TRUE; + public BlocksPredicate LAVA = new LavaPredicate(); + public BlocksPredicate WATER = new WaterPredicate(); + public LeavesPredicate LEAVES = new LeavesPredicate(); + + public Predicates() { + super(); + } + + @Override + public void onReload() { + } + + public StateBuilder stateBuilder() { + return new StateBuilder(); + } + + StatePredicate create(IBlockState blockState) { + return new StatePredicate(blockState); + } + + PropertyPredicate create(IBlockState blockState, String... properties) { + return new PropertyPredicate(blockState, Arrays.stream(properties).map(x -> blockState.getBlock().getBlockState().getProperty(x)).filter(Objects::nonNull).collect(Collectors.toList())); + } + + PropertyPredicate create(IBlockState blockState, Collection properties) { + return new PropertyPredicate(blockState, properties.stream().map(x -> blockState.getBlock().getBlockState().getProperty(x)).filter(Objects::nonNull).collect(Collectors.toList())); + } + + WorldBlockStatePredicate above(BlockStatePredicate blockState) { + return new BlockStateAbove(blockState); + } + + WorldBlockStatePredicate below(BlockStatePredicate blockState) { + return new BlockStateBelow(blockState); + } + + public static class StateBuilder extends AbstractRecipeBuilder { + + private final Collection properties = new ArrayList<>(); + private IBlockState blockstate; + private boolean above = false; + private boolean below = false; + + public StateBuilder blockstate(IBlockState blockstate) { + this.blockstate = blockstate; + return this; + } + + public StateBuilder block(Block block) { + this.blockstate = block.getDefaultState(); + return this; + } + + public StateBuilder properties(String... properties) { + Collections.addAll(this.properties, properties); + return this; + } + + public StateBuilder properties(Collection properties) { + this.properties.addAll(properties); + return this; + } + + public StateBuilder above() { + this.above = true; + return this; + } + + public StateBuilder below() { + this.below = true; + return this; + } + + @Override + public String getErrorMsg() { + return "Error creating Roots Predicate"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg); + validateFluids(msg); + msg.add(above && below, "both above and below cannot be true"); + msg.add(blockstate == null, "blockstate must be defined"); + + BlockStateContainer container = blockstate.getBlock().getBlockState(); + properties.forEach(prop -> { + if (container.getProperty(prop) == null) { + msg.add("property {} is not a property of the provided blockstate {}", prop, blockstate); + } + }); + } + + @Override + public @Nullable MatchingStates register() { + if (!validate()) return null; + BlockStateContainer container = blockstate.getBlock().getBlockState(); + + BlockStatePredicate predicate = properties.isEmpty() + ? new StatePredicate(blockstate) + : new PropertyPredicate(blockstate, properties.stream().map(container::getProperty).collect(Collectors.toList())); + + if (above) return new BlockStateAbove(predicate); + if (below) return new BlockStateBelow(predicate); + return predicate; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Pyre.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Pyre.java new file mode 100644 index 000000000..c4e7edeee --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Pyre.java @@ -0,0 +1,138 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.PyreCraftingRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.*; + +public class Pyre extends VirtualizedRegistry> { + + public Pyre() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> removePyreCraftingRecipe(pair.getKey())); + restoreFromBackup().forEach(pair -> addPyreCraftingRecipe(pair.getKey(), pair.getValue())); + } + + public void add(PyreCraftingRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, PyreCraftingRecipe recipe) { + addPyreCraftingRecipe(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(PyreCraftingRecipe recipe) { + for (Map.Entry entry : getPyreCraftingRecipes().entrySet()) { + if (entry.getValue().matches(recipe.getRecipe())) return entry.getKey(); + } + return null; + } + + public ResourceLocation findRecipeByOutput(ItemStack output) { + for (Map.Entry entry : getPyreCraftingRecipes().entrySet()) { + if (ItemStack.areItemsEqual(entry.getValue().getResult(), output)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + PyreCraftingRecipe recipe = getCraftingRecipe(name); + if (recipe == null) return false; + removePyreCraftingRecipe(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByOutput(ItemStack output) { + for (Map.Entry x : getPyreCraftingRecipes().entrySet()) { + if (ItemStack.areItemsEqual(x.getValue().getResult(), output)) { + getPyreCraftingRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + getPyreCraftingRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getPyreCraftingRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getPyreCraftingRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private int xp = 0; + private int burnTime = 200; + + public RecipeBuilder xp(int xp) { + this.xp = xp; + return this; + } + + public RecipeBuilder levels(int levels) { + this.xp = levels; + return this; + } + + public RecipeBuilder burnTime(int burnTime) { + this.burnTime = burnTime; + return this; + } + + public RecipeBuilder time(int time) { + return this.burnTime(time); + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Pyre recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_pyre_recipe_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 5, 5, 1, 1); + validateFluids(msg); + msg.add(xp < 0, "xp must be a nonnegative integer, yet it was {}", xp); + } + + @Override + public @Nullable PyreCraftingRecipe register() { + if (!validate()) return null; + PyreCraftingRecipe recipe = new PyreCraftingRecipe(output.get(0), xp); + input.forEach(i -> recipe.addIngredient(i.toMcIngredient())); + recipe.setBurnTime(this.burnTime); + recipe.setRegistryName(name); + ModSupport.ROOTS.get().pyre.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Rituals.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Rituals.java new file mode 100644 index 000000000..cf7ea12c9 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Rituals.java @@ -0,0 +1,151 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.core.mixin.roots.RitualBaseAccessor; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.properties.Property; +import epicsquid.roots.properties.PropertyTable; +import epicsquid.roots.ritual.RitualBase; +import epicsquid.roots.ritual.RitualRegistry; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public class Rituals extends VirtualizedRegistry { + + public Rituals() { + super(); + } + + public static RitualWrapper ritual(String ritual) { + return ritual(RitualRegistry.getRitual(ritual)); + } + + public static RitualWrapper ritual(RitualBase ritual) { + return new RitualWrapper(ritual); + } + + public static RitualWrapper.RecipeBuilder recipeBuilder() { + return new RitualWrapper.RecipeBuilder(); + } + + @Override + public void onReload() { + // TODO Roots: I don't have a good idea for how to implement reloading here + } + + public void disableAll() { + RitualRegistry.getRituals().forEach(r -> { + ((RitualBaseAccessor) r).setDisabled(true); + r.setRecipe(RitualBase.RitualRecipe.EMPTY); + }); + } + + public static class RitualWrapper { + + private final RitualBase ritual; + + public RitualWrapper(String name) { + this.ritual = RitualRegistry.getRitual(name); + } + + public RitualWrapper(RitualBase ritual) { + this.ritual = ritual; + } + + public RitualWrapper recipe(IIngredient... input) { + if (ritual == null) { + GroovyLog.msg("Error modifying Roots Ritual recipe").add("No ritual specified when recipe change requested.").error().post(); + } else { + new RecipeBuilder(ritual).input(input).register(); + } + return this; + } + + public RitualWrapper recipe(Collection input) { + if (ritual == null) { + GroovyLog.msg("Error modifying Roots Ritual recipe").add("No ritual specified when recipe change requested.").error().post(); + } else { + new RecipeBuilder(ritual).input(input).register(); + } + return this; + } + + public RitualWrapper setDisabled() { + ((RitualBaseAccessor) this.ritual).setDisabled(true); + return this; + } + + public RitualWrapper setEnabled() { + ((RitualBaseAccessor) this.ritual).setDisabled(false); + return this; + } + + public RitualWrapper set(String propertyName, T value) { + PropertyTable table = this.ritual.getProperties(); + Property prop = table.get(propertyName); + if (prop == null) GroovyLog.msg("Property {} was undefined for ritual {}", propertyName, ritual).error().post(); + else table.set(prop, value); + return this; + } + + public RitualWrapper setProperty(String propertyName, double value) { + return this.set(propertyName, value); + } + + public RitualWrapper setProperty(String propertyName, float value) { + return this.set(propertyName, value); + } + + public RitualWrapper setProperty(String propertyName, int value) { + return this.set(propertyName, value); + } + + public RitualWrapper setProperty(String propertyName, String value) { + return this.set(propertyName, value); + } + + public RitualWrapper setDuration(int value) { + return this.set("duration", value); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private RitualBase ritual; + + public RecipeBuilder() { + } + + public RecipeBuilder(RitualBase ritual) { + this.ritual = ritual; + } + + public RecipeBuilder ritual(RitualBase ritual) { + this.ritual = ritual; + return this; + } + + @Override + public String getErrorMsg() { + return "Error creating Roots Ritual Recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 5, 5, 0, 0); + validateFluids(msg); + msg.add(ritual == null, "ritual must be defined"); + } + + @Override + public @Nullable RitualBase.RitualRecipe register() { + if (!validate()) return null; + RitualBase.RitualRecipe recipe = new RitualBase.RitualRecipe(ritual, input.stream().map(IIngredient::toMcIngredient).toArray()); + this.ritual.setRecipe(recipe); + return recipe; + } + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Roots.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Roots.java new file mode 100644 index 000000000..f746805cf --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Roots.java @@ -0,0 +1,71 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.brackets.BracketHandlerManager; +import com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer; +import epicsquid.roots.init.HerbRegistry; +import epicsquid.roots.modifiers.CostType; +import epicsquid.roots.modifiers.ModifierRegistry; +import epicsquid.roots.ritual.RitualRegistry; +import epicsquid.roots.spell.SpellRegistry; +import net.minecraft.util.ResourceLocation; + +import java.util.Locale; + +public class Roots extends ModPropertyContainer { + + AnimalHarvest animalHarvest = new AnimalHarvest(); + AnimalHarvestFish animalHarvestFish = new AnimalHarvestFish(); + BarkCarving barkCarving = new BarkCarving(); + Chrysopoeia chrysopoeia = new Chrysopoeia(); + FeyCrafter feyCrafter = new FeyCrafter(); + FlowerGeneration flowerGeneration = new FlowerGeneration(); + LifeEssence lifeEssence = new LifeEssence(); + Modifiers modifiers = new Modifiers(); + Moss moss = new Moss(); + Mortar mortar = new Mortar(); + Pacifist pacifist = new Pacifist(); + Predicates predicates = new Predicates(); + Pyre pyre = new Pyre(); + Rituals rituals = new Rituals(); + RunicShearBlock runicShearBlock = new RunicShearBlock(); + RunicShearEntity runicShearEntity = new RunicShearEntity(); + Spells spells = new Spells(); + SummonCreature summonCreature = new SummonCreature(); + Transmutation transmutation = new Transmutation(); + + + public Roots() { + addRegistry(animalHarvest); + addRegistry(animalHarvestFish); + addRegistry(barkCarving); + addRegistry(chrysopoeia); + addRegistry(feyCrafter); + addRegistry(flowerGeneration); + addRegistry(lifeEssence); + addRegistry(modifiers); + addRegistry(moss); + addRegistry(mortar); + addRegistry(pacifist); + addRegistry(pyre); + addRegistry(predicates); + addRegistry(rituals); + addRegistry(runicShearBlock); + addRegistry(runicShearEntity); + addRegistry(spells); + addRegistry(summonCreature); + addRegistry(transmutation); + } + + @Override + public void initialize() { + BracketHandlerManager.registerBracketHandler("ritual", RitualRegistry::getRitual); + BracketHandlerManager.registerBracketHandler("herb", HerbRegistry::getHerbByName); + BracketHandlerManager.registerBracketHandler("cost", s -> CostType.valueOf(s.toUpperCase(Locale.ROOT))); + BracketHandlerManager.registerBracketHandler("spell", s -> { + if (s.contains(":")) return SpellRegistry.getSpell(new ResourceLocation(s)); + if (s.startsWith("spell_")) return SpellRegistry.getSpell(s); + return SpellRegistry.getSpell("spell_" + s); + }); + BracketHandlerManager.registerBracketHandler("modifier", s -> ModifierRegistry.get(new ResourceLocation(s))); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/RunicShearBlock.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/RunicShearBlock.java new file mode 100644 index 000000000..6a69871c7 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/RunicShearBlock.java @@ -0,0 +1,148 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.RunicShearRecipe; +import epicsquid.roots.recipe.transmutation.BlockStatePredicate; +import epicsquid.roots.recipe.transmutation.StatePredicate; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static epicsquid.roots.init.ModRecipes.getRunicShearRecipes; + +public class RunicShearBlock extends VirtualizedRegistry> { + + public RunicShearBlock() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> getRunicShearRecipes().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> getRunicShearRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(RunicShearRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, RunicShearRecipe recipe) { + getRunicShearRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(RunicShearRecipe recipe) { + for (Map.Entry entry : getRunicShearRecipes().entrySet()) { + if (entry.getValue().equals(recipe)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + RunicShearRecipe recipe = getRunicShearRecipes().get(name); + if (recipe == null) return false; + getRunicShearRecipes().remove(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByState(IBlockState state) { + for (Map.Entry x : getRunicShearRecipes().entrySet()) { + if (x.getValue().matches(state)) { + getRunicShearRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public boolean removeByOutput(ItemStack output) { + for (Map.Entry x : getRunicShearRecipes().entrySet()) { + if (ItemStack.areItemsEqual(x.getValue().getDrop(), output)) { + getRunicShearRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + getRunicShearRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getRunicShearRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getRunicShearRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private ItemStack displayItem; + private BlockStatePredicate state; + private IBlockState replacementState; + + public RecipeBuilder displayItem(ItemStack displayItem) { + this.displayItem = displayItem; + return this; + } + + public RecipeBuilder state(IBlockState state) { + this.state = new StatePredicate(state); + return this; + } + + public RecipeBuilder state(BlockStatePredicate state) { + this.state = state; + return this; + } + + public RecipeBuilder replacementState(IBlockState replacementState) { + this.replacementState = replacementState; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Runic Shear Block recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_runic_shear_block_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 0, 0, 1, 1); + validateFluids(msg); + msg.add(state == null, "state must be defined"); + msg.add(replacementState == null, "replacementState must be defined"); + if (displayItem == null) { + displayItem = state.matchingItems().get(0); + } + } + + @Override + public @Nullable RunicShearRecipe register() { + if (!validate()) return null; + RunicShearRecipe recipe = new RunicShearRecipe(name, state, replacementState, output.get(0), displayItem); + ModSupport.ROOTS.get().runicShearBlock.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/RunicShearEntity.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/RunicShearEntity.java new file mode 100644 index 000000000..592ec236a --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/RunicShearEntity.java @@ -0,0 +1,148 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +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 epicsquid.roots.recipe.RunicShearConditionalEntityRecipe; +import epicsquid.roots.recipe.RunicShearEntityRecipe; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.EntityEntry; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Map; +import java.util.function.Function; + +import static epicsquid.roots.init.ModRecipes.getRunicShearEntityRecipes; + +public class RunicShearEntity extends VirtualizedRegistry> { + + public RunicShearEntity() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + // FIXME Not currently reloadable due to being controlled by the Capability RunicShearsCapabilityProvider + removeScripted().forEach(pair -> getRunicShearEntityRecipes().remove(pair.getKey())); + restoreFromBackup().forEach(pair -> getRunicShearEntityRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(RunicShearEntityRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, RunicShearEntityRecipe recipe) { + getRunicShearEntityRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(RunicShearEntityRecipe recipe) { + for (Map.Entry entry : getRunicShearEntityRecipes().entrySet()) { + if (entry.getValue().equals(recipe)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + RunicShearEntityRecipe recipe = getRunicShearEntityRecipes().get(name); + if (recipe == null) return false; + getRunicShearEntityRecipes().remove(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByOutput(ItemStack output) { + return getRunicShearEntityRecipes().entrySet().removeIf(x -> { + if (ItemStack.areItemsEqual(x.getValue().getDrop(), output)) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public boolean removeByEntity(EntityEntry entity) { + return removeByEntity((Class) entity.getEntityClass()); + } + + public boolean removeByEntity(Class clazz) { + for (Map.Entry x : getRunicShearEntityRecipes().entrySet()) { + if (x.getValue().getClazz() == clazz) { + getRunicShearEntityRecipes().remove(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + getRunicShearEntityRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + getRunicShearEntityRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(getRunicShearEntityRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private Class entity; + private int cooldown; + private Function functionMap; + + public RecipeBuilder entity(EntityEntry entity) { + this.entity = (Class) entity.getEntityClass(); + return this; + } + + public RecipeBuilder cooldown(int cooldown) { + this.cooldown = cooldown; + return this; + } + + public RecipeBuilder functionMap(Function functionMap) { + this.functionMap = functionMap; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Runic Shear Entity recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_runic_shear_entity_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + msg.add(!input.isEmpty(), () -> "No item inputs allowed, but found " + input.size()); + validateFluids(msg); + msg.add(output.size() > 1 && functionMap == null, "if output is greater than 1, functionMap must be defined"); + msg.add(entity == null , "entity must be defined and extended EntityLivingBase, instead it was {}", entity); + } + + @Override + public @Nullable RunicShearEntityRecipe register() { + if (!validate()) return null; + RunicShearEntityRecipe recipe; + if (functionMap == null) recipe = new RunicShearEntityRecipe(name, output.get(0), entity, cooldown); + else recipe = new RunicShearConditionalEntityRecipe(name, functionMap, new HashSet<>(output), entity, cooldown); + ModSupport.ROOTS.get().runicShearEntity.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Spells.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Spells.java new file mode 100644 index 000000000..4763444bc --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Spells.java @@ -0,0 +1,295 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.core.mixin.roots.ModifierAccessor; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.api.Herb; +import epicsquid.roots.config.SpellConfig; +import epicsquid.roots.modifiers.*; +import epicsquid.roots.properties.Property; +import epicsquid.roots.properties.PropertyTable; +import epicsquid.roots.spell.SpellBase; +import epicsquid.roots.spell.SpellRegistry; +import net.minecraft.util.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class Spells extends VirtualizedRegistry { + + public Spells() { + super(); + } + + public static SpellWrapper spell(String spell) { + return new SpellWrapper(spell); + } + + public static SpellWrapper spell(ResourceLocation spell) { + return new SpellWrapper(spell); + } + + public static SpellWrapper spell(SpellBase spell) { + return new SpellWrapper(spell); + } + + public static SpellWrapper.RecipeBuilder recipeBuilder() { + return new SpellWrapper.RecipeBuilder(); + } + + public static CostBuilder costBuilder() { + return new CostBuilder(); + } + + @Override + public void onReload() { + // TODO Roots: I don't have a good idea for how to implement reloading here + } + + public void disableAll() { + SpellRegistry.getSpells().forEach(r -> r.setDisabled(true)); + } + + public static class SpellWrapper { + + private final SpellBase spell; + + public SpellWrapper(String name) { + ResourceLocation rl; + if (name.contains(":")) rl = new ResourceLocation(name); + else if (name.startsWith("spell_")) rl = new ResourceLocation("roots", name); + else rl = new ResourceLocation("roots", "spell_" + name); + this.spell = SpellRegistry.getSpell(rl); + } + + public SpellWrapper(ResourceLocation rl) { + this.spell = SpellRegistry.getSpell(rl); + } + + public SpellWrapper(SpellBase spell) { + this.spell = spell; + } + + public static RecipeBuilder recipe() { + return new RecipeBuilder(); + } + + public SpellWrapper recipe(IIngredient... input) { + if (spell == null) { + GroovyLog.msg("Error modifying Roots Spell recipe").add("No spell specified when recipe change requested.").error().post(); + } else { + new RecipeBuilder(spell).input(input).register(); + } + return this; + } + + public SpellWrapper recipe(Collection input) { + if (spell == null) { + GroovyLog.msg("Error modifying Roots Spell recipe").add("No spell specified when recipe change requested.").error().post(); + } else { + new RecipeBuilder(spell).input(input).register(); + } + return this; + } + + public SpellWrapper setDisabled() { + this.spell.setDisabled(true); + return this; + } + + public SpellWrapper setEnabled() { + this.spell.setDisabled(false); + return this; + } + + public SpellWrapper disableSound() { + SpellConfig.SpellSoundsCategory.SpellSound sound = new SpellConfig.SpellSoundsCategory.SpellSound(); + sound.enabled = false; + this.spell.setSound(sound); + return this; + } + + public SpellWrapper setSound(boolean enabled) { + SpellConfig.SpellSoundsCategory.SpellSound sound = new SpellConfig.SpellSoundsCategory.SpellSound(); + sound.enabled = enabled; + this.spell.setSound(sound); + return this; + } + + public SpellWrapper setSound(double volume) { + SpellConfig.SpellSoundsCategory.SpellSound sound = new SpellConfig.SpellSoundsCategory.SpellSound(); + sound.volume = volume; + this.spell.setSound(sound); + return this; + } + + public SpellWrapper setSound(boolean enabled, double volume) { + SpellConfig.SpellSoundsCategory.SpellSound sound = new SpellConfig.SpellSoundsCategory.SpellSound(); + sound.enabled = enabled; + sound.volume = volume; + this.spell.setSound(sound); + return this; + } + + public SpellWrapper set(String propertyName, T value) { + PropertyTable table = this.spell.getProperties(); + Property prop = table.get(propertyName); + if (prop == null) GroovyLog.msg("Property {} was undefined for spell {}", propertyName, spell.getName()).error().post(); + else table.set(prop, value); + return this; + } + + public SpellWrapper setProperty(String propertyName, double value) { + return this.set(propertyName, value); + } + + public SpellWrapper setProperty(String propertyName, float value) { + return this.set(propertyName, value); + } + + public SpellWrapper setProperty(String propertyName, int value) { + return this.set(propertyName, value); + } + + public SpellWrapper setProperty(String propertyName, String value) { + return this.set(propertyName, value); + } + + public SpellWrapper setCooldown(int value) { + return this.set("cooldown", value); + } + + public SpellWrapper setDamage(float value) { + return this.set("damage", value); + } + + public SpellWrapper setCost(Herb herb, double value) { + this.spell.getCosts().clear(); + this.spell.getCosts().put(herb, value); + return this; + } + + public SpellWrapper setSpellCost(Herb herb, double value) { + return this.setCost(herb, value); + } + + public SpellWrapper addCost(Herb herb, double value) { + this.spell.getCosts().put(herb, value); + return this; + } + + public SpellWrapper addSpellCost(Herb herb, double value) { + return this.addCost(herb, value); + } + + public SpellWrapper clearCost() { + this.spell.getCosts().clear(); + return this; + } + + public SpellWrapper clearSpellCost() { + return this.clearCost(); + } + + public SpellWrapper setModifierCost(Modifier mod, Map costs) { + ((ModifierAccessor) mod).getCosts().clear(); + ((ModifierAccessor) mod).getCosts().putAll(costs); + return this; + } + + public SpellWrapper addModifierCost(Modifier mod, Map costs) { + ((ModifierAccessor) mod).getCosts().putAll(costs); + return this; + } + + public SpellWrapper clearModifierCost(Modifier mod) { + ((ModifierAccessor) mod).getCosts().clear(); + return this; + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private SpellBase spell; + + public RecipeBuilder() { + } + + public RecipeBuilder(SpellBase spell) { + this.spell = spell; + } + + public RecipeBuilder spell(SpellBase spell) { + this.spell = spell; + return this; + } + + @Override + public String getErrorMsg() { + return "Error creating Roots Spell Recipe"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg, 1, 5, 0, 0); + validateFluids(msg); + } + + @Override + public @Nullable SpellBase.SpellRecipe register() { + if (!validate()) return null; + SpellBase.SpellRecipe recipe = new SpellBase.SpellRecipe(input.stream().map(IIngredient::toMcIngredient).toArray()); + this.spell.setRecipe(recipe); + return recipe; + } + } + } + + public static class CostBuilder extends AbstractRecipeBuilder> { + + List list = new ArrayList<>(); + + public CostBuilder cost(CostType cost, double value, IModifierCore herb) { + list.add(new epicsquid.roots.modifiers.Cost(cost, value, herb)); + return this; + } + + public CostBuilder cost(CostType cost, IModifierCore herb, double value) { + return this.cost(cost, value, herb); + } + + public CostBuilder cost(CostType cost, Herb herb, double value) { + return this.cost(cost, value, ModifierCores.fromHerb(herb)); + } + + public CostBuilder cost(CostType cost, double value) { + return this.cost(cost, value, null); + } + + public CostBuilder cost(CostType cost) { + return this.cost(cost, 0.0, null); + } + + @Override + public String getErrorMsg() { + return "Error creating Roots Spell Cost"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateItems(msg); + validateFluids(msg); + } + + @Override + public @Nullable Map register() { + if (!validate()) return null; + if (list.size() == 0) return epicsquid.roots.modifiers.Cost.noCost(); + return epicsquid.roots.modifiers.Cost.of(list.toArray(new IModifierCost[0])); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/SummonCreature.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/SummonCreature.java new file mode 100644 index 000000000..a817e4875 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/SummonCreature.java @@ -0,0 +1,125 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.core.mixin.roots.ModRecipesAccessor; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.recipe.SummonCreatureRecipe; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.EntityEntry; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static epicsquid.roots.init.ModRecipes.*; + +public class SummonCreature extends VirtualizedRegistry> { + + public SummonCreature() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> removeSummonCreatureEntry(pair.getKey())); + restoreFromBackup().forEach(pair -> addSummonCreatureEntry(pair.getValue())); + } + + public void add(SummonCreatureRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, SummonCreatureRecipe recipe) { + addSummonCreatureEntry(recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(SummonCreatureRecipe recipe) { + for (Map.Entry entry : ModRecipesAccessor.getSummonCreatureEntries().entrySet()) { + if (entry.getValue().equals(recipe)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + SummonCreatureRecipe recipe = getSummonCreatureEntry(name); + if (recipe == null) return false; + removeSummonCreatureEntry(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByEntity(EntityEntry entity) { + return removeByEntity((Class) entity.getEntityClass()); + } + + public boolean removeByEntity(Class clazz) { + for (Map.Entry x : ModRecipesAccessor.getSummonCreatureEntries().entrySet()) { + if (x.getValue().getClazz() == clazz) { + removeSummonCreatureEntry(x.getKey()); + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + } + return false; + } + + public void removeAll() { + ModRecipesAccessor.getSummonCreatureEntries().forEach((key, value) -> addBackup(Pair.of(key, value))); + ModRecipesAccessor.getSummonCreatureEntries().clear(); + ModRecipesAccessor.getSummonCreatureClasses().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(ModRecipesAccessor.getSummonCreatureEntries().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private Class entity; + + public RecipeBuilder entity(EntityEntry entity) { + this.entity = (Class) entity.getEntityClass(); + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Summon Creature recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_summon_creature_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 1, 10, 0, 0); + validateFluids(msg); + msg.add(entity == null, "entity must be defined"); + } + + @Override + public @Nullable SummonCreatureRecipe register() { + if (!validate()) return null; + List ingredients = input.stream().map(IIngredient::toMcIngredient).collect(Collectors.toList()); + SummonCreatureRecipe recipe = new SummonCreatureRecipe(name, entity, ingredients); + ModSupport.ROOTS.get().summonCreature.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Transmutation.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Transmutation.java new file mode 100644 index 000000000..37ecb14be --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/roots/Transmutation.java @@ -0,0 +1,177 @@ +package com.cleanroommc.groovyscript.compat.mods.roots; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.compat.mods.ModSupport; +import com.cleanroommc.groovyscript.core.mixin.roots.ModRecipesAccessor; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; +import com.cleanroommc.groovyscript.helper.recipe.AbstractRecipeBuilder; +import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; +import epicsquid.roots.recipe.TransmutationRecipe; +import epicsquid.roots.recipe.transmutation.BlockStatePredicate; +import epicsquid.roots.recipe.transmutation.StatePredicate; +import epicsquid.roots.recipe.transmutation.WorldBlockStatePredicate; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +import static epicsquid.roots.init.ModRecipes.removeTransmutationRecipe; + +public class Transmutation extends VirtualizedRegistry> { + + public Transmutation() { + super(); + } + + public static RecipeBuilder recipeBuilder() { + return new RecipeBuilder(); + } + + @Override + public void onReload() { + removeScripted().forEach(pair -> removeTransmutationRecipe(pair.getKey())); + restoreFromBackup().forEach(pair -> ModRecipesAccessor.getTransmutationRecipes().put(pair.getKey(), pair.getValue())); + } + + public void add(TransmutationRecipe recipe) { + add(recipe.getRegistryName(), recipe); + } + + public void add(ResourceLocation name, TransmutationRecipe recipe) { + ModRecipesAccessor.getTransmutationRecipes().put(name, recipe); + addScripted(Pair.of(name, recipe)); + } + + public ResourceLocation findRecipe(TransmutationRecipe recipe) { + for (Map.Entry entry : ModRecipesAccessor.getTransmutationRecipes().entrySet()) { + if (entry.getValue().equals(recipe)) return entry.getKey(); + } + return null; + } + + public boolean removeByName(ResourceLocation name) { + TransmutationRecipe recipe = ModRecipesAccessor.getTransmutationRecipes().get(name); + if (recipe == null) return false; + removeTransmutationRecipe(name); + addBackup(Pair.of(name, recipe)); + return true; + } + + public boolean removeByInput(IBlockState input) { + return ModRecipesAccessor.getTransmutationRecipes().entrySet().removeIf(x -> { + if (x.getValue().getStartPredicate().test(input)) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public boolean removeByOutput(ItemStack output) { + return ModRecipesAccessor.getTransmutationRecipes().entrySet().removeIf(x -> { + if (ItemStack.areItemsEqual(x.getValue().getStack(), output)) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public boolean removeByOutput(IBlockState output) { + return ModRecipesAccessor.getTransmutationRecipes().entrySet().removeIf(x -> { + Optional state = x.getValue().getState(); + if (!state.isPresent()) return false; + + Collection> incoming = output.getPropertyKeys(); + Collection> current = state.get().getPropertyKeys(); + + if (state.get().getBlock() == output.getBlock() && + output.getPropertyKeys().stream().allMatch(prop -> incoming.contains(prop) && + current.contains(prop) && + state.get().getValue(prop).equals(output.getValue(prop))) + ) { + addBackup(Pair.of(x.getKey(), x.getValue())); + return true; + } + return false; + }); + } + + public void removeAll() { + ModRecipesAccessor.getTransmutationRecipes().forEach((key, value) -> addBackup(Pair.of(key, value))); + ModRecipesAccessor.getTransmutationRecipes().clear(); + } + + public SimpleObjectStream> streamRecipes() { + return new SimpleObjectStream<>(ModRecipesAccessor.getTransmutationRecipes().entrySet()) + .setRemover(r -> this.removeByName(r.getKey())); + } + + public static class RecipeBuilder extends AbstractRecipeBuilder { + + private BlockStatePredicate start; + private IBlockState state; + private WorldBlockStatePredicate condition = WorldBlockStatePredicate.TRUE; + + public RecipeBuilder start(BlockStatePredicate start) { + this.start = start; + return this; + } + + public RecipeBuilder start(IBlockState start) { + this.start = new StatePredicate(start); + return this; + } + + public RecipeBuilder state(IBlockState state) { + this.state = state; + return this; + } + + public RecipeBuilder output(IBlockState state) { + this.state = state; + return this; + } + + public RecipeBuilder condition(WorldBlockStatePredicate condition) { + this.condition = condition; + return this; + } + + @Override + public String getErrorMsg() { + return "Error adding Roots Transmutation recipe"; + } + + public String getRecipeNamePrefix() { + return "groovyscript_transmutation_"; + } + + @Override + public void validate(GroovyLog.Msg msg) { + validateName(); + validateItems(msg, 0, 0, 0, 1); + validateFluids(msg); + msg.add(start == null, "start must be defined"); + msg.add(state == null && (output.size() != 1 || output.get(0).isEmpty()), "either state must be defined or recipe must have an output"); + } + + @Override + public @Nullable TransmutationRecipe register() { + if (!validate()) return null; + TransmutationRecipe recipe = new TransmutationRecipe(start); + recipe.setRegistryName(name); + if (state == null) recipe.item(output.get(0)); + else recipe.state(state); + recipe.condition(condition); + ModSupport.ROOTS.get().transmutation.add(name, recipe); + return recipe; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/LateMixin.java index 8e98391a6..4fba06d22 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", "botania"); + "tcomplement", "extendedcrafting", "botania", "roots"); @Override public List getMixinConfigs() { diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModRecipesAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModRecipesAccessor.java new file mode 100644 index 000000000..cb8c9f5ea --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModRecipesAccessor.java @@ -0,0 +1,43 @@ +package com.cleanroommc.groovyscript.core.mixin.roots; + + +import epicsquid.roots.init.ModRecipes; +import epicsquid.roots.recipe.ChrysopoeiaRecipe; +import epicsquid.roots.recipe.MortarRecipe; +import epicsquid.roots.recipe.SummonCreatureRecipe; +import epicsquid.roots.recipe.TransmutationRecipe; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(value = ModRecipes.class, remap = false) +public interface ModRecipesAccessor { + + @Accessor + static Map getChrysopoeiaRecipes() { + throw new AssertionError(); + } + + @Accessor + static Map getTransmutationRecipes() { + throw new AssertionError(); + } + + @Accessor("summonCreatureRecipes") + static Map getSummonCreatureEntries() { + throw new AssertionError(); + } + + @Accessor + static Map, SummonCreatureRecipe> getSummonCreatureClasses() { + throw new AssertionError(); + } + + @Accessor + static Map getMortarRecipes() { + throw new AssertionError(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModifierAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModifierAccessor.java new file mode 100644 index 000000000..66c28ab49 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModifierAccessor.java @@ -0,0 +1,16 @@ +package com.cleanroommc.groovyscript.core.mixin.roots; + +import epicsquid.roots.modifiers.CostType; +import epicsquid.roots.modifiers.IModifierCost; +import epicsquid.roots.modifiers.Modifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(value = Modifier.class, remap = false) +public interface ModifierAccessor { + + @Accessor + Map getCosts(); +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModifierRegistryAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModifierRegistryAccessor.java new file mode 100644 index 000000000..3506867ed --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/ModifierRegistryAccessor.java @@ -0,0 +1,17 @@ +package com.cleanroommc.groovyscript.core.mixin.roots; + +import epicsquid.roots.modifiers.ModifierRegistry; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(value = ModifierRegistry.class, remap = false) +public interface ModifierRegistryAccessor { + + @Accessor + static Set getDisabledModifiers() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/MossConfigAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/MossConfigAccessor.java new file mode 100644 index 000000000..2cac3f1da --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/MossConfigAccessor.java @@ -0,0 +1,35 @@ +package com.cleanroommc.groovyscript.core.mixin.roots; + +import com.google.common.collect.BiMap; +import epicsquid.roots.config.MossConfig; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(value = MossConfig.class, remap = false) +public interface MossConfigAccessor { + + @Accessor + static Map getMossyCobblestones() { + throw new UnsupportedOperationException(); + } + + @Accessor("mossyCobblestones") + static void setMossyCobblestones(Map map) { + throw new UnsupportedOperationException(); + } + + @Accessor + static BiMap getMossyBlocks() { + throw new UnsupportedOperationException(); + } + + @Accessor + static BiMap getMossyStates() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/PacifistEntryAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/PacifistEntryAccessor.java new file mode 100644 index 000000000..c9da5c4bf --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/PacifistEntryAccessor.java @@ -0,0 +1,13 @@ +package com.cleanroommc.groovyscript.core.mixin.roots; + +import epicsquid.roots.recipe.PacifistEntry; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(value = PacifistEntry.class, remap = false) +public interface PacifistEntryAccessor { + + @Accessor + void setName(ResourceLocation name); +} diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/RitualBaseAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/RitualBaseAccessor.java new file mode 100644 index 000000000..577628357 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/roots/RitualBaseAccessor.java @@ -0,0 +1,15 @@ +package com.cleanroommc.groovyscript.core.mixin.roots; + +import epicsquid.roots.ritual.RitualBase; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(value = RitualBase.class, remap = false) +public interface RitualBaseAccessor { + + @Accessor("disabled") + boolean getDisabled(); + + @Accessor("disabled") + void setDisabled(boolean value); +} diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/recipe/AbstractRecipeBuilder.java b/src/main/java/com/cleanroommc/groovyscript/helper/recipe/AbstractRecipeBuilder.java index fb77bae9f..1158c32c6 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/recipe/AbstractRecipeBuilder.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/recipe/AbstractRecipeBuilder.java @@ -1,22 +1,39 @@ package com.cleanroommc.groovyscript.helper.recipe; +import com.cleanroommc.groovyscript.GroovyScript; import com.cleanroommc.groovyscript.api.GroovyLog; import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.helper.ingredient.FluidStackList; import com.cleanroommc.groovyscript.helper.ingredient.IngredientList; import com.cleanroommc.groovyscript.helper.ingredient.ItemStackList; import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; import net.minecraftforge.fluids.FluidStack; import java.util.Collection; public abstract class AbstractRecipeBuilder implements IRecipeBuilder { + protected ResourceLocation name; protected final IngredientList input = new IngredientList<>(); protected final ItemStackList output = new ItemStackList(); protected final FluidStackList fluidInput = new FluidStackList(); protected final FluidStackList fluidOutput = new FluidStackList(); + public String getRecipeNamePrefix() { + return "groovyscript_"; + } + + public AbstractRecipeBuilder name(String name) { + this.name = new ResourceLocation(GroovyScript.getRunConfig().getPackId(), name); + return this; + } + + public AbstractRecipeBuilder name(ResourceLocation name) { + this.name = name; + return this; + } + public AbstractRecipeBuilder input(IIngredient ingredient) { this.input.add(ingredient); return this; @@ -104,6 +121,12 @@ public boolean validate() { public abstract void validate(GroovyLog.Msg msg); + public void validateName() { + if (name == null) { + name = new ResourceLocation(GroovyScript.getRunConfig().getPackId(), RecipeName.generate(getRecipeNamePrefix())); + } + } + public void validateFluids(GroovyLog.Msg msg, int minFluidInput, int maxFluidInput, int minFluidOutput, int maxFluidOutput) { fluidInput.trim(); fluidOutput.trim(); diff --git a/src/main/resources/mixin.groovyscript.roots.json b/src/main/resources/mixin.groovyscript.roots.json new file mode 100644 index 000000000..de197c73d --- /dev/null +++ b/src/main/resources/mixin.groovyscript.roots.json @@ -0,0 +1,15 @@ +{ + "package": "com.cleanroommc.groovyscript.core.mixin.roots", + "refmap": "mixins.groovyscript.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "ModifierAccessor", + "ModifierRegistryAccessor", + "ModRecipesAccessor", + "MossConfigAccessor", + "PacifistEntryAccessor", + "RitualBaseAccessor" + ] +} \ No newline at end of file