Tutorial: Making a Custom NPC

From Stardew Modding Wiki
Jump to navigation Jump to search

Introduction

So, you're interested in creating a new character for Stardew? Good news: with version 1.6, creating a character is easier and allows for more flexibility than ever. However, NPCs are one of the more complicated things to create for Stardew, as they have a LOT of moving parts. This guide aims to provide a basic but thorough walkthrough for each part of character creation.

All of the basics (and indeed, most of the extras) can be done in Content Patcher, so it's recommended that you get some familiarity with Content Patcher's format and functions (perhaps by making a small mail mod or dialogue mod). This tutorial will assume you didn't do that and will explain some basic terminology, but not too in depth for the purposes of not making this longer than it already is. Make sure to keep the modding wiki (here!) and the modding documentation on the official wiki on hand for more details.

I'll be using my NPCs, Jorts and Jean, as examples throughout this tutorial. If you want to see all the parts put together, their initial release is a good example of a basic NPC without too many frills to get lost in (albeit in 1.5 format). I also link a couple of my other mods as examples here and there - feel free to steal their code, just change the dialogue.

The basics

Some thoughts on character creation

One of the hardest parts of creating a character can be, well, creating the character. As a writer, I have a lot of thoughts on coming up with an engaging, interesting character that I have attempted to condense down here. For Your Consideration: Creating a Character

File structure

Make yourself a folder for the mod. Best practice is to preface the folder name with [NPC] or [CP]. Here's Jorts and Jean as an example (they will be an example a lot):

NPCFileStructure.png

The assets are all going to come later. First we're going to look at the manifest and the content.json.

Manifest

Every mod has a manifest which tells SMAPI the basics of the mod. I've done a little breakdown here.

Content.json

Start with this:

{
    
	"Format": "2.0.0", //if you have a 1.5 NPC, make sure this is updated once you have the NPC converted, or certain things like map patches will break

"Changes":
	[

]
}

Everything you're doing from here on out will go between those two [brackets]. There are a number of different types of changes you can do here - some of the ones we'll be using:

  • Load: when an asset doesn't exist in the base game at all.
  • EditData: when an asset exists (either in base game or via being loaded in prior) but you want to edit it.
  • Include: tells content patcher to include the entire contents of another .json file elsewhere in your mod folder. We will use this a lot for the purposes of not having everything mashed together in the content.json (trust me, you will prefer this.)

(A quick note: you may see me put things in the code behind //double slashes like this. This is denoting a comment - SMAPI won't look at anything behind the double slashes, so you can use it to markup your code with important notes.)

Speaking of Load, let's go ahead and load the basics we'll need. For this purpose, create a .json in your Assets folder (as organized as you want it) named 'blank.json' which only contains:

{ }

Now put this inside your Changes [brackets]:


   	 {
   		 "LogName": "Blank JSON load", //The logline is not required, but it's useful down the road for labeling your patches so you can tell your problem is, say "NPC Dialogue Patch" and not "NPC Patch #14"
        	"Action": "Load",
        	"Target": "Characters/Dialogue/NPCName, Characters/schedules/NPCName, Strings/schedules/NPCName",
        	"FromFile": "assets/data/blank.json", //this should be wherever you put it in your assets folder - put it in a subfolder if you want.
   	 },

This creates the Dialogue, schedules, and schedule strings (that last one is optional - only if you want schedule-specific dialogue) for your NPC.

Speaking of NPCName, let's also discuss that before we go into it fully on the NPC disposition coming up. NPCName here is your NPC's internal name: like your unique mod ID, this name needs to be completely unique from other characters, otherwise any conflicting characters will not be loaded into the game. Best practice is to have a prefix or suffix with your name, just to prevent overlaps with other characters of the same name: e.g., SBVAicha (Aicha from Sunberry Village) or JeanCat (Jean, who is a cat). Note that this name does not show up in dialogue or your character's display, so you can make it as weird as you want, as long as you can stand typing it approximately four million times. Content Patcher in 1.6 allows for the token {{ModID}} which you can preface to your name to automatically add your mod ID for a unique name, e.g., {{ModID}}JortsCat will produce tiakall.jortsandjean.JortsCat as an internal name without you having to type out the same thing every time.

Disposition

Inbetween your [brackets], we're going to put in an include to tell Content Patcher to include a different .json, which we will do next. We're going to be doing includes a lot, so get used to these:

// NPC Disposition - updated for 1.6	 
    	{
        	"LogName": "Jorts and Jean exist!",
   		 "Action": "Include",
   		 "FromFile": "assets/data/dispos.json"
    	},

Now we're going to create a new file that matches the FromFile name. I'm going to use Wren as an example here over Jorts since hers is a little more normal (being a human and all):

{
    "Changes": [
   	 
   	 {
   		 "Action": "EditData",
   		 "Target": "Data/Characters",
   		 "Entries": {
   			 "WrenSong": {
   				 "DisplayName": "Wren",
   				 "Gender": "Female", //self-explanatory: can be male, female, or undefined (for nonbinary)
   				 "Age": "Adult", //can be child, teen or adult; affects certain default strings
   				 "Manner": "Neutral", //can be polite, neutral, or rude; affects certain default strings
   				 "SocialAnxiety": "Neutral", //can be outgoing, neutral, or shy; affects certain default strings
   				 "Optimism": "Positive", //can be positive, neutral, or negative; affects certain default strings
   				 "BirthSeason": "fall", //self-explanatory
   				 "BirthDay": "10", //self-explanatory
   				 "HomeRegion": "Town", //can be Town, Desert, or Other - I suggest Town so they're included in certain activities like the Luau friendship boost and Winter Star, unless you're doing a hidden or non-human NPC, in which case you might want Other. 
   				 "CanBeRomanced": false, //Whether or not the NPC is dateable/marryable. 
    				"CanVisitIsland": true, //Whether the NPC can visit the resort. Set to false if you don't want them at the beach or don't have your resort strings set up.
   				 "SpouseAdopts": true, //If your character is marryable, you can use this field to have your character always adopt.
   				 "IntroductionsQuest": true, //If you don't want your NPC to be included as part of the introductions quest (perhaps if you have an NPC you want hidden at the start), set to false
   				 "ItemDeliveryQuests": true, //set to false if you want to not have them included in the Help Wanted regular quests for fetching items for reasons, e.g., if they are a cat.
   				 "WinterStarParticipant": true, //set to false if you don't want your NPC to be part of the random gifter/giftee pool.
   				 
   				 "Home": [ //set this to where you want the NPC to spawn in normally.  
   					 {
   						 "Id": "Default",
   						 "Location": "Custom_SBV_WrenHouse2",
   						 "Tile": {
   							 "X": 5,
   							 "Y": 11
   						 },
   						 "Direction": "right"
   					 }
   				 ],
   			 },    
   		 }
   	 }
    ]
}

The entry name should be the internal name for your NPC, the same one you used before. The only required fields here is DisplayName and the two Birthday fields, but a number of the fields in this list are also useful things you want to include. There's a ton of things you can specify in NPC Dispositions: here's the list on the official wiki.

Portraits

Portraits are used in character dialogue for your character to express themselves, so they're not smiling while they tell you their cat just died or about how much they hate capitalism. Your character's portraits should be loaded in like so (put this inside the [brackets] of your content.json):

{
        	"LogName": "Base Portraits",
        	"Action": "Load",
        	"Target": "Portraits/NPCName",
        	"FromFile": "assets/images/npcname.png",
   	 },

Again, the FromFile can be wherever you have it relative to your content.json. I put all my portraits and sprites together in one Images folder, some people decide to subdivide further, especially if they're doing multiple NPCs (or are more organized than me.) Up to you!

Now, the npcname.png should be a PNG file (not GIF, JPEG, or Yoba forbid, WEBP). The character's portraits should be 64x64 pixels each, arranged on a grid two across and down as far as you need. (Note: this needs to be exact: if you're even a pixel off, something in your portraits will break.) You can technically have up to 50 rows for 100 portraits, but I greatly recommend you start with a basic six, in this order:

