Modding Terraria – Part 6 adding a custom NPC
Modding Terraria is a fun method to add and adjust features playfully and learn more about programming. In this part, we will cover adding a custom NPC (Non playable character) in Terraria!
This post has been updated to work with tModLoader v2023.9.3.0
We won’t need our previous code for this specific tutorial, but if you haven’t read it yet and are interested in loot tables, you can find it here.
Adding a custom NPC in Terraria
We assume you are somewhat accustomed to TmodLoader by now. If you are not, please refer to our first part, where we go over TmodLoader. You can find that one here.
Let’s add our NPC now! In our case, we will name our NPC Bob
.
Note: Bob will be our file name. You can choose to spawn your NPC with random names later too.
Inside your mod project, add a new directory called NPCs
. Inside this directory, we are going to need 5 files.
- Bob_Head.png
- Bob.cs
- Bob.png
- Bob_Shimmer_Head.png
- Bob_Shimmer.png
Let’s start by adding the images first. You can use your own images or use the guide NPC if you’re terrible at art like us.




The first image is our sprite sheet. This lengthy image contains all our NPC’s frames. Theoretically, you could have as many as you want or make them as small or big as you want. Keep in mind, however, that the default size works best. The default size is 18x40 pixels
.
The second image is our head image. We need this image for our minimap and housing flag image. This way, we can see our NPC on the map.
After you’ve added both images, create a new file inside the NPCs
directory called Bob.cs
. It’s time to start coding!
Let’s start with the basic skeleton layout of our file.
using Terraria.ModLoader;
namespace FirstMod.NPCs
{
[AutoloadHead]
public class Bob : ModNPC
{
}
}
The code here is not anything too exciting. The only thing we did here is extend Bob
from ModNPC
.
One thing which might seem different is, the addition of [AutoloadHead]
. This will load our Bob_Head.png
file so we can see it on the minimap.
Adding default values
Like our mount mod and custom laser sword, we will set default values and copy some behaviors from existing NPCs.
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace FirstMod.NPCs
{
[AutoloadHead]
public class Bob : ModNPC
{
public override void SetStaticDefaults()
{
Main.npcFrameCount[Type] = 26;
NPCID.Sets.ExtraFramesCount[Type] = 9;
NPCID.Sets.AttackFrameCount[Type] = 5;
NPCID.Sets.DangerDetectRange[Type] = 500;
NPCID.Sets.AttackType[Type] = 1;
NPCID.Sets.AttackTime[Type] = 45;
NPCID.Sets.AttackAverageChance[Type] = 30;
NPCID.Sets.HatOffsetY[Type] = -6;
}
public override void SetDefaults()
{
NPC.townNPC = true;
NPC.friendly = true;
NPC.width = 18;
NPC.height = 40;
NPC.aiStyle = 7;
NPC.damage = 10;
NPC.defense = 50;
NPC.lifeMax = 1000;
NPC.HitSound = SoundID.NPCHit1;
NPC.DeathSound = SoundID.NPCDeath1;
NPC.knockBackResist = 0.5f;
AnimationType = NPCID.Steampunker;
}
}
}
We’ve added two more namespaces to our file Terraria
and Terraria.ID
. We use these two for the Main
class and NPCID / SoundID
, respectively.
SetStaticDefaults
Inside our SetStaticDefaults()
function, we register how many frames our NPC has. In our case, this is 26 frames. A good thing to note here is the NPC.type
we use throughout this file is to register specific settings to our NPC. Upon creating a new NPC, we also assign a unique ID for our NPC. The NPC.type
retrieves this ID.
The other important part of the static defaults is AttackFrameCount
. We are setting this to 5
since the last 5 frames in our sprite sheet are attack animations. The other numbers in this function are yours to play with and should be straightforward.
SetDefaults
The next function we added is, SetDefaults()
. Inside this function, an important thing to note is the aiStyle
. Within TmodLoader we have a default set of AI styles we can use. These are
- Passive: The NPC walks back and forth on a platform.
- Fighter: The NPC jumps, dodges when attacked and attempts to close in on the player.
- Flying: The NPC flies toward the player and tries to maintain a certain distance.
- Worm: The NPC moves worm-like, burrowing through blocks and popping up to attack.
- Passive Worm: The NPC moves worm-like but does not attack the player.
- Walker: The NPC walks toward the player but does not jump or dodge.
- Swimmer: The NPC swims in water and attempts to maintain a certain depth.
- Passive Gun: The NPC stands still and fires a gun at the player.
- Caster: The NPC casts spells and attempts to maintain a certain distance.
- Passive Caster: The NPC casts spells but does not move.
The other settings are, again, yours to play with. We do want to address the last property, however. We are setting AnimationType
to NPCID.Steampunker
means that our NPC will inherit the behavior of the respective NPC. In the case of the steampunker, this will be the following
- The Steampunker NPC has a default walking animation where she moves her arms and legs mechanically.
- When the Steampunker NPC is hit, she will display a hurt animation where she will flinch, and her eyes will change color.
- When the Steampunker NPC is killed, she will display a death animation where she falls to the ground, and her eyes will close.
- The Steampunker NPC also has a variety of other animations, such as a talking animation, a waving animation, and a shopping animation.
Our sprite sheet might not be the best example, but it’s helpful to know that default animation types exist and can be used.
Adding spawn chance
Our NPC isn’t very helpful unless it can spawn and move into our house. To do this, we add the following piece of code
using Terraria.ModLoader.Utilities; // Add this one above with the other namespaces
using static Terraria.ModLoader.ModContent; // Add this one above with the other namespaces
public override float SpawnChance(NPCSpawnInfo spawnInfo)
{
if (NPC.CountNPCS(ModContent.NPCType<Bob>()) >= 1) {
return 0;
}
return SpawnCondition.OverworldDay.Chance * 1.0f;
}
SpawnChance
In the SpawnChance
method, we first check if there’s already an instance of our NPC in the world. If there is, we don’t want to instantiate another one.
Next, we set the spawn chance for our custom NPC. In this case, this is based on the default OverworldDay spawn condition. For demonstration purposes, this is a reasonably common spawn chance.
Right now, we have a working NPC. However, it’s still a bit boring because it doesn’t do anything else besides exist.
Adding dialogue
Adding dialogue is easy. To do this, we add the following method
public override string GetChat()
{
string[] chat = {
"Hello there, reader!",
"Have you come to purchase some wares?",
"I've heard rumors of an amazing blog post on www.onlineblogzone.com"
};
return Main.rand.Next(chat);
}
The above method will return a random message of the 3 each time you interact with the NPC. In a later part we will go over localization so you can have translatable strings
If you want to spice things up even more, you can also add buttons to the NPC. For this, we use 2 more methods.
public override void SetChatButtons(ref string button, ref string button2)
{
button = "Shop";
button2 = "Quote";
}
public override void OnChatButtonClicked(bool firstButton, ref string shop) {
if (firstButton) {
shop = ShopName;
} else {
Main.npcChatText = "Don't forget to share OnlineBlogZone with your friends and family ♥";
}
}
In the first method SetChatButtons
, we set our 2 button titles for which we handle the interaction in OnChatButtonClicked
. If you pay close attention to the latter method, you will notice the following line shop = ShopName;
. This refers to the shop name we’ve set at the top of our file.
Adding a custom shop
To start selling items, we only need 1 more method.
public override void AddShops()
{
var npcShop = new NPCShop(Type, ShopName)
.Add(new Item(ModContent.ItemType<Items.Mounts.HotAirBalloonMountItem>()) { shopCustomPrice = Item.buyPrice(copper: 23) })
.Add(<Items.LaserSword>)
.Add(new Item(ItemID.PlatinumCoin), Condition.IsNpcShimmered);
npcShop.Register();
}
In the above method, the NPC’s shop will sell our HotAirBalloonMountItem for 23 copper coins, our laser sword we made in an earlier tutorial and platinum coins when the NPC is in shimmer mode. You can add as many items as you like with one of the 3 ways shown above.
Note that AddShops is called every time the player opens the NPC’s shop, so you can dynamically generate the contents of the shop based on the game state or other factors if desired.
Adding custom names
We promised custom naming for our NPC, so let’s get into it.
Inside our Bob.cs
file, add the following code
public override List<string> SetNPCNameList() {
return new List<string>() { "Matthias", "Richard", "Bob" };
}
We’ve added a list of strings for our NPC to use, so that it will spawn with a random name each time this NPC gets instantiated.
Our final class will look like this.
using System.Collections.Generic;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
using Terraria.ModLoader.Utilities;
namespace FirstMod.NPCs
{
[AutoloadHead]
public class Bob : ModNPC
{
public const string ShopName = "Shop";
private static int ShimmerHeadIndex;
public override void Load() {
// Adds our Shimmer Head to the NPCHeadLoader.
ShimmerHeadIndex = Mod.AddNPCHeadTexture(Type, Texture + "_Shimmer_Head");
}
public override void SetStaticDefaults()
{
Main.npcFrameCount[Type] = 26;
NPCID.Sets.ExtraFramesCount[Type] = 9;
NPCID.Sets.AttackFrameCount[Type] = 5;
NPCID.Sets.DangerDetectRange[Type] = 500;
NPCID.Sets.AttackType[Type] = 1;
NPCID.Sets.AttackTime[Type] = 45;
NPCID.Sets.AttackAverageChance[Type] = 30;
NPCID.Sets.HatOffsetY[Type] = -6;
}
public override void SetDefaults()
{
NPC.townNPC = true;
NPC.friendly = true;
NPC.width = 18;
NPC.height = 40;
NPC.aiStyle = 7;
NPC.damage = 10;
NPC.defense = 50;
NPC.lifeMax = 1000;
NPC.HitSound = SoundID.NPCHit1;
NPC.DeathSound = SoundID.NPCDeath1;
NPC.knockBackResist = 0.5f;
AnimationType = NPCID.Steampunker;
}
public override float SpawnChance(NPCSpawnInfo spawnInfo)
{
if (NPC.CountNPCS(ModContent.NPCType<Bob>()) >= 1) {
return 0;
}
return SpawnCondition.OverworldDay.Chance * 1.0f;
}
public override string GetChat()
{
string[] chat = {
"Hello there, reader!",
"Have you come to purchase some wares?",
"I've heard rumors of an amazing blog post on www.onlineblogzone.com"
};
return Main.rand.Next(chat);
}
public override void SetChatButtons(ref string button, ref string button2)
{
button = "Shop";
button2 = "Quote";
}
public override void OnChatButtonClicked(bool firstButton, ref string shop) {
if (firstButton) {
shop = ShopName;
} else {
Main.npcChatText = "Don't forget to share OnlineBlogZone with your friends and family ♥";
}
}
public override void AddShops()
{
var npcShop = new NPCShop(Type, ShopName)
Add(new Item(ModContent.ItemType<Items.Mounts.HotAirBalloonMountItem>()) { shopCustomPrice = Item.buyPrice(copper: 23) })
.Add(<Items.LaserSword>)
.Add(new Item(ItemID.PlatinumCoin), Condition.IsNpcShimmered);
npcShop.Register();
}
public override List<string> SetNPCNameList() {
return new List<string>() { "Matthias", "Richard", "Bob" };
}
}
}
Conclusion
If you’ve followed along, you now have a basic understanding of how to add NPCs. You can do many more things with NPCs, which we will cover in later parts. We hope you gained a lot of knowledge on how to create your own NPC within Terraria. If you have any questions, don’t hesitate to contact us in the comments or email us directly!