[Tutorial] Adding custom creatures

So an aspiring modder asked how one does to add a creature with its own animations into the world of Stonehearth, and I thought I’d write up some sort of tutorial on this. And then I thought: “let’s start from the beginning, why not!” So here we go!

I’m going to use the Golem from The Mythical Creatures mod for the examples. Furthermore, this tutorial is still valid even if you don’t have your own animations; just ignore those parts.

This turned out to be a whole lot longer than I anticipated… :grin:


Part 1 - The beginnings of an entity


The first and most important step is to add the models in the mod! Yeah ok, chanses are you’ve already done this, though I want to point out that you should have a good organization for your files. It will make your modding life easier (trust me).

Here’s the folder structure for this tutorial:

It’s a bit hard to see, but manifest.json is actually directly under the tmc folder.


Before we get to how the entity’s description is supposed to look like, let’s take a quick gander at the manifest.
Here, it’s enough to add one or two extra values under "aliases":

{
   "info":
   {
      "name"    : "The Mythical Creatures",
      "version" : 1
   },
   "aliases":
   {
      "forest:golem" : "file(entities/forest_myth/golem)", // The actual creature
      "skeletons:golem" : "file(data/rigs/golem)"          // The effects and animations for the golem
   }
}

These will be used to get a path to other files much easier, which will be more apparent later on.


## 1.1 - Molding a personality

So, with that out of the way, we can take a look at how an entity is described. There’s much to consider in what kind of behavior an entity should have. It’s a good thing though that there are existing entities that pretty much behave how we want to and can then do a copy-paste and changing some details. When defining the Golem; I took the description off of the Goblin and changed it to fit the Golem.

Though it’s always a good thing to know what detail you’re changing, so let’s take a look at the Golem’s description and go into more detail of each part.

{
   "type"   : "entity",
   "mixins" : "stonehearth:mixins:monster",

   "components":
   {
      "render_info":
      {
         "animation_table" : "tmc:skeletons:golem",
         "scale"           : 0.12
      },
      "model_variants":
      {
         "default":
         {
            "models": [ "file(golem.qb)" ]
         }
      },
      "sensor_list":
      {
         "sensors":
         {
            "sight":
            {
               "radius" : 64
            }
         }
      },
      "unit_info":
      {
         "name"        : "A Golem",
         "description" : "An ancient guardian of the Dryads",
         "player_id"   : "critters"
      },
      "stonehearth:material":
      {
         "tags" : "stone golem"
      },
      "stonehearth:attributes":
      {
         "max_health":
         {
            "type"  : "basic",
            "value" : 200
         },
         "health":
         {
            "type"     : "variable",
            "equation" : "max_health"
         },
         "speed":
         {
            "type"  : "basic",
            "value" : 20
         },
         "courage":
         {
            "type"  : "basic",
            "value" : 99999
         },
         "menace":
         {
            "type"  : "basic",
            "value" : 70
         }
      },
      "stonehearth:equipment" : {}
   },
   "entity_data":
   {
      "stonehearth:entity_radius" : 1.8,
      "stonehearth:entity_reach"  : 2.2,
      "stonehearth:observers:avoid_threatening_entities":
      {
         "min_avoidance_distance" : 16,
         "max_avoidance_distance" : 16
      }
   }
}

