Add comprehensive character state tracking system for {{char}}
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.
This commit is contained in:
@@ -0,0 +1,479 @@
|
||||
# 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
|
||||
|
||||
1. **LLM receives current character state** as input before generating a response
|
||||
2. **LLM generates the character's response** based on their current emotional/physical state
|
||||
3. **LLM updates character states** based on what happened in the response
|
||||
4. **Parser extracts and applies updates** to the character state
|
||||
5. **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.js`
|
||||
- `src/systems/generation/characterPromptBuilder.js`
|
||||
- `src/systems/generation/characterParser.js`
|
||||
- `src/systems/rendering/characterStateRenderer.js`
|
||||
|
||||
### 2. Integration with Main Extension
|
||||
|
||||
You'll need to modify `index.js` to integrate the character tracking system:
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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`:
|
||||
|
||||
```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`:
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```css
|
||||
.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:
|
||||
|
||||
```javascript
|
||||
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:
|
||||
|
||||
```javascript
|
||||
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:
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
1. Check console for parsing errors
|
||||
2. Verify the LLM is including the state update block in responses
|
||||
3. Make sure the format matches exactly what the parser expects
|
||||
|
||||
### UI not displaying
|
||||
|
||||
1. Check that the container `#rpg-character-state-container` exists
|
||||
2. Verify jQuery selectors are working
|
||||
3. Check browser console for JavaScript errors
|
||||
|
||||
### LLM not following format
|
||||
|
||||
1. Adjust the prompt to be more explicit
|
||||
2. Use a better model (Claude Sonnet 4.5, GPT-4, etc.)
|
||||
3. Increase temperature slightly for more creative state updates
|
||||
4. Add examples to the prompt
|
||||
|
||||
---
|
||||
|
||||
## 📚 Examples
|
||||
|
||||
### Example Character State Update (from LLM)
|
||||
|
||||
```character-state
|
||||
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:
|
||||
|
||||
1. Add new state categories to `characterState.js`
|
||||
2. Update `characterPromptBuilder.js` to instruct the LLM about new states
|
||||
3. Update `characterParser.js` to parse new state formats
|
||||
4. Update `characterStateRenderer.js` to 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:
|
||||
|
||||
1. Check the console for error messages
|
||||
2. Verify your LLM model supports structured outputs
|
||||
3. Review the prompt and parsing logic
|
||||
4. 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!** 🎭✨
|
||||
Reference in New Issue
Block a user