$0: Neutral: Your character's regular expression, most of the time. Do they usually smile, or have RBF? $1: Happy: How big do they smile? Do they smile, or just crinkle the corners of their eyes?
$2: Sad: Crying optional. $3: Unique: Something that doesn't fall into one of these categories, but is something you can see yourself using often. For example, Jean's unique is her exasperated/amused "Classic Jorts..." expression.
$4: Blushing: Usually used for romantic situations, but can also be embarrassed. $5: Angry: A scowl? Full-on shouty face?

This is the same set that a lot of the dateable characters have, and what a lot of custom NPCs use as well. That said, you don't have to be beholden to them--if you have a character that isn't romanceable, maybe you don't need a blushing portrait. Do note that gift tastes default to certain portraits (e.g., $1 for loved gifts) so if you don't have this order, you'll want to specify your appropriate portrait in Gift Tastes.

Now... how to get said portraits?

There are two good ways:

  • Try it yourself--if you have some art experience, you can try from scratch, or if you're new, you can try editing some vanilla portraits. Note: a lot of people edit vanilla portraits, so you'll want to do more than just recolor Shane's hair or removing Harvey's moustache. We have a tutorials on doing pixel art here!. You can also use the timeline art for each NPC for some additional inspiration or base--Wren's initial drafts had her stealing one of beta Abigail's jackets (and also her eyes.)
  • Commission a Stardew pixel artist to create your portraits. There's a list of folks with open commissions right here on this wiki. If you decide to go this route, some tips on commissioning:
    • Have some visual references/sketches of what you want.
    • I recommend writing a good chunk of dialogue and drafting some events before you commission, so you have a good idea of what expressions you'll want.
    • Be flexible, but if the portraits aren't aligning with what you want - communicate with your artist! They are happy to adjust as you need!
    • Don't haggle over price or try to get art for free/cheap. Just don't.

There are also a few bad ways:

  • Taking other people's portraits and claiming them as your own. This will get your mod removed.
  • Taking other people's portraits and editing them, and then claiming them as your own. This will also get your mod removed.
  • Using AI art. One, it's stealing from actual artists, and two, it looks like crap.

