aether-shards/.cursor/rules/data/Inventory/RULE.md

190 lines
6.5 KiB
Markdown
Raw Normal View History

---
description: Inventory system architecture - item management for individual Explorer loadouts and shared storage
globs: src/managers/InventoryManager.js, src/types/Inventory.ts, src/ui/components/InventoryUI.js
alwaysApply: false
---
# **Inventory System Rule**
The Inventory system operates in two distinct contexts:
1. **Run Context (The Expedition):**
- **Unit Loadout:** Active gear affecting stats. Locked during combat (usually), editable during rest
- **Run Stash (The Bag):** Temporary storage for loot found during the run. Infinite (or high) capacity
- **Rule:** If the squad wipes, the _Run Stash_ is lost. Only equipped gear might be recovered (depending on difficulty settings)
2. **Hub Context (The Armory):**
- **Master Stash:** Persistent storage for all unequipped items
- **Management:** Players move items between the Master Stash and Unit Loadouts to prepare for the next run
- **Extraction:** Upon successful run completion, _Run Stash_ contents are merged into _Master Stash_
## **1. Visual Description (UI)**
### **A. Unit Loadout (The Paper Doll)**
- **Visual:** A silhouette or 3D model of the character
- **Slots:**
- **Primary Hand:** Weapon (Sword, Staff, Wrench)
- **Off-Hand:** Shield, Focus, Tool, or 2H (occupies both)
- **Body:** Armor (Plate, Robes, Vest)
- **Accessory/Relic:** Stat boosters or Passive enablers
- **Belt (2 Slots):** Consumables (Potions, Grenades) usable in combat via Bonus Action
- **Interaction:** Drag-and-drop from Stash to Slot. Invalid slots highlight Red. Valid slots highlight Green
### **B. The Stash (Grid View)**
- **Visual:** A grid of item tiles on the right side of the screen
- **Filters:** Tabs for [All] [Weapons] [Armor] [Utility] [Consumables]
- **Sorting:** By Rarity (Color Coded border) or Tier
- **Context Menu:** Right-click an item to "Equip to Active Unit" or "Salvage/Sell"
## **2. TypeScript Interfaces (Data Model)**
```typescript
// src/types/Inventory.ts
export type ItemType =
| "WEAPON"
| "ARMOR"
| "RELIC"
| "UTILITY"
| "CONSUMABLE"
| "MATERIAL";
export type Rarity = "COMMON" | "UNCOMMON" | "RARE" | "ANCIENT";
export type SlotType = "MAIN_HAND" | "OFF_HAND" | "BODY" | "ACCESSORY" | "BELT";
/**
* A specific instance of an item.
* Allows for RNG stats or durability in the future.
*/
export interface ItemInstance {
uid: string; // Unique Instance ID (e.g. "ITEM_12345_A")
defId: string; // Reference to static registry (e.g. "ITEM_RUSTY_BLADE")
isNew: boolean; // For UI "New!" badges
quantity: number; // For stackables (Potions/Materials)
}
/**
* The inventory of a single character.
*/
export interface UnitLoadout {
mainHand: ItemInstance | null;
offHand: ItemInstance | null;
body: ItemInstance | null;
accessory: ItemInstance | null;
belt: [ItemInstance | null, ItemInstance | null]; // Fixed 2 slots
}
/**
* The shared storage (Run Bag or Hub Stash).
*/
export interface InventoryStorage {
id: string; // "RUN_LOOT" or "HUB_VAULT"
items: ItemInstance[]; // Unordered list
currency: {
aetherShards: number;
ancientCores: number;
};
}
/**
* Data payload for moving items.
*/
export interface TransferRequest {
itemUid: string;
sourceContainer: "STASH" | "UNIT_LOADOUT";
targetContainer: "STASH" | "UNIT_LOADOUT";
targetSlot?: SlotType; // If moving to Unit
slotIndex?: number; // For Belt slots (0 or 1)
unitId?: string; // Which unit owns the loadout
}
```
## **3. Logic & Rules**
### **A. Equipping Items**
1. **Validation:**
- Check `Item.requirements` (Class Lock, Min Stats) against `Unit.baseStats`
- Check Slot Compatibility (Can't put Armor in Weapon slot)
- _Two-Handed Logic:_ If equipping a 2H weapon, unequip Off-Hand automatically
2. **Swapping:**
- If target slot is occupied, move the existing item to Stash (or Swap if source was another slot)
3. **Stat Recalculation:**
- Trigger `unit.recalculateStats()` immediately
### **B. Stacking**
- **Equipment:** Non-stackable. Each Sword is a unique instance
- **Consumables/Materials:** Stackable up to 99
- **Logic:** When adding a Potion to Stash, check if `defId` exists. If yes, `quantity++`. If no, create new entry
### **C. The Extraction (End of Run)**
```js
function finalizeRun(runInventory, hubInventory) {
// 1. Transfer Currency
hubInventory.currency.shards += runInventory.currency.shards;
// 2. Transfer Items
for (let item of runInventory.items) {
hubInventory.addItem(item);
}
// 3. Clear Run Inventory
runInventory.clear();
}
```
## **4. Conditions of Acceptance (CoA)**
**CoA 1: Class Restrictions**
- Attempting to equip a "Tinker Only" item on a "Vanguard" must fail
- The UI should visually dim incompatible items when a unit is selected
**CoA 2: Stat Updates**
- Equipping a `+5 Attack` sword must immediately update the displayed Attack stat in the Character Sheet
- Unequipping it must revert the stat
**CoA 3: Belt Logic**
- Using a Consumable in combat (via `ActionSystem`) must reduce its quantity
- If quantity reaches 0, the item reference is removed from the Belt slot
**CoA 4: Persistence**
- Saving the game must serialize the `InventoryStorage` array correctly
- Loading the game must restore specific item instances (not just generic definitions)
## **5. Integration Strategy (Wiring)**
### **A. Game Loop (Looting)**
- **Trigger:** Player unit moves onto a tile with a Loot Chest / Item Drop
- **Logic:** `GameLoop` detects collision -> calls `InventoryManager.runStash.addItem(foundItem)`
- **Visual:** `VoxelManager` removes the chest model. UI shows "Item Acquired" toast
### **B. Character Sheet (Management)**
- **Trigger:** Player opens Character Sheet -> Inventory Tab
- **Logic:** The UI Component imports `InventoryManager`
- **Display:** It renders `InventoryManager.runStash.items` (if in dungeon) or `hubStash.items` (if in hub)
- **Action:** Dragging an item to a slot calls `InventoryManager.equipItem(activeUnit, itemUid, slot)`
### **C. Combat System (Consumables)**
- **Trigger:** Player selects a "Potion" from the Combat HUD Action Bar
- **Logic:**
1. Check `unit.equipment.belt` for the item
2. Execute Effect (Heal)
3. Call `InventoryManager.consumeItem(unit, slotIndex)`
4. Update Unit Inventory state
### **D. Persistence (Saving)**
- **Trigger:** `GameStateManager.saveRun()` or `saveRoster()`
- **Logic:** The `Explorer` class's `equipment` object and the `InventoryManager`'s `runStash` must be serialized to JSON
- **Requirement:** Ensure `ItemInstance` objects are saved with their specific `uid` and `quantity`, not just `defId`