fix: several issues
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import { getContext } from '../../../../../../extensions.js';
|
||||
import { chat, getCurrentChatDetails, characters, this_chid } from '../../../../../../../script.js';
|
||||
import { selected_group, getGroupMembers, getGroupChat } from '../../../../../../group-chats.js';
|
||||
import { extensionSettings, committedTrackerData, FEATURE_FLAGS } from '../../core/state.js';
|
||||
import { extensionSettings, committedTrackerData } from '../../core/state.js';
|
||||
import { generateSchemaExample } from '../../types/trackerData.js';
|
||||
|
||||
// Type imports
|
||||
@@ -18,12 +18,6 @@ import { generateSchemaExample } from '../../types/trackerData.js';
|
||||
*/
|
||||
export const DEFAULT_HTML_PROMPT = `If appropriate, include inline HTML, CSS, and JS segments whenever they enhance visual storytelling (e.g., for in-world screens, posters, books, letters, signs, crests, labels, etc.). Style them to match the setting's theme (e.g., fantasy, sci-fi), keep the text readable, and embed all assets directly (using inline SVGs only with no external scripts, libraries, or fonts). Use these elements freely and naturally within the narrative as characters would encounter them, including animations, 3D effects, pop-ups, dropdowns, websites, and so on. Do not wrap the HTML/CSS/JS in code fences!`;
|
||||
|
||||
/**
|
||||
* Default tracker instruction prompt text (legacy text format)
|
||||
* Use {{user}} as placeholder for the user's name (will be replaced at runtime)
|
||||
*/
|
||||
export const DEFAULT_TRACKER_PROMPT = `At the start of every reply, you must attach an update to the trackers in EXACTLY the same format as below, enclosed in separate Markdown code fences. Replace X with actual numbers (e.g., 69) and replace all [placeholders] with concrete in-world details that {{user}} perceives about the current scene and the present characters. Do NOT keep the brackets or placeholder text in your response. For example: [Location] becomes Forest Clearing, [Mood Emoji] becomes 😊. Consider the last trackers in the conversation (if they exist). Manage them accordingly and realistically; raise, lower, change, or keep the values unchanged based on the user's actions, the passage of time, and logical consequences (0% if the time progressed only by a few minutes, 1-5% normally, and above 5% only if a major time-skip/event occurs).`;
|
||||
|
||||
/**
|
||||
* Default JSON tracker instruction prompt text
|
||||
* Use {{user}} as placeholder for the user's name (will be replaced at runtime)
|
||||
@@ -158,12 +152,12 @@ function buildAttributesString() {
|
||||
|
||||
// Get enabled attributes from config
|
||||
const rpgAttributes = userStatsConfig?.rpgAttributes || [
|
||||
{ id: 'str', name: 'STR', enabled: true },
|
||||
{ id: 'dex', name: 'DEX', enabled: true },
|
||||
{ id: 'con', name: 'CON', enabled: true },
|
||||
{ id: 'int', name: 'INT', enabled: true },
|
||||
{ id: 'wis', name: 'WIS', enabled: true },
|
||||
{ id: 'cha', name: 'CHA', enabled: true }
|
||||
{ id: 'str', name: 'STR', description: '', enabled: true },
|
||||
{ id: 'dex', name: 'DEX', description: '', enabled: true },
|
||||
{ id: 'con', name: 'CON', description: '', enabled: true },
|
||||
{ id: 'int', name: 'INT', description: '', enabled: true },
|
||||
{ id: 'wis', name: 'WIS', description: '', enabled: true },
|
||||
{ id: 'cha', name: 'CHA', description: '', enabled: true }
|
||||
];
|
||||
|
||||
const enabledAttributes = rpgAttributes.filter(attr => attr && attr.enabled && attr.name && attr.id);
|
||||
@@ -180,327 +174,6 @@ function buildAttributesString() {
|
||||
return attributeParts.join(', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use generateJSONTrackerInstructions instead. This legacy text format
|
||||
* is kept for backwards compatibility with older LLM responses.
|
||||
*
|
||||
* Generates an example block showing current tracker states in markdown code blocks.
|
||||
* Uses COMMITTED data (not displayed data) for generation context.
|
||||
*
|
||||
* @returns {string} Formatted example text with tracker data in code blocks
|
||||
*/
|
||||
export function generateTrackerExample() {
|
||||
let example = '';
|
||||
|
||||
// Use COMMITTED data for generation context, not displayed data
|
||||
// Wrap each tracker section in markdown code blocks
|
||||
|
||||
// Build a combined stats/inventory/quests block if any are enabled
|
||||
let statsBlock = '';
|
||||
|
||||
if (extensionSettings.showUserStats && committedTrackerData.userStats) {
|
||||
statsBlock += committedTrackerData.userStats;
|
||||
}
|
||||
|
||||
// Add inventory example if enabled (and not already in userStats) - case-insensitive check
|
||||
if (extensionSettings.showInventory && extensionSettings.userStats?.inventory) {
|
||||
const inventorySummary = buildInventorySummary(extensionSettings.userStats.inventory);
|
||||
if (inventorySummary && inventorySummary !== 'None') {
|
||||
// Only add if not already present in userStats (case-insensitive)
|
||||
const statsBlockLower = statsBlock.toLowerCase();
|
||||
if (!statsBlockLower.includes('on person:') && !statsBlockLower.includes('inventory:')) {
|
||||
if (statsBlock) statsBlock += '\n';
|
||||
statsBlock += inventorySummary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add quests example if enabled - case-insensitive check
|
||||
if (extensionSettings.showQuests && extensionSettings.quests) {
|
||||
let questsText = '';
|
||||
if (extensionSettings.quests.main && extensionSettings.quests.main !== 'None') {
|
||||
questsText += `Main Quests: ${extensionSettings.quests.main}\n`;
|
||||
}
|
||||
if (extensionSettings.quests.optional && extensionSettings.quests.optional.length > 0) {
|
||||
const optionalQuests = extensionSettings.quests.optional.filter(q => q && q !== 'None').join(', ');
|
||||
if (optionalQuests) {
|
||||
questsText += `Optional Quests: ${optionalQuests}`;
|
||||
}
|
||||
}
|
||||
// Only add if not already present in userStats (case-insensitive)
|
||||
const statsBlockLower = statsBlock.toLowerCase();
|
||||
if (questsText && !statsBlockLower.includes('main quest') && !statsBlockLower.includes('optional quest')) {
|
||||
if (statsBlock) statsBlock += '\n';
|
||||
statsBlock += questsText;
|
||||
}
|
||||
}
|
||||
|
||||
if (statsBlock) {
|
||||
example += '```\n' + statsBlock.trim() + '\n```\n\n';
|
||||
}
|
||||
|
||||
if (extensionSettings.showInfoBox && committedTrackerData.infoBox) {
|
||||
example += '```\n' + committedTrackerData.infoBox + '\n```\n\n';
|
||||
}
|
||||
|
||||
if (extensionSettings.showCharacterThoughts && committedTrackerData.characterThoughts) {
|
||||
example += '```\n' + committedTrackerData.characterThoughts + '\n```';
|
||||
}
|
||||
|
||||
return example.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use generateJSONTrackerInstructions instead. This legacy text format
|
||||
* is kept for backwards compatibility with older LLM responses.
|
||||
*
|
||||
* Generates the instruction portion - format specifications and guidelines.
|
||||
*
|
||||
* @param {boolean} includeHtmlPrompt - Whether to include the HTML prompt (true for main generation, false for separate tracker generation)
|
||||
* @param {boolean} includeContinuation - Whether to include "After updating the trackers, continue..." instruction
|
||||
* @param {boolean} includeAttributes - Whether to include RPG attributes (false for separate tracker generation)
|
||||
* @returns {string} Formatted instruction text for the AI
|
||||
*/
|
||||
export function generateTrackerInstructions(includeHtmlPrompt = true, includeContinuation = true, includeAttributes = true) {
|
||||
const userName = getContext().name1;
|
||||
const classicStats = extensionSettings.classicStats;
|
||||
const trackerConfig = extensionSettings.trackerConfig;
|
||||
let instructions = '';
|
||||
|
||||
// Check if any trackers are enabled (including inventory, skills and quests as independent sections)
|
||||
const hasAnyTrackers = extensionSettings.showUserStats || extensionSettings.showInfoBox ||
|
||||
extensionSettings.showCharacterThoughts || extensionSettings.showSkills ||
|
||||
extensionSettings.showInventory || extensionSettings.showQuests;
|
||||
|
||||
// Only add tracker instructions if at least one tracker is enabled
|
||||
if (hasAnyTrackers) {
|
||||
// Universal instruction header - use custom prompt if set, otherwise use default
|
||||
const trackerPrompt = (extensionSettings.customTrackerPrompt || DEFAULT_TRACKER_PROMPT).replace(/\{\{user\}\}/g, userName);
|
||||
instructions += `\n${trackerPrompt}\n`;
|
||||
|
||||
// Check if we need a combined stats/inventory/quests code block
|
||||
const hasStatsBlock = extensionSettings.showUserStats || extensionSettings.showInventory || extensionSettings.showQuests;
|
||||
|
||||
if (hasStatsBlock) {
|
||||
const userStatsConfig = trackerConfig?.userStats;
|
||||
const enabledStats = userStatsConfig?.customStats?.filter(s => s && s.enabled && s.name) || [];
|
||||
|
||||
instructions += '```\n';
|
||||
|
||||
// Add user stats section if enabled
|
||||
if (extensionSettings.showUserStats) {
|
||||
instructions += `${userName}'s Stats\n`;
|
||||
instructions += '---\n';
|
||||
|
||||
// Add custom stats dynamically
|
||||
for (const stat of enabledStats) {
|
||||
instructions += `- ${stat.name}: X%\n`;
|
||||
}
|
||||
|
||||
// Add status section if enabled
|
||||
if (userStatsConfig?.statusSection?.enabled) {
|
||||
const statusFields = userStatsConfig.statusSection.customFields || [];
|
||||
const statusFieldsText = statusFields.map(f => `${f}`).join(', ');
|
||||
|
||||
if (userStatsConfig.statusSection.showMoodEmoji) {
|
||||
instructions += `Status: [Mood Emoji${statusFieldsText ? ', ' + statusFieldsText : ''}]\n`;
|
||||
} else if (statusFieldsText) {
|
||||
instructions += `Status: [${statusFieldsText}]\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add skills section if enabled in config AND NOT shown as separate section
|
||||
// When showSkills is true, skills are in their own tab and have their own code block
|
||||
if (userStatsConfig?.skillsSection?.enabled && !extensionSettings.showSkills) {
|
||||
const skillFields = userStatsConfig.skillsSection.customFields || [];
|
||||
const skillFieldsText = skillFields.map(f => `[${f}]`).join(', ');
|
||||
instructions += `Skills: [${skillFieldsText || 'Skill1, Skill2, etc.'}]\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add inventory format - independent of showUserStats
|
||||
if (extensionSettings.showInventory) {
|
||||
if (extensionSettings.useSimplifiedInventory) {
|
||||
// Simplified single-line inventory format
|
||||
instructions += 'Inventory: [Items currently carried/worn/owned, or "None"]\n';
|
||||
} else if (FEATURE_FLAGS.useNewInventory) {
|
||||
// Full v2 categorized inventory format
|
||||
instructions += 'On Person: [Items currently carried/worn, or "None"]\n';
|
||||
instructions += 'Stored - [Location Name]: [Items stored at this location]\n';
|
||||
instructions += '(Add multiple "Stored - [Location]:" lines as needed for different storage locations)\n';
|
||||
instructions += 'Assets: [Vehicles, property, major possessions, or "None"]\n';
|
||||
} else {
|
||||
// Legacy v1 format
|
||||
instructions += 'Inventory: [Clothing/Armor, Inventory Items (list of important items, or "None")]\\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Add quests section - independent of showUserStats
|
||||
if (extensionSettings.showQuests) {
|
||||
instructions += 'Main Quests: [Short title of the currently active main quest (for example, "Save the world"), or "None"]\n';
|
||||
instructions += 'Optional Quests: [Short titles of the currently active optional quests (for example, "Find Zandik\'s book"), or "None"]\n';
|
||||
}
|
||||
|
||||
instructions += '```\n\n';
|
||||
}
|
||||
|
||||
// Add separate skills section when showSkills is enabled
|
||||
if (extensionSettings.showSkills) {
|
||||
const skillsConfig = trackerConfig?.userStats?.skillsSection;
|
||||
const skillFields = skillsConfig?.customFields || [];
|
||||
|
||||
if (skillFields.length > 0) {
|
||||
instructions += '```\n';
|
||||
instructions += 'Skills\n';
|
||||
instructions += '---\n';
|
||||
|
||||
// Each skill category contains a list of abilities
|
||||
for (const skillName of skillFields) {
|
||||
if (extensionSettings.enableItemSkillLinks) {
|
||||
instructions += `${skillName}: [Abilities in this category, e.g. "Sword Fighting (Iron Sword), Parry" or "None"]\n`;
|
||||
} else {
|
||||
instructions += `${skillName}: [Abilities in this category, e.g. "Lockpicking, Sneaking" or "None"]\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (extensionSettings.enableItemSkillLinks) {
|
||||
instructions += '\n(Abilities from items use parentheses: "Skill (Item)". Remove if item is removed or unequipped.)\n';
|
||||
}
|
||||
|
||||
instructions += '```\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (extensionSettings.showInfoBox) {
|
||||
const infoBoxConfig = trackerConfig?.infoBox;
|
||||
const widgets = infoBoxConfig?.widgets || {};
|
||||
|
||||
instructions += '```\n';
|
||||
instructions += 'Info Box\n';
|
||||
instructions += '---\n';
|
||||
|
||||
// Add only enabled widgets
|
||||
if (widgets.date?.enabled) {
|
||||
instructions += 'Date: [Weekday, Month, Year]\n';
|
||||
}
|
||||
if (widgets.weather?.enabled) {
|
||||
instructions += 'Weather: [Weather Emoji, Forecast]\n';
|
||||
}
|
||||
if (widgets.temperature?.enabled) {
|
||||
const unit = widgets.temperature.unit === 'F' ? '°F' : '°C';
|
||||
instructions += `Temperature: [Temperature in ${unit}]\n`;
|
||||
}
|
||||
if (widgets.time?.enabled) {
|
||||
instructions += 'Time: [Time Start → Time End]\n';
|
||||
}
|
||||
if (widgets.location?.enabled) {
|
||||
instructions += 'Location: [Location]\n';
|
||||
}
|
||||
if (widgets.recentEvents?.enabled) {
|
||||
instructions += 'Recent Events: [Up to three past events leading to the ongoing scene (short descriptors with no details, for example, "last-night date with Mary")]\n';
|
||||
}
|
||||
|
||||
instructions += '```\n\n';
|
||||
}
|
||||
|
||||
if (extensionSettings.showCharacterThoughts) {
|
||||
const presentCharsConfig = trackerConfig?.presentCharacters;
|
||||
const enabledFields = presentCharsConfig?.customFields?.filter(f => f && f.enabled && f.name) || [];
|
||||
const relationshipFields = presentCharsConfig?.relationshipFields || [];
|
||||
const thoughtsConfig = presentCharsConfig?.thoughts;
|
||||
const characterStats = presentCharsConfig?.characterStats;
|
||||
const enabledCharStats = characterStats?.enabled && characterStats?.customStats?.filter(s => s && s.enabled && s.name) || [];
|
||||
|
||||
instructions += '```\n';
|
||||
instructions += 'Present Characters\n';
|
||||
instructions += '---\n';
|
||||
|
||||
// Build relationship placeholders (e.g., "Lover/Friend")
|
||||
const relationshipPlaceholders = relationshipFields
|
||||
.filter(r => r && r.trim())
|
||||
.map(r => `${r}`)
|
||||
.join('/');
|
||||
|
||||
// Build custom field placeholders (e.g., "[Appearance] | [Current Action]")
|
||||
const fieldPlaceholders = enabledFields
|
||||
.map(f => `[${f.name}]`)
|
||||
.join(' | ');
|
||||
|
||||
// Character block format
|
||||
instructions += `- [Name (do not include ${userName}; state "Unavailable" if no major characters are present in the scene)]\n`;
|
||||
|
||||
// Details line with emoji and custom fields
|
||||
if (fieldPlaceholders) {
|
||||
instructions += `Details: [Present Character's Emoji] | ${fieldPlaceholders}\n`;
|
||||
} else {
|
||||
instructions += `Details: [Present Character's Emoji]\n`;
|
||||
}
|
||||
|
||||
// Relationship line (only if relationships are enabled)
|
||||
if (relationshipPlaceholders) {
|
||||
instructions += `Relationship: [(choose one: ${relationshipPlaceholders})]\n`;
|
||||
}
|
||||
|
||||
// Stats line (if enabled)
|
||||
if (enabledCharStats.length > 0) {
|
||||
const statPlaceholders = enabledCharStats.map(s => `${s.name}: X%`).join(' | ');
|
||||
instructions += `Stats: ${statPlaceholders}\n`;
|
||||
}
|
||||
|
||||
// Thoughts line (if enabled)
|
||||
if (thoughtsConfig?.enabled) {
|
||||
const thoughtsName = thoughtsConfig.name || 'Thoughts';
|
||||
const thoughtsDescription = thoughtsConfig.description || 'Internal monologue (in first person POV, up to three sentences long)';
|
||||
instructions += `${thoughtsName}: [${thoughtsDescription}]\n`;
|
||||
}
|
||||
|
||||
instructions += `- … (Repeat the format above for every other present major character)\n`;
|
||||
|
||||
instructions += '```\n\n';
|
||||
}
|
||||
|
||||
// Only add continuation instruction if includeContinuation is true
|
||||
if (includeContinuation) {
|
||||
instructions += `After updating the trackers, continue directly from where the last message in the chat history left off. Ensure the trackers you provide naturally reflect and influence the narrative. Character behavior, dialogue, and story events should acknowledge these conditions when relevant, such as fatigue affecting the protagonist's performance, low hygiene influencing their social interactions, environmental factors shaping the scene, a character's emotional state coloring their responses, and so on. Remember, all bracketed placeholders (e.g., [Location], [Mood Emoji]) MUST be replaced with actual content without the square brackets.\n\n`;
|
||||
}
|
||||
|
||||
// Include attributes based on settings (only if includeAttributes is true)
|
||||
if (includeAttributes) {
|
||||
const alwaysSendAttributes = trackerConfig?.userStats?.alwaysSendAttributes;
|
||||
const shouldSendAttributes = alwaysSendAttributes || extensionSettings.lastDiceRoll;
|
||||
|
||||
if (shouldSendAttributes) {
|
||||
const attributesString = buildAttributesString();
|
||||
instructions += `${userName}'s attributes: ${attributesString}\n`;
|
||||
|
||||
// Add dice roll context if there was one
|
||||
if (extensionSettings.lastDiceRoll) {
|
||||
const roll = extensionSettings.lastDiceRoll;
|
||||
instructions += `${userName} rolled ${roll.total} on the last ${roll.formula} roll. Based on their attributes, decide whether they succeeded or failed the action they attempted.\n\n`;
|
||||
} else {
|
||||
instructions += `\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append HTML prompt if enabled AND includeHtmlPrompt is true
|
||||
if (extensionSettings.enableHtmlPrompt && includeHtmlPrompt) {
|
||||
// Add newlines only if we had tracker instructions
|
||||
if (hasAnyTrackers) {
|
||||
instructions += ``;
|
||||
} else {
|
||||
instructions += `\n`;
|
||||
}
|
||||
|
||||
// Use custom HTML prompt if set, otherwise use default
|
||||
const htmlPrompt = extensionSettings.customHtmlPrompt || DEFAULT_HTML_PROMPT;
|
||||
instructions += htmlPrompt;
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates JSON-based tracker instructions.
|
||||
* Creates a prompt asking the LLM to output structured JSON data.
|
||||
@@ -581,7 +254,7 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
|
||||
infoParts.push(` "temperature": "22${unit}"`);
|
||||
}
|
||||
if (widgets.location?.enabled) infoParts.push(' "location": "Forest Clearing"');
|
||||
if (widgets.recentEvents?.enabled) infoParts.push(' "recentEvents": "Brief summary of recent events"');
|
||||
if (widgets.recentEvents?.enabled) infoParts.push(' "recentEvents": ["Event 1", "Event 2"]');
|
||||
|
||||
if (infoParts.length > 0) {
|
||||
sections.push(' "infoBox": {\n' + infoParts.join(',\n') + '\n }');
|
||||
@@ -591,10 +264,12 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
|
||||
// Characters section
|
||||
if (showCharacters) {
|
||||
const charConfig = trackerConfig?.presentCharacters || {};
|
||||
let charExample = ' {\n "name": "Character Name"';
|
||||
let charExample = ' {\n "name": "Character Name",\n "emoji": "🧑"';
|
||||
|
||||
if (charConfig.relationshipFields?.length > 0) {
|
||||
charExample += `,\n "relationship": "${charConfig.relationshipFields[0]}"`;
|
||||
// Show allowed relationship values as explanation
|
||||
const allowedRelationships = charConfig.relationshipFields.join(' | ');
|
||||
charExample += `,\n "relationship": "(${allowedRelationships})"`;
|
||||
}
|
||||
|
||||
const enabledFields = charConfig.customFields?.filter(f => f.enabled) || [];
|
||||
@@ -603,6 +278,14 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
|
||||
charExample += `,\n "fields": {\n${fieldsJson}\n }`;
|
||||
}
|
||||
|
||||
// Character stats (Health, Arousal, etc.)
|
||||
const charStatsConfig = charConfig.characterStats;
|
||||
const enabledCharStats = charStatsConfig?.enabled && charStatsConfig?.customStats?.filter(s => s?.enabled && s?.name) || [];
|
||||
if (enabledCharStats.length > 0) {
|
||||
const statsJson = enabledCharStats.map(s => ` "${s.name}": 75`).join(',\n');
|
||||
charExample += `,\n "stats": {\n${statsJson}\n }`;
|
||||
}
|
||||
|
||||
if (charConfig.thoughts?.enabled) {
|
||||
charExample += ',\n "thoughts": "Character\'s inner thoughts in first person..."';
|
||||
}
|
||||
@@ -640,14 +323,21 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
|
||||
// Skills section
|
||||
if (showSkills) {
|
||||
const skillCategories = trackerConfig?.userStats?.skillsSection?.customFields || [];
|
||||
if (skillCategories.length > 0) {
|
||||
// Filter to only enabled categories and handle both old (string) and new (object) formats
|
||||
const enabledCategories = skillCategories.filter(cat => {
|
||||
if (typeof cat === 'string') return true;
|
||||
return cat.enabled !== false;
|
||||
});
|
||||
|
||||
if (enabledCategories.length > 0) {
|
||||
let skillsSection = ' "skills": {\n';
|
||||
const categoryExamples = skillCategories.map(cat => {
|
||||
const categoryExamples = enabledCategories.map(cat => {
|
||||
const catName = typeof cat === 'string' ? cat : cat.name;
|
||||
let skillExample = '{ "name": "Ability Name", "description": "What this ability does" }';
|
||||
if (enableItemSkillLinks) {
|
||||
skillExample = '{ "name": "Ability", "description": "Description", "grantedBy": "Item Name" }';
|
||||
}
|
||||
return ` "${cat}": [${skillExample}]`;
|
||||
return ` "${catName}": [${skillExample}]`;
|
||||
});
|
||||
skillsSection += categoryExamples.join(',\n');
|
||||
skillsSection += '\n }';
|
||||
@@ -675,10 +365,41 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
|
||||
instructions += '- Empty arrays [] for sections with no items\n';
|
||||
instructions += '- null for main quest if none active\n';
|
||||
|
||||
// Add stat descriptions if any have descriptions
|
||||
if (showStats) {
|
||||
const customStats = trackerConfig?.userStats?.customStats || [];
|
||||
const statsWithDesc = customStats.filter(s => s?.enabled && s?.description);
|
||||
if (statsWithDesc.length > 0) {
|
||||
instructions += '- Stat meanings:\n';
|
||||
statsWithDesc.forEach(stat => {
|
||||
instructions += ` • "${stat.name}": ${stat.description}\n`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (showSkills) {
|
||||
const skillsLabel = trackerConfig?.userStats?.skillsSection?.label || 'Skills';
|
||||
if (skillsLabel !== 'Skills') {
|
||||
instructions += `- The "skills" section represents "${skillsLabel}" in this context\n`;
|
||||
}
|
||||
|
||||
// Add skill category descriptions if any have descriptions
|
||||
const skillCategories = trackerConfig?.userStats?.skillsSection?.customFields || [];
|
||||
const categoriesWithDesc = skillCategories.filter(cat =>
|
||||
typeof cat === 'object' && cat.description && cat.enabled !== false
|
||||
);
|
||||
if (categoriesWithDesc.length > 0) {
|
||||
instructions += `- ${skillsLabel} categories:\n`;
|
||||
categoriesWithDesc.forEach(cat => {
|
||||
instructions += ` • "${cat.name}": ${cat.description}\n`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (enableItemSkillLinks) {
|
||||
instructions += '- Items can grant skills: add "grantsSkill": "Skill Name" to the item\n';
|
||||
instructions += '- Skills from items: add "grantedBy": "Item Name" to the skill\n';
|
||||
instructions += '- If an item is removed/lost, remove its linked skill too\n';
|
||||
instructions += '- Items can grant skills: add {"grantsSkill": "Skill Name"} to the item object\n';
|
||||
instructions += '- When a skill comes from an item, add {"grantedBy": "Item Name"} to that skill object\n';
|
||||
instructions += '- If an item is removed/lost, also remove any skill it granted\n';
|
||||
}
|
||||
|
||||
instructions += '\n';
|
||||
@@ -696,6 +417,16 @@ export function generateJSONTrackerInstructions(includeHtmlPrompt = true, includ
|
||||
if (shouldSendAttributes) {
|
||||
const attributesString = buildAttributesString();
|
||||
instructions += `${userName}'s attributes: ${attributesString}\n`;
|
||||
|
||||
// Add attribute descriptions if any have descriptions
|
||||
const rpgAttributes = trackerConfig?.userStats?.rpgAttributes || [];
|
||||
const attrsWithDesc = rpgAttributes.filter(a => a?.enabled && a?.description);
|
||||
if (attrsWithDesc.length > 0) {
|
||||
instructions += 'Attribute meanings:\n';
|
||||
attrsWithDesc.forEach(attr => {
|
||||
instructions += ` • ${attr.name}: ${attr.description}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
if (extensionSettings.lastDiceRoll) {
|
||||
const roll = extensionSettings.lastDiceRoll;
|
||||
@@ -862,17 +593,38 @@ export function generateRPGPromptText() {
|
||||
previousState.infoBox = extensionSettings.infoBoxData;
|
||||
}
|
||||
|
||||
// Characters
|
||||
// Characters - format to match schema
|
||||
if (extensionSettings.showCharacterThoughts && extensionSettings.charactersData?.length > 0) {
|
||||
previousState.characters = extensionSettings.charactersData;
|
||||
// Ensure characters match the expected schema format
|
||||
previousState.characters = extensionSettings.charactersData.map(char => {
|
||||
const formatted = { name: char.name };
|
||||
if (char.relationship) formatted.relationship = char.relationship;
|
||||
if (char.emoji) formatted.emoji = char.emoji;
|
||||
if (char.fields && Object.keys(char.fields).length > 0) formatted.fields = char.fields;
|
||||
if (char.stats && Object.keys(char.stats).length > 0) formatted.stats = char.stats;
|
||||
if (char.thoughts) formatted.thoughts = char.thoughts;
|
||||
return formatted;
|
||||
});
|
||||
}
|
||||
|
||||
// Inventory
|
||||
if (extensionSettings.showInventory) {
|
||||
if (extensionSettings.inventoryV3 && (extensionSettings.inventoryV3.onPerson?.length > 0 ||
|
||||
Object.keys(extensionSettings.inventoryV3.stored || {}).length > 0 ||
|
||||
extensionSettings.inventoryV3.assets?.length > 0)) {
|
||||
previousState.inventory = extensionSettings.inventoryV3;
|
||||
// Inventory - format to match schema (use "items" for simplified mode)
|
||||
if (extensionSettings.showInventory && extensionSettings.inventoryV3) {
|
||||
const inv = extensionSettings.inventoryV3;
|
||||
if (extensionSettings.useSimplifiedInventory) {
|
||||
// Simplified mode uses "items" key
|
||||
const items = inv.simplified || inv.onPerson || [];
|
||||
if (items.length > 0) {
|
||||
previousState.inventory = { items };
|
||||
}
|
||||
} else {
|
||||
// Full categorized mode
|
||||
if (inv.onPerson?.length > 0 || Object.keys(inv.stored || {}).length > 0 || inv.assets?.length > 0) {
|
||||
previousState.inventory = {
|
||||
onPerson: inv.onPerson || [],
|
||||
stored: inv.stored || {},
|
||||
assets: inv.assets || []
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user