And a not recommended way:

  • Using the farmer portrait creator Jazzybee made (from Poltergeister's art) for your portraits. This won't get your mod removed, but we can tell when you did it (it's not really designed for NPC portrait making) and it looks so low-effort.

Sprites

Sprites are your character's overworld full-body images. They will be loaded in similar to your portraits (inside the [brackets] of your content.json), but with one important difference:

{
        	"LogName": "Base Portraits",
        	"Action": "Load",
        	"Target": "Characters/NPCName",
        	"FromFile": "assets/images/npcnamesprites.png",
   	 },

While portraits are loaded into Portraits/NPCName, sprites are loaded into Characters/NPCName. Do not try to load them into Sprites/NPCName despite it being the logical choice, it will not work.

Sprites are 16x32 pixels in rows of four. (You technically can have NPCs larger than 16x32 but it requires some advanced/C# knowledge, so I really recommend you stick with the basic size.) Here is a great template TheLimeyDragon made for how to position your sprites:

Spritetemplate.png

The first four rows are four frames of a character's walking animation (usually neutral, one foot forward, neutral, other foot forward.) Below that you have free rein over what sprites you want to put in, for custom animations (or if you don't want them doing any custom animations, you can just leave them blank.) 28 is where the kiss sprite will default to (as a left-facing sprite) but you can specify what spot it's in and what direction it faces with KissSpriteIndex and KissSpriteFacingDirection. (I'm pretty sure flower dance and wedding sprites are still hardcoded, so don't move those - if your character doesn't attend/dance at the flower dance and isn't marryable, again, you can use them for whatever you want. For starting out, though, I would recommend focusing on just the walking sprites + kiss sprite since they're the only things that are required. (Okay, kiss sprite isn't required either, but it's good to have for compatibility with any mods that add kiss or hug functions.)

When making the actual art for the sprite sheet, the same advice as for portraits applies: consider editing vanilla sprites or commissioning an artist. You can also consider using the farmer as a base for NPC sprites, but the farmer is a bit differently shaped than NPCs, so keep that in mind.

Dialogue

Dialogue. If you're a writer, it's your favorite part. Let's make an include for this--like the other edits, this is going to go inside your content.json [brackets]:

	 
    	{
        	"LogName": "NPC Dialogue",
   		 "Action": "Include",
   		 "FromFile": "assets/data/dialogue.json"
    	},

Now we're going to make a separate dialogue.json (every time we do an include, we're going to make a separate .json file). For the dialogue.json itself, we have a lovely template to go ahead and get you started, or I have a copy of the one I use when starting an NPC. There are a few different kinds of dialogue:

Daily dialogue: What the character will default to on a given day. I like to separate it out by day of the week+ heart level (e.g., Mon4. You can also further subdivide it by season or even a specific date (useful for the days before festivals or birthdays) but I generally find season+heart+day to be overkill because a lot of them are unlikely to be seen, e.g, winter_Mon is unlikely to be seen if your NPC is already at 2 hearts by winter's start. If you want more dialogue options, randomizing is a better approach. My template has an example of how to randomize under the rain entry, and Immersive Saloon also uses this to randomize.
Location dialogue: You can specify a dialogue by a location (e.g., Saloon) or even by a specific coordinate (for example, Wren has a specific key for Custom_SBV_SunberryVillage_62_91 which is her position in the Sunberry hangout. Note that it will fire every time your NPC is in that specific location, so you may want to randomize. You can also combine it with day of the week or heart level (e.g., Saloon_Mon) but not at the same time, so if you want location+day of the week+heart level you'll have to conditionally add them (Realistic Writer Elliott has an example of this.
Strings: Certain strings, such as rain and DumpsterDiveComment, are situational and added by the base game - 1.6 adds a number of them in particular to play with!
Conversation Topics: Conversation Topics are one of the easiest crossover contents you can add, by having your character remark on other NPC's events or situations. I've included a few of my favorites in my template, but there's a much bigger list here.

A few mechanics to know about:

  • @ will fill in the farmer's name, so NPCs can address the player character by name.
  • expressions can be changed for a dialogue line by putting $1 (with 1 being whatever position for the portrait you want - they start at 0 and go across then down, like the 0-5 in the portraits section.
  • putting in #$b# will break the next section of dialogue into its own window (good for long-winded characters, or you can use several with short lines to make a character feel fast-spoken or out of breath.) #$e# will break the next section into its own separate dialogue (where you have to talk to the character a second time.)
  • You can have dialogue reveal your or other characters' gift tastes with %revealtaste NPCName ItemID--for example, %revealtaste WrenSong 219 will reveal Wren's thoughts on vanilla item 219 (Joja Cola) while %revealtaste JortsCat tiakall.bass.seabass will reveal Jorts's feeling about the modded item Sea Bass from Not You Again.
  • You can put ^ between two sets of dialogue lines to gender them in the order male^female (e.g., "Hello, Mr. Farmer!^Hello, Ms. Farmer!". 1.6 now allows a third option for undefined/nonbinary which needs to be surrounded by $. Unlike the two-gender method, it can now be used on a partial line instead of requiring the full (e.g., "Hello, ${Mr.^Ms.^Mx.}$ Farmer!")

As for what to write in your dialogue, there are a few different tip pages on here, but Daily Dialogue Tips by SMC is the one that I've contributed to that contains my thoughts.

Schedule

Schedules will determine when and where your NPC can be found. To start, let's make another include inside our content.json [brackets]:

	 
    	{
        	"LogName": "NPC Schedule",
   		 "Action": "Include",
   		 "FromFile": "assets/data/schedule.json"
    	},

Now let's make your schedule.json. Here's a trimmed version of Jorts's:

	  {
"Changes":
	[
 
   	 {
        	"LogName": "Jorts Schedule",
        	"Action": "EditData",
        	"Target": "Characters/schedules/JortsCat",
   		 "Entries": {
   			 
   			 "rain": "610 AdventureGuild 9 12 1/2500 AdventureGuild 9 12 1 JortsCat_catnap",
   			 "spring": "610 Town 102 55 2/1600 ManorHouse 2 8 0/2130 AdventureGuild 9 12 1 JortsCat_catnap/a2400 Town 44 73 0/2410 Town 72 70 0/2440 AdventureGuild 9 12 1 JortsCat_catnap",
   			 
   		 },
    	},
],
}

Each schedule should contain one or more sections (if more than one, separated by a /) in the following format:
Time MapName Xcoord Ycoord Facing Animation StringDialogue

Time: This should be in 24-hour format, ranging from 610 (6:10 AM, the first time passing after the farmer wakes at 6AM) to 2600 (2AM, but let's be real, your character shouldn't be starting to go anywhere at the time the day ends.) Note: 600 can do weird things, so start with 610. A 610 schedule is also recommended because it'll wake your character up if they have a _sleep animation. If you put an a in front of the time (e.g., a1200), it will have the character arrive at that time instead of leave at that time (making them leave however early they need to to make it to the new location in time.)
MapName: The name of the destination map you want to end up on (e.g., Town for the main Pelican Town. You can find the vanilla map names in Data/Locations.json while custom maps will be defined in the map loading.
Xcoord and Ycoord: the X and Y coordinates for the specific spot you want the NPC to move to. You can find this by opening the map in Tiled or using Debug Mode.
Facing: The direction the character faces once they arrive at their destination tile. The directions are up = 0, right = 1, down = 2, left = 3.
Animation and StringDialogue: these are optional, I'll talk about them more in the Extras section.

Repeat this for each movement you want them to have. I prefer 2-3 total stops a day maximum, with at least 3 hours in a location - if your NPC is constantly moving around, they're going to be hard to find. Also keep in mind that moving places takes time - it's 3+ hours to get into town from an expansion mod or the outside maps (e.g., Railroad). Ginger Island Mainland Adjustments is an excellent tool to check how long a schedule takes in detail. It is NOT required for your NPC to have enough time to make it home, so don't worry about late-night schedules leaving your NPC in town at midnight.

Now, what kind of schedules can you do? spring is the default schedule a character will use when no other schedule is provided, so it's really recommended to have one of those. Like dialogue, you can get more granular with season, day of the week, and even specific days. rain is another special schedule that will take precedence when it rains (but not over specific-day schedules, eg. fall_10). I like to do a schedule for each day of the week, with occasional seasonal variations (e.g., not putting characters outside in the snow for hours at a time.)

0 schedules? You can set the time to 0 at the beginning of a schedule, which can basically allow you to set a new spawn point. This is useful if you want to start elsewhere: for example, if you want to spend the day on a map NPCs can't path into, such as Secret Woods, or if you want to have a character visit a distant spot (e.g., Ridgeside) and not spend 5-6 hours getting there. One word of caution with 0 schedules - if you have a randomization, make sure it's all or nothing for your 0 schedules. In other words, if your Tuesday has a 50% chance for schedule A and a 50% chance for schedule B, either both A and B should have a 0 schedule, or neither should.

Gift Tastes

Let's make one more include inside our content.json [brackets] for the road, this time for gift tastes:

    	{
   		 "LogName": "Gift Tastes Include",
   		 "Action": "Include",
   		 "FromFile": "assets/data/gifttastes.json"
   	 },

And a new file, gifttastes.json.

Before you get too into the coding of gift tastes, take a moment to map out what your gift tastes are going to be. Ideally, your gift tastes are going to relate back to who your character is--maybe they love dwarf gadgets because they're a nerd, maybe they hate alcohol because they don't drink, maybe they're meh on artisan goods because they're a cat. You don't have to map out every category or item in the game--the game will default to certain tastes, such as liking artisan goods. Most people appreciate homemade things even if they're not to their taste, after all!

NOW let's look at the actual gift taste patch. Here's Wren's as an example:

{
	"Changes": 
    [
		{
            "LogName": "Wren Gift Taste Data",
            "Action": "EditData",
            "Target": "Data/NPCGiftTastes",
            "Entries":
            {
                "WrenSong": "You're awesome, @! Is this where I tell you you're a goat or you slap?/444 112 122 167 403 404 440/Thanks, @! I'll put this to good use./248 279 286 287 288 334 335 336 337 338 396 399 787 917 372 428 -12/I appreciate the thought, but uh, this isn't really my thing./196 219 225 202 209 tiakall.bass.seabass/@, I feel like you really had to work to find something I'd dislike this much. This isn't appropriate to give to anyone, you know./747/Thanks for thinking of me, @./446 746 766 -7 -8 -9 -10 -15 -16 item_alcohol/",
				
			}
		},
]
}

The first, third, fifth, seventh and ninth sections here are what the character says as a reaction to loved, liked, disliked, hated, and neutral gifts, respectively, while the second, fourth, sixth, eighth and tenth fields are the actual items that will trigger those lines. Regarding the reaction lines, they will default to certain reactions ($1 for loved, $0 for liked and neutral, $2 for dislike, $5 for hated, but in 1.6 you can override that by putting the desired reaction at the end of the gift taste line. The actual items can be one of a few things:

  • Item IDs from vanilla: 404 is a common mushroom, while 287 is a bomb. You can look in Data/ObjectInformation for the object IDs, or if you don't want to bother and don't mind the 1.5.6 list (the numbers haven't changed, they just won't include any 1.6 items), I put it on the wiki. (As an aside, I recommend putting in //comments as to which items are in which categories so you don't have to look them up every time!)
  • Item categories: Negative numbers are categories. A list can be found on the official wiki (note that many categories, such as -99, cannot be gifted). Setting a gift taste for a specific item (e.g.,399) will override any category setting (e.g., -81 for forage.)
  • Context tags: You can use context tags similar to categories to assign tastes to groups of items. Vanilla doesn't use a lot of them, they're more commonly used with modded items that may not fall into easy categories. For example, Jorts and Jean have tea_sachet_item in their loved list, which adds all of {https://www.nexusmods.com/stardewvalley/mods/14667 Wildflour's tea sachet items].
  • Modded item IDs: Modded items can now be added directly via CP and have a string as an item idea (usually in the format modID.itemName). The game will ignore any modded item strings that are not actually in the game - if Not You Again isn't installed, the game will ignore the tiakall.bass.seabass entry. Note that if a 1.5 item hasn't been converted from JSON Assets, you'll still need to use the JA item format: {{spacechase0.JsonAssets/ObjectId:ItemNameHere}}.

In regards to gifting, 1.6 adds a new feature: rejecting gifts. Simply add a dialogue key for the item using reject_(O)ItemNameOrNumber (e.g., reject_(O)196 or reject_(O)tiakall.bass.seabass). This bypasses the gift tastes completely, so there's no friendship gain/loss.

The vanilla game will set certain gift tastes by default. You can override these in your NPC's gift tastes, similar to how Haley hates Prismatic Shards or Penny hates Rabbit's Feet.


The Extras

Mapmaking your own house

Tiled and I do not get along, so I try to avoid mapmaking if I can. You can do that, too--it isn't required to have a separate map location and patch for your NPC (and let's face it, the vanilla maps are starting to get a little crowded compatibility-wise). While you can spawn them in a vanilla NPC's house and have them live with vanilla NPCs (this works easier if they are, say, a pet and don't expect to have their own things or space), the easiest way is to just have them "arrive" via the bus by setting their Home spawn-in location to the bus entrance or near it. You could also use the train station for this purpose (though be aware it doesn't open until Summer 1.) So don't feel as if you must make a map - Jorts and Jean are just fine without them, as an example!

That said, if you do want to make a map, Max has a wonderful tutorial that I used when making Wren on how to add in your NPC's location warps and map patches.

Schedule animations and string dialogue

Made a bunch of sprites to have your NPC read a book, do some desk work, or bounce around inside a trash can? You can define custom animations for your NPCs inside your content.json:

        {
            "LogName": "Jorts and Jean Animations",
            "Action": "EditData",
            "Target": "Data/animationdescriptions",
            "Entries":
            {
                "JortsCat_catnap": "36/36 36 37 37 37 32 32 32 33 33 33 34 34 34 35 35 35 37 37 37/36",
				"JortsCat_trashcan": "16/17 18 19 20 21 22 23/16",
				"JortsCat_cuphead": "40/40 40 40 40 40 40 41 41 41 42 43 43 43 42 41 41 41 42 43 43 43/40",
			}
		},

The syntax for these is very easy: each entry should be named NPCName_action. Note that _sleep has some hardcoded behavior (the NPC will not respond until they move to the next part of their schedule, and it does not allow for naps during the day) so it's recommended you name your sleep animation something else (Jorts uses "catnap" for example, since he also uses it during the day). The formatting is starting frame/animation frames/ending frame where the animation will start on the first frame and cycle through the frames in the middle section until it's time to end, at which point it will go to the last frame. Each frame has a 100ms duration - if you want to slow animations down, just repeat the frame. I recommend viewing your animations in-game to see how well they pace themselves and tweak as needed. From there, you can use the name to have the animation done during schedules following the format mentioned in the Schedules section. (Note that custom animations can't be used at festivals or events - you'll need to list out the frames again.)

The other fancy thing you can do with schedules is have schedule-specific dialogue. This is useful if you have your character doing the same thing in different locations, or don't want a location-specific dialogue (and a coordinate-specific dialogue is too specific). We're going to now edit the Schedule Strings file we added as a blank along with our dialogue and schedules (remember all the way back there?)

		//Schedule dialogue 
		{
            "Action": "EditData",
		  	"Target": "Strings/Schedules/JortsCat",
            "Entries": 
            {
                "Trash.000": "{{Random: Jean...Jean, help....$s && I am SWIMMING in scrungy smells!$h && Um... I might be stuck.$s && Jean! Jean, I found a fish!$h && Someone just left the lid unfastened on this, what else was I supposed to do? && Something just moved! I'mma get it! ...Oh, it was my tail. |inputSeparator=&&}}",
				"ColaShop.000": "{{Random: I AM CUPCAT, MASTER OF PELICAN TOWN!$3 && I've got a hat! Thanks Vincent! I think it needs eyeholes though.$3 && Huh, it's kinda dark out today, isn't it?$3 && Look, I'm recycling!$3 && This doesn't count as a trashcan mishap, right?$3 |inputSeparator=&&}}",
				"Pride.000": "It's a party for everybody to love who they are! That's the best kind!$1#$b#Don't forget that I love you too, @!",
			},
		},

From there, just include them in your schedules as normal. The format for that is \"Strings\\schedules\\NPCName:StringName\". (the slashes and quotation marks are important to have in exactly this order and number!) Note that the schedule dialogue will kick in once the character has arrived, not while they're en route. Here's an example from Jorts's schedule with both an animation and a string dialogue:

"Wed": "610 JoshHouse 18 20 3/a1800 Town 47 71 3 JortsCat_trashcan \"Strings\\schedules\\JortsCat:Trash.000\"/a2400 Town 44 73 0/2410 Town 72 70 0/2440 AdventureGuild 9 12 1 JortsCat_catnap",

Events

Events are some of the biggest pieces of content you can add to an NPC. They're not required--if this is your first NPC, focus on the requirements first--but they're a great way to express your character's story and people love them.

So what do you put in events? Many people use the heart level progression (2/4/6/8/10/14 hearts) to put together a character story arc-- Tips for Heart Events - Writing and Narrative Design has a great breakdown of how to structure your heart events and what to put in them. That said, a character arc is not required or possibly even desirable, depending on what your character is like and your plans for them. Jorts and Jean are two examples of characters that don't have character arcs, because their concepts didn't include any life-changing events that would necessitate one. If your character is stable and happy in their current lifestyle, having a character arc may not make sense and that's fine. It's also not necessary to be bound to 2/4/6/8/10 hearts - Jorts has 1, 3, 4 and 8, while Wren has 1, 2, 3, 4, 5, 8 and 10!

What kind of events can you create, if you're not doing a character arc?

  • an early introduction: some mods like to add a heart even immediately or at very low heart levels (e.g., 50 friendship points) so that a character can be introduced if they're added mid-save. Note that you want this to be more than just "Hi, I'm NPC, I [job and home here], see you!" Show them doing something that creates interest, such as doing a hobby, sharing info with another NPC, or being stuck in a trash can.
  • your character in the process of doing their hobby or passion: (e.g., Sebastian doing programming work or having a game of Solarion)
  • character shows off an unexpected side (e.g., Pierre's secret stash)
  • your character interacting with someone else (great for crossovers!) Maybe they're just chatting, maybe they're doing an activity together, maybe they're having a fight. Showing conflict is fine!
  • your character reminiscing or being affected from something in their past (e.g., Leah receiving a call from her ex)--but beware of Tragic Backstory Dump

Is your farmer required to be in the event? Not at all! Vanilla events generally feature the farmer (a notable exception is Linus's special order event) but a lot of modded NPCs (and event mods) are getting away from the idea of having everything revolve around the farmer. Also, events don't have to be tied to heart levels - you can tie them to other events, to conversation topics, to mail flags, or even to specific dates or locked behind days played.

You'll note I've talked a lot about what to think about putting in an event, but not that much about how to do an event: that's because there are much better tutorials on this wiki for that. I used Tips for Making Events from a Novice by LenneDalben when I was first starting out, but I also recommend Tutorial: Anatomy of an Event and Tutorial: Events For Babies.

Festivals

Having your NPCs visit festivals isn't required, but a lot of people like doing it because it's an easy inclusion and a fun way to integrate your NPC into the game. Plus a lot of people love crowded festivals with all their friends there! That said, you're perfectly welcome to decide to have your NPC skip out on one or more festivals--or all of them!

It's time for another include!

    	{
   		 "LogName": "Festival Include",
   		 "Action": "Include",
   		 "FromFile": "assets/data/Festivals.json"
   	 },

For the actual content, I'm going to direct you to another easy template. This is all the coding you'll need for the vanilla festivals (plus a few modded ones I like.)

There's two important parts to adding an NPC to a festival: placement and dialogue. Of the two, dialogue is easy: just EditData their dialogue into the festival data, using the code in the template above. Placement is just a matter of their X/Y coordinates on the map plus the direction for which direction they face (e.g., 26 62 right). I highly recommend either checking the Custom NPC Tiles spreadsheet for other NPC placements, or load up a bunch of NPCs and use debug festival festivaldate (e.g., spring13 for the Egg Hunt) and take some screenshots.

Note: You can animate your character at a festival the same way you would define a custom animation.

Resort

Remember that "CanVisitIsland" in the NPC dispositions we talked about earlier? If you want your NPC to visit the island now, set this to true (or just delete the whole line). If your NPC has a beach set of portraits and sprites (the portrait/sprite name with _beach appended), they will change into that.

There are several different resort specific dialogue strings:

Resort: Required. What the character defaults to when they're not in a more specific situation.
Resort_Entering: When your character is en route to the island (in pelican town) or exiting the boat.
Resort_Leaving: When your character is en route to the boat when leaving.
Resort_chair: What your character says when they're seated in one of the beach chairs.
Resort_bar: What your character says when they're standing by the bar (not behind it as only Gus has that functionality outside of C# mods or GIMA.)

If you want to go above and beyond for your resort fun times, GIMA adds a wealth of new dialogue keys, allows you to set alternate schedules when leaving the island, specify groups your NPC attends the island with, and even allows them to explore the island itself!

If you're doing GIMA additions, I recommend walling your resort island changes off in their own resort.json, with an include and a HasMod:

    	// Resort include (with GIMA)
   	 {
   		 "LogName": "Resort Include",
   		 "Action": "Include",
   		 "When": { "HasMod: |contains=atravita.GingerIslandMainlandAdjustments": true},
   		 "FromFile": "assets/data/Resort.json"
   	 },

Movie Concessions and Reactions

Movie stuff consists of two things: concessions and movie reactions. Concessions are an easy, single EditData patch (I like including them in the GiftTastes.json):

//ConcessionsTastes
		{   
			"Action": "EditData",
			"Target": "Data/ConcessionTastes",
			"Entries": { 
				"WrenSong": {
					"Name": "WrenSong",
					"LovedTags": [
						"Joja Cola",
						"Truffle Popcorn",
						"Black Licorice",
					],
					"LikedTags": [
						"Healthy",
					],
					"DislikedTags": [
						"Sweet",
						"Salty",
						"Fatty",
						"Panzanella Salad",
					]
				} 
			}
		},

Tags can take either a specific item (e.g., "Joja Cola") or a descriptive tag (e.g., "Sweet") and will default to the specific item over the tag if both apply.

Now movie descriptions: here's an example of Jorts reacting to the first spring movie, The Brave Little Sapling:

{   "LogName": "Movie Theatre Jorts",
			"Action": "EditData", 
			"Target": "Data/MoviesReactions",	 
			"Entries": {

				"JortsCat": {
				"NPCName": "JortsCat",
				"Reactions": [
					{
						"Tag": "spring_movie_0",
						"Response": "love",
						"Whitelist": [],
						"SpecialResponses": {
							"BeforeMovie": {
								"ResponsePoint": null,
								"Script": "",
								"Text": "Don't you think animated movies are great? They're so colorful and the stories are often happy.$h",
							},
							"DuringMovie": {
								"ResponsePoint": null,
								"Script": "message \"Jorts is sitting forward, emotionally invested in the sapling's success.\"",
								"Text": ""
							},
							"AfterMovie": {
								"ResponsePoint": null,
								"Script": "",
								"Text": "I love animated movies, but I loved that one even more!$h"
							}
						},
						"ID": "reaction_0"
					},
				],
				},
			},
		},

The important things to notice here:

  • Tag: The internal name of the movie here. There are eight vanilla movies, but you can also add reactions to custom movies since that's now a thing in 1.6!
  • Reaction: "love", "like", and "dislike" are your choices here.
  • Whitelist: You can use this to define special rules (i.e., if Penny sees a movie and Pam is also there) but generally you can just leave this blank.
  • Special Responses: The actual reactions to the movie. The fun part! ResponsePoint can be used to chime in at specific points (e.g., if you want a character to react to a specific line in the movie) but generally, just leave it as null. Text is the dialogue that will play, while Script allows you to do a small event-style script (for example, displaying a non-text dialogue box, having your character jump or use an emote, etc.) Leave Script blank for before and after.
    • Before: The dialogue that plays when you're waiting in the lobby before entering the theater.
    • During: The dialogue/script that plays at a set point during the movie
    • After: The dialogue that plays after the actual movie when you're back in the movie theater.

It's pretty common to have separate reactions to each of the vanilla movies with the specific headers (e.g., spring_movie_0) but you can also use tags (e.g, Brave Little Saplings has "comedy" and "family" tags) or use * to apply to any movie that doesn't have more specific conditions. Characters can't be invited to a movie if they don't have a reaction for it; something to keep in mind for custom movies!


Dynamic Tokens

DynamicTokens.png

Dynamic tokens are basically variables that you can define based on differing conditions--think of them like little toggles you can use to plug in different values depending on conditions. They're good for setting a specific value without having to do two separate sets of code dependent on the same conditions but with slightly different values. Note that dynamic tokens must be included in your content.json - they can't be in any of the includes or they won't work.

Here's an example from Wren's file:

{
			"Name": "OceanAtNight",
			"Value": "", 
		},
		
		{
			"Name": "OceanAtNight",
			"Value": "/t 2000 2500", 
			"When":
			{
				"FourHeartsAtNight": true,
			}
		},

This returns a blank value by default, but will include the code <nocode>/t 2000 2500</nocode> if FourHeartsAtNight (a config value - we'll cover those later) is set to true. Now, I just have to use the actual token, {{OceanAtNight}} in the spot where I want it in the code - in my case, it's in an event precondition:

"tiakall.wren.H4/f WrenSong 1000Template:OceanAtNight": "continue/79 24/farmer 999 999 0 WrenSong 79 24 2/rest of event here...

So normally this will return:

"tiakall.wren.H4/f WrenSong 1000": "continue/79 24/farmer 999 999 0 WrenSong 79 24 2/rest of event here...

But when the condition is met (in this case, the config is set to true, it will return this:

"tiakall.wren.H4/f WrenSong 1000/t 2000 2500": "continue/79 24/farmer 999 999 0 WrenSong 79 24 2/rest of event here...

Easy enough, right? You can also use multiple conditions (for example, maybe I want OceanAtNight to have a different range if Dynamic Night Time is installed, I can set a condition for FourHeartsAtNight to be true, and a separate line for <nocode>"HasMod |contains=knakamura.dynamicnighttime": true,<nocode> and my new value will only apply if both conditions apply. You can have as many sets of conditions/values as you want! You can see the Content Patcher page on dynamic tokens for full info.

Configs

Sometimes, there are options you want to leave in the hands of your players: for example, maybe you have multiple portrait options, or you have certain options (like inclusion in the Introductions quest) you want to toggle on or off. A config schema can do just that! The Content Patcher docs have a breakdown of all the specifics, but here's an example use: the option from Wren to toggle the OceanAtNight dynamic token we looked at above.

"ConfigSchema":
	{
		"FourHeartsAtNight":
		{
			"AllowValues": "true, false",
			"Default": "true",
			"AllowBlank": false,
			"Description": "Sets Wren's 4-heart event to only trigger after dark for ~aesthetic~.,"
		},
	},

This allows the user to define if they want the extra nighttime conditions on Wren's event, defaulting to "no, I don't want extra condition". These configs will pull into Generic Mod Config Menu so users can edit them easily. For this reason, I highly recommend adding a description so people know what your configs do! (If you are looking at the i18n section below - this field is also i18n-able!)

Seasonal Portraits

I am making Airyn write this section.

Translation (i18n)

Many NPCs are translated into other languages. You can make the job easier on translators by having all of your language-specific strings (such as display name, dialogue, event text, text strings, and so on) in i18n. This tutorial explains the basics of what i18n is, along with some examples.

If your NPC makes use of dynamic tokens, you can also use those within i18n strings. The Content Patcher author guide briefly explains how here, but the gist of it is that you need to feed the value of your token into the i18n file or else it will display as plain text.

For instance, say I have a {{Nickname}} token set up so the player can enter in a custom nickname for the NPC to call the farmer. If I wanted the NPC to greet the farmer by that nickname on Monday at 6 hearts, I would set up my default.json to contain "{{ModID}}.Mon6": "Hello, {{Nickname}}! Happy Monday!", and my dialogue.json to contain "Mon6": "{{i18n: {{ModID}}.Mon6}}",. However, if I were to run this, the NPC would say {{Nickname}} as plain text rather than using the token. I need to tell Content Patcher in my dialogue.json to pass over the information of the token to the i18n, like so: "Mon6": "{{i18n: {{ModID}}.Mon6|Nickname={{Nickname}}}}",. I have not yet tried this with multiple tokens but I imagine it's theoretically possible.

Changes from 1.5

1.6 had a massive amount of changes for modders, but not a lot of them directly impact NPCs themselves (which were largely in Content Patcher to begin with). Pathoschild has set up SMAPI so that 1.5 characters will be kept compatible with 1.6 wherever possible, and has some general notes about breaking changes on the official wiki and on the Content Patcher documentation. Some notable differences:

  • Dispositions:
    • The format for loading in dispositions has completely changed, as well as adding in a ton of options. The old 1.5 dispositions will still work, with certain edge cases (e.g., since the birthday is now split into season and day, mods that use a token to put in the birthdate will not parse correctly.)
    • A lot of these options obsolete things that previously needed frameworks, such as exclusions from the Winter Star gift pool or the resort.
    • Internal names now allow underscore - these would previously break in 1.5.
  • Dialogue strings: A lot of dialogue strings, such as birthday reactions or pendant acceptance, are now de-hardcoded so they can be customized to each character.
  • Gift rejecting: Characters can now reject a gift if they have a "[item]_reject" dialogue string (note: you can make a romanceable character non-marryable by having them reject the pendant!)
  • Events: Events have gotten better error handling with more descriptive error descriptions. The downside is that event commands have gotten less forgiving of incorrect/extra data at the end of commands, so events that use these commands (such as events that include a facing in warp, e.g., warp farmer 30 12 0 instead of warp farmer 30 12 will fail to execute the command, which will probably result in weirdness.
  • Events also can now have string IDs (e.g., modid.NPCevent1 instead of 12345678). Numerical IDs will still work, and you may not want to change them if other mods have conditions that rely on those old IDs.
  • Map loading: I know map loading has changed (I am not entirely sure how because I am bad at maps), but the old way will still work if your mod is still in 1.5 mode.
  • Festivals: The layouts for some vanilla festivals have changed, so you may need to adjust your NPC's positions.

Pathos wants you to know this ONE strange trick!: Have a 1.5 NPC and a copy of 1.6 with Content Patcher? Load your character into the game like normal, and then you can put patch export Data/Characters in your SMAPI window to get an already-formatted JSON with a 1.6 updated disposition! Also works for other data you can export from Content Patcher, such as spritesheets, items and crops. The files will be sent to the "Patch export" folder inside your Stardew folder, the same one where your Mods folder is.

What to do when something doesn't work

General troubleshooting

One of the reasons I mention having examples to look at, particularly with mods that are doing something similar to what you want to do, is that you can see what it looks like when it's correct. For example, if you have an NPC with a schedule similar to what you want to do, and theirs is working and yours isn't, you can go on a section by section comparison to see what you did different, or even try a temporary code-swap to see if that works. (There was a brief point in time during Wren's codewriting where she was Jean while I tried to figure out why she wouldn't load in.)

Patch export (which can be used to export everything with full or exporting specific items, e.g., Data/Schedules/NPCname) is a great tool for seeing 1. If your code is actually loaded, and 2. If the code is actually applied. Errors with the former usually mean you have some kind of error in your EditData or the data you're importing, while the latter means that the condition to apply it isn't met (e.g., your character's marriage dialogue patch isn't applied, but you aren't married to your character.) This is where LogName comes in handy!

Typos and formatting errors (particularly the infamous Missing Comma) will be your greatest enemy. That's okay. We've all been there.

When you get red text in SMAPI

Red text means that something is not loading in at all due to errors (which may prevent your entire mod from loading, or certain files from it.) Most SMAPI errors are fairly self-explanatory, but a couple of common ones you might encounter:

  • "This mod wasn't loaded because X mod is not installed": you have a dependency that isn't in your current modset (e.g., you forgot to put Content Patcher in your test mod file).
  • "This doesn't appear to be valid JSON": You have a punctuation error, usually a missing } or ,. You can use the JSON validator to find where the problem is - there are also ways to set up Notepad++ or Visual Studio to check your JSON formatting.
  • "NullExceptionReference": You're telling the game to look at an object that doesn't exist, usually because you made a typo in the your ID/object/NPC/etc names.

When you get yellow text in SMAPI

Yellow text generally means that everything is loaded in fine, but may not work as intended. These are probably also typos, but not of the kind that prevents SMAPI from loading the file (e.g., you misspelled a Content Patcher token or your own dynamic token.)

When the NPC won't load in

If an NPC won't load in at all (i.e., debug where NPCname cannot find the NPC), the error is usually in your disposition or your portrait/sprite upload. Make sure your path to your images is correct and that the image dimensions are correct, and that your JSON formatting doesn't have errors. Also make sure you have a Home point set in your NPC's disposition so they have a default spawn point.

When the portraits are blank

If one of your NPC's portraits fails to show in their dialogue, the problem is usually either that you're pointing at a portrait that doesn't exist (e.g., you typoed $1 as $12 and you don't have 13 portraits) or your portraits are not the right size (if the portrait is on the bottom row, meaning your image dimensions are not right, likely too short.)

When the NPC loads, but won't move

If an NPC loads in but doesn't move from their spawn point, there's some issue with their schedule - if they never move even when different schedules should be active, it could be a problem with your whole file. The most likely issues is formatting (e.g., setting overlapping times or a time that doesn't exist) or incorrect destinations (you pathed them to a map that doesn't exist or that they can't access, e.g., the sewers or backwoods, the Jojamart post-CC completion, etc.) GIMA is a great tool for troubleshooting schedules, but if you don't have it/don't want to use it, try stripping your schedules down to the default and once that works, add things back in until it's all working again.

When an event doesn't work right

Running an event with debug ebi eventID will allow you to view the event as if it was just activated. If you've made a typo or formatting issue in an event command, SMAPI should tell you exactly what went wrong. (This is much improved from 1.5!)

Recommended frameworks

With 1.6, Content Patcher allows you to do a wealth of things that previously relied on other frameworks: editing certain hardcoded dialogue, adding custom items, crops, and animals, creating shops, and so on. That said, there are still a few frameworks you might find handy for your NPCs for additional content:

  • Farm Type Manager will allow you to add custom forage and spawn monsters - useful if your mod adds custom items or mine-like areas.
  • Custom Companions allows you to add antisocial NPCs and creatures, such as farm animals and pets, or visiting characters you don't want to turn into full NPCs.
  • Ginger Island Mainland Adjustments (GIMA) allows you to set additional resort dialogue, visit in groups with other NPCs, and explore the island.
  • Tilesheets such as Lumisteria's (indoor and outdoor) or HxW Tilesheets allow you to fancy up your map with furniture or terrain not seen in vanilla tilesheets.