Jump to content

[Tutorial] Creating a mob from scratch and some general information.


-t-
 Share

Recommended Posts

Hello and hi!

I haven't seen a lot of tutorials on the subject of creating custom mobs, outside of some really old mod-tutorials by Cheerio (I think), like explaining brains, behaviours in-depth. So, here I go, making this tutorial for beginners trying to create their own mob.

First of all I would like to say that creating an entirely unique mob takes time, and by unique I mean a mob with a unique mechanic, attack, behaviour etc. as it requires you to create a more complex brain, possibly a new component or a behaviour, and that takes time.
In this tutorial I'll be focusing on a simple mob, without any special qualities. Worry not however as this does not mean that this tutorial won't cover much. There is a lot to cover.

 

NoticeI've never modded in Don't Starve specifically, only Don't Starve Together so if what I'm about to present doesn't work in Don't Starve, know that you've been warned.

 

Let's start with a quick summary:

Spoiler

1. What to expect?

2. What is a prefab?

3. What is a brain?

4. What is a stategraph?

5. Behaviours? Don't we already have a brain?

6. A component.

7. Creating a prefab for a mob.

8. Creating a brain.

9. Creating a stategraph.

 

1. What to expect?

Spoiler

Even though I'm making this tutorial very beginner friendly, basic Lua knowledge is needed.

Every mob in Don't Starve Together is compiled of several different files. A prefab, a brain, a stategraph, behaviours and components. I'll explain all about how to create a prefab for a mob, how to make a custom brain with a stategraph as well as explain some of the behaviours and components that are in the game.

2. What is a prefab?

Spoiler

A prefab is a collection of data that an entity is created from. A prefab is not the same as an entity. Think of a prefab as a template that the game uses to create entities. There isn't really much more that needs to be said but if you want a more detailed explanation check this thread by Hornete:

[Documentation] Understanding Prefabs

3. What is a brain?

Spoiler

The brain of a mob, as the name would suggest, is a brain, used by the entity to respond to certain conditions. To make the entity 'think' you have to create a BehaviourTree for the entity. Let's look at the spiderbrain.lua, the simplest brain in the game. Don't worry about any 'requires' or local functions/values in this file for now. The main function needed for every brain is the 'OnStart()' function, other than the brain class itself:

Screenshot_1.thumb.png.348fad8c4e988921258687c07e5e90f0.png

This function is what creates the BehaviourTree that the entity uses to respond to certain conditions. As you can see above, a local 'root' is created and is assigned the PriorityNode class, then the self.bt is created and assigned BehaviourTree class with 2 values: self.inst, which is the entity that this brain belongs to, and the root.

Now let's talk PriorityNode. Information about the PriorityNode can be found in the behaviourtree.lua file at line 505 . PriorityNode is a class with 3 values: 'children', 'period' and 'noscatter'.

Screenshot_1.png

'children' - This is a table of nodes (more on that later).
'period' - This one is a bit complicated. I'm still not entirely sure if I understand it correctly but after researching brain.lua and behaviourtree.lua I think this is the sleep time for the brain. The higher the value the longer the intervals between running a node. However this apparently doesn't apply to ConditionNodes, that being WhileNodes and IfNodes, for example. It is also tied to the games tick value.
'noscatter' - This one is tied to the 'period' so I'm also not entirely sure if I'm correct but it basically determines whether the sleep time should be slightly randomized. The value is determined by this equation: period * 0.5 + (period * math.random()). 'noscatter' is almost never used and you probably won't use it either.

The main thing you should be worried about is the 'children' table since it houses all of the nodes to make the mob react to certain conditions. Every node has a status. Statuses can have one of four values: SUCCESS, FAILED, READY or RUNNING.

SUCCESS is set if the entity performed the appropriate action.
FAILED is set when the conditions are not met and so the node is not run.
READY is set if the node is not running and is ready to be checked again. That's because every node goes to sleep after running. Every node starts by checking if the nodes status is READY and if it's not then it checks whether the status is already RUNNING.
RUNNING is set if the nodes conditions are met or the node is running.

