This implements a complete Katherine RPG-based character state tracking
system that tracks the AI character ({{char}}) instead of the user.
Features:
- 40+ primary personality traits (dominance, honesty, empathy, etc.)
- 70+ secondary emotional states (happy, horny, anxious, playful, etc.)
- Physical stats tracking (energy, hunger, arousal, health, pain, etc.)
- Relationship tracking per-NPC (trust, love, attraction, thoughts, etc.)
- Clothing/outfit dynamic tracking
- Internal thoughts and contextual awareness
- LLM-driven automatic state updates based on responses
- Full UI rendering with tabbed interface
New Files:
- src/core/characterState.js (528 lines) - Core state data structure
- src/systems/generation/characterPromptBuilder.js (407 lines) - LLM prompts
- src/systems/generation/characterParser.js (456 lines) - Response parsing
- src/systems/rendering/characterStateRenderer.js (401 lines) - UI rendering
- CHARACTER_TRACKING_README.md - Complete documentation
- INTEGRATION_EXAMPLE.js - Step-by-step integration guide
- IMPLEMENTATION_SUMMARY.md - System overview and deliverables
System tracks 150+ individual stats per character with full LLM integration
for contextual, realistic character simulation.
All code is production-ready and copy-paste complete.
13 KiB
Character State Tracking System for SillyTavern RPG Companion
📖 Overview
This is a comprehensive character state tracking system based on the Katherine RPG framework. Unlike traditional RPG companions that track {{user}} stats, this system tracks {{char}} (the AI character's) internal states, emotions, relationships, and physical condition.
What It Tracks
🧬 Primary Traits (Personality DNA)
- 40+ personality traits that define who the character IS
- Core disposition (dominance, introversion, emotional stability)
- Sexual personality (perversion, exhibitionism, masochism, etc.)
- Moral core (honesty, empathy, corruption, etc.)
- Intellectual traits (intelligence, wisdom, creativity)
- These change SLOWLY - only through sustained experiences over time
🌤️ Secondary States (Emotional Weather)
- 70+ temporary emotional states that change frequently
- Core emotions (happy, sad, angry, anxious, etc.)
- Arousal & sexual states (horny, frustrated, seductive, etc.)
- Social states (lonely, confident, playful, etc.)
- Energy & altered states (drunk, exhausted, euphoric, etc.)
- These change FAST - minute to hour timescales
💭 Beliefs & Worldview
- Track character's beliefs with strength and stability
- Moral beliefs, spiritual beliefs, self-concept
- Relationship beliefs, sexual morality
- Beliefs can fracture during pivotal moments
🏃 Physical Stats
- Survival needs (hunger, thirst, bladder, energy, sleep)
- Physical condition (health, pain, temperature, cleanliness)
- Physical attributes (strength, stamina, agility)
👗 Outfit/Clothing System
- Dynamic tracking of what character is wearing
- Per-piece tracking (bra, panties, shirt, pants, etc.)
- Status tracking (worn properly, shifted, removed, torn, wet)
- Coverage calculation (0-100% body coverage)
❤️ Relationship Tracking
- Per-NPC detailed relationship stats
- Core metrics: Trust, Love, Loyalty, Attraction, Respect, Fear
- Social dynamics: Closeness, Openness, Comfort, Dependency
- Sexual dynamics: Flirtiness, Sexual Compatibility, Satisfaction
- Power dynamics: Dominance, Submissiveness, Possessiveness
- Current thoughts about each person
🎬 Contextual Information
- Location, time of day, weather
- Present characters in the scene
- Recent events
- Current activity
🔄 How It Works
The Flow
- LLM receives current character state as input before generating a response
- LLM generates the character's response based on their current emotional/physical state
- LLM updates character states based on what happened in the response
- Parser extracts and applies updates to the character state
- UI displays updated states for the user to see
Example
Before Response:
- Character: Katherine
- Emotional State: Lonely (70), Anxious (40), Horny (30)
- Relationship with User: Trust 85, Love 60, Attraction 75
- Physical: Energy 50%, Arousal 30%
- Location: Katherine's apartment
- Thoughts: "I wish {{user}} would stay longer..."
LLM generates response where Katherine invites {{user}} to stay for dinner
After Response:
- Emotional State Changes:
- Lonely: -20 (reason: {{user}} accepted invitation)
- Happy: +25 (reason: spending time with {{user}})
- Hopeful: +15 (reason: possibility of intimacy)
- Relationship Updates:
- Trust: +5 (reason: {{user}} agreed to stay)
- Closeness: +10 (reason: intimate setting)
- Thoughts: "Maybe tonight is finally the night..."
- Physical Changes:
- Energy: -5 (reason: cooking dinner)
- Arousal: +15 (reason: anticipation of being alone with {{user}})
📁 File Structure
src/
├── core/
│ ├── characterState.js # Character state data structure & management
│ └── state.js # Original extension state (keep for compatibility)
│
├── systems/
│ ├── generation/
│ │ ├── characterPromptBuilder.js # Generates prompts for character tracking
│ │ ├── characterParser.js # Parses LLM responses and updates states
│ │ ├── promptBuilder.js # Original prompt builder (still used for user tracking)
│ │ └── parser.js # Original parser
│ │
│ └── rendering/
│ ├── characterStateRenderer.js # Renders character state in UI
│ └── [other renderers...]
│
└── [other modules...]
🚀 Getting Started
1. Installation
Copy all the new files into your RPG Companion extension:
src/core/characterState.jssrc/systems/generation/characterPromptBuilder.jssrc/systems/generation/characterParser.jssrc/systems/rendering/characterStateRenderer.js
2. Integration with Main Extension
You'll need to modify index.js to integrate the character tracking system:
// Import character tracking modules
import {
getCharacterState,
updateCharacterState,
initializeRelationship
} from './src/core/characterState.js';
import {
generateCharacterTrackingPrompt,
generateSeparateCharacterTrackingPrompt
} from './src/systems/generation/characterPromptBuilder.js';
import {
parseAndApplyCharacterStateUpdate,
removeCharacterStateBlock
} from './src/systems/generation/characterParser.js';
import {
renderCharacterStateOverview,
updateCharacterStateDisplay
} from './src/systems/rendering/characterStateRenderer.js';
3. Hook into Message Received Event
// In your onMessageReceived handler
async function onMessageReceived(data) {
if (!extensionSettings.enabled) return;
// Parse character state update from the response
const stateUpdate = parseAndApplyCharacterStateUpdate(data.mes);
// Update UI
updateCharacterStateDisplay();
// Optionally remove the state block from the displayed message
if (stateUpdate) {
data.mes = removeCharacterStateBlock(data.mes);
}
}
4. Hook into Generation Started Event
// In your onGenerationStarted handler
async function onGenerationStarted(data) {
if (!extensionSettings.enabled) return;
// Add character tracking prompt to the generation
const characterPrompt = generateCharacterTrackingPrompt();
// Inject into the prompt (method depends on your setup)
// Example: use extension_prompts system
setExtensionPrompt(
'CHARACTER_STATE_TRACKING',
characterPrompt,
extension_prompt_types.AFTER_SCENARIO,
0, // position
false, // scan depth
extension_prompt_roles.SYSTEM
);
}
5. Add UI Container
Add this to your template.html:
<div id="rpg-character-state-container" class="rpg-section">
<!-- Character state will be rendered here -->
</div>
🎨 Customization
Choosing Which States to Track
You can customize which states to track by modifying characterState.js:
// Focus on emotional tracking only
export let characterState = {
characterName: null,
secondaryStates: {
happy: 50,
sad: 0,
angry: 0,
horny: 0
// Add only the emotions you care about
},
// Remove sections you don't need
};
Customizing the Prompt
Edit characterPromptBuilder.js to change how the LLM is instructed:
// Simplify the tracking instructions
instructions += `Update only these states:\n`;
instructions += `- Emotions: happy, sad, angry, aroused\n`;
instructions += `- Energy level\n`;
instructions += `- Thoughts about {{user}}\n`;
Styling the UI
Add custom CSS for the character state display:
.rpg-character-overview {
background: rgba(0, 0, 0, 0.7);
border-radius: 8px;
padding: 15px;
}
.rpg-emotion-item {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.rpg-relationship-card {
background: rgba(255, 255, 255, 0.05);
padding: 10px;
border-radius: 5px;
margin-bottom: 10px;
}
💡 Advanced Features
Automatic Character Initialization
When starting a new chat, you can automatically initialize the character's personality traits from their character card:
import { generateCharacterInitializationPrompt } from './src/systems/generation/characterPromptBuilder.js';
import { parseCharacterInitialization } from './src/systems/generation/characterParser.js';
async function initializeCharacterFromCard() {
const prompt = await generateCharacterInitializationPrompt();
// Send to LLM (using your API client)
const response = await generateRaw(messages, api, false);
// Parse and apply
const traits = parseCharacterInitialization(response);
if (traits) {
updateCharacterState({ primaryTraits: traits });
}
}
Relationship Analysis
Automatically analyze relationships when new characters appear:
import { generateRelationshipAnalysisPrompt } from './src/systems/generation/characterPromptBuilder.js';
import { parseRelationshipAnalysis } from './src/systems/generation/characterParser.js';
async function analyzeRelationship(npcName) {
const prompt = generateRelationshipAnalysisPrompt(npcName);
// Send to LLM
const response = await generateRaw([{role: 'user', content: prompt}], api, false);
// Parse and apply
const relationshipData = parseRelationshipAnalysis(response);
if (relationshipData) {
updateRelationship(npcName, relationshipData);
}
}
Persistent State Storage
Save character state to chat metadata:
import { getCharacterState } from './src/core/characterState.js';
function saveCharacterState() {
const charState = getCharacterState();
// Save to SillyTavern chat metadata
chat_metadata.rpg_character_state = charState;
saveChatDebounced();
}
function loadCharacterState() {
if (chat_metadata.rpg_character_state) {
setCharacterState(chat_metadata.rpg_character_state);
}
}
📊 State Change Guidelines
Emotional States (Secondary States)
Small changes (+/- 5-15):
- Normal conversation
- Minor events
- Gradual mood shifts
Medium changes (+/- 20-40):
- Significant events
- Important revelations
- Strong emotional moments
Large changes (+/- 50+):
- Life-changing events
- Trauma
- Peak experiences
Relationship Changes
Trust:
- Vulnerability rewarded: +5 to +15
- Promise kept: +5
- Betrayal: -30 to -60
Love:
- Romantic moment: +5 to +20
- Declaration of feelings: +20 to +40
- Heartbreak: -40 to -80
Attraction:
- Attractive behavior: +5 to +15
- Sexual tension: +10 to +30
- Turn-off: -10 to -30
🐛 Troubleshooting
Character state not updating
- Check console for parsing errors
- Verify the LLM is including the state update block in responses
- Make sure the format matches exactly what the parser expects
UI not displaying
- Check that the container
#rpg-character-state-containerexists - Verify jQuery selectors are working
- Check browser console for JavaScript errors
LLM not following format
- Adjust the prompt to be more explicit
- Use a better model (Claude Sonnet 4.5, GPT-4, etc.)
- Increase temperature slightly for more creative state updates
- Add examples to the prompt
📚 Examples
Example Character State Update (from LLM)
Katherine's State Update
---
**Emotional Changes**:
- happy: +20 (reason: {{user}} complimented her cooking)
- confident: +10 (reason: successful dinner preparation)
- horny: +15 (reason: intimate candlelit atmosphere with {{user}})
- anxious: -15 (reason: {{user}}'s presence is comforting)
**Physical Changes**:
- Energy: -10 (reason: cooking and cleaning)
- Arousal: +20 (reason: anticipation of being alone with {{user}})
**Relationship Updates**:
- {{user}}:
- Trust: +5 (reason: {{user}} was vulnerable about their past)
- Closeness: +15 (reason: deep conversation during dinner)
- Attraction: +10 (reason: {{user}} looked particularly attractive tonight)
- Thoughts: "I want this moment to never end. Maybe I should make a move..."
**Scene Context**:
- Location: Katherine's apartment, dining room
- Time: 8:30 PM
- Present: {{user}}, Katherine
**Katherine's Thoughts**:
"This is perfect. The wine, the candlelight, {{user}} opening up to me... I can feel the tension between us. Should I reach across the table and touch their hand? My heart is racing just thinking about it."
🤝 Contributing
This system is based on the Katherine RPG Complete Master document. If you want to extend it:
- Add new state categories to
characterState.js - Update
characterPromptBuilder.jsto instruct the LLM about new states - Update
characterParser.jsto parse new state formats - Update
characterStateRenderer.jsto display new states
📄 License
This extends the RPG Companion SillyTavern extension. Follow the same license as the main extension.
🙏 Credits
- Katherine RPG System: Original comprehensive character simulation framework
- RPG Companion: Base extension by Marysia
- Character State Tracking: Integration of Katherine RPG into SillyTavern
📞 Support
If you encounter issues:
- Check the console for error messages
- Verify your LLM model supports structured outputs
- Review the prompt and parsing logic
- Open an issue on GitHub with:
- Error messages
- LLM response example
- What you expected vs what happened
Enjoy deep, realistic character simulation with full emotional and psychological tracking! 🎭✨