Traits

Edit tabs/traits.json.

Trait Categories

Traits must be organized into categories in tabs/traits.json under traitCategories.

Field Description
name Display name for the category
maxSelections How many traits player can pick. 0 means none can be picked — don’t use it. Applies to the whole category, subcategories included
traits Array of trait keys in this category
description Optional player-facing text explaining what the category represents
subcategories Optional groups within the category: { name, traits, description? }. In character creation, subcategory traits are listed first, then the ungrouped root traits

Category patterns:

  • Single selection (class/background): maxSelections: 1
  • Single selection (species): maxSelections: 1
  • Multiple selection (miscellaneous): maxSelections: 3

Category selection at character creation ignores requirements (level-up picks only). unlockedBy and excludedBy do constrain creation choices: see Trait Dependencies below.

Required Fields

Field Requirement
name Must match object key exactly
description What the trait is — shown to players at character creation (full paragraph)
traitNarrativeEffects The narrator’s reference for portraying the trait. Usually the same as description; elaborate further or add hidden aspects the player shouldn’t see
attributes Array of attribute modifiers (can be empty [])
skills Array of skill modifiers (can be empty [])
resources Array of resource modifiers (can be empty [])
startingItems Array of items granted (can be empty [])
abilities Array of ability names granted (can be empty [])
requirements Array of requirement objects (can be empty []). Only gates level-up trait picks — ignored at character creation
unlockedBy Prerequisites (OR logic): trait keys or story start ids. Affects level-up picks and character creation (see Trait Dependencies)
excludedBy Conflicts: trait keys or story start ids. Affects level-up picks and character creation (see Trait Dependencies)

description and traitNarrativeEffects

For non-species traits:

  • By default, description and traitNarrativeEffects carry the same content — write both as full paragraphs.
  • description is shown to players at character creation. traitNarrativeEffects is the narrator’s reference during play.
  • Let them diverge only when: (a) the narrative effects hold hidden aspects the player shouldn’t know, or (b) the description is a shorter summary and traitNarrativeEffects is the elaborated, fuller version.

Older configs used a field named quirk for this; it is auto-migrated to traitNarrativeEffects verbatim. Always write traitNarrativeEffects in new configs.

For species traits, see the Species Traits section below.

Conditional Fields

Field When to Include
attributes (non-empty) Class/background traits that affect core stats
skills (non-empty) Traits that grant expertise or training
resources (non-empty) Traits that affect resource pools
startingItems (non-empty) Class/background traits with signature equipment
abilities (non-empty) Traits that unlock special perks or powers
requirements (non-empty) Traits in the level-up pool that should be gated behind stats, skills, other traits, or character level
vulnerabilities / resistances / immunities Traits that should change how much typed damage the player takes in combat (e.g. a fire-elemental species immune to fire). Values must match combatSettings.damageTypes

Level-Up Trait Picks

Traits can be offered as level-up rewards via progressionSettings.levelUpTraitPool in tabs/settings.json (default cadence: 1 pick every 10 levels). When a pick is pending, the player chooses from pool traits they don’t already have whose unlockedBy/excludedBy conditions pass and whose requirements are met; the chosen trait applies immediately, exactly like a starting trait. An empty pool means no picks are ever granted.

Gotcha: requirements does nothing at character creation, it only filters the level-up pick list. To keep a trait out of starting selection everywhere, leave it out of every trait category.

Trait Dependencies (unlockedBy and excludedBy)

Both fields list trait keys or story start ids: a trait can require, or be blocked by, other traits or specific story starts. At character creation, categories are evaluated in traitCategories order after the story start is chosen. A story start dependency always takes effect; a trait dependency only constrains choices in categories after the referenced trait’s own category. A dependency that is neither an earlier-category trait nor a story start leaves the trait unconditionally unlocked. Changing an earlier selection removes later selections that are no longer eligible. Both fields also filter level-up trait picks.

Trigger-granted traits: a trait added by a player-traits trigger effect applies its modifiers and abilities but does NOT grant its startingItems. Only permanent acquisition paths (character creation, level-up picks) hand out starting items. Trait bonuses are reconciled as net deltas, so swapping traits never double-applies a modifier.

TraitModifier Format

All modifiers use the same structure:

{ attribute: string, modifier: number }  // For attributes
{ skill: string, modifier: number }      // For skills
{ resource: string, modifier: number }   // For resources

The modifier is an additive value (positive or negative). Multiple traits stack.

Point Cost Guidelines

Traits should be balanced around a point budget. Use these guidelines:

Modifier Type Point Cost
Attribute 1 point
Skill at modifier 0 1 point
Resource per 10 1 point
Starting item 1 point
Ability 1 points

Every trait within a single traitCategory should be roughly equal.

Target values:

  • The isHealth resource should be set at roughly 100.
  • Attributes should be at an average of 12, ranging from 0-20.

startingItems Format

{ item: string, quantity: number }

The item must reference a valid item key from tabs/items.json.

requirements Format

Array of prerequisite checks. All must be met for the trait to appear as a level-up pick. Same format as ability requirements.

{ type: 'skill', variable: 'skill name', amount: 3 }      // Skill level >= 3
{ type: 'attribute', variable: 'strength', amount: 14 }   // Attribute value >= 14
{ type: 'characterLevel', amount: 5 }                     // Character level >= 5 (no variable)
{ type: 'resource', variable: 'mana', amount: 50 }        // Resource max >= 50
{ type: 'trait', variable: 'fire affinity', amount: 1 }   // Has trait (amount ignored)

Schema

interface TraitCategory {
  name: string
  maxSelections: number
  traits: string[]
  description?: string
  subcategories?: TraitSubcategory[]
}

interface TraitSubcategory {
  name: string
  traits: string[]
  description?: string
}

interface Trait {
  name: string
  description: string
  traitNarrativeEffects: string
  attributes: Array<{attribute: string, modifier: number}>
  skills: Array<{skill: string, modifier: number}>
  resources: Array<{resource: string, modifier: number}>
  startingItems: Array<{item: string, quantity: number}>
  abilities: string[]
  requirements: TraitRequirement[]
  unlockedBy: string[]
  excludedBy: string[]
  vulnerabilities?: string[]
  resistances?: string[]
  immunities?: string[]
}

type TraitRequirement =
  | { type: 'resource' | 'attribute' | 'skill' | 'trait'; variable: string; amount: number }
  | { type: 'characterLevel'; amount: number }

Species Traits

When creating a trait that represents a species (playable race):

  1. Must have corresponding NPC Type and World Lore entries
  2. The description contains lore paragraphs + skill blocks separated by \n\n
  3. The traitNarrativeEffects contains lore paragraphs only (no skills)
  4. Lore paragraphs are identical across NPC Type description, Trait description, and Trait traitNarrativeEffects
  5. Must include exactly 3 skills that work as both player skills AND NPC abilities
  6. Skills should reflect innate species abilities or cultural training

See Species Consistency Rules for the full requirements.

Reference

For detailed documentation, see traits-reference.md.


This site uses Just the Docs, a documentation theme for Jekyll.