Let’s break down and try to explain each part:

  • "type" - Describes what type this is for; must have value “entity” for creatures. So no touchie!

  • "mixins" - Takes data from another file. In this case it’s from “monster”, though it doesn’t necessarily mean that this creature is a monster so don’t take it too literally. :wink: I’ll cover different kinds of mixins, which are useful for creatures, below.

  • "components" - Here’s a collection of all the components this entity starts off with, remember that all interactable objects that exist in the world are called entities and that components describes an entity. Note that there are much more components but these are among the more important ones for creatures.

    • "render_info" - The renderer component.

      • "animation_table" - Points to a file which describes the animations and effects for this entity. Note that we’re using the alias “skeletons:golem” from the mod “tmc” in this example.
      • "scale" - This entity’s scale. Standard value is 0.1.
    • "model_variants" - What models to use.

      • "default" - The default models. There can be other kinds other than default, for example: berry bushes also has “depleted” to show a different model when it has no more berries to harvest.
        - "models" - An array of all the models to use.
    • "sensor_list" - A core component that gives the entity a kind of awareness of its surroundings. This one has a sight of radius 64, which means the golem can see up to 64 units away from itself.

    • "unit_info" - Some basic information of the entity.

      • "name" - Its name.
      • "description" - Its description.
      • "player_id" - What faction this entity belongs to. Currently, this is most commonly used for animals with the id being "critters". Obviously the golem doesn’t belong to that faction, but this is for the sake of showing an example. If you’re curious about how to create a faction of your own, maybe there’ll be a different tutorial on that. At least I can mention that you can take a look at the factions that already exist and go from there (located in "stonehearth/services/server/population/data")
    • "stonehearth:material" - What the entity is made of.

      • "tags" - Contains all the relevant tags for this entity. Example: the golem has two tags: “stone” and “golem”.
    • "stonehearth:attributes" - All the attributes for this entity. In other words: these are the values that makes this entity perform differently from others. Note that these attributes are far from all that exist, though I’ve found that these are the most important for regular creatures at least.

      • "max_health" - Maximum health.
      • "health" - Current health (it’s independent of max_health).
      • "speed" - The running speed.
      • "courage" - How brave the creature is.
      • "menace" - How terryfying it is.
        • "type" - What kind of value to expect. Here we see the types “variable” and “basic”, but there’s also “random_range”.
        • "value" - Just some number. Expected when type = “basic”.
        • "equation" - Some equation which calculates what value to use. Useful if you want to derive from a variable, “health” uses “max_health” to decide its value for example. Expected when type = “variable”.
    • "stonehearth:equipment" - This simply states that the entity can equip an item; it can’t take any values to start off with. Remember though, that if you want this creature to be able to attack, it needs to have a weapon equipped.

  • "entity_data" - Has some general information of this entity.

    • "stonehearth:entity_radius" - The size of the entity.

    • "stonehearth:entity_reach" - How far the entity can reach.

    • "stonehearth:observers:avoid_threatening_entities" - The distance to check for enemies and run from them.

Mixins:

  • "stonehearth:mixins:monster": For creatures that are more aggressively inclined. Use this if you want your creature to be more combat oriented.

  • "stonehearth:mixins:critter": Simple creatures, these are given simple attributes and AI.

  • "stonehearth:mixins:pet": A kind of critter with some extra attributes.

  • "stonehearth:mixins:base_human": A mixin for humans. Note though that this gives your creature the same AI and attributes as the hearthlings have, so it’s (currently) only recommended if your creature going to belong directly to a player and be part of building up a kingdom, ie build houses, craft various stuff, and so on.

  • "stonehearth:mixins:undead": For undead creatures, obviously.

  • "stonehearth:mixins:mob": The core mixin for any creature. All the above mixins have this mixin included so there’s rarely any need for this one, though if you’re specifically going to use this as your mixin; you’ll have to include what AI you want to use for the creature, since it has none itself.

AI. This is a really large subject and not something this tutorial will go into great detail, though it’s worth mentioning that you can specify what kind of AI you want your creature to have. Again, this tutorial won’t go into detail of what you can choose, but you can see an example of how to choose in the file "stonehearth/mixins/base_human/base_human.json".
Furthermore, in stonehearth’s manifest; you can take a peek at what kinds of AI you can choose (you can find them under "aliases", and they all begin with "ai_pack:").


## 1.2 - Putting on the moves

So now we’re looking at adding in custom animations for the entity.

Let’s start off with looking at the Golem’s skeleton file (located where its alias is pointing at):

{
   "type" : "rig",
   "skeleton":
   {
      BUNCH o' DATA
   },
   "animation_root" : "file(animations)",
   "effects_root"   : "file(effects)",
   "postures"       : "file(postures.json)"
}

Let’s break these down once more:

  • "type" - As before this describes what type this is, and this must be set to “rig”.

  • "skeleton" - Contains the origin position of each object in the model. The origin of an object is the point around which that object animates.

  • "animation_root" - Points to the folder where all of the animations are stored.

  • "effects_root" - Points to the folder where all of the effects are stored (more on that in a bit).

  • "postures" - The file which describes what postures the entity uses. It’s used to replace specific effects for specific circumstances. E.g. when a hearthling is sitting down, all its idle animations will then be replaced with a sitting down animation.


