-
Notifications
You must be signed in to change notification settings - Fork 0
feat: NPC & Companion System (spec #28) #29
Description
Feature Spec: NPC & Companion System
| Field | Value |
|---|---|
| Feature | NPC & Companion System |
| Issue | #TBD |
| Status | Draft |
| Author | The Doctor |
| Date | 2026-04-10 |
Overview
What it does
Adds persistent NPCs with names, roles, personality snippets, and relationships to players. NPCs can become companions that join a party during sessions and appear in combat. The Human DM manages NPCs via the management UI, and Ollama receives NPC context during narrative generation.
Why it's needed
NPCs are the backbone of narrative continuity. Persisted NPCs enable quests, world-building, and companions that evolve across sessions.
Out of scope
- Full merchant economy or shop UI
- Romance/affinity branching narratives beyond a simple relationship score
- AI-generated NPC creation (future spec)
Architecture Notes (The Doctor)
Projects / layers touched
-
DungeonMaster.Core— NPC entities + services -
DungeonMaster.Infrastructure— EF Core mappings + migrations -
DungeonMaster.Api— NPC endpoints -
DungeonMaster.Web— NPC management UI -
DungeonMaster.Bot— NPC interaction commands -
DungeonMaster.Shared— NPC DTOs
New interfaces in DungeonMaster.Core
INpcService— CRUD for NPCsICompanionService— add/remove companionsINpcRelationshipService— relationship tracking per player
Integration points with existing systems
- Spec feat: World Building — configure world lore and settings for the AI Dungeon Master #4 (World Building): NPCs reference factions and regions
- Spec [SPEC] Narrative Engine (Ollama Integration) #13 (Narrative Engine): NPC dialogue uses
NpcContext - Spec feat: Discord Gameplay Commands & Narrative Flow (spec #21) #22 (Battle System): companions act as combatants
Domain Model
Entities
Npc
Name(string)Description(string, markdown)FactionId(Guid?, optional)Role(NpcRole enum)PersonalityPrompt(string)
NpcRelationship
NpcId(Guid FK)PlayerCharacterId(Guid FK)Disposition(Disposition enum)AffinityScore(int, 0–100)
Companion
NpcId(Guid FK)CampaignId(Guid FK)JoinedAt(DateTimeOffset)LeftAt(DateTimeOffset?)
Enums
NpcRole
ShopkeeperQuestGiverCompanionVillainCitizen
Disposition
FriendlyNeutralHostile
Service Interfaces (CQRS)
public interface INpcService
{
Task<IReadOnlyList<Npc>> GetAllAsync(CancellationToken ct = default);
Task<Npc?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<Npc> CreateAsync(Npc npc, CancellationToken ct = default);
Task<Npc> UpdateAsync(Npc npc, CancellationToken ct = default);
Task DeleteAsync(Guid id, CancellationToken ct = default);
}
public interface ICompanionService
{
Task AddCompanionAsync(Guid campaignId, Guid npcId, CancellationToken ct = default);
Task RemoveCompanionAsync(Guid campaignId, Guid npcId, CancellationToken ct = default);
}API Contract (Rory)
Endpoints
GET /api/npcsPOST /api/npcs— DM-onlyPUT /api/npcs/{id}— DM-onlyDELETE /api/npcs/{id}— DM-onlyGET /api/campaigns/{id}/companionsPOST /api/campaigns/{id}/companions— DM-onlyDELETE /api/campaigns/{id}/companions/{npcId}— DM-only
UI Specification (Clara)
Page / component breakdown
- NpcRegistry (
/npcs) — list, search, and edit NPCs - CompanionRoster (
/campaigns/{id}/companions) — active companions
Blazor component list
| Component | File | Purpose |
|---|---|---|
NpcRegistry |
Pages/Npcs/NpcRegistry.razor / .razor.cs |
NPC list and CRUD |
NpcDetail |
Pages/Npcs/NpcDetail.razor / .razor.cs |
NPC detail/edit view |
CompanionRoster |
Components/Npcs/CompanionRoster.razor / .razor.cs |
Displays active companions |
RelationshipTable |
Components/Npcs/RelationshipTable.razor / .razor.cs |
Player disposition list |
Theme / styling requirements
- Use
var(--color-surface)for cards - Use
var(--color-primary)for friendly disposition badges - Use
var(--color-danger)for hostile disposition badges
Discord Commands (Rory — Bot Layer)
/npc talk {name}— initiates NPC dialogue via narrative engine/npc recruit {name}— DM-only; adds companion to campaign/npc dismiss {name}— DM-only; removes companion
NLP / AI Behaviour (Missy)
Trigger
NPC dialogue requests from /npc talk or narrative contexts that include NPCs.
Context sent to Ollama
- NPC name, role, and personality prompt
- Relationship disposition and affinity score
- Recent conversation history (last N turns)
Expected behaviour
- NPC stays in character based on personality prompt
- Must not contradict the Human DM’s overrides
- Max length: 1–3 paragraphs
Test Scenarios (Danny) ⚠️ COMPLETE BEFORE IMPLEMENTATION
Happy path
-
Given a DM creates an NPC
When GET/api/npcsis called
Then the NPC appears in the list with correct fields -
Given a DM recruits a companion
When POST/companionsruns
Then the NPC appears in the companion roster
Edge cases
-
Given affinity drops below 0
When relationship updates
Then disposition becomes Hostile -
Given an NPC is marked as Companion role
When added to combat
Then CombatantType is Companion
Error / failure cases
-
Given a non-DM attempts NPC creation
When POST/npcsis called
Then the API returns403 Forbidden -
Given a missing NPC ID
When/npc talkexecutes
Then the bot returns an error message
Acceptance Criteria
Functional
- NPCs are persisted with name, role, description, and personality prompt
- Companions can be added and removed from campaigns
- NPC dialogue uses narrative engine with relationship context
- DM UI supports CRUD and relationship review
Non-functional
- API responses under 300 ms for NPC queries
- All UI components use code-behind pattern
- CSS uses custom properties only
Dependencies
- Spec feat: World Building — configure world lore and settings for the AI Dungeon Master #4: World Building
- Spec [SPEC] Narrative Engine (Ollama Integration) #13: Narrative Engine
Agent Work Breakdown
| Agent | Task | Depends On |
|---|---|---|
| The Doctor | Approve spec | — |
| Danny | Write failing tests from Test Scenarios | Spec approved |
| Rory | Implement NPC entities + API endpoints | Danny's tests |
| Clara | Implement NPC UI | Rory's API |
| Danny | Confirm all tests pass | All implementation |
Definition of Done
- Test Scenarios section completed and approved by The Doctor before implementation
- All failing tests written by Danny before implementation (TDD)
- All tests written and passing (xUnit + bUnit)
- Code reviewed and approved by The Doctor
- EF Core migration created for NPC tables
- GitHub issue closed and linked to merged PR