Traits

traits

Traits are the character creation choices (race, class, background, and any other categories you define), and can also be earned as level-up picks or applied by triggers. Each trait bundles the capabilities the character receives.

In the editor

"Character traits"

Editor location
Files → Mechanics → Traits
Editor type
Edit in Studio + View JSON
Size limits
  • traits.*.description and traits.*.traitNarrativeEffects combined — 6,000
  • traits (entire section) — 1,750,000
  • traits.*.requirements (array length) — 10 entries

Schema

json
{
  "traits": {
    "<key>": {
      "name": "string",
      "description": "string",
      "traitNarrativeEffects": "string",
      "skills": [
        {
          "skill": "string",
          "modifier": "number"
        }
      ],
      "attributes": [
        {
          "attribute": "string",
          "modifier": "number"
        }
      ],
      "resources": [
        {
          "resource": "string",
          "modifier": "number"
        }
      ],
      "startingItems": [
        {
          "item": "string",
          "quantity": "number"
        }
      ],
      "abilities": [
        "string"
      ],
      "unlockedBy": [
        "string"
      ],
      "excludedBy": [
        "string"
      ],
      "requirements": [
        {
          "type": "resource | attribute | skill | trait",
          "variable": "string",
          "amount": "number"
        }
      ],
      "vulnerabilities": [
        "string"
      ],
      "resistances": [
        "string"
      ],
      "immunities": [
        "string"
      ]
    }
  }
}

Example

json
{
  "traits": {
    "Human": {
      "name": "Human",
      "description": "Humans are adaptable and ambitious. Quick to learn and equally quick to scheme. They dominate the central territories politically and commercially.",
      "skills": [
        { "skill": "persuasion", "modifier": 10 },
        { "skill": "history", "modifier": 10 }
      ],
      "attributes": [
        { "attribute": "charisma", "modifier": 1 },
        { "attribute": "intelligence", "modifier": 1 }
      ],
      "resources": [
        { "resource": "stamina", "modifier": 10 }
      ],
      "traitNarrativeEffects": "You are Human. Humans are adaptable and opportunistic — no environment is entirely foreign, and no skill tree is completely closed off. When you attempt something outside your established capabilities, apply a subtle edge of competence that reflects a lifetime of picking things up from context. NPCs from other species may underestimate you initially; humans read as ordinary, which they use. In social situations where your background would be unknown, default to being underestimated rather than recognised.",
      "startingItems": [
        { "item": "leather boots", "quantity": 1 }
      ],
      "abilities": ["Determined Stand"],
      "unlockedBy": [],
      "requirements": [],
      "excludedBy": ["Elf", "Dwarf", "Halfling"]
    }
  }
}

Fields

name

Display name of the trait, shown to players. Required, and the outer key must match it exactly: Voyage rejects a world whose key and name differ.

resources

Additive modifiers to a resource's maximum, as { resource, modifier } pairs; stacking across traits.

json
"resources": [
  { "resource": "health", "modifier": 20 },
  { "resource": "mana", "modifier": -10 }
]

Positive values raise the maximum; negative values lower it.

description

Player-facing description shown during trait selection.

traitNarrativeEffects

The narrator's primary reference for how the trait behaves in play.

attributes

Additive attribute modifiers as { attribute, modifier } pairs; positive or negative, stacking across traits. Attribute modifiers apply before resources initialize, so an attribute boost also feeds attributeStatModifiers.

skills

Additive skill level modifiers ({ skill, modifier }).

startingItems

Items granted when the character takes the trait at character creation or as a level-up pick, as { item, quantity } with keys from itemTypes. A trait applied by a trigger does not grant them. A negative quantity removes that many of the item instead (useful for backgrounds that take away default gear); 0 is a no-op.

abilities

Ability names unlocked by the trait; deduplicated across sources.

vulnerabilities

Damage types a character with this trait is vulnerable to, taking 1.5x damage from each. Every value must match an entry in combatSettings.damageTypes; a value that does not is reported as a warning and has no mechanical effect.

resistances

Damage types a character with this trait is resistant to, taking 0.5x damage from each. Every value must match an entry in combatSettings.damageTypes; a value that does not is reported as a warning and has no mechanical effect.

immunities

