aether-shards/specs/Inventory.spec.md
Matthew Mone 68646f7f7b Add integration strategy for Inventory System in game engine
Define connections between the Inventory System and other components, including Game Loop interactions for looting, character sheet management, combat system consumables, and persistence requirements for saving game state. Outline triggers, logic, display, and actions for each integration point to enhance overall gameplay experience.
2025-12-27 16:54:12 -08:00

190 lines
6.5 KiB
Markdown

# **Inventory System Specification**
This document defines the architecture for item management, covering individual Explorer loadouts and the shared Party/Hub storage.
## **1. System Overview**
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_.
## **2. 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".
## **3. 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
}
```
## **4. 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();
}
```
## **5. 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).
---
## **6. Integration Strategy (Wiring)**
This section defines where the Inventory System connects to the rest of the engine.
### **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`.