Skip to content

Commit

Permalink
GroovyScript Fixed Shaped Conversion Recipes
Browse files Browse the repository at this point in the history
  • Loading branch information
IntegerLimit committed Mar 28, 2024
1 parent 5085109 commit e718775
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 32 deletions.
25 changes: 25 additions & 0 deletions src/main/groovy-tests/customRecipeClassTests.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import com.nomiceu.nomilabs.groovy.ShapedConversionRecipe

// Use a Custom Recipe Class in Shaped Recipes!

// Using Key Based Matrix
crafting.shapedBuilder()
.output(metaitem('dye.white') * 2)
.matrix('D')
.key('D', item('minecraft:dye', 15))
// Two Options:
// ItemStack output, int width, int height, List<IIngredient> ingredients
// ItemStack output, int width, int height, List<IIngredient> ingredients, boolean mirrored, Closure<ItemStack> recipeFunction, Closure<Void> recipeAction
.recipeClassFunction((output, width, height, ingredients, mirrored, recipeFunction, recipeAction) -> new ShapedConversionRecipe(output, ingredients, width, height, mirrored, recipeFunction, recipeAction))
.register()


// Using Ingredient Matrix
crafting.shapedBuilder()
.output(metaitem('dye.white'))
.matrix([[null, null], [item('minecraft:dye', 15), null]])
// Two Options:
// ItemStack output, int width, int height, List<IIngredient> ingredients
// ItemStack output, int width, int height, List<IIngredient> ingredients, boolean mirrored, Closure<ItemStack> recipeFunction, Closure<Void> recipeAction
.recipeClassFunction((output, width, height, ingredients) -> new ShapedConversionRecipe(output, ingredients, width, height))
.register()
3 changes: 2 additions & 1 deletion src/main/java/com/nomiceu/nomilabs/core/LabsLateMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public class LabsLateMixin implements ILateMixinLoader {
new AbstractMap.SimpleImmutableEntry<>(LabsValues.ARCHITECTURE_MODID,
LabsConfig.modIntegration.enableArchitectureCraftIntegration),
new AbstractMap.SimpleImmutableEntry<>(LabsValues.EFFORTLESS_MODID,
LabsConfig.modIntegration.effortlessBuildingIntegration.enableEffortlessBuildingIntegration)
LabsConfig.modIntegration.effortlessBuildingIntegration.enableEffortlessBuildingIntegration),
new AbstractMap.SimpleImmutableEntry<>(LabsValues.GROOVY_MODID, true)
)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nomiceu.nomilabs.gregtech.mixinhelper;