Damage types a character with this trait is immune to, taking no damage from each. Every value must match an entry in combatSettings.damageTypes; a value that does not is reported as a warning and has no mechanical effect.

unlockedBy

Prerequisites (OR logic). An entry names either a trait key or a Story Start key. At character creation the trait is shown once the player holds any one of the listed traits from an earlier category, or has selected one of the listed Story Starts; the trait list updates when the player changes their Story Start. Trait entries from the same or a later category have no effect there. For level-up trait picks a pool trait is offered once the character holds any listed trait, whatever its category. The engine can always set traits directly, so premades and triggers bypass it either way.

excludedBy

Traits and Story Starts this one conflicts with (OR logic). An entry names either a trait key or a Story Start key. At character creation the trait is hidden once the player holds any one of the listed traits from an earlier category, or has selected one of the listed Story Starts; the trait list updates when the player changes their Story Start. Trait entries from the same or a later category have no effect there. For level-up trait picks a pool trait is dropped from the offer once the character holds any listed trait, whatever its category. The engine can always set traits directly, so premades and triggers bypass it either way.

requirements

Required array of requirement entries; use [] for none. They gate whether the trait is offered as a level-up trait pick (see Level-up trait picks); character-creation starting selection ignores them. Each entry's type is one of resource, attribute, skill, trait, or characterLevel, in the same shapes as ability requirements.

Required arrays

Every array except the three damage-profile fields must be present on every trait, even when empty: skills, attributes, resources, requirements, and the four below.

  • startingItems - Array<{item, quantity}> - required even if []. A negative quantity removes that many of the item instead of granting it (useful for backgrounds that strip default gear); 0 is a no-op
  • abilities - Array<string> - required even if []
  • unlockedBy - Array<string> - required even if []
  • excludedBy - Array<string> - required even if []

Assigning a trait to a category

A trait is placed in a category by listing its name in that category's traits array (see traitCategories). The trait carries no category field of its own — the link runs one way, from the category down to its members. To move a trait between categories, edit the traits arrays, not the trait.

Attribute modifiers

The attribute string must exactly match one of the names defined in attributeSettings.attributeNames. The validator flags a name that does not appear there. Always cross-check against your world's attribute list before authoring trait modifiers.

Skill modifiers

Modifiers add to the skill's level and stack across traits. Negative modifiers are valid for tradeoff builds.

Note (trait skills array): Granting a skill via a trait is a mechanical unlock. If the player did not already have the skill, the engine adds it to their sheet at level 0 and then applies the modifier, so the trait's modifier is the level they end up with. If the character already had the skill, only the modifier applies.

Narrative effects

traitNarrativeEffects is the AI narrator's reference for the trait during play. When a character has a trait, the narrator reads traitNarrativeEffects as the active instruction for how that trait affects the world — how NPCs treat the character, what the character can and cannot do, what behavioral constraints apply. For example, a King trait might read "The player is a king. NPCs recognize and treat them as royalty."

description vs traitNarrativeEffects

description is what the trait is — shown to the player in the selection UI at character creation, so write it as a readable, informative paragraph. traitNarrativeEffects is the narrator's reference for the trait during play.

By default, write description and traitNarrativeEffects as the same full-paragraph content — the player-facing explanation usually doubles as the narrator's portrayal guidance. Let them diverge only when:

  • the traitNarrativeEffects needs to carry hidden aspects the player should not see (true motives, secret consequences, GM-only behavioral constraints), or
  • the description is a shorter summary and the traitNarrativeEffects is the fuller, elaborated version the narrator works from.

For species traits (races), the traitNarrativeEffects tends to run longer than the player-facing description regardless, because the narrator needs more behavioral context to portray the species consistently across every scene.

Damage profile (resistances, vulnerabilities, immunities)

Optional. A trait can grant the character permanent damage modifiers through three string arrays of damage types:

FieldMultiplierEffect
vulnerabilities1.5x+50% damage taken
resistances0.5x-50% damage taken
immunities0xno damage taken
json
"resistances": ["fire", "poisoning"],
"vulnerabilities": ["water"],
"immunities": ["dark"]

