Make RPG attributes (STR/DEX/etc) customizable and editable

- Replace showRPGAttributes boolean with rpgAttributes array in trackerConfig
- Add RPG Attributes section in Edit Trackers with add/remove/rename/toggle
- Dynamically generate attribute display from config in userStats.js
- Add migration from old showRPGAttributes to new rpgAttributes array
- Initialize new attributes with default value of 10 in classicStats
- Default attributes: STR, DEX, CON, INT, WIS, CHA (all enabled)
This commit is contained in:
Spicy_Marinara
2025-11-03 11:09:42 +01:00
parent 883212b5e9
commit f20710f5a3
7 changed files with 260 additions and 59 deletions
+72
View File
@@ -0,0 +1,72 @@
{
"chat_completion_source": "custom",
"openai_model": "gpt-5-2025-08-07",
"claude_model": "claude-sonnet-4-0",
"openrouter_model": "anthropic/claude-sonnet-4.5",
"openrouter_use_fallback": false,
"openrouter_group_models": true,
"openrouter_sort_models": "context_length",
"openrouter_providers": [],
"openrouter_allow_fallbacks": false,
"openrouter_middleout": "off",
"ai21_model": "jamba-1.5-large",
"mistralai_model": "mistral-large-latest",
"cohere_model": "command-r-plus",
"perplexity_model": "",
"groq_model": "llama3-70b-8192",
"xai_model": "grok-3-beta",
"pollinations_model": "openai",
"aimlapi_model": "gpt-4o-mini-2024-07-18",
"electronhub_model": "gpt-4o-mini",
"electronhub_sort_models": "alphabetically",
"electronhub_group_models": false,
"moonshot_model": "kimi-latest",
"fireworks_model": "accounts/fireworks/models/kimi-k2-instruct",
"cometapi_model": "gpt-4o",
"zai_model": "glm-4.6",
"custom_model": "claude-opus-4-1-20250805-thinking-16k-v2",
"custom_prompt_post_processing": "semi",
"google_model": "gemini-2.0-flash-thinking-exp-01-21",
"vertexai_model": "gemini-2.0-flash-001",
"nanogpt_model": "gpt-4o-mini",
"deepseek_model": "chatgpt-4o-latest",
"azure_api_version": "2024-02-15-preview",
"azure_openai_model": "",
"temperature": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
"top_p": 1,
"top_k": 0,
"top_a": 1,
"min_p": 0,
"repetition_penalty": 1,
"openai_max_context": 2000000,
"openai_max_tokens": 8192,
"wrap_in_quotes": false,
"names_behavior": -1,
"send_if_empty": "",
"impersonation_prompt": "Change of plans! Write a response as the user's {{user}} now. Match their style from the conversation history so far.",
"new_chat_prompt": "",
"new_group_chat_prompt": "",
"new_example_chat_prompt": "{{trim}}",
"continue_nudge_prompt": "Your last message got cut off. Continue writing it. Do not include any parts of the original one. Respond with a direct continuation only.",
"bias_preset_selected": "Marinara's Logit Bias",
"max_context_unlocked": false,
"wi_format": "{0}",
"scenario_format": "{{scenario}}",
"personality_format": "{{personality}}{{trim}}",
"group_nudge_prompt": "",
"stream_openai": true,
"show_external_models": false,
"assistant_prefill": "",
"assistant_impersonation": "",
"claude_use_sysprompt": true,
"use_makersuite_sysprompt": false,
"squash_system_messages": true,
"image_inlining": true,
"inline_image_quality": "high",
"bypass_status_check": false,
"continue_prefill": false,
"seed": -1,
"n": 1
}
+30
View File
@@ -0,0 +1,30 @@
[
{
"scriptName": "RPG: Clean HTML (From Outgoing Prompt)",
"findRegex": "/\\s?<(?!\\!--)(?:\"[^\"]*\"|'[^']*'|[^'\">])*>/g",
"replaceString": "",
"trimStrings": [],
"placement": [2],
"disabled": false,
"markdownOnly": false,
"promptOnly": true,
"runOnEdit": true,
"substituteRegex": 0,
"minDepth": null,
"maxDepth": null
},
{
"scriptName": "RPG: Clean Tracker Blocks (Keeping Last)",
"findRegex": "/```\\n[\\s\\S]*?\\n---\\n[\\s\\S]*?```\\n+/g",
"replaceString": "",
"trimStrings": [],
"placement": [1, 2],
"disabled": false,
"markdownOnly": false,
"promptOnly": true,
"runOnEdit": true,
"substituteRegex": 0,
"minDepth": 3,
"maxDepth": null
}
]
+11
View File
@@ -118,6 +118,9 @@ import { ensureHtmlCleaningRegex, detectConflictingRegexScripts } from './src/sy
import { setupMemoryRecollectionButton, updateMemoryRecollectionButton } from './src/systems/features/memoryRecollection.js';
import { initLorebookLimiter } from './src/systems/features/lorebookLimiter.js';
// Utility modules
import { importAllDefaults } from './src/utils/importDefaults.js';
// Integration modules
import {
commitTrackerData,
@@ -601,6 +604,14 @@ jQuery(async () => {
// Non-critical - continue anyway
}
// Import default preset and regexes if user doesn't have them
try {
await importAllDefaults();
} catch (error) {
console.error('[RPG Companion] Failed to import defaults:', error);
// Non-critical - continue anyway
}
// Register all event listeners
try {
registerAllEvents({
+44 -1
View File
@@ -365,7 +365,14 @@ function migrateToTrackerConfig() {
extensionSettings.trackerConfig = {
userStats: {
customStats: [],
showRPGAttributes: true,
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 }
],
statusSection: {
enabled: true,
showMoodEmoji: true,
@@ -423,6 +430,42 @@ function migrateToTrackerConfig() {
}
}
// Migrate old showRPGAttributes boolean to rpgAttributes array
if (extensionSettings.trackerConfig.userStats.showRPGAttributes !== undefined) {
const shouldShow = extensionSettings.trackerConfig.userStats.showRPGAttributes;
extensionSettings.trackerConfig.userStats.rpgAttributes = [
{ id: 'str', name: 'STR', enabled: shouldShow },
{ id: 'dex', name: 'DEX', enabled: shouldShow },
{ id: 'con', name: 'CON', enabled: shouldShow },
{ id: 'int', name: 'INT', enabled: shouldShow },
{ id: 'wis', name: 'WIS', enabled: shouldShow },
{ id: 'cha', name: 'CHA', enabled: shouldShow }
];
delete extensionSettings.trackerConfig.userStats.showRPGAttributes;
console.log('[RPG Companion] Migrated showRPGAttributes to rpgAttributes array');
}
// Ensure rpgAttributes exists even if no migration was needed
if (!extensionSettings.trackerConfig.userStats.rpgAttributes) {
extensionSettings.trackerConfig.userStats.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 }
];
}
// Ensure all rpgAttributes have corresponding values in classicStats
if (extensionSettings.classicStats) {
for (const attr of extensionSettings.trackerConfig.userStats.rpgAttributes) {
if (extensionSettings.classicStats[attr.id] === undefined) {
extensionSettings.classicStats[attr.id] = 10;
}
}
}
// Migrate old presentCharacters structure to new format
if (extensionSettings.trackerConfig.presentCharacters) {
const pc = extensionSettings.trackerConfig.presentCharacters;
+9 -2
View File
@@ -71,8 +71,15 @@ export let extensionSettings = {
{ id: 'hygiene', name: 'Hygiene', enabled: true },
{ id: 'arousal', name: 'Arousal', enabled: true }
],
// RPG Attributes toggle
showRPGAttributes: true,
// RPG Attributes (customizable D&D-style attributes)
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 }
],
// Status section config
statusSection: {
enabled: true,
+27 -48
View File
@@ -86,7 +86,14 @@ export function renderUserStats() {
{ id: 'hygiene', name: 'Hygiene', enabled: true },
{ id: 'arousal', name: 'Arousal', enabled: true }
],
showRPGAttributes: true,
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 }
],
statusSection: { enabled: true, showMoodEmoji: true, customFields: ['Conditions'] },
skillsSection: { enabled: false, label: 'Skills' }
};
@@ -171,60 +178,32 @@ export function renderUserStats() {
html += '</div>'; // Close rpg-stats-left
// RPG Attributes section (conditionally rendered)
if (config.showRPGAttributes) {
// RPG Attributes section (dynamically generated from config)
const rpgAttributes = config.rpgAttributes || [];
const enabledAttributes = rpgAttributes.filter(attr => attr && attr.enabled && attr.name && attr.id);
if (enabledAttributes.length > 0) {
html += `
<div class="rpg-stats-right">
<div class="rpg-classic-stats">
<div class="rpg-classic-stats-grid">
<div class="rpg-classic-stat" data-stat="str">
<span class="rpg-classic-stat-label">STR</span>
`;
enabledAttributes.forEach(attr => {
const value = extensionSettings.classicStats[attr.id] !== undefined ? extensionSettings.classicStats[attr.id] : 10;
html += `
<div class="rpg-classic-stat" data-stat="${attr.id}">
<span class="rpg-classic-stat-label">${attr.name}</span>
<div class="rpg-classic-stat-buttons">
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="str"></button>
<span class="rpg-classic-stat-value">${extensionSettings.classicStats.str}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="str">+</button>
</div>
</div>
<div class="rpg-classic-stat" data-stat="dex">
<span class="rpg-classic-stat-label">DEX</span>
<div class="rpg-classic-stat-buttons">
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="dex"></button>
<span class="rpg-classic-stat-value">${extensionSettings.classicStats.dex}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="dex">+</button>
</div>
</div>
<div class="rpg-classic-stat" data-stat="con">
<span class="rpg-classic-stat-label">CON</span>
<div class="rpg-classic-stat-buttons">
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="con"></button>
<span class="rpg-classic-stat-value">${extensionSettings.classicStats.con}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="con">+</button>
</div>
</div>
<div class="rpg-classic-stat" data-stat="int">
<span class="rpg-classic-stat-label">INT</span>
<div class="rpg-classic-stat-buttons">
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="int"></button>
<span class="rpg-classic-stat-value">${extensionSettings.classicStats.int}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="int">+</button>
</div>
</div>
<div class="rpg-classic-stat" data-stat="wis">
<span class="rpg-classic-stat-label">WIS</span>
<div class="rpg-classic-stat-buttons">
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="wis"></button>
<span class="rpg-classic-stat-value">${extensionSettings.classicStats.wis}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="wis">+</button>
</div>
</div>
<div class="rpg-classic-stat" data-stat="cha">
<span class="rpg-classic-stat-label">CHA</span>
<div class="rpg-classic-stat-buttons">
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="cha"></button>
<span class="rpg-classic-stat-value">${extensionSettings.classicStats.cha}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="cha">+</button>
<button class="rpg-classic-stat-btn rpg-stat-decrease" data-stat="${attr.id}"></button>
<span class="rpg-classic-stat-value">${value}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="${attr.id}">+</button>
</div>
</div>
`;
});
html += `
</div>
</div>
</div>
+67 -8
View File
@@ -127,7 +127,14 @@ function resetToDefaults() {
{ id: 'hygiene', name: 'Hygiene', enabled: true },
{ id: 'arousal', name: 'Arousal', enabled: true }
],
showRPGAttributes: true,
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 }
],
statusSection: {
enabled: true,
showMoodEmoji: true,
@@ -212,11 +219,31 @@ function renderUserStatsTab() {
html += '</div>';
html += '<button class="rpg-btn-secondary" id="rpg-add-stat"><i class="fa-solid fa-plus"></i> Add Custom Stat</button>';
// RPG Attributes toggle
html += '<div class="rpg-editor-toggle-row">';
html += `<input type="checkbox" id="rpg-show-rpg-attrs" ${config.showRPGAttributes ? 'checked' : ''}>`;
html += '<label for="rpg-show-rpg-attrs">Show RPG Attributes (STR, DEX, etc.)</label>';
// RPG Attributes section
html += '<h4><i class="fa-solid fa-dice-d20"></i> RPG Attributes</h4>';
html += '<div class="rpg-editor-stats-list" id="rpg-editor-attrs-list">';
const rpgAttributes = config.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 }
];
rpgAttributes.forEach((attr, index) => {
html += `
<div class="rpg-editor-stat-item" data-index="${index}">
<input type="checkbox" ${attr.enabled ? 'checked' : ''} class="rpg-attr-toggle" data-index="${index}">
<input type="text" value="${attr.name}" class="rpg-attr-name" data-index="${index}" placeholder="Attribute Name">
<button class="rpg-attr-remove" data-index="${index}" title="Remove attribute"><i class="fa-solid fa-trash"></i></button>
</div>
`;
});
html += '</div>';
html += '<button class="rpg-btn-secondary" id="rpg-add-attr"><i class="fa-solid fa-plus"></i> Add Attribute</button>';
// Status Section
html += '<h4><i class="fa-solid fa-face-smile"></i> Status Section</h4>';
@@ -287,9 +314,41 @@ function setupUserStatsListeners() {
extensionSettings.trackerConfig.userStats.customStats[index].name = $(this).val();
});
// RPG attributes toggle
$('#rpg-show-rpg-attrs').off('change').on('change', function() {
extensionSettings.trackerConfig.userStats.showRPGAttributes = $(this).is(':checked');
// Add attribute
$('#rpg-add-attr').off('click').on('click', function() {
if (!extensionSettings.trackerConfig.userStats.rpgAttributes) {
extensionSettings.trackerConfig.userStats.rpgAttributes = [];
}
const newId = 'attr_' + Date.now();
extensionSettings.trackerConfig.userStats.rpgAttributes.push({
id: newId,
name: 'NEW',
enabled: true
});
// Initialize value in classicStats if doesn't exist
if (extensionSettings.classicStats[newId] === undefined) {
extensionSettings.classicStats[newId] = 10;
}
renderUserStatsTab();
});
// Remove attribute
$('.rpg-attr-remove').off('click').on('click', function() {
const index = $(this).data('index');
extensionSettings.trackerConfig.userStats.rpgAttributes.splice(index, 1);
renderUserStatsTab();
});
// Toggle attribute
$('.rpg-attr-toggle').off('change').on('change', function() {
const index = $(this).data('index');
extensionSettings.trackerConfig.userStats.rpgAttributes[index].enabled = $(this).is(':checked');
});
// Rename attribute
$('.rpg-attr-name').off('blur').on('blur', function() {
const index = $(this).data('index');
extensionSettings.trackerConfig.userStats.rpgAttributes[index].name = $(this).val();
});
// Status section toggles