"Okay, but how does the brain know what node to run if multiple conditions are met?"

Because nodes have priority (hence the 'PriorityNode'). The higher the node is in the table the higher its priority is. If all of the nodes' statuses are set to FAILED, the spider will just wander, since Wander can't set it's status to FAILED. That's why any mob that walks has it's Wander node at the bottom.

Screenshot_1.thumb.png.f34790e5490d9880babd4899697c061c.png

(There is an exception I found in the bearger brain, where the StandStill behaviour is set below Wander, but seeing as it isn't even used or required by the brain I think it's just an oversight)

If, for example, it's day, the spider has to follow a leader but sees food on the ground he will ignore every node but the DoAction and go for the food:

Screenshot_2.thumb.png.82bc5fed6e09a27263cc11a202227cec.png

In this brain the 'PanicWhenScared' has the highest priority so it overrides any other node in this tree.

4. What is a stategraph?

Spoiler

A stategraph is a huge table of states used by the mob. It is mostly used to play sounds, animations, control the order in which the animations play and respond to some events (through EventHandlers). Most stategraphs are composed of 3 tables:

'actionhandlers' - Contains ActionHandlers which are used to make the entity go to a certain state when performing a certain action, for example, in the spider stategraph: ActionHandler(ACTIONS.EAT, "eat") - this means that the spider will go to the 'eat' state when performing the 'eat' action.
'events' - Contains EventHandlers which are used to make the entity go to a certain state if a certain event happens, for example, in the spider stategraph: EventHandler("attacked", function(inst) ... end) - this will run the function when the spider is attacked.
'states' - Contains states. Everything from idling through attacking to dying.

States, as I said before, are mostly used to play animations, sounds and more. In the spider stategraph there is a state called 'taunt' (a good, easy to understand state):

Screenshot_3.png.2d51a97af63e62608f870cdc4eef44cd.png

