Copying Recipes

The Artisan Worktables builder API allows you to copy recipes in a reasonably flexible manner. Copying recipes, however, is a little more involved than simply calling a method on the builder, but not by much.

Warning

Artisan Worktables can only copy vanilla crafting recipes.

Here you will use a new type of builder: Copy. You will use this new builder to create a task that defines what to copy and how to copy it. This new builder works comparably to the RecipeBuilder in that you must first call a method to retrieve a builder object and can chain more methods on that object to provide additional directives to the task.

The copy task that you define with the Copy builder will then be passed to the RecipeBuilder.

First, you'll need an extra import:

import mods.artisanworktables.builder.Copy;

Next, one, and only one, of the following methods must be called to initialize a new Copy task:

// copies a recipe by recipe name
Copy byName(String recipeName);

// copies the provided recipe
Copy byRecipe(ICraftingRecipe recipe);

// copies all recipes that match the provided recipe ingredients
Copy byOutput(IIngredient[] outputs);

By default, the initializing methods above will instruct the Copy task to copy both the input and output of the provided recipe(s). This behavior can be altered by chaining additional calls to further define how you want to copy the recipe(s) indicated. Altering the behavior of a copy task is described below in the section titled Refining a Copy Task.

Note

Calling byOutput(IIngredient[]) may result in multiple recipes being copied and created if multiple recipes match the outputs provided.

Finally, to tell a RecipeBuilder about your copy task, you will pass this configured Copy object into the following recipe builder method:

RecipeBuilder setCopy(Copy copy);

For example, initializing a Copy task and passing it to a RecipeBuilder will look something like this:

builder.setCopy(Copy.byName("minecraft:furnace"));

Note

Due to the way things work internally, the mirrored flag of a recipe won't be copied. When copying a shaped recipe that is mirrored, you must explicily call setMirrored() on the RecipeBuilder if you want the copy to also be mirrored.

Refining a Copy Task

After calling one of the initializing methods above, you can call the following methods to refine exactly what is copied.

Exclude Input

The following method will instruct the Copy task to not copy a recipe's input. If you call this method, you will need to supply your own recipe input on the RecipeBuilder.

Copy noInput();

For example, to copy the furnace recipe by name and exclude the input, define a Copy task like this:

builder.setCopy(Copy.byName("minecraft:furnace").noInput());

Warning

The noInput() method is mutually exclusive with the following methods:

  • noOutput()
  • replaceInput(IIngredient, IIngredient)
  • replaceShapedInput(int, int, IIngredient)

Replace Input

The input ingredients of copied recipes can be replaced when copying, either by IIngredient or grid position.

Copy replaceInput(@Nullable IIngredient toReplace, @Nullable IIngredient replacement);
Copy replaceShapedInput(int col, int row, @Nullable IIngredient replacement);

Replacements are processed in the order the methods are shown above and only support recipes up to 3x3. Any ingredient parameter may be null, however, when using replaceInput(IIngredient, IIngredient), only one parameter may be null.

Note

Using replaceShapedInput(int, int, IIngredient) when copying a recipe may increase the size of the recipe to include the grid position given.

For example, the following recipe will copy the furnace recipe and replace each corner of the crafting grid and the empty ingredient in the middle:

RecipeBuilder.get("basic")
    .setCopy(
        Copy.byName("minecraft:furnace")
            .replaceInput(null, <ore:stickWood>)
            .replaceShapedInput(0, 0, <minecraft:diamond>)
            .replaceShapedInput(2, 0, null)
            .replaceShapedInput(0, 2, null)
            .replaceShapedInput(2, 2, <minecraft:diamond>)
    )
    .addTool(<ore:artisansHammer>, 1)
    .create();

Recipe Image

Warning

The input replacement methods described above are mutually exclusive with the noInput() method.

Exclude Output

The following method will instruct the Copy task to not copy a recipe's output. If you call this method, you will need to supply at least one recipe output to the RecipeBuilder.

Copy noOutput();

For example, to copy the furnace recipe by name and exclude the output, define a Copy task like this:

builder.setCopy(Copy.byName("minecraft:furnace").noOutput());

Warning

The noOutput() method is mutually exclusive with both the noInput() method and the replaceOutput(IItemStack) method.

Replace Output

The following method will instruct the Copy task to replace a recipe's output with the IItemStack provided.

Copy replaceOutput(IItemStack replacement);

The output quantity of the replaced item will respect the output quantity of the copied recipe. This means that if the recipe you are replacing outputs 9 items, the copied recipe with the replacement output will also output 9 items.

When copying a recipe with this directive, it is not required to supply the RecipeBuilder with an output because the Copy task will supply the output for you. You can, however, supply additional output to the recipe for a weighted output pool. See Weighted Primary Output for more information on weighted output pools.

For example, to copy the furnace recipe by name and replace the output with a <minecraft:diamond>, define a Copy task like this:

builder.setCopy(Copy.byName("minecraft:furnace").replaceOutput(<minecraft:diamond>));

Warning

The replaceOutput(IItemStack) method is mutually exclusive with the noOutput() method.

Copying CraftTweaker Recipes

By default, all recipes are copied before CraftTweaker actions run. This means that any vanilla crafting recipes you add via CraftTweaker won't be available to copy when Artisan Worktables evaluates copied recipes.

The Copy task can be instructed to run after CraftTweaker adds and removes recipes using the following method.

Copy runAfter();

For example, to copy a recipe named crafttweaker:customrecipe, define a Copy task like this:

builder.setCopy(Copy.byName("crafttweaker:customrecipe").runAfter());

Examples

This sounds complicated, but it's really not. Here are some examples to better illustrate the syntax.

By Name

This recipe will copy both the input and output of the recipe with the name minecraft:furnace and add a tool requirement:

import mods.artisanworktables.builder.RecipeBuilder;
import mods.artisanworktables.builder.Copy;

RecipeBuilder.get("basic")
  .setCopy(Copy.byName("minecraft:furnace"))
  .addTool(<ore:artisansHammer>, 10)
  .create();

By Recipe

This recipe will iterate through all recipes for <ore:ingotGold>, copy each recipe's input, add a tool requirement, and replace each recipe's output with a <minecraft:diamond> with respect to the original recipe's output quantity.

import mods.artisanworktables.builder.RecipeBuilder;
import mods.artisanworktables.builder.Copy;

val builder = RecipeBuilder.get("basic");

for recipe in recipes.getRecipesFor(<ore:ingotGold>) {
    builder
        .setCopy(Copy.byRecipe(recipe).replaceOutput(<minecraft:diamond>))
        .addTool(<ore:artisansHammer>, 10)
        .create();
}

By Recipe Output

This recipe will copy only the input of all recipes that have an output of either <ore:ingotIron> or <ore:ingotGold>, add a tool requirement, and add an output of <minecraft:string>.

import mods.artisanworktables.builder.RecipeBuilder;
import mods.artisanworktables.builder.Copy;

RecipeBuilder.get("basic")
  .setCopy(Copy.byOutput([<ore:ingotIron>, <ore:ingotGold>]).noOutput())
  .addTool(<ore:artisansHammer>, 10)
  .addOutput(<minecraft:string>)
  .create();