So, effects.
Effects are those that play an animation, start up audio, show some particle effect, etc. So whenever some entity is doing something, an effect is being played! This can be anything from being idle to admiring a fire to fighting off a goblin horde.

Here’s the file structure for the Golem’s effects and animations:

It should be noted that while the animations can be named however you wish, most effects has to have a specific name. For example, when an entity is running the “run.json” effect is being played, and so there must exist be such a file, or else you will witness the big red wall of errors!

Let’s look at a couple of the effects. We’ve already mentioned “run.json” so let’s go with that one:

{
   "type" : "effect",
   "tracks":
   {
      "animation":
      {
         "type"       : "animation_effect",
         "animation"  : "walk.json",
         "start_time" : 0,
         "loop"       : true
      }
   }
}

As usual let’s break it down:

  • "type" - You know this already!

  • "tracks" - Contains all the animations, audio, etc that will be played.

    • "animation" - An animation will be played, this is only symbolic though and can be named however you wish.
      • "type" - What kind of track will be played, this also decides what other values are to be expected. Since this is an animation type, the following data will show for such types.
      • "start_time" - How long to wait before playing the animation (in milliseconds).
      • "animation" - The name of the animation to play (it must exist under the animation root folder, which is specified in the rig file).
      • "loop" - Whether or not this animation should loop.

The second effect I’ll be showing is “combat_block.json”, though this time I’ll exclude the detailing; see if you can figure it all out on your own!

{
   "type" : "effect",
   "tracks":
   {
      "animation":
      {
         "type"       : "animation_effect",
         "animation"  : "combat_block.json",
         "start_time" : 0,
         "loop"       : false
      },
      "sound":
      {
         "type" : "sound_effect",
         "duration"     : 1000,
         "start_time"   : 200,
         "min_distance" : 30,
         "falloff"      : 3.6,
         "volume"       : 30,
         "track":
         {
            "type" : "one_of",
            "items":
            [
               "stonehearth:sounds:woodsword_block_01",
               "stonehearth:sounds:woodsword_block_02",
               "stonehearth:sounds:woodsword_block_03"
            ]
         }
      },
      "hit_spark":
      {
         "type" : "cubemitter",
         "start_time" : 200,
         "end_time"   : 240,
         "cubemitter" : "particles/combat/parry.cubemitter.json",
         "transforms":
         {
            "x"  :  0,
            "y"  :  2,
            "z"  : -1.5,
            "rx" :  0,
            "ry" :  0,
            "rz" :  0
         }
      }
   }
}

And that’s pretty much it! This ought to be everything you need to create your own creatures!

Next we’ll be looking at how to have these creatures show up in your game!

14 Likes

Reserving for:

Part 2 - Hi, I exist!

Right. So after you’d created all the necessary parts for your creature, the next logical step would be to make them appear in the world.

There are several ways to go about this; I’m going to bring up few here. Also because of how large each section can become, I’m going to split these into their own posts and paste a link to each of them here.

###2.1 - Scenarios, threats, and loot!

###2.2 - Critters all around!

###2.3 - Mastering ambient threats

###2.4 - Camp creation 101

6 Likes

wow… just wow!

thanks for putting together this very thorough guide! I’m sure it will prove immensely helpful to the modders/animators in our midst! :smile: :+1:

4 Likes

Yep. I think we should add little modding tutorials at least once a week. The problem is that tutorials like this one aren’t exactly short… :sweat_smile:

If I can’t find the time to put together one of these, at least I’ll try to make a list of the current useful tutorials so people can find them all together (since the modding category also contains mods, and modding questions…)

I wanted it to be a section in the website, but here it’s appropiate too, since anyone can contribute and ask their doubts. :smile:

I’m so happy that we have this friendly, active community. Recently we’ve been having a new wave of modders :smiley:

5 Likes

Woohh! that was fast! Alot of modders were waiting for this :smiley:

I will update my progress.

3 Likes

Now do a Blender animation tutorial. xD

1 Like

I would say “Contains the origin position of each object in the model. The origin of an object is the point around which that object animates.”

2 Likes

What do you want to learn?

