Change want.primary from narrative strings to WantKind enum keywords (Safety, Wealth, Knowledge, etc.) across all 20 NPC profiles. Move narrative text to want.description. Add Happiness to WantKind enum. Add enum constraint to npc-profile.schema.json so make validate-content catches invalid values at authoring time. Fixes silent Want component drop where parse_want_kind() returned None for all real NPCs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
210 lines
6.2 KiB
JSON
210 lines
6.2 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "npc-profile.schema.json",
|
|
"title": "NPC Profile",
|
|
"description": "10-axis NPC model profile — one file per NPC (D-024, D-034).",
|
|
"type": "object",
|
|
"required": ["canonical_id", "display_name", "tier", "pattern", "motivation"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"canonical_id": {
|
|
"type": "string",
|
|
"pattern": "^npc:([a-z][a-z0-9-]+\\.)?[a-z][a-z0-9-]*$",
|
|
"description": "Canonical ID: npc:{slug} (within district) or npc:{district}.{slug} (cross-district)"
|
|
},
|
|
"display_name": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"tier": {
|
|
"type": "integer",
|
|
"enum": [1, 2, 3],
|
|
"description": "NPC tier: 1 = conspiracy (full depth), 2 = template (social context), 3 = filler (atmosphere)"
|
|
},
|
|
"pattern": {
|
|
"type": "string",
|
|
"enum": ["FRIEND", "MIRROR", "ANCHOR", "GHOST", "CATALYST", "THRESHOLD", "REMNANT", "SYSTEM", "NOBODY"],
|
|
"description": "Thematic pattern — System A (D-050)"
|
|
},
|
|
"motivation": {
|
|
"type": "string",
|
|
"enum": ["HANDLER", "WITNESS", "TURNCOAT", "CIVILIAN", "OPERATOR", "SKEPTIC"],
|
|
"description": "Functional motivation — System B (D-050)"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"want": {
|
|
"type": "object",
|
|
"description": "Primary want/need driving this NPC",
|
|
"properties": {
|
|
"primary": { "type": "string", "enum": ["Wealth", "Safety", "Knowledge", "Connection", "Power", "Freedom", "Justice", "Revenge", "Happiness"] },
|
|
"intensity": { "type": "integer", "minimum": 0, "maximum": 10 },
|
|
"description": { "type": "string" }
|
|
},
|
|
"required": ["primary"]
|
|
},
|
|
"secret": {
|
|
"type": "string",
|
|
"description": "The NPC's hidden truth — what they don't want the player to know"
|
|
},
|
|
"relationships": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/relationship" },
|
|
"description": "Named relationships to other NPCs"
|
|
},
|
|
"tolerance": {
|
|
"type": "object",
|
|
"description": "Stress tolerance threshold",
|
|
"properties": {
|
|
"threshold": { "type": "integer" },
|
|
"description": { "type": "string" }
|
|
}
|
|
},
|
|
"routine": {
|
|
"type": "object",
|
|
"description": "Summary of daily routine (full schedule in routines/schedules.yaml)",
|
|
"properties": {
|
|
"summary": { "type": "string" }
|
|
}
|
|
},
|
|
"information": {
|
|
"type": "object",
|
|
"description": "What this NPC knows",
|
|
"properties": {
|
|
"knows": {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "Fact IDs this NPC knows"
|
|
},
|
|
"access_tier": {
|
|
"type": "string",
|
|
"enum": ["public", "insider", "authority", "peer", "hostile"]
|
|
}
|
|
}
|
|
},
|
|
"contentment": {
|
|
"type": "object",
|
|
"properties": {
|
|
"level": { "type": "integer", "minimum": -10, "maximum": 10 },
|
|
"description": { "type": "string" }
|
|
}
|
|
},
|
|
"personality": {
|
|
"type": "object",
|
|
"description": "Personality traits and behavioral tendencies",
|
|
"additionalProperties": { "type": "string" }
|
|
},
|
|
"tells": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/tell" },
|
|
"description": "Observable behavioral tells (D-024)"
|
|
},
|
|
"skills": {
|
|
"type": "object",
|
|
"description": "Skill set and combat capability",
|
|
"properties": {
|
|
"combat_trained": { "type": "boolean" },
|
|
"skills": {
|
|
"type": "object",
|
|
"additionalProperties": { "type": "integer", "minimum": 0, "maximum": 10 }
|
|
}
|
|
}
|
|
},
|
|
"triangle_membership": {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "Triangle slugs this NPC participates in"
|
|
},
|
|
"trust_levels": {
|
|
"type": "object",
|
|
"description": "What the NPC reveals at each trust level",
|
|
"properties": {
|
|
"surface": { "type": "string" },
|
|
"real": { "type": "string" },
|
|
"secret": { "type": "string" }
|
|
}
|
|
},
|
|
"friend_arc": {
|
|
"type": "object",
|
|
"description": "FRIEND arc data — only valid on FRIEND-pattern Tier 1 NPCs (D-034)",
|
|
"properties": {
|
|
"bonded_character": {
|
|
"type": "string",
|
|
"enum": ["smuggler", "detective"]
|
|
},
|
|
"phases": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/friend_phase" }
|
|
}
|
|
},
|
|
"required": ["bonded_character", "phases"]
|
|
},
|
|
"dual_lens": {
|
|
"type": "object",
|
|
"description": "Authoring-only: how smuggler vs detective perceives this NPC",
|
|
"properties": {
|
|
"smuggler": { "type": "string" },
|
|
"detective": { "type": "string" }
|
|
}
|
|
},
|
|
"notes": {
|
|
"type": "string",
|
|
"description": "Authoring-only: design notes"
|
|
}
|
|
},
|
|
"if": {
|
|
"properties": { "pattern": { "const": "FRIEND" } }
|
|
},
|
|
"then": {
|
|
"required": ["friend_arc"]
|
|
},
|
|
"$defs": {
|
|
"relationship": {
|
|
"type": "object",
|
|
"required": ["target", "kind"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"target": {
|
|
"type": "string",
|
|
"pattern": "^npc:([a-z][a-z0-9-]+\\.)?[a-z][a-z0-9-]*$"
|
|
},
|
|
"kind": {
|
|
"type": "string",
|
|
"enum": ["colleague", "friend", "rival", "romantic", "family", "superior", "subordinate"]
|
|
},
|
|
"trust": {
|
|
"type": "integer",
|
|
"minimum": -10,
|
|
"maximum": 10
|
|
},
|
|
"notes": { "type": "string" }
|
|
}
|
|
},
|
|
"tell": {
|
|
"type": "object",
|
|
"required": ["trigger", "behavior"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"trigger": { "type": "string" },
|
|
"behavior": { "type": "string" },
|
|
"visible_to": {
|
|
"type": "string",
|
|
"enum": ["forward", "peripheral", "any"]
|
|
}
|
|
}
|
|
},
|
|
"friend_phase": {
|
|
"type": "object",
|
|
"required": ["phase", "description"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"phase": { "type": "integer", "minimum": 1, "maximum": 5 },
|
|
"description": { "type": "string" },
|
|
"trigger": { "type": "string" },
|
|
"routine_deviation": { "type": "string" }
|
|
}
|
|
}
|
|
}
|
|
}
|