Tutorial: Adding Recipes with CP (1.6)

From Stardew Modding Wiki
Jump to navigation Jump to search


Introduction

Hello! Rokugin here with another tutorial on how to make some simple stuff with Content Patcher. If you need any help with this or any of the stuff on my Git, feel free to try to reach me on the Discord server.


Git repo for this tutorial will be available eventually...


The focus of this guide will be adding crafting and cooking recipes to the game using Content Patcher with tailoring recipes coming at a later date.

Getting Started

I'll assume you've already installed SMAPI and Content Patcher. Pictures of this process can be found in my first tutorial.

Make sure you have file name extensions enabled.


The first thing you'll want to do is create a new folder to contain your mod, located inside your Mods folder or a folder inside your mods folder.

Naming convention for the folder is to start it with [CP] to indicate that it's a Content Patcher mod.


Inside the folder, you'll create your manifest.json and content.json.


Manifest

Here's my manifest as an example:

{
    "Name": "Recipe Tutorial",
    "Author": "rokugin",
    "Version": "1.0.0",
    "Description": "Tutorial mod for adding recipes to the game.",
    "UniqueID": "rokugin.tutorialrecipes",
    "UpdateKeys": [],
    "ContentPackFor": {
        "UniqueID": "Pathoschild.ContentPatcher"
    }
}


Name can be anything you want.

Author is you.

Version is what version of your mod this is.

Description is a description of what your mod does or is for.

UniqueID is usually YourName.YourModName, but just has to be unique from other mods you have installed.

UpdateKeys is used if/when you upload the mod to places like Nexus to indicate when there's a version change to users.

ContentPackFor will remain unchanged.


Content

The initial content.json:

{
	"Format": "2.0.0",
	"Changes": [
		
	]
}


Format indicates which major version of Content Patcher is being used, leave this as 2.0.0.

Changes is our main block where all of our different patches will be contained.

Crafting Recipes

A table you can reference can be found here.

New Recipe for an Existing Item

In order to avoid having to add a whole section about adding custom items to this tutorial, we'll start by making a new recipe for an item that's already in the game.

While playing I noticed that when you upgrade your chest to a big chest, you then have a leftover chest.

Not a problem if you have somewhere to put it, but instead of having a bunch of regular chests potentially lying around, I created a recipe that uses the chest to offset the wood cost of the big chests.

{
	"Format": "2.0.0",
	"Changes": [
		{
			"LogName": "Create new big chest crafting recipe",
			"Action": "EditData",
			"Target": "Data/CraftingRecipes",
			"When": {
				"HasCraftingRecipe": "Big Chest"
			},
			"Entries": {
				"{{ModId}}_BigChest": "(BC)130 1 388 70 334 2/Home/BigChest/true/default/"
			}
		}
	]
}


LogName is what shows up in SMAPI's console if there's a problem with this patch block, making it easier to locate when a problem arises.

Action indicates what kind of patch we're attempting to perform, in this case we're doing EditData.

Target is what we're attempting to patch.

When sets a condition for the patch to be performed.

HasCraftingRecipe is the condition we're looking for, because the big chest recipe is sold by Robin, we only want this recipe to appear after buying that recipe.

Entries is where the body of our patch will go, in this case it's where the actual recipe will go.


Breaking Down the Recipe

Now that we have an example to compare to, we can go over how the chart on the wiki relates to the format of the crafting recipe.


{{ModId}}_BigChest is the key/ID/name of the entry, since we're adding a new recipe this needs to be unique so we use the ModId token to tell CP to insert our UniqueID from our manifest here.

Each section of the recipe is divided by a /.


The first section is the ingredients for the recipe. (Index 0 on the chart)

(BC)130 1 388 70 334 2

(BC)130 1 is the qualified item ID for chests and the amount we need.

Most recipes use unqualified item ID's because they're only using normal objects as materials, but since we want to use a big craftable we have to include the (BC) qualifier.

Without the qualifier, this recipe would instead call for the object ID 130, which is a tuna.

388 70 is the unqualified item ID for wood and the amount of 70, the difference between what the original crafting recipe calls for and the amount of wood it takes to craft a chest.

334 2 is the unqualified item ID for copper bar and the unmodified amount from the original recipe, as regular chests only use wood, I've only opted to change the amount of wood required to use this alternate recipe.

So put plainly, this recipe calls for 1 chest, 70 wood, and 2 copper bars.


Home here is just a placeholder, the other value you'll see here is field which is also a placeholder. This field isn't used, however it is still required to fill out or we'll get errors. (Index 1 on the chart)

BigChest is the unqualified ID, this field is the yield, which indicates what you'll get when you perform the craft. (Index 2 on the chart)