Thanks, I’ve updated it accordingly.

Haha yeeeeaaah, I’m a coder not an animator. :stuck_out_tongue:

You should take a look over at voxel_pirate’s stuff for those kinds of tutorials.
Here’s an overview:

3 Likes

you should most definately pour that into a youtube video, it just adds a visual part making it easier for ppl to learn since they watch someone do it!

I’ve added a new entry to the golem’s description file ("player_id") and, of course, described how it’s used.

It’s not really a bad idea, though there are some cons with that. When it’s written down like this the modder can go at this at their own pace, while with a video they have to pause and retrace if something wasn’t all too clear at first.

Definitely not dissing the idea though, but we’ll see what happens. :smile:

well if you want, I can try and make some time to do the editing for you. My JSON is too outdated to actually follow your written tutorial and record it with your help… I’d need to catch up on it first :stuck_out_tongue:

This modding is making my head explode :tired_face:

Sooo… I did follow this guide and this is showing up:

> -- Script Error (lua) Begin ------------------------------- 
> 2015-Jul-08 17:53:38.566044 | client |  0 |                         lua.code |    std::exception: 'invalid file path '/additionalpets/entities/additional_pets/little_dragon'.'
> 2015-Jul-08 17:53:38.566044 | client |  0 |                         lua.code |    stack traceback:
> 2015-Jul-08 17:53:38.566044 | client |  0 |                         lua.code |    	[C]: ?
> 2015-Jul-08 17:53:38.566044 | client |  0 |                         lua.code |    	[C]: in function 'create_authoring_entity'
> 2015-Jul-08 17:53:38.566044 | client |  0 |                         lua.code |    	radiant/modules/client_entities.luac:3: in function 'create_entity'
> 2015-Jul-08 17:53:38.566044 | client |  0 |                         lua.code |    	debugtools/call_handlers/debugtools_commands.lua:15: in function 

How is this pad invalid? Before it gave me a could not find manifest.json error and now this

Full path:

D:\Program Files (x86)\Steam\SteamApps\common\Stonehearth\mods\additionalpets\entities\additional_pets\little_dragon

Full manifest.json

{
    "info": {
        "name": "additionalpets",
        "description": "",
        "version": 1
    },
    "aliases": {
        
	  
       "pets:littledragon": "file(entities/additional_pets/little_dragon)",
       "skeletons:littledragon": "file(data/rigs/little_dragon)"
        }
}

There are no typos on your folders or on the actual files?

Would you mind if you uploaded your mod for me to take a look?

1 Like

I can already see the type, in the first picture it says "/additionalpets/entities/additional_pets/little_dragon’."
In the second one the path is \additionalpets\entities\additional_pets\little_dragon

I believe you need to use the forward slas like this “/”

1 Like

An update!

I’ve rewritten how the AI is added to entities since that’s been completely changed in Alpha 11.
Also I’ve added which mixins you can use for your creature (it’s detailed right under the breakdown of the golem’s description).

@Feashrind, we’ve managed to solve the issue; it was a typo in the file names, they didn’t have the same name as the folders they were contained in and therefore “file(entities/additional_pets/little_dragon)” couldn’t find the file.

2 Likes

2.1.1 - Scenario Schmenario


Let’s start off with how to add through scenarios. So far the only creatures that come from scenarios are wild animals, so that’s also what this section will focus on; both friendly and hostile.

For this part of the turotial, we’ll be using the Mandragora for our examples. It’s not quite an animal, but good enough to be a wild creature. We’ll also be creating a new mod: scenario_ex.


First, we’ll take a look at the overview of the folders and files that we have:

You might notice a few differences from what we had before; we’ve got a new folder: scenarios. Within it we find a couple of files, which will be used to add the mandragoras into the scenario system.


Let’s look at the first one of these: the mandragora_nest.json file.

{
   "mixins"        : "stonehearth/scenarios/static/critter_nest/critter_nest.json",
   "name"          : "mandragora_nest",
   "habitat_types" : [ "mountains", "foothills" ],
   "weight"        : 3,
   "unique"        : false,
   "size":
   {
      "width"  : 16,
      "length" : 16
   },
   "data":
   {
      "critter_type" : [ "scenario_ex:mandragora" ],
      "num_critters":
      {
         "min" : 1,
         "max" : 2
      }
   }
}

