In this page, we will create our own custom recipe type, including the shapeless version! We won't use any vanilla Minecraft classes, it will be a fully custom recipe type.
You can find and learn how to make other recipe types. Creating a custom cooking recipe type
We will need to create a block to open the recipe gui. Some of the classes will be created later.
public class TestRecipeBlock extends Block { private static final Text TITLE = new TranslatableText("container.test_crafting"); public TestRecipeBlock(Settings settings) { super(settings); } @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { if(world.isClient) { return ActionResult.SUCCESS; } else { player.openHandledScreen(state.createScreenHandlerFactory(world, pos)); player.incrementStat(Stats.INTERACT_WITH_CRAFTING_TABLE); return ActionResult.CONSUME; } } @Override public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) { return new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> { return new TestCraftingScreenHandler(i, playerInventory, ScreenHandlerContext.create(world, pos)); }, TITLE); } }
Now we need to register the Block and BlockItem.
public class ExampleCustomRecipeMod implements ModInitializer { public static final TestRecipeBlock TEST_RECIPE_BLOCK = new TestRecipeBlock(FabricBlockSettings.of(Material.METAL)); @Override public void onInitialize() { // Resuming later... } }
Now we will do the most fun part of this page, creating the recipe class.
Shaped recipes are a recipe type that requires an item to be in that specific slot or pattern. It is mostly used on grid recipes such as Crafting Recipe.
The Recipe interface expects an Inventory class as a type parameter. Any inventory that contains the ingredients for the recipe would work. But we will be using CraftingInventory.
public class TestRecipe implements Recipe<CraftingInventory> { //You can add as much inputs as you want here. //It is important to always use Ingredient, so you can support tags. private final Ingredient inputA; private final Ingredient inputB; private final ItemStack result; private final Identifier id; public TestRecipe(Identifier id, ItemStack result, Ingredient inputA, Ingredient inputB) { this.id = id; this.inputA = inputA; this.inputB = inputB; this.result = result; } public Ingredient getInputA() { return this.inputA; } public Ingredient getInputB() { return this.inputB; } @Override public ItemStack getOutput() { return this.result; } @Override public Identifier getId() { return this.id; } //[...] }
We would need to care about craft, because it is needed to craft the result. In craft, it is a good idea to return
this.getOutput().copy()
. fits
should return true.
public class TestRecipe implements Recipe<CraftingInventory> { //[...] @Override public ItemStack craft(CraftingInventory inv) { return this.getOutput().copy(); } @Override public boolean fits(int width, int height) { return true; } }
In matches
, we need to say if a given inventory satisfies a recipe's input. We will return true if the first and second
slot matches first and second input.
public class TestRecipe implements Recipe<CraftingInventory> { //[...] @Override public boolean matches(CraftingInventory inv, World world) { if(inv.size(0) < 2) return false; return inputA.test(inventory.getStack(0)) && inputB.test(inventory.getStack(1)); } }
Now we need the Type
of the recipe. All you have to do is to create an instance of a class that extends
RecipeType<TestRecipe>.
public class TestRecipe implements Recipe<CraftingInventory> { //[...] public static class Type implements RecipeType<TestRecipe> { private Type() {} public static final Type INSTANCE = new Type(); public static final String ID = "test_recipe"; } @Override pubilc RecipeType<?> getType() { return Type.INSTANCE; } }
What's still missing is the RecipeSerializer, for now try to figure it out using the SOURCE link below.
SOURCE: Defining Custom Crafting Recipes