true refers to whether this is a recipe for a big craftable or not, the reason our yield ID is unqualified is because this bool tells the game what kind of ID it's looking for instead. (Index 3 on the chart)

default is how the recipe is learned, because this is an alternate recipe to one we purchased and doesn't get patched in until we purchase the other recipe, we set this to default so it's learned automatically.

Other accepted values here include setting it to unlock at certain skill levels or from friendship with NPC's, setting this value to anything other than the accepted values will tell the game that you are going to add obtaining the recipe in another way.

(Index 4 on the chart)


The last part of this recipe is blank as it's where a translation token would be, without one it just shows the name of the item it yields. (Index 5 on the chart)


Changing a Recipe for an Existing Item

If you happen to disagree with the balance of the game and think the recipe for something should be different, changing it is really easy.

For this example we'll adjust the materials required to make the regular chest.

{
	"Format": "2.0.0",
	"Changes": [
		{
			"LogName": "Create new big chest crafting recipe",
			"Action": "EditData",
			"Target": "Data/CraftingRecipes",
			"When": {
				"HasCraftingRecipe": "Big Chest"
			},
			"Entries": {
				"{{ModId}}_BigChest": "(BC)130 1 388 70 334 2/Home/BigChest/true/default/"
			}
		},
		{
			"LogName": "Adjust regular chest crafting recipe",
			"Action": "EditData",
			"Target": "Data/CraftingRecipes",
			"Entries": {
				"Chest": "388 10/Home/130/true/default/"
			}
		}
	]
}


The first Action block is our previous patch adding a new recipe for an existing item, you can see that for the entry in that Action my key is {{ModId}}_BigChest which creates a new entry as there isn't one already named that in the data asset.

The second Action block is our new patch changing the recipe for chest's. In order to target an existing recipe, I find it in the Data/CraftingRecipes.json and make sure my entry key matches.


Here is the block on its own:

{
  "LogName": "Adjust regular chest crafting recipe",
  "Action": "EditData",
  "Target": "Data/CraftingRecipes",
  "Entries": {
    "Chest": "388 10/Home/130/true/default/"
  }
}


The original recipe in Data/CraftingRecipes ingredients is 388 50 which plainly written is 50 wood. By changing the 50 to 10, we change the recipe to only require 10 wood.

We could also fully replace the ingredients with whatever we want instead:

{
  "LogName": "Adjust regular chest crafting recipe",
  "Action": "EditData",
  "Target": "Data/CraftingRecipes",
  "Entries": {
    "Chest": "771 1/Home/130/true/default/"
  }
}


Here we've replaced 388 10 with 771 1 , which if we look in the Data/Objects.json, or using an online resource, we can see is the unqualified ID for fiber.

Now when we check in game, we can see that a chest only costs a single fiber to craft.


In this case, we know that this field doesn't require the qualified ID of items, but remember that when you put an unqualified ID in a place that takes both it will look through Objects first and pick the first match it finds.

Most of the time, you can find what you need to put into a field on the wiki on the appropriate page or on the migration guide if the individual page still hasn't been updated.

Cooking Recipes

The same table as before can be used as reference.

Bonus: Adding Recipes to Shops

Adding a recipe to a shop is like adding an object to a shop but with an extra field.

Make sure the unlock condition of the recipe is null or none and then add the recipe to the shop like so:

{
  "LogName": "Add example recipe to shop",
  "Action": "EditData",
  "Target": "Data/Shops",
  "TargetField": [
    "Saloon",
    "Items"
  ],
  "Entries": {
    "{{ModId}}_Example_Recipe": {
      "Id": "{{ModId}}_ExampleItem_Recipe",
      "ItemId": "(O){{ModId}}_ExampleItem",
      "ObjectInternalName": "{{ModId}}_ExampleItem",
      "IsRecipe": true,
      "Price": 100
    }
  }
}


TargetField is used to specifically target just one field in an existing entry, allowing you to make changes to that field without changing the rest of the entry.

Saloon is the name of the shop that we're trying to edit.

Items is the field we're editing.

Id is semi-optional and will be auto generated from the ItemId, RandomItemId, and IsRecipe fields. This should be unique however and it's best to make it the same as your entry key, which should also be unique.

ItemId is the qualified or unqualified item ID of the item you're adding.

ObjectInternalName is used when your item ID is different from your recipe entry key, must match the recipe entry key.

IsRecipe informs that the item you're trying to add is a recipe and that it should show in the shop as such and unlock the appropriate recipe when purchased.

Price is a semi-optional field and if not set here will default to the price set on the item's data, if it has one.