These three are optional - unlike the required arrays above, omit them when a trait has no damage profile. The modifiers last as long as the character holds the trait. A character's lists are the union across all their traits; when a damage type appears in more than one list, immunity beats vulnerability and vulnerability beats resistance. A matching buff stacks with them (a vulnerability plus a vulnerability buff is 2x, a resistance plus a resistance buff is 0.25x). They use the same damage system as npcType damage profiles: values must match strings in combatSettings.damageTypes verbatim, and any value that is not a defined damage type has no mechanical effect.

Trait design philosophy

Think of traits as a bucket that can contain any number of skills, attributes, resources, abilities, startingItems, and narrative effects. Races and classes use the exact same structure. The world creator decides how much mechanical weight each carries.

Level-up trait picks

When progressionSettings.levelUpTraitPool lists traits, a character is offered a trait pick every traitPickEveryLevels levels. The offered list is that pool filtered to traits the character does not already hold, whose unlockedBy / excludedBy conditions pass, and whose requirements are met. A chosen trait applies exactly like a starting trait: every skill, attribute, resource, ability, and item modifier fires. A pool trait does not need a traitCategories entry; it can be pool-only (never a character-creation option) or both.

  • unlockedBy (OR logic): the trait is offered only once the character holds at least one listed trait.
  • excludedBy: the trait is dropped from the offer once the character holds any listed trait.
  • requirements: each entry (resource / attribute / skill / trait / characterLevel) must be satisfied.

Character creation applies unlockedBy and excludedBy, category by category. Categories are offered in the order they appear in traitCategories, and a gate is checked only against traits the player already holds from earlier categories. A trait with unlockedBy is shown once the player holds any one of the listed traits; a trait with excludedBy is hidden once the player holds any one of them. An entry naming a trait from the same category, a later category, or no category has no effect at creation. A gate entry may also name a Story Start instead of a trait: the Story Start the player selected satisfies an unlockedBy naming it and blocks an excludedBy naming it, and the trait list updates when the player changes their Story Start. Randomized characters are filtered by the same rule, category by category. requirements are not applied at creation; they gate level-up picks only. Changing an earlier selection removes later selections that are no longer eligible. The only other constraint is each category's maxSelections. The engine can always set traits directly, so premades and triggers bypass all of this either way.

Note: Because creation only looks at earlier categories, an excludedBy between two traits of the same category does nothing there; the category's own selection limit is what keeps them apart. The same lists are read again for level-up trait picks, where every held trait counts regardless of category.

Required fields

Warning: startingItems, abilities, unlockedBy, and excludedBy are required; omitting any causes decoder errors even when the array is empty. The traitCategories.traits array (listing trait keys per category) is also required, and is what places each trait in its category.

Trait removal at runtime

Via player-traits trigger effect:

  • Attribute modifiers subtracted
  • Skill modifiers subtracted
  • Resource modifiers subtracted (current resource value may drop if it sat above the new max)
  • Abilities removed if no other source still grants them
  • Items stay in the inventory

Dynamic trait changes via triggers

For the canonical worked example of swapping one race trait for another mid-game (with the transformation delivered as a present-tense scene interrupt), see the Race Evolution worked example on the Scripting Patterns page. The branching-paths variant handles player-choice trait progression.

json
{ "type": "player-traits", "operator": "add",    "value": "Cursed" }
{ "type": "player-traits", "operator": "remove", "value": "Cursed" }

A trigger-applied trait applies its attribute, skill, and resource modifiers and its abilities, but not its startingItems. Modifiers are reconciled as net deltas, so adding and removing a trait repeatedly never double-applies them.

Authoring tips

3-tier trait chain (Race → Origin → Class)

  • Origin traits: "unlockedBy": ["Human"] (or relevant race)
  • Class traits: "unlockedBy": ["City Guard Background"] (or relevant origin)
  • Competing origins: "excludedBy": ["Other Origin 1", "Other Origin 2"]

Ability-granting and class design patterns

abilities is the primary ability-granting mechanism — not ability requirements. Abilities listed in a trait's abilities array are granted directly to any character who takes that trait, bypassing the ability's requirements field entirely. This is the correct and reliable way to give characters class-specific or character-specific starting abilities. The requirements field on an ability controls what can be learned during play, and every entry in it must be met. Do not rely on ability requirements to prevent a trait from granting an ability — if the trait's abilities array contains it, the character gets it regardless.

Character-exclusive abilities