The first element, the 'name', pretty self-explanatory.
The second element, the 'tags', tags are used mostly to check for conditions, like, for example, when a function is checking for: inst.sg:HasStateTag("busy"). The 'busy' tag is used the most as it is used for when the state is supposed to be uninterruptible (in most cases at least).
The third element, the 'onenter', defines what should happen when the entity enters the state. In the state above you can see the spider has to stop, play the 'taunt' animation (that's the one where it screams) and play a 'scream' sound.
The fourth element, 'events', holds events in the same way the 'events' table mentioned before does. In this case, when the 'animover' event is pushed, which is when the 'taunt' animation is done, the spider will go into the 'idle' state.

There are a few more elements that can be inside of a State but I'll talk about them later in Creating a stategraph.

5. Behaviours? Don't we already have a brain?

Spoiler

Well, yes, but as I said before, brains are composed of nodes and these nodes can be behaviours. So behaviours are kind of like personalized nodes. Let's look at the AttackWall behaviour since it's not too complicated:

Screenshot_4.thumb.png.f148a8f7094cdccf36453f153ca16f57.png

The first thing this function does is check if the status = READY. If yes, self.target is assigned an entity based on these conditions:

Screenshot_5.thumb.png.48b7bcccee38587456a4e460c2ca810b.png

ATTACKWALL_MUST_TAGS = { "wall" } so the entity has to have the 'wall' tag. Next, the function checks if self.target was assigned a value:

Screenshot_6.png.2f3dd8f32d57750debd6cec0bdec0bba.png

If self.target has a value(an entity) set the status to RUNNING, stop moving and set self.done to 'false'. If self.target is 'nil' (is empty, no entity) set the status to FAILED.

After that the function checks whether the status = RUNNING:

Screenshot_7.thumb.png.409e256cd6c97314e291246c88e99853.png

If it is running, check if the target is 'nil', if it's valid and if it's dead. If the target is 'nil' or is not valid or is dead set the status to FAILED and stop moving else set the status to SUCCESS if the attacker can attack. If it can't set it to FAILED then go to sleep.

That's just a quick rundown on how the behaviours look like/work.

6. A component.

Spoiler

A component is a bunch of variables and functions that can be used by prefabs without having to write it inside the prefab itself. By creating a component you allow any prefab to use the code inside it. A lot of prefabs share the same components, for example, almost every prefab in the game uses the 'inspectable' component. This component allows characters to 'inspect' the prefab and say something.

7. Creating a prefab for a mob.

Spoiler

Now, it's time to create a custom mob. Starting with a prefab. If you don't have one, create a folder called 'prefabs' in your 'scripts' folder and create a file called 'custommob.lua' (make sure to change the '.txt' extension to '.lua'). Open this file with a text editor. I'll be using Notepad++.

A basic prefab file should look something like this:

prefabbasic.thumb.png.b6e665731706fc2a918ed6c2d3b1a6b4.png

If you're interested in knowing what inst.entity:SetPristine() does, you'll have to ask someone smart because I don't know. Check out Simplex's explanation:

What is SetPristine()?

Other than these you'll need some components and a stategraph. We'll add some basic ones:

moreprefab.thumb.png.955117bc849c6bc7ac279c9432dd3db9.png

And that is all for the prefab. Now onto...

8. Creating a brain.

Spoiler

Start with making a folder called brains in your scripts folder. In there create a file named custombrain.lua. Our mobs brain will be very primitive with only Wander as the node. You can add more nodes if you want, it's very simple.

brain.thumb.png.ba715315bc73893c5a138a43b3d3d86c.png

The 'Wander' node takes a few arguments, the first one being the entity itself (self.inst), the second one being the point that's the entities 'home'. It will wander around this point with max distance away from it being defined by MAX_WANDER_DIST, the third argument.

That's all for the brain. Can't get any simpler.

9. Creating a stategraph.

Spoiler

For the stategraph I'll have to leave a file since there are too many lines of code in there to take a screenshot.

SGcustom.lua

Everything in there is described.

You can use this stategraph without changing anything if you want.

- - - - - - -

And that's all! You can now spawn your mob in-game with either the console or the Too Many Items Plus mod. But wait! Why is it called MISSING NAME?

mob.png.8ccb0f3180201f8e9792af511f78bc5a.png

That's because you have to add the appropriate strings into the strings table in modmain.lua:

GLOBAL.STRINGS.NAMES.CUSTOMMOB = "Custom Mob"
GLOBAL.STRINGS.CHARACTERS.GENERIC.DESCRIBE.CUSTOMMOB = "..."
GLOBAL.STRINGS.CHARACTERS.WILLOW.DESCRIBE.CUSTOMMOB = "..."
GLOBAL.STRINGS.CHARACTERS.WOLFGANG.DESCRIBE.CUSTOMMOB = "..."
GLOBAL.STRINGS.CHARACTERS.WENDY.DESCRIBE.CUSTOMMOB = "..."
GLOBAL.STRINGS.CHARACTERS.WX78.DESCRIBE.CUSTOMMOB = "..."
GLOBAL.STRINGS.CHARACTERS.WICKERBOTTOM.DESCRIBE.CUSTOMMOB = "..."
GLOBAL.STRINGS.CHARACTERS.WOODIE.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WAXWELL.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WATHGRITHR.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WEBBER.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WINONA.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WARLY.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WORTOX.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WORMWOOD.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WURT.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WALTER.DESCRIBE = "..."
GLOBAL.STRINGS.CHARACTERS.WANDA.DESCRIBE = "..."

NAMES for the mob name and the rest, each for one character. The dots are a placeholder.

And now you're done! You can play around with the brain, components, maybe behaviours, and create a more complex creature. Looking through the code of already existing creatures is a very good practice. It will help you discover new ways you could code your mob.

- - - - - - -

Edited by -t-
  • Like 14
  • Thanks 3
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...