import com.nomiceu.nomilabs.groovy.ShapedConversionRecipe;
import gregtech.common.crafting.GTShapedOreRecipe;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
Expand All @@ -12,6 +13,7 @@
*/
public class GTDisassemblingOreRecipe extends GTShapedOreRecipe {
private final int inputLocation;
private Integer[] cache;

public GTDisassemblingOreRecipe(boolean isClearing, ResourceLocation group, @NotNull ItemStack result, Object... recipe) {
super(isClearing, group, result, recipe);
Expand All @@ -29,36 +31,6 @@ public GTDisassemblingOreRecipe(boolean isClearing, ResourceLocation group, @Not
*/
@Override
public boolean matches(@NotNull InventoryCrafting inv, @NotNull World world) {
if (inv.getWidth() < 2 || inv.getWidth() > 3 || inv.getHeight() < 2 || inv.getHeight() > 3 || inv.getWidth() != inv.getHeight()) return false;
if (inputLocation == -1) return false;
var location = getLocationForDim(inv.getWidth());
if (location == -1) return false;

int locationX = location % inv.getWidth();
int locationY = location / inv.getWidth();

// Not using inv.stackList, as the stackList may not be correct for a given invCrafting.
for (int x = 0; x < inv.getWidth(); x++) {
for (int y = 0; y < inv.getHeight(); y++) {
if (x == locationX && y == locationY) {
if (!input.get(inputLocation).apply(inv.getStackInRowAndColumn(x, y))) return false;
continue;
}
if (!inv.getStackInRowAndColumn(x, y).isEmpty()) return false;
}
}
return true;
}

private int getLocationForDim(int dim) {
switch (dim) {
case 2 -> {
if (inputLocation > 4 || inputLocation == 2) return -1; // Not in 2x2 square
if (inputLocation > 2) return inputLocation - 1;
return inputLocation;
}
case 3 -> { return inputLocation; }
default -> { return -1; }
}
return ShapedConversionRecipe.matchesShaped(inv, inputLocation, (stack) -> input.get(inputLocation).apply(stack), cache, (cache1) -> cache = cache1);
}
}
103 changes: 103 additions & 0 deletions src/main/java/com/nomiceu/nomilabs/groovy/ShapedConversionRecipe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.nomiceu.nomilabs.groovy;

import com.cleanroommc.groovyscript.api.GroovyLog;
import com.cleanroommc.groovyscript.api.IIngredient;
import com.cleanroommc.groovyscript.compat.vanilla.ShapedCraftingRecipe;
import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper;
import groovy.lang.Closure;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
* A shaped conversion recipe where there must be only one input.
*/
@SuppressWarnings("unused")
public class ShapedConversionRecipe extends ShapedCraftingRecipe {
private int recipeInputLocation;
private int trueInputLocation;

private Integer[] cache;

public ShapedConversionRecipe(ItemStack output, List<IIngredient> input, int width, int height) {
this(output, input, width, height, false, null, null);
}

public ShapedConversionRecipe(ItemStack output, List<IIngredient> input, int width, int height, boolean mirrored, @Nullable Closure<ItemStack> recipeFunction, @Nullable Closure<Void> recipeAction) {
super(output, input, width, height, mirrored, recipeFunction, recipeAction);
recipeInputLocation = -1;
if (width != height) {
GroovyLog.get()
.exception(new IllegalArgumentException("Shaped Conversion Recipes must have the same width and height!"));
return;
}
for (int i = 0; i < input.size(); i++) {
var ing = input.get(i);
if (IngredientHelper.isEmpty(ing)) continue;
if (recipeInputLocation != -1) {
GroovyLog.get()
.exception(new IllegalArgumentException("Shaped Conversion Recipes can only have one non-empty input!"));
return;
}
recipeInputLocation = i;
}
trueInputLocation = recipeInputLocation;
if (width == 1) {
// Default to center
recipeInputLocation = 4;
}
else if (width == 2) {
if (recipeInputLocation > 1) {
recipeInputLocation++;
}
}
}

@Override
public boolean matches(@NotNull InventoryCrafting inv, @NotNull World worldIn) {
return matchesShaped(inv, recipeInputLocation, (stack) -> input.get(trueInputLocation).test(stack), cache, (cache1) -> cache = cache1);
}

public static boolean matchesShaped(@NotNull InventoryCrafting inv, int inputLocation, Predicate<ItemStack> accepts, Integer[] cache, Consumer<Integer[]> setCache) {
if (inv.getWidth() < 2 || inv.getWidth() > 3 || inv.getHeight() < 2 || inv.getHeight() > 3 || inv.getWidth() != inv.getHeight()) return false;
if (inputLocation == -1) return false;
var location = getLocationForDim(inv.getWidth(), inputLocation, cache, setCache);
if (location == -1) return false;

int locationX = location % inv.getWidth();
int locationY = location / inv.getWidth();

// Not using inv.stackList, as the stackList may not be correct for a given invCrafting.
for (int x = 0; x < inv.getWidth(); x++) {
for (int y = 0; y < inv.getHeight(); y++) {
if (x == locationX && y == locationY) {
if (!accepts.test(inv.getStackInRowAndColumn(x, y))) return false;
continue;
}
if (!inv.getStackInRowAndColumn(x, y).isEmpty()) return false;
}
}
return true;
}

private static int getLocationForDim(int dim, int inputLocation, Integer[] cache, Consumer<Integer[]> setCache) {
if (cache == null) {
cache = new Integer[2];

cache[0] = inputLocation;
if (inputLocation > 4 || inputLocation == 2) cache[0] = -1; // Not in 2x2 square
else if (inputLocation > 2) cache[0] = inputLocation - 1;

cache[1] = inputLocation;
setCache.accept(cache);
}
// 0 when cache is 2, 1 when cache is 3
return cache[dim - 2];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.nomiceu.nomilabs.groovy.mixinhelper;

import com.cleanroommc.groovyscript.api.IIngredient;
import com.cleanroommc.groovyscript.compat.vanilla.ShapedCraftingRecipe;
import groovy.lang.Closure;
import net.minecraft.item.ItemStack;

import java.util.List;

@FunctionalInterface
public interface ShapedRecipeClassFunction {
ShapedCraftingRecipe createRecipe(ItemStack output, int width, int height, List<IIngredient> ingredients, boolean mirrored, Closure<ItemStack> recipeFunction, Closure<Void> recipeAction);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.nomiceu.nomilabs.groovy.mixinhelper;

import com.cleanroommc.groovyscript.api.IIngredient;
import com.cleanroommc.groovyscript.compat.vanilla.ShapedCraftingRecipe;
import net.minecraft.item.ItemStack;

import java.util.List;

@FunctionalInterface
public interface ShapedRecipeClassFunctionSimplified {
ShapedCraftingRecipe createRecipe(ItemStack output, int width, int height, List<IIngredient> ingredients);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.nomiceu.nomilabs.mixin.groovyscript;

import com.cleanroommc.groovyscript.api.GroovyLog;
import com.cleanroommc.groovyscript.api.IIngredient;
import com.cleanroommc.groovyscript.compat.vanilla.CraftingRecipeBuilder;
import com.nomiceu.nomilabs.groovy.mixinhelper.ShapedRecipeClassFunction;
import com.nomiceu.nomilabs.groovy.mixinhelper.ShapedRecipeClassFunctionSimplified;
import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import java.util.List;

@Mixin(value = CraftingRecipeBuilder.Shaped.class, remap = false)
@SuppressWarnings("unused")
public abstract class ShapedRecipeBuilderMixin extends CraftingRecipeBuilder {
@Shadow
protected boolean mirrored;

@Unique
private ShapedRecipeClassFunction recipeClassFunction = null;

/**
* Default Ignored Constructor
*/
public ShapedRecipeBuilderMixin(int width, int height) {
super(width, height);
}

@Unique
public CraftingRecipeBuilder.Shaped recipeClassFunction(ShapedRecipeClassFunction recipeClassFunction) {
this.recipeClassFunction = recipeClassFunction;
return (CraftingRecipeBuilder.Shaped) (Object) this;
}

@Unique
public CraftingRecipeBuilder.Shaped recipeClassFunction(ShapedRecipeClassFunctionSimplified recipeClassFunction) {
this.recipeClassFunction = (output1, width1, height1, ingredients, _mirrored, _recipeFunction, _recipeAction) -> recipeClassFunction.createRecipe(output1, width1, height1, ingredients);
return (CraftingRecipeBuilder.Shaped) (Object) this;
}

@Redirect(method = "register()Lnet/minecraft/item/crafting/IRecipe;", at = @At(value = "INVOKE", target = "Lcom/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder$Shaped;validateShape(Lcom/cleanroommc/groovyscript/api/GroovyLog$Msg;Ljava/util/List;[Ljava/lang/String;Lit/unimi/dsi/fastutil/chars/Char2ObjectOpenHashMap;Lcom/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder$IRecipeCreator;)Ljava/lang/Object;"), require = 1)
public Object registerWithClassFunction1(Shaped instance, GroovyLog.Msg msg, List<String> errors, String[] keyBasedMatrix, Char2ObjectOpenHashMap<IIngredient> keyMap, IRecipeCreator<?> recipeCreator) {
if (recipeClassFunction == null) return validateShape(msg, errors, keyBasedMatrix, keyMap, recipeCreator);
return validateShape(msg, errors, keyBasedMatrix, keyMap, (width1, height1, ingredients) -> recipeClassFunction.createRecipe(output, width1, height1, ingredients, mirrored, recipeFunction, recipeAction));
}

@Redirect(method = "register()Lnet/minecraft/item/crafting/IRecipe;", at = @At(value = "INVOKE", target = "Lcom/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder$Shaped;validateShape(Lcom/cleanroommc/groovyscript/api/GroovyLog$Msg;Ljava/util/List;Lcom/cleanroommc/groovyscript/compat/vanilla/CraftingRecipeBuilder$IRecipeCreator;)Ljava/lang/Object;"), require = 1)
public Object registerWithClassFunction2(Shaped instance, GroovyLog.Msg msg, List<List<IIngredient>> ingredientMatrix, IRecipeCreator<?> recipeCreator) {
if (recipeClassFunction == null) return validateShape(msg, ingredientMatrix, recipeCreator);
return validateShape(msg, ingredientMatrix, (width1, height1, ingredients) -> recipeClassFunction.createRecipe(output, width1, height1, ingredients, mirrored, recipeFunction, recipeAction));
}
}
12 changes: 12 additions & 0 deletions src/main/resources/mixins.nomilabs.groovyscript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"package": "com.nomiceu.nomilabs.mixin.groovyscript",
"refmap": "mixins.nomilabs.refmap.json",
"target": "@env(DEFAULT)",
"minVersion": "0.8",
"compatibilityLevel": "JAVA_8",
"mixins": [
"ShapedRecipeBuilderMixin"
],
"client": [],
"server": []
}

0 comments on commit e718775

Please sign in to comment.