Create a trait specific to that character (e.g. "Merlin's Gifts") and list the exclusive abilities in its abilities array. Do NOT place those abilities on a generic class trait (e.g. "Archmage") that other characters might also take — any character with the generic trait receives all abilities in that trait's abilities array. The abilities array bypasses requirements entirely, so listing an ability on a generic class trait grants it to all characters with that trait. The exclusive trait must also be absent from all traitCategories so custom characters cannot select it in the character creator.

Two tiers of ability within a character-specific trait

  • Abilities with no learnable skill path — truly exclusive. If no skill threshold in the world would let a player naturally reach an ability, the only way to have it is via the trait abilities array. Use this for abilities that define the character's uniqueness and must never be learnable by any other character during play.
  • Skill-threshold requirement — learnable by any character. An ability with {"type": "skill", ...} requirements will appear in any character's learnable pool once they meet the threshold, regardless of what trait it is also listed on. Listing it on a trait grants it at character creation for premade characters, but it remains universally learnable during play. Do not use skill-threshold requirements to try to restrict an ability to a single character.

When designing a character-specific trait: use abilities with no reachable skill path for defining uniqueness. Use skill-threshold requirements for abilities strongly associated with that character archetype but legitimate for any sufficiently trained character.

Class-based ability isolation

The trait skills array is the real gate for which ability trees a class can access. If a magic class trait grants no martial arts skill bonus, mage characters start without that skill, so martial arts thresholds are out of reach until they learn and train it in play. Keep skill bonus families separated by class type; cross-contamination in the skills array hands characters a head start on the wrong tree. For a hard gate, use a trait requirement or the trait's abilities list instead.

Multi-axis character creation

Character creation usually offers several trait axes, each its own traitCategory contributing something different. A D&D-style four-axis layout looks like this:

  1. Race - physical/biological traits
  2. Background - history and starting skills
  3. Class - combat/profession role
  4. Alignment - moral compass (D&D 9-alignment system or equivalent)

It is not the only working layout. A modern scenario might use Profession + Origin + Personality; a sci-fi setting might use Species + Faction + Specialization + Era; a slice-of-life world might use just Background + Personality + Defining Relationship; a horror world might collapse to Era + Survival Background + Mental State. Pick the axes that produce meaningful choices in your genre, then design traits within each. The schema is genre-agnostic — the structure is whatever your world needs.

Trait design rules

Illustrated D&D-style; the rules generalize to other axis structures.

  • Give each trait a traitNarrativeEffects that instructs the narrator on the trait's signature features and behavioral constraints (e.g., "This character has Second Wind: once per short rest they can recover HP as a bonus action. The narrator should apply this and similar class features consistently."). The description field is what players read in the selection UI; traitNarrativeEffects is what the AI narrator reads during play. The same applies to non-class traits — a Background, Profession, Faction, etc. can carry a traitNarrativeEffects describing how that origin colors the character.
  • Include startingItems that fit the trait's role and any weight/equipment restrictions you want signalled at character creation.
  • Do not use excludedBy to keep two traits of the same category apart (e.g. two classes, two factions); at creation it only reads earlier categories. Set the category's maxSelections to 1 instead.

Equipment restrictions via aiInstructions

The trait system grants starting items but doesn't restrict ongoing use. If your world wants ongoing restrictions (e.g. "mages can only use light weapons", "civilians can't carry firearms", "common-tier characters can't wield Tier 3 equipment", "non-pilots can't operate mechs"), add an Equipment Restrictions section to the action-resolution instructions, generateActionInfo, defining who can use what.

D&D-style example:

Mages (Wizard, Sorcerer, Warlock): light implements only. Cannot wield swords, axes, or medium/heavy armour.
Fighters: all weapon types. Cannot cast spells without a multiclass.

Modern example:

Civilians: cannot equip firearms or military gear.
Trained Operators: may equip any class of firearm; subject to ammo availability.

The mechanism is genre-agnostic — the restrictions are however your world frames "appropriate gear".

Hiding trigger-only traits from the character creator

If you have traits that are assigned only by triggers (status effects, hidden states, condition stages, etc.) and should not be player-selectable, leave them out of every traitCategories entry, including subcategories. Character creation only offers traits listed there. The traits still exist in the traits dict and are fully functional: triggers can apply them by key at any point during play.