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
+6 -2
View File
@@ -23,6 +23,7 @@ import { buildInventorySummary } from '../generation/promptBuilder.js';
import { isItemLocked, setItemLock } from '../generation/lockManager.js';
import { updateFabWidgets } from '../ui/mobile.js';
import { getStatBarColors } from '../ui/theme.js';
import { getEquipmentBonuses } from '../interaction/equipmentActions.js';
/**
* Extracts the base name (before parentheses) and converts to snake_case for use as JSON key.
@@ -410,20 +411,23 @@ export function renderUserStats() {
const enabledAttributes = rpgAttributes.filter(attr => attr && attr.enabled && attr.name && attr.id);
if (enabledAttributes.length > 0) {
const equipmentBonuses = getEquipmentBonuses();
html += `
<div class="rpg-stats-right">
<div class="rpg-classic-stats">
<div class="rpg-classic-stats-grid">
`;
`;
enabledAttributes.forEach(attr => {
const value = extensionSettings.classicStats[attr.id] !== undefined ? extensionSettings.classicStats[attr.id] : 10;
const bonus = equipmentBonuses[attr.id] || 0;
const bonusHtml = bonus > 0 ? `<span class="rpg-classic-stat-bonus" title="Equipment bonus: +${bonus}"> +${bonus}</span>` : '';
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="${attr.id}"></button>
<span class="rpg-classic-stat-value">${value}</span>
<span class="rpg-classic-stat-value">${value}${bonusHtml}</span>
<button class="rpg-classic-stat-btn rpg-stat-increase" data-stat="${attr.id}">+</button>
</div>
</div>