As per usual; we’ll be looking at each of these in detail now. Only a few of these are necessary to use, but you can use it all if you wish; if you don’t then default values will be used instead.

  • "mixins" - Part 1 already covers how mixins work. The value shown here is what you want to use if you’re adding critters or other wild creatures.
  • "name" - Just the name of this scenario. Default is critter_nest.
  • "habitat_types" - On what type of area(s) you’d want this creature to appear on. The types you can choose at the moment are: mountains, foothills, plains, and forest. Defaults are plains and foothills.
  • "weight" - How big of a chance this scenario has to trigger; the higher the value the bigger the chance. Default is 10.
  • "unique" - If this scenario should only occur once. Default is false.
  • "size" - How big of an area the creatures can spawn in. Default is 16 by 16.
  • "data" - Some specific data for this kind of scenario.
    • "critter_type" - What creature(s) you want this scenario to spawn.
    • "num_critters" - Randomly choose how many critters there should be; from min through max. Default is 1 through 3.

Next we’ll be looking at scenario_index.json.

{
   "static":
   {
      "scenarios":
      [
         "file(static/mandragora_nest)"
      ]
   }
}

This one’s short and sweet! It will be used to say that we have our own scenario to add to their list of scenarios, which we’ll do through the manifest.
Speaking of the manifest; let’s look at it now.

{
   "info":
   {
      "name"    : "Scenario Example",
      "version" : 1
   },
   "aliases":
   {
      "mandragora"           : "file(entities/mandragora)",
      "skeletons:mandragora" : "file(data/rigs/mandragora)",
   },
   "mixintos":
   {
      "stonehearth/scenarios/scenario_index.json" : "file(scenarios/scenario_index.json)"
   }
}

Notice that we’re now using “mixintos.” If you’re unfamiliar with it, essentially what it does is to take a data file and combine it with another. Mixintos only work with json files. If you want to know more about it, and about overrides, you can take a peek over here.

And that is it! That’s all the extra work to populate the world of Stonehearth with your own critters and/or wild creatures.

Here are the mandragoras in all their glory!


### 2.1.2 - Hostile lands, but with great loot

How about we stray away (for a bit) from adding creatures into the world, and get into how to make them get their own, specific, weapons and loot drops, and how to make them hostile against the player of course.

For the mandragora; we’re adding a new weapon for it to use and a couple of new items for it to drop. Here’s how we’ve set up the new files.


For this we need to add some values to the mandragora’s description, here’s the new and improved mandragora:

{
   "type"        : "entity",
   "mixins"      : "stonehearth:mixins:monster",
   "init_script" : "file(mandragora_init_script.lua)",
   "components":
   {
      "render_info":
      {
         "animation_table" : "scenario_ex:skeletons:mandragora",
         "scale"           : 0.1
      },
      "model_variants":
      {
         "default":
         {
            "models" : [ "file(mandragora.qb)" ]
         }
      },
      "unit_info":
      {
         "name"        : "A Mandragora",
         "description" : "A rare, dangerous, and mind-numbingly loud plant",
         "player_id"   : "undead"
      },
      "stonehearth:material":
      {
         "tags" : "plant"
      },
      "stonehearth:equipment" : {},
      "stonehearth:attributes":
      {
         "max_health":
         {
            "type"  : "basic",
            "value" : 70
         },
         "health":
         {
            "type"     : "variable",
            "equation" : "max_health"
         },
         "speed":
         {
            "type"  : "basic",
            "value" : 35
         },
         "menace":
         {
            "type"  : "basic",
            "value" : 30
         }
      }
   },
   "entity_data":
   {
      "stonehearth:entity_radius" : 0.75,
      "stonehearth:entity_reach"  : 1.0,
      "stonehearth:observers:avoid_threatening_entities":
      {
         "min_avoidance_distance" : 16,
         "max_avoidance_distance" : 16
      },
      "stonehearth:destroyed_loot_table":
      {
         "num_rolls":
         {
            "min" : 1,
            "max" : 1
         },
         "items":
         [
            { "uri" : "scenario_ex:mandrake:leaves", "weight" : 4 },
            { "uri" : "scenario_ex:mandrake:root",   "weight" : 2 }
         ]
      }
   }
}

There are some differences here from what we’ve seen of the golem before; let’s focus on those.
First off is "init_script," with a lua file as its value; we’ll take a look at that file soon.
Next up is that there’s the mandragora has undead as its player_id. This is a bit of a hack to make them hostile against the player, of course they’re not undead but this is the only population which is always hostile against anything. So it’s an easy way to make a creature into your enemy.
And then under entity_data there is "stonehearth:destroyed_loot_table," this gives the creature loot to drop on death. You can specify how much loot you want the creature to drop through "num_rolls," and in "items" you say what items should be dropped; where each item has a “uri” and a “weight.”


On to the script file!

local init_fn = function(entity)
   entity:add_component('stonehearth:equipment')
            :equip_item('scenario_ex:mandragora:voice')
end

return init_fn

It’s not too much you need to worry about here. You can easily copy this text into your own init script file, just be sure to change 'scenario_ex:mandragora:voice' into whatever weapon you’d want your creature to wield.


Let’s show off how the weapon’s json file looks like:

{
   "type" : "entity",
   "components":
   {
      "stonehearth:equipment_piece":
      {
         "slot"   : "mainhand",
         "ilevel" : 0,
         "roles"  : "combat"
      }
   },
   "entity_data":
   {
      "stonehearth:combat:weapon_data":
      {
         "base_damage" : 13,
         "reach"       : 1.0
      },
      "stonehearth:combat:armor_data":
      {
         "base_damage_reduction" : 0
      },
      "stonehearth:combat:melee_attacks":
      [
         {
            "name"         : "combat_scream",
            "active_frame" : 17,
            "cooldown"     : 2000,
            "priority"     : 1
         }
      ],
      "stonehearth:combat:melee_defenses":
      [
         {
            "name"         : "combat_dodge",
            "active_frame" : 8,
            "cooldown"     : 10000,
            "priority"     : 1
         }
      ]
   }
}

Its type is entity, as it must be. It’s got only one component: the equipment_piece component. It basically what’s needed for this entity to become wielded as an equipment by other entities.
Other interesting points here are those under “entity_data.”

  • "stonehearth:combat:weapon_data" - Here we specify how much damage the weapon inflicts and how much range it has from its user.
  • "stonehearth:combat:armor_data" - How much armor it grants to its user.
  • "stonehearth:combat:melee_attacks" - Here’s a list of all the attacks that can be done by this equipment. What we want to note here is that there must be an effect with the name shown here, which will be playing the attack animation with sounds and such. "active_frame" tells us which frame the hit occurs in.
  • "stonehearth:combat:melee_defenses" - Almost like the one above, only here it’s used to defend against incoming blows.

Note that this file holds data specific for only the mandragora; it’s not intended for anything else. If you’re looking to add a weapon that you’re hearthlings can also use; take one of Stonehearth’s weapons (located in stonehearth/entities/weapons) and change some values to reflect your weapon.


We’ll also have to update the manifest with three more entries to aliases:

   "aliases":
   {
      "mandragora"           : "file(entities/mandragora)",
      "skeletons:mandragora" : "file(data/rigs/mandragora)",
      "mandragora:voice"     : "file(entities/mandragora/mandragora_voice.json)",
      "mandrake:leaves"      : "file(entities/mandrake_leaves)",
      "mandrake:root"        : "file(entities/mandrake_root)"
   },

And with that, you can add your creatures in-game, give them some weapons to fight with, some loot to drop, and some extra fun for the player.

4 Likes

Oh god this is so helpfull :smiley: i was looking for something like this for my mod (wich hasnt progressed in a long time.

2 Likes

2.2 - Critters all around!


So now we’re going to look at how to make new critters to be captured by the trappers. First of all though; I’d like to give thanks to @RepeatPan with his solution, without that this section would not exist!


We’ll be using the mandragora once again for this. Though now we’re stripping away a lot of components and other unnecessary stuff, and only focus on making it into a pure critter. So let’s look at its new description file.

{
   "type"   : "entity",
   "mixins" : "stonehearth:mixins:critter",
   "components":
   {
      "render_info":
      {
         "animation_table" : "critter_ex:skeletons:mandragora",
         "scale"           : 0.1
      },
      "model_variants":
      {
         "default":
         {
            "models" : [ "file(mandragora.qb)" ]
         }
      },
      "unit_info":
      {
         "name"        : "A Mandragora",
         "description" : "A rare, dangerous, and mind-numbingly loud plant",
         "player_id"   : "critters"
      }
   },
   "entity_data":
   {
      "stonehearth:harvest_beast_loot_table":
      {
         "num_rolls":
         {
            "min" : 1,
            "max" : 1
         },
         "items":
         [
            { "uri" : "critter_ex:mandrake:leaves", "weight" : 4 },
            { "uri" : "critter_ex:mandrake:root",   "weight" : 2 }
         ]
      }
   }
}

Notice that we’re using critter as a mixin here; it holds a lot of data specific for all critters, and because of that we don’t have to add that much more to this except for data specific for this critter (of course).

Looking at "entity_data," we can see that it’s got its loot table just as decribed in 2.1.2. The difference here is that we’re using "stonehearth:harvest_beast_loot_table" instead of "stonehearth:destroyed_loot_table." There are a few differences between them: "destroyed_loot_table" is what the entity will drop when it’s destroyed at any time now matter the circumstance, and the loot will always belong to the faction that the entity belonged to (so the player will have to use the loot command to take the items), while "stonehearth:harvest_beast_loot_table" is specific for when the entity is ‘harvested’ through the trapper, which means that the loot will only drop by such an event and it will always belong to the faction which ‘harvested’ the poor creature (removing the need of the loot command).


Right, now the critter is ready to go; we just need to have it become a magnet for traps. For that we have to look at some Lua code, and add something new to the manifest. Let’s look at the manifest before getting into the nitty-gritty.

{
   "info":
   {
      "name"    : "Critter Example",
      "version" : 1
   },
   "server_init_script" : "file(scenario_server)",
   "aliases":
   {
      "mandragora"           : "file(entities/mandragora)",
      "skeletons:mandragora" : "file(data/rigs/mandragora)",
      "mandrake:leaves"      : "file(entities/mandrake_leaves)",
      "mandrake:root"        : "file(entities/mandrake_root)"
   }
}

I bet you’ve noticed the new entry here: "server_init_script." Basically, this points to a script file which is run, on the server side, at the game’s start-up. There is also another one ("client_init_script") for the client side, but it’s not needed here so we’re ignoring that. One thing I’d like to point out is that the file extension must be excluded, or else it won’t run (for some reason).


Now we can take a look at the Lua file.

local rng = _radiant.csg.get_default_rng()
local critter_ex = class()

function critter_ex:_init()
   local component = radiant.mods.require('stonehearth.components.trapping.trapping_grounds_component')

   local old_initialize = component.initialize
   function component:initialize(...)
      local ret = { old_initialize(self, ...) }
      self._spawnable_critters = self._spawnable_critters or { 'stonehearth:rabbit', 'stonehearth:racoon', 'stonehearth:red_fox', 'stonehearth:squirrel' }
      -- This is where we add our own critter. If you have multiple critters you'd like to add; copy the below line for each critter.
      table.insert(self._spawnable_critters, 'critter_ex:mandragora')
      return unpack(ret)
   end

   function component:_choose_spawned_critter_type()
      return self._spawnable_critters[rng:get_int(1, #self._spawnable_critters)]
   end
end

radiant.events.listen_once(radiant, 'radiant:required_loaded', critter_ex, critter_ex._init)

return critter_ex()

Again, this is from @RepeatPan’s solution, so all credit goes to him.

This tutorial won’t go into detail exactly how this works. Let’s just say that we’re getting the trapping grounds component, and changing a couple of functions in it into our own implementation which allows us to add new critters.
With this kind of implementation, even if several mods were to add their own critter that can be captured, it will work for all of them, which makes this solution very powerful in that regard. But that’s assuming that all of the mods are using exactly this solution.
If you’re using this implementation for yourself; be sure to change 'critter_ex:mandragora' to your own critter, and also change the 'critter_ex' variable to the name of your mod.

To close this up; let’s take a look at some poor critters in cages (because why not).

The mandragora is not pleased.

A wild, and tiny, golem. It looks confused as to how it got there.

4 Likes