feat: add Equipment tab with slot-type validation

Add a new Equipment tab to manage player gear and stat bonuses.

Features:
- 19 equipment slots across 8 categories (helmet, necklace, body armor, gloves, pants, shoes, rings, accessories)
- Type-to-slot validation: each type has max equipped limits (1 helmet, 10 rings, 3 accessories, etc.)
- Auto-slot assignment: equipping a ring fills the first available ring slot
- Stat bonuses from equipped items display on RPG attributes (e.g. STR 10 +2)
- Create/edit modal with stat checkboxes per RPG attribute
- Inventory list for unequipped items

Architecture:
- Shared constants in src/systems/equipment/constants.js
- Category-based types (Ring, Accessory) with auto-slot assignment
- v7 migration converts legacy slot-specific types to generic categories
- Full i18n support for all UI strings

Files:
- New: src/systems/equipment/constants.js
- New: src/systems/interaction/equipmentActions.js
- New: src/systems/rendering/equipment.js
- Modified: state.js, persistence.js, template.html, index.js
- Modified: userStats.js, desktop.js, mobile.js, layout.js, modals.js
- Modified: apiClient.js, sillytavern.js, style.css, en.json
This commit is contained in:
2026-07-03 11:11:23 +02:00
parent 38fb3d8c51
commit 10cfe581ac
16 changed files with 1428 additions and 17 deletions
+64 -1
View File
@@ -23,7 +23,7 @@ import { validateStoredInventory, cleanItemString } from '../utils/security.js';
import { migrateToV3JSON } from '../utils/jsonMigration.js';
const extensionName = 'third-party/rpg-companion-sillytavern';
const CURRENT_SETTINGS_VERSION = 5;
const CURRENT_SETTINGS_VERSION = 7;
const DEFAULT_USER_STATS = {
health: 100,
@@ -616,6 +616,69 @@ export function loadSettings() {
settingsChanged = true;
}
// Migration to version 6: Add equipment data structure
if (currentVersion < 6) {
// console.log('[RPG Companion] Migrating settings to version 6 (adding equipment)');
if (!extensionSettings.userStats.equipment) {
extensionSettings.userStats.equipment = {
items: [],
slots: {
helmet: null,
ring1: null,
ring2: null,
ring3: null,
ring4: null,
ring5: null,
ring6: null,
ring7: null,
ring8: null,
ring9: null,
ring10: null,
necklace: null,
bodyArmor: null,
pants: null,
shoes: null,
gloves: null,
accessory1: null,
accessory2: null,
accessory3: null
}
};
}
if (extensionSettings.showEquipment === undefined) {
extensionSettings.showEquipment = true;
}
extensionSettings.settingsVersion = 6;
settingsChanged = true;
}
// Migration to version 7: Convert equipment types to generic categories + add item.slot
if (currentVersion < 7) {
const equipment = extensionSettings.userStats?.equipment;
if (equipment) {
const typeMap = {
ring1: 'ring', ring2: 'ring', ring3: 'ring', ring4: 'ring', ring5: 'ring',
ring6: 'ring', ring7: 'ring', ring8: 'ring', ring9: 'ring', ring10: 'ring',
accessory1: 'accessory', accessory2: 'accessory', accessory3: 'accessory'
};
for (const item of equipment.items || []) {
if (!item.slot && equipment.slots) {
for (const [slotId, itemId] of Object.entries(equipment.slots)) {
if (itemId === item.id) {
item.slot = slotId;
break;
}
}
}
if (item.type && typeMap[item.type]) {
item.type = typeMap[item.type];
}
}
}
extensionSettings.settingsVersion = 7;
settingsChanged = true;
}
// Normalize additive settings without introducing another schema bump.
if (!extensionSettings.thoughtsInChatStyle) {
extensionSettings.thoughtsInChatStyle = 'corner';