NPCs, Souls, Roles, Metaroles, Characters, Brains Documentation
This article describes everything you need to know about NPCs and various principles – NPC entity, souls, roles, metaroles, characters, NPC brains.
The following image explains the relationships between all the concepts
Any human or animal object that can be controlled by AI MBT trees is an NPC entity. There are several NPC entity classes – each for every animal and human type with different animation skeletons.
- NPC (=males)
The entities are available for placing in the level in [Sandbox Editor] in RollupBar under “Entities > AI”. (Note: there are also non-functional legacy entities from basic CryEngine).
An NPC entity, by default, has the default head, default body, default clothes and plays idle animations ad infinitum. The NPC entity, like any other, is saved in its constituent layer/LYR file (.lyr).
NPC entity properties that be changed:
Most NPC properties, which make it a unique individual, are saved in its soul.
If you save the aforementioned setup (default NPC entity placed in level) right away a local soul data is stored in the LYR. This is a basic CryEngine functionality. KCD, however, uses something called “shared souls” – an equivalent saved in DB and with expanded capabilities.
The main table is “soul” where every record is a separate soul. Soul DB table contains a wide number of settings – from head model, to faction membership, to daycycle schedule settings.
|soul_id||Soul GUID||Created in WH NPC Editor|
|soul_name||Name of the soul||Uniqueness not required, but strongly suggested|
|soul_class_id||Not required. Used for custom grouping of NPCs||Trackable in Quest Flow Graph|
|initial_clothing_preset_id||Reference to the initial clothes of the NPC||Set in WH Inventory Editor|
|character_head_id||Reference to the head model of the NPC||Set in WH NPC Editor|
|character_hair_id||Reference to the hair model of the NPC||Set in WH NPC Editor|
|initial_clothing_dirt||Initial dirtiness of the NPC|
|inventory_id||Reference to initial inventory template||Set in WH Inventory Editor|
|*str, agi, spc, vit, gearing, barter, courage, charisma, vision, shadiness||various skills, stats and PRG parameters||Set in Storm|
|reputation||initial personal reputation towards player||Set in Storm|
|brain_id||Reference to a brain class||Set in WH NPC Editor|
|faction||NPC's faction membership||Set in Swift|
|social_class_id||NPC's social class setting||Set in Swift. Plays role in Crime System|
|*activity_0-8||name of daycycle schedule activity||Set in Storm|
|*time_0-8||starting time of the daycycle schedule activity||Set in Storm|
|soul_vip_class_id||VIP systems protection settings||Set manually in DB|
|initial_weapon_preset_id||Reference to the initial weapons of the NPC||Set in WH Inventory Editor|
|_soul_ui_name_id||NPC's UI name if not defined by Character (in Skald)||Created manually. Set in Storm|
|skald_character_id||Reference to NPC's Character||Character created in Skald, linked in Swift|
|combat_level||Combat level of the NPC||Determines combat abilities. Interpreted by Storm|
One of the important setting is a reference to the soul’s brain. Brain is a construct that holds all MBT trees and thus allows you to script the NPC’s behavior. Typically a human NPC has the brain “npc_daycycle” (see documentation).
There are also other related tables with additional setting or definitions referenced from the soul table:
Pairing entity to soul
The link between entity and soul is save in the LYR file.
A new soul is created in WH NPC Editor/Properties.
It is strongly advised for the soul name and entity name to be the same.
Soul/Entity naming conventions
We strongly recommend incorporating the name of your mod or its part (quest) in the name. This will prevent naming clashes with other mods. We recommend following this format
locationPrefix - roughly tells you where the NPC stands in the level. Each significant zone in the map has its own prefix (rat_ is Rattay, led_ is for Ledetchko, etc.)
morOrQuestName – unique name for your mod or quest the NPC is related to
additionInfo – any additional info if needed
nameOrRole – name of the NPC (e.g. peter) or their role in the world (e.g. guard). The most generic NPCs are usually named man or woman
numberIfNotAlone - If there are more people eligible for the same name, add a number
Select an NPC entity in the level and name it properly. Click “Create new shared soul”. This will create a temporary local (!) soul of the same name as the entity name. Not yet committed soul is indicated with this line
Sometimes (e.g. when you create a twin for certain situations, or when you want to have the same NPC in different level) you may want to link to an existing soul. In that case click the button “Connect to existing soul” in WH NPC Editor/Properties.
A character is an independently created “design counterpart” for a soul. It can hold information that facilitates development in purely design/writing phase so that it can be oblivious to the state of the game level or souls.
However, the only character data critical for the game are:
- Voice – The basic VO unit. Typically, one voice = one voice actor. However if a voice actor is capable of delivering more distinct voices they can have more voices assigned to them.
- Character UI name – The name that appears in the game right above the “Talk” prompt.
Characters are created in Skald.
Characters are linked to souls in Swift.
A role defines a participant in dialog and holds all of their lines across the entire game. Roles are created in Skald when you write a dialog (see more in Dialog System).
A single role can appear in multiple separate dialogs (like EXAMPLE MAN in the image above). In other words - a new dialog can re-use any existing roles.
One role can be assigned to multiple souls. This in effect means that all these souls can take part in all dialog where the role is present, and that all these lines of that role must be recorded by the voice specified in the soul-character relationships.
Roles are assigned to souls in Storm and the data is stored in DB table “soul2role”
Metarole is a concept that allows roles to be temporarily available or unavailable to the NPC.
Each role is assigned to a metarole. The default metarole, to which all new roles are assigned, is called NPC and is switched on by default on all NPCs. A role can be assigned to a different metarole in Swift.
The NPC must have assigned ALL the roles it can use at any time. Roles which are “under” non-default metarole can then be switched on and off (respectively) by the following LUA functions:
soul:AddMetaRoleByName(“metaroleName”) OR soul:AddMetaRole(metaroleID)
soul:RemoveMetaRoleByName(“metaroleName”) OR soul:RemoveMetaRole(metaroleID)
What are metarole good for in practice? Examples:
Situation 1 – roles can be too general and their assignment to souls too ambiguous.
Premise: Any NPC can go to any shop and have a dialog with the shopkeeper.
Problem: Any shopkeeper can also become a shopper. This means that if shopkeeper1 goes shopping and attempts to talk with another shopkeeper2, both shopkeeper1 and shopkeeper2 have both roles. The Dialog System, despite both NPCs performing different behaviors, doesn’t know at this point who should be the customer and who the shopkeeper. In the best scenario the NPCs will play their role in this situation randomly each time, in the worst case they will refuse to talk altogether.
Solution: You still give the role SHOPPER to all NPCs in the world, and the role SHOPKEEPER to all shopkeepers. But you will set the SHOPPER role to metarole META_SHOPPER and SHOPKEEPER to META_SHOPKEEPER and both will be removed by default (no setup is needed for that). Then, the NPC who plays the customer, will have LUA script soul:AddMetaRoleByName(“META_SHOPPER”) at the start of its behavior, while any shopkeeper ready to accept customer will have soul:RemoveMetaRoleByName(“META_SHOPPER”) at the start of its shopkeeper behavior. Both behaviors will perform soul:RemoveMetaRoleByName(“metaroleName”) by the time their dialogs are no longer needed. These LUA call pairs are often used in MBT tree node LUAWrapper.
Situation 2 – overriding systemic metarole-driven monologs
Premise: There are several systems (similar to the shopkeeper-customer problem described above) primarily driven by metaroles, such as dudeprox (“Henry has come to see us!”) or trespass comments (“Get out of here before I call the guards”). You don’t want to reinvent the wheel. You want the use the functionality behind these systems. You want to use a custom monolog, not the generic ones.
Solution: You create a monolog with new role (e.g. EXAMPLE MAN – DUDEPROX) and give it higher priority (more in Skald tutorial/documentation). Then you assign that role to system metarole KOMENTAR_NA_JINDRU (see more on how to make sense of the by-default Czech role and metarole names here). That’s all. What happens is that the dudeprox system temporarily switches on the core dudeprox role (KOMENTAR_NA_JINDRU) and attempts to play the dudeprox dialogs. It will look at all the valid monologs. All of them are always valid, but your custom monolog is valid in the specific situation and has a higher priority, so its played. When the custom monolog becomes invalid again your NPC will revert back to using generic monologs in these situations.
Situation 3 – minimizing possible interference of in-game or any other special dialog with regular dialogs.
Premise: There is NPC1 and player. By pressing “E to Talk” you engage in numerous conversations with NPC1 (with role EXAMPLE MAN).
But under certain circumstances there is also another special dialog which you don’t want to appear among the listed topics. For example, a quest gets suddenly activated which leads to a situation where a short exchange is automatically played when you get close to NPC1. If this was handled as a NPC’s bark (= ingame monologue), there would be no interference, but the designer wants an ingame dialog here which is a dialog like any other but without cameras and dialog selection interface and you also have full control over you character.
Problem: When you press “E to Talk”, the system selects all dialogs where player and NPC1 and player are the sole participant. Then it filters out any dialogs with FALSE condition. Now if you do a poor job with conditions your ingame dialog may be listed among the normal topics. Worse even, if the ingame dialog has non-default priority, you may end in situation where the ingame dialog always takes over and you can’t talk to the person normally (BTW that is how the refusal bark on waking NPC up in the night works by design).
Solution: There are several possible solutions to curb these clashes. One of them consists of adding the ingame dialog NPC’s role to a new metarole and switching it on only temporarily. It allows you to avoid having to write complex sets of entry conditions (for both the ingame dialog and the other dialogs) and having to restrict normal dialogs altogether (greyed-out “E to talk”) while the ingame is being played.
NPC entity belongs among SmartEntities (along with SmartObjects, SmartAreas etc.) which means they can hold a brain. Brain is what allows you to control that entity via AI MBT trees.
Brains are assigned to souls. Default brain of any newly created soul is “npc_test_base” which, as you can see upon inspection, does nothing.
Normal NPCs have the brain “npc_daycycle” which takes care of every aspect of their basic functionality – from reactions to various systems (hearing, perception, crime, trespass, dialog requests, player presence etc.), to being able to process their daycycle schedule settings as the world time goes on.
There are also other specialized brains for humans with limited capabilities due to specialized focus – npc_battle, npc_deadbody. These brains are simplified for the sake of optimization. After all, the dead don’t need to react to sound.
Animal brains also have dedicated brains – npc_horse, npc_horse etc.
Player is an NPC entity like any other. He must have the soul “dude” which has the brain “player”. Without it the NPCs and many other systems cannot function properly as many systems react to player and need to communicate with the player brain.
Brains npc_daycycle and player are further described in their respective documentations.