[Tutorial] Adding custom creatures

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