Last updated: January 30th, 2019. Latest version here.

Traits

The traits component holds a map of all of an entity's traits. For hearthlings it's added in the base_human.json mixin.

Traits can be added, removed and retrieved through this component during the game. You can find all the existing traits inside stonehearth/data/traits.

Creating a trait

A trait consists of a JSON file that describes features such as the display name, description, and icon.

To create a new trait, add a new alias to your manifest.json. For example:

  "aliases": {
     "traits:impatient" : "file(data/traits/impatient/impatient_trait.json)"
  }

Then mixinto that alias entry to stonehearth/data/traits/traits_index.json so that it can be included in the list of traits that can be added to a new hearthling:

  "mixintos": {
     "stonehearth:traits_index" : "file(data/traits/traits_index.json)"
  }

The mixinto would look like this:

  {
     "traits": {
        "my_mod:traits:impatient": {}
     }
  }

Our impatient_trait.json file looks like this:

  {
     "type": "trait",
     "display_name": "i18n(my_mod:data.traits.impatient.impatient_trait.display_name)",
     "description": "i18n(my_mod:data.traits.impatient.impatient_trait.description)",
     "icon": "file(impatient_trait.png)",
     "script_controller": "my_mod:trait_scripts:impatient",
     "data": {
        "impatient_thought": "stonehearth:thoughts:traits:got_delayed",
        "negative_thought": "stonehearth:thoughts:traits:made_me_hurry"
     },
     "attribute_modifiers": {
        "speed": {
           "add": 30
        },
        "social_loss_override": {
           "min": 0.3
        },
        "diligence": {
           "add": 15
        }
     }
  }

Traits files have these fields:

  • "type" : "trait" -- this file represents a trait.

  • "display_name" -- localized name for the trait to display in the UI.

  • "description" -- localized description for the trait to display in the UI. You can reference the name of the hearthling that has this trait by using [name(i18n_data.entity)] in your description text in the en.json file.

  • "icon" -- icon to display in the UI.

  • "script_controller" -- optional field. A controller that will be created with the arguments: entity, uri, parent, args. The script can be used to create custom behavior associated with the trait.

    For example, the food_preference trait script (stonehearth/data/traits/food_preference/food_preference_script.lua) gets data from the parent trait (using the uri parameter), and uses the data to change the entity's consumption preference and also adds a thought when the entity eats a food they don't like.

    icon Remember that the alias for the script controller must be added inside the "controllers" key of your manifest, not under the "aliases" key.

  • "data" -- optional field. If we defined a script controller for the trait, we can add data inside this field to retrieve it from the controller.

  • "attribute_modifiers" -- optional field. Modifiers for the hearthling's attributes. See below for more details.

  • "subject_overrides" -- optional field. An array of overrides for subject matters (topics used in hearthling conversations). Each subject that is added to the hearthling has an associated opinion. The opinions are randomly chosen, unless something else overrides them. For example, the item preferences observed in the Appeal tab of the character sheet will produce positive opinions for the loved/liked items and negative opinions for the disliked items.

    This field is used to add permanent subject matters to the hearthling or override their opinions if they already exist. Examples can be seen in the cultist trait and in the passion_job traits. Each entry in the array has 4 keys: "subject" (the URI of the subject), "sentiment" (-1 for negative, 0 for neutral, 1 for positive), "permanent" (whether this subject should be permanent or disappear after some time) and "unvarying" (hearthlings can change their opinion about subjects when having conversations, if this property is true they won't).

  • "properties" -- optional field. It's an array of strings, we can add like custom tags in it and check for them in Lua scripts. See below for more info.

icon The "magnificent_beard" trait has some properties listed in the traits index.

The "immigration_only" : true property will make it so that this trait can only be assigned if the hearthling is an immigrant (so it will never appear randomly at the select roster screen during embarkation).

The "gender" : "male" property will make it so that only male hearthlings can get this trait assigned. You may use this property without the "immigration_only" one, but the players will still be able to change the gender in the roster screen (which won't remove the trait).

This trait has both properties since females don't have the facial hair feature in the customization list, so to prevent errors during embarkation it was made as "immigration_only".

To test traits we can reroll many times in the customization screen until we get our new trait, but that takes time. Instead, let's add/remove our traits manually to the hearthlings via the default console. We can select a hearthling and type this in the default console:

  add_trait my_mod:traits:impatient
  remove_trait my_mod:traits:impatient

Notice that the URI is passed as a parameter without quotes. traits

Attributes will appear in green in the Attributes tab if they're above their normal value, and in red if they're below it. Their tooltip will show the buffs that are causing the values to increase/decrease, but it won't show the traits that affect them (you can hint at that in the trait description though).

Attribute modifiers

If a trait changes some numerical value, add it inside "attribute_modifiers" in the trait's JSON file, and have whatever manages the behavior that the numerical value changes check for the attribute value.

Beware that attribute modifiers aren't saved and are reapply to the entity on load.

These modifiers are managed in stonehearth/lib/modifiers/modifiers_lib.lua. We can also add attributes to the entity this way (for instance, the "social_loss_override" from the example above is not included by default in the hearthlings attributes, but there are scripts that check for it. We can inject new numerical values with this and check for them in other Lua files).

The possible values for the modifiers are "add", "multiply", "min", and "max". With "add" we can also substract (if the value we provide is negative), and with "multiply" we can also divide (if we provide a decimal value; for example, to change the attribute to one fifth of its current value we'd multiply by 0.2, which is 1/5). Multiply is done before add, then min, then max. So if there are several buffs or traits affecting this attribute, they would get applied in that order.

Properties

We can add custom properties to the hearthlings via traits, and then in other Lua scripts check for them. For example, the social component checks for this property that is injected to hearthlings with the loner trait:

  if radiant.entities.has_property(self._entity, 'avoids_conversations_with_citizens') then
     return -- entity doesn't initiate conversations with fellow citizens
  end

Trait exclusions

If you take a look at traits_index.json from the stonehearth mod, you'll notice that there's a "groups" key at the top. This allows to define groups of related or opposite traits to create exclusions. When rolling the traits, if the hearthling gets assigned a trait from one of these groups, they won't get assigned a different trait from within the same group (hearthlings can have either 1 or 2 traits by default).

If our custom trait doesn't have to do with any other trait, we can define them under the "traits" key instead, like in the example above.

We can also define exclusions for traits inside either "groups" or "traits" like this:

  "stonehearth:traits:green_thumb": {
     "excludes": {
        "stonehearth:traits:carnivore": true
     }
  }

That way we don't have to add or move the traits inside "groups", which might affect other traits (traits are only defined once in all the index file) and can exclude the same trait for different traits.

Custom traits index

The base kingdoms get their traits from the "stonehearth:data:traits" index by default, but we can specify a different traits index for modded populations. In their population JSON file, add this field:

  "traits_index" : "my_mod:data:traits"

Usually NPC factions don't make use of traits, only the playable ones do.

Mod from a community member that adds many traits to the game: Kai Monkey's Traits