diff --git a/src/main/groovy-tests/customRecipeClassTests.groovy b/src/main/groovy-tests/craftingRecipeTests.groovy
similarity index 53%
rename from src/main/groovy-tests/customRecipeClassTests.groovy
rename to src/main/groovy-tests/craftingRecipeTests.groovy
index badcd7d1..40d1cf46 100644
--- a/src/main/groovy-tests/customRecipeClassTests.groovy
+++ b/src/main/groovy-tests/craftingRecipeTests.groovy
@@ -1,6 +1,11 @@
+import com.nomiceu.nomilabs.LabsValues
import com.nomiceu.nomilabs.groovy.ShapedConversionRecipe
import com.nomiceu.nomilabs.groovy.ShapedDummyRecipe
+import com.nomiceu.nomilabs.groovy.SimpleIIngredient
+import com.nomiceu.nomilabs.util.ItemMeta
+import net.minecraft.item.ItemStack
import net.minecraft.util.text.TextFormatting
+import net.minecraftforge.fml.common.Loader
import static com.nomiceu.nomilabs.groovy.GroovyHelpers.NBTClearingRecipeHelpers.*
import static com.nomiceu.nomilabs.groovy.GroovyHelpers.TranslationHelpers.*
@@ -43,17 +48,20 @@ crafting.shapedBuilder()
// Examples: NBT Clearing
// Note that provided OUTPUT must match the output item in ALL CASES for that recipe!
// Simplest Form, Same Item for Input and Output
-nbtClearingRecipe(item('storagedrawers:compdrawers'))
+if (Loader.isModLoaded(LabsValues.STORAGE_DRAWERS_MODID))
+ nbtClearingRecipe(item('storagedrawers:compdrawers'))
// Different Item for Input and Output
nbtClearingRecipe(item('forge:bucketfilled'), item('minecraft:bucket'))
// Same Input/Output Item, Custom NBT Clearer
-nbtClearingRecipe(item('storagedrawers:basicdrawers'), {
- var tag = transferSubTags(it, 'material') // Transfer material, saved generated tag
- tag = transferDrawerUpgradeData(it, tag) // Transfer Upgrades
- it.tagCompound = tag // Remember to Save!
-})
+if (Loader.isModLoaded(LabsValues.STORAGE_DRAWERS_MODID)) {
+ nbtClearingRecipe(item('storagedrawers:basicdrawers'), {
+ var tag = transferSubTags(it, 'material') // Transfer material, saved generated tag
+ tag = transferDrawerUpgradeData(it, tag) // Transfer Upgrades
+ it.tagCompound = tag // Remember to Save!
+ })
+}
// Different Input/Output Item, Custom NBT Clearer, Custom CanClear/Warning Tooltips
nbtClearingRecipe(item('minecraft:water_bucket'), item('minecraft:bucket'),
@@ -64,3 +72,62 @@ nbtClearingRecipe(item('minecraft:water_bucket'), item('minecraft:bucket'),
translatableLiteral('Warning: Great Water?').addFormat(TextFormatting.RED)
)
+// Examples: Strict Recipes
+
+/**
+ * This means, in JEI, the list of 'matching stacks' will be displayed
+ * exactly as set, instead of expanding wildcards and removing duplicates.
+ */
+// Only JEI display is affected! This is only useful with custom IIngredients!
+
+// Custom IIngredient: Returns different NBTs of Stone (NBT's are auto-condensed by JEI)
+class TestStone extends SimpleIIngredient {
+
+ ItemStack stone = item('minecraft:stone')
+
+ @Override
+ ItemStack[] getMatchingStacks() {
+ return [
+ item('minecraft:stone'),
+ item('minecraft:stone').withNbt(['display': ['Name': 'Test']]),
+ item('minecraft:stone').withNbt(['display': ['Name': 'Test 2']]),
+ item('minecraft:stone').withNbt(['display': ['Name': 'Test 3']]),
+ ].toArray()
+ }
+
+ @Override
+ boolean test(ItemStack itemStack) {
+ return ItemMeta.compare(itemStack, stone)
+ }
+}
+
+// Without Strict: Shaped
+crafting.shapedBuilder()
+ .name('shaped-no-strict')
+ .output(item('minecraft:dirt'))
+ .matrix([[new TestStone()]])
+ .register()
+
+// With Strict: Shaped
+crafting.shapedBuilder()
+ .name('shaped-strict')
+ .output(item('minecraft:dirt'))
+ .matrix([[new TestStone()]])
+ .strictJEIHandling()
+ .register()
+
+// Without Strict: Shapeless
+crafting.shapelessBuilder()
+ .name('shapeless-no-strict')
+ .output(item('minecraft:dirt'))
+ .input(new TestStone())
+ .register()
+
+// With Strict: Shapeless
+crafting.shapelessBuilder()
+ .name('shapeless-strict')
+ .output(item('minecraft:dirt'))
+ .input(new TestStone())
+ .strictJEIHandling()
+ .register()
+
diff --git a/src/main/groovy-tests/dmeSimChamberTests.groovy b/src/main/groovy-tests/dmeSimChamberTests.groovy
index cbe747b0..22d41941 100644
--- a/src/main/groovy-tests/dmeSimChamberTests.groovy
+++ b/src/main/groovy-tests/dmeSimChamberTests.groovy
@@ -1,10 +1,12 @@
+//MODS_LOADED: deepmoblearning
+
import com.nomiceu.nomilabs.LabsValues
import mustapelto.deepmoblearning.common.metadata.MetadataDataModel
import mustapelto.deepmoblearning.common.metadata.MetadataManager
import net.minecraftforge.fml.common.Loader
// Demonstration of Dynamically Generated DME Sim Chamber Recipes. (Goes in Post Init)
-// DOES NOT WORK IF DME IS NOT INCLUDED IN LOAD!
+// WILL NOT LOAD IF DME IS NOT INCLUDED!
def models = MetadataManager.dataModelMetadataList
for (var model : models) {
diff --git a/src/main/groovy-tests/miscTests.groovy b/src/main/groovy-tests/ncCoolerTests.groovy
similarity index 88%
rename from src/main/groovy-tests/miscTests.groovy
rename to src/main/groovy-tests/ncCoolerTests.groovy
index 315ddbd7..30f0f191 100644
--- a/src/main/groovy-tests/miscTests.groovy
+++ b/src/main/groovy-tests/ncCoolerTests.groovy
@@ -1,7 +1,11 @@
+//MODS_LOADED: nuclearcraft
+
import com.nomiceu.nomilabs.groovy.NCActiveCoolerHelper
import static nc.enumm.MetaEnums.CoolerType.*
+// Does not load if NuclearCraft is not loaded!
+
/**
* Change a NuclearCraft Active Cooler Recipe.
* Inputs:
diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/SimpleIIngredient.java b/src/main/java/com/nomiceu/nomilabs/groovy/SimpleIIngredient.java
new file mode 100644
index 00000000..418475e3
--- /dev/null
+++ b/src/main/java/com/nomiceu/nomilabs/groovy/SimpleIIngredient.java
@@ -0,0 +1,42 @@
+package com.nomiceu.nomilabs.groovy;
+
+import net.minecraft.item.crafting.Ingredient;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.cleanroommc.groovyscript.api.IIngredient;
+
+/**
+ * A template for a simple implementation of IIngredient, with stack size 1, no marks, and no copying.
+ *
+ * Also sets `toMcIngredient` to return an ingredient of the matching stacks.
+ */
+public abstract class SimpleIIngredient implements IIngredient {
+
+ @Override
+ public Ingredient toMcIngredient() {
+ return Ingredient.fromStacks(getMatchingStacks());
+ }
+
+ @Override
+ public int getAmount() {
+ return 1;
+ }
+
+ @Override
+ public void setAmount(int amount) {}
+
+ @Override
+ public IIngredient exactCopy() {
+ return this;
+ }
+
+ @Nullable
+ @Override
+ public String getMark() {
+ return null;
+ }
+
+ @Override
+ public void setMark(String mark) {}
+}
diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/mixinhelper/StrictableItemRecipeWrappers.java b/src/main/java/com/nomiceu/nomilabs/groovy/mixinhelper/StrictableItemRecipeWrappers.java
new file mode 100644
index 00000000..9daaee7b
--- /dev/null
+++ b/src/main/java/com/nomiceu/nomilabs/groovy/mixinhelper/StrictableItemRecipeWrappers.java
@@ -0,0 +1,80 @@
+package com.nomiceu.nomilabs.groovy.mixinhelper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.IRecipe;
+import net.minecraftforge.common.crafting.IShapedRecipe;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.cleanroommc.groovyscript.compat.mods.jei.ShapedRecipeWrapper;
+
+import mezz.jei.api.IJeiHelpers;
+import mezz.jei.api.ingredients.IIngredients;
+import mezz.jei.api.ingredients.VanillaTypes;
+import mezz.jei.api.recipe.IStackHelper;
+import mezz.jei.plugins.vanilla.crafting.ShapelessRecipeWrapper;
+import mezz.jei.recipes.BrokenCraftingRecipeException;
+import mezz.jei.util.ErrorUtil;
+
+/**
+ * Recipe wrappers that can return the matching stacks exactly for JEI,
+ * ignoring duplicates according to JEI and wildcard itemstacks.
+ */
+public class StrictableItemRecipeWrappers {
+
+ public static void getIngredientsImpl(@NotNull IIngredients ingredients, IRecipe recipe, IJeiHelpers jeiHelpers) {
+ ItemStack recipeOutput = recipe.getRecipeOutput();
+ List> inputLists;
+
+ try {
+ if (recipe instanceof StrictableRecipe strict && strict.labs$getIsStrict()) {
+ inputLists = new ArrayList<>();
+ for (var input : recipe.getIngredients()) {
+ inputLists.add(new ArrayList<>(Arrays.asList(input.getMatchingStacks())));
+ }
+ } else {
+ IStackHelper stackHelper = jeiHelpers.getStackHelper();
+ inputLists = stackHelper.expandRecipeItemStackInputs(recipe.getIngredients());
+ }
+ ingredients.setInputLists(VanillaTypes.ITEM, inputLists);
+ ingredients.setOutput(VanillaTypes.ITEM, recipeOutput);
+ } catch (RuntimeException e) {
+ String info = ErrorUtil.getInfoFromBrokenCraftingRecipe(recipe, recipe.getIngredients(), recipeOutput);
+ throw new BrokenCraftingRecipeException(info, e);
+ }
+ }
+
+ public static class Shapeless extends ShapelessRecipeWrapper
> list, IRecipeCreator> recipeCreator) {
- if (recipeClassFunction == null) return validateShape(msg, list, recipeCreator);
- return validateShape(msg, list, (width1, height1, ingredients) -> recipeClassFunction
+ if (labs$recipeClassFunction == null) return validateShape(msg, list, recipeCreator);
+ return validateShape(msg, list, (width1, height1, ingredients) -> labs$recipeClassFunction
.createRecipe(output, width1, height1, ingredients, mirrored, recipeFunction, recipeAction));
}
}
diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/groovyscript/ShapelessRecipeBuilderMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/groovyscript/ShapelessRecipeBuilderMixin.java
new file mode 100644
index 00000000..d158afa2
--- /dev/null
+++ b/src/main/java/com/nomiceu/nomilabs/mixin/groovyscript/ShapelessRecipeBuilderMixin.java
@@ -0,0 +1,42 @@
+package com.nomiceu.nomilabs.mixin.groovyscript;
+
+import net.minecraft.item.crafting.IRecipe;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import com.cleanroommc.groovyscript.compat.vanilla.CraftingRecipeBuilder;
+import com.nomiceu.nomilabs.groovy.mixinhelper.StrictableRecipe;
+
+/**
+ * Allows for recipes to be 'strict'. This means, in JEI, the list of 'matching stacks' will be displayed
+ * exactly as set, instead of expanding wildcards and removing duplicates.
+ */
+@Mixin(value = CraftingRecipeBuilder.Shapeless.class, remap = false)
+@SuppressWarnings("unused")
+public class ShapelessRecipeBuilderMixin {
+
+ @Unique
+ private boolean labs$isStrict = false;
+
+ /**
+ * Makes recipes 'strict'. This means, in JEI, the list of 'matching stacks' will be displayed
+ * exactly as set, instead of expanding wildcards and removing duplicates.
+ */
+ @Unique
+ public CraftingRecipeBuilder.Shapeless strictJEIHandling() {
+ labs$isStrict = true;
+ return (CraftingRecipeBuilder.Shapeless) (Object) this;
+ }
+
+ @Inject(method = "register()Lnet/minecraft/item/crafting/IRecipe;", at = @At("RETURN"))
+ private void setStrict(CallbackInfoReturnable