diff --git a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java index fdcd76501..9d61890b2 100644 --- a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java +++ b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java @@ -36,13 +36,18 @@ import org.cyclops.integratedcrafting.part.aspect.CraftingAspectWriteBuilders; import org.cyclops.integratedcrafting.part.aspect.CraftingAspects; import org.cyclops.integrateddynamics.RegistryEntries; +import org.cyclops.integrateddynamics.api.evaluate.operator.IOperator; import org.cyclops.integrateddynamics.api.part.PartPos; import org.cyclops.integrateddynamics.api.part.write.IPartStateWriter; import org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus; +import org.cyclops.integrateddynamics.core.evaluate.operator.CurriedOperator; +import org.cyclops.integrateddynamics.core.evaluate.operator.Operators; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean; +import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes; +import org.cyclops.integrateddynamics.core.evaluate.variable.Variable; import org.cyclops.integrateddynamics.core.helper.NetworkHelpers; import org.cyclops.integrateddynamics.core.helper.PartHelpers; import org.cyclops.integratedtunnels.part.aspect.TunnelAspects; @@ -100,6 +105,80 @@ public void testItemsCraftChestOne(GameTestHelper helper) { }); } + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftChestOneOperator(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS); + + // Insert items in interface chest + ChestBlockEntity chestIn = helper.getBlockEntity(POS.east()); + chestIn.setItem(0, new ItemStack(Items.OAK_PLANKS, 64)); + + // Add chest recipe to crafting interface + ResourceLocation recipeName = ResourceLocation.fromNamespaceAndPath("minecraft", "chest"); + positions.interfaceRecipeAdders().get(0).accept(Triple.of(0, RecipeType.CRAFTING, recipeName)); + + // Enable operator crafting aspect in crafting writer, with an operator that returns the chest recipe + placeOperatorInWriter(helper, positions.writer(), new CurriedOperator(Operators.GENERAL_IDENTITY, + new Variable<>(ValueTypes.OBJECT_RECIPE, ValueObjectTypeRecipe.ValueRecipe.of(createRecipeDefinition(helper.getLevel(), RecipeType.CRAFTING, recipeName))))); + + helper.succeedWhen(() -> { + IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(positions.writer()).getState(); + helper.assertValueEqual(partStateWriter.getActiveAspect(), CraftingAspects.Write.OPERATOR_CRAFT, "Active aspect is incorrect"); + helper.assertTrue(partStateWriter.getErrors(CraftingAspects.Write.OPERATOR_CRAFT).isEmpty(), "Active aspect has errors"); + + // Check if items have been crafted + helper.assertValueEqual(chestIn.getItem(0).getItem(), Items.OAK_PLANKS, "Slot 0 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(0).getCount(), 56, "Slot 0 amount is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getItem(), Items.CHEST, "Slot 1 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getCount(), 1, "Slot 1 amount is incorrect"); + }); + } + + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftChestOneOperatorEmptyRecipe(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS); + + // Insert items in interface chest + ChestBlockEntity chestIn = helper.getBlockEntity(POS.east()); + chestIn.setItem(0, new ItemStack(Items.OAK_PLANKS, 64)); + + // Add chest recipe to crafting interface + positions.interfaceRecipeAdders().get(0).accept(Triple.of(0, RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest"))); + + // Enable operator crafting aspect in crafting writer, with an operator that returns an empty recipe + placeOperatorInWriter(helper, positions.writer(), new CurriedOperator(Operators.GENERAL_IDENTITY, + new Variable<>(ValueTypes.OBJECT_RECIPE, ValueObjectTypeRecipe.ValueRecipe.of(null)))); + + helper.runAfterDelay(100, () -> { + IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(positions.writer()).getState(); + helper.assertValueEqual(partStateWriter.getActiveAspect(), CraftingAspects.Write.OPERATOR_CRAFT, "Active aspect is incorrect"); + helper.assertTrue(partStateWriter.getErrors(CraftingAspects.Write.OPERATOR_CRAFT).isEmpty(), "Active aspect has errors"); + + // Check that nothing has been crafted + helper.assertValueEqual(chestIn.getItem(0).getCount(), 64, "Slot 0 amount is incorrect"); + helper.assertTrue(chestIn.getItem(1).isEmpty(), "Slot 1 is not empty"); + helper.succeed(); + }); + } + + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftOperatorWithInputs(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS); + + // Enable operator crafting aspect in crafting writer, with an operator that still requires an input + placeOperatorInWriter(helper, positions.writer(), Operators.GENERAL_IDENTITY); + + helper.succeedWhen(() -> { + IPartStateWriter partStateWriter = (IPartStateWriter) PartHelpers.getPart(positions.writer()).getState(); + helper.assertFalse(partStateWriter.getErrors(CraftingAspects.Write.OPERATOR_CRAFT).isEmpty(), "Active aspect has no errors"); + }); + } + + private static void placeOperatorInWriter(GameTestHelper helper, PartPos writerPos, IOperator operator) { + placeVariableInWriter(helper.getLevel(), writerPos, CraftingAspects.Write.OPERATOR_CRAFT, + createVariableForValue(helper.getLevel(), ValueTypes.OPERATOR, ValueTypeOperator.ValueOperator.of(operator))); + } + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) public void testItemsCraftChestOneRecipeDuration(GameTestHelper helper) { GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS); diff --git a/src/main/java/org/cyclops/integratedcrafting/part/PartTypeCraftingWriter.java b/src/main/java/org/cyclops/integratedcrafting/part/PartTypeCraftingWriter.java index 623f602da..64d856fc1 100644 --- a/src/main/java/org/cyclops/integratedcrafting/part/PartTypeCraftingWriter.java +++ b/src/main/java/org/cyclops/integratedcrafting/part/PartTypeCraftingWriter.java @@ -22,7 +22,8 @@ public PartTypeCraftingWriter(String name) { CraftingAspects.Write.RECIPE_CRAFT, CraftingAspects.Write.ITEMSTACK_CRAFT, CraftingAspects.Write.FLUIDSTACK_CRAFT, - CraftingAspects.Write.ENERGY_CRAFT + CraftingAspects.Write.ENERGY_CRAFT, + CraftingAspects.Write.OPERATOR_CRAFT )); } diff --git a/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspectWriteBuilders.java b/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspectWriteBuilders.java index 8e7f397de..1c991a73c 100644 --- a/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspectWriteBuilders.java +++ b/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspectWriteBuilders.java @@ -1,6 +1,7 @@ package org.cyclops.integratedcrafting.part.aspect; import com.google.common.collect.ImmutableList; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.fluids.FluidStack; import org.apache.commons.lang3.tuple.Triple; @@ -11,18 +12,26 @@ import org.cyclops.integratedcrafting.api.network.ICraftingNetwork; import org.cyclops.integratedcrafting.core.CraftingHelpers; import org.cyclops.integratedcrafting.part.PartTypeCraftingWriter; +import org.cyclops.integrateddynamics.api.evaluate.EvaluationException; +import org.cyclops.integrateddynamics.api.evaluate.operator.IOperator; +import org.cyclops.integrateddynamics.api.evaluate.variable.IValue; +import org.cyclops.integrateddynamics.api.evaluate.variable.IValueType; +import org.cyclops.integrateddynamics.api.evaluate.variable.IVariable; import org.cyclops.integrateddynamics.api.network.INetwork; import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients; import org.cyclops.integrateddynamics.api.part.PartPos; import org.cyclops.integrateddynamics.api.part.PartTarget; import org.cyclops.integrateddynamics.api.part.aspect.property.IAspectProperties; import org.cyclops.integrateddynamics.api.part.aspect.property.IAspectPropertyTypeInstance; +import org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeFluidStack; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger; +import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes; +import org.cyclops.integrateddynamics.core.helper.L10NValues; import org.cyclops.integrateddynamics.core.helper.PartHelpers; import org.cyclops.integrateddynamics.core.part.aspect.build.AspectBuilder; import org.cyclops.integrateddynamics.core.part.aspect.build.IAspectValuePropagator; @@ -86,6 +95,25 @@ public class CraftingAspectWriteBuilders { BUILDER_INTEGER = AspectWriteBuilders.BUILDER_INTEGER.byMod(IntegratedCrafting._instance) .appendKind("craft").handle(AspectWriteBuilders.PROP_GET_INTEGER); + public static final IAspectValuePropagator, Triple> + PROP_GET_OPERATOR_RECIPE = input -> { + IOperator operator = input.getRight().getRawValue(); + if (operator.getRequiredInputLength() == 0 + && ValueHelpers.correspondsTo(operator.getOutputType(), ValueTypes.OBJECT_RECIPE)) { + IValue result = ValueHelpers.evaluateOperator(operator, new IVariable[0]); + if (result instanceof ValueObjectTypeRecipe.ValueRecipe valueRecipe) { + return Triple.of(input.getLeft(), input.getMiddle(), valueRecipe.getRawValue().orElse(null)); + } + } + Component current = ValueTypeOperator.getSignature(operator); + Component expected = ValueTypeOperator.getSignature(new IValueType[0], ValueTypes.OBJECT_RECIPE); + throw new EvaluationException(Component.translatable(L10NValues.ASPECT_ERROR_INVALIDTYPE, + expected, current)); + }; + public static final AspectBuilder> + BUILDER_OPERATOR = AspectWriteBuilders.BUILDER_OPERATOR.byMod(IntegratedCrafting._instance) + .appendKind("craft").handle(PROP_GET_OPERATOR_RECIPE); + public static final IAspectValuePropagator, CraftingJobData> PROP_ITEMSTACK_CRAFTINGDATA = input -> { PartTarget partTarget = input.getLeft(); diff --git a/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspects.java b/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspects.java index 677b4d62c..075799fd7 100644 --- a/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspects.java +++ b/src/main/java/org/cyclops/integratedcrafting/part/aspect/CraftingAspects.java @@ -19,6 +19,7 @@ import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList; +import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeOperator; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes; import org.cyclops.integrateddynamics.part.aspect.read.AspectReadBuilders; @@ -41,6 +42,11 @@ public static final class Write { .withProperties(CraftingAspectWriteBuilders.PROPERTIES_CRAFTING_RECIPE) .handle(CraftingAspectWriteBuilders.PROP_CRAFT_RECIPE) .buildWrite(); + public static final IAspectWrite OPERATOR_CRAFT = + CraftingAspectWriteBuilders.BUILDER_OPERATOR + .withProperties(CraftingAspectWriteBuilders.PROPERTIES_CRAFTING_RECIPE) + .handle(CraftingAspectWriteBuilders.PROP_CRAFT_RECIPE) + .buildWrite(); public static final IAspectWrite ITEMSTACK_CRAFT = CraftingAspectWriteBuilders.BUILDER_ITEMSTACK .withProperties(CraftingAspectWriteBuilders.PROPERTIES_CRAFTING) diff --git a/src/main/resources/assets/integratedcrafting/lang/en_us.json b/src/main/resources/assets/integratedcrafting/lang/en_us.json index 285d97627..298f89112 100644 --- a/src/main/resources/assets/integratedcrafting/lang/en_us.json +++ b/src/main/resources/assets/integratedcrafting/lang/en_us.json @@ -76,6 +76,8 @@ "_comment": "# Crafting", "aspect.integratedcrafting.write.recipe.craft": "Craft Recipe", "aspect.integratedcrafting.write.recipe.craft.info": "Craft the given recipe", + "aspect.integratedcrafting.write.operator.craft": "Craft Recipe from Operator", + "aspect.integratedcrafting.write.operator.craft.info": "Craft the recipe returned by the given operator without inputs. If the operator returns an empty recipe, nothing is crafted.", "aspect.integratedcrafting.write.itemstack.craft": "Craft Item", "aspect.integratedcrafting.write.itemstack.craft.info": "Craft the given item", "aspect.integratedcrafting.write.fluidstack.craft": "Craft Fluid", diff --git a/src/main/resources/assets/integratedcrafting/models/aspect/write/operator/craft.json b/src/main/resources/assets/integratedcrafting/models/aspect/write/operator/craft.json new file mode 100644 index 000000000..6a3566f31 --- /dev/null +++ b/src/main/resources/assets/integratedcrafting/models/aspect/write/operator/craft.json @@ -0,0 +1,6 @@ +{ + "parent": "builtin/generated", + "textures": { + "layer0": "integratedcrafting:aspect/write/recipe/craft" + } +} \ No newline at end of file