6.5 KiB
6.5 KiB
| description | globs | alwaysApply |
|---|---|---|
| Inventory system architecture - item management for individual Explorer loadouts and shared storage | src/managers/InventoryManager.js, src/types/Inventory.ts, src/ui/components/InventoryUI.js | false |
Inventory System Rule
The Inventory system operates in two distinct contexts:
- 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)
- 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)
// 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
- Validation:
- Check
Item.requirements(Class Lock, Min Stats) againstUnit.baseStats - Check Slot Compatibility (Can't put Armor in Weapon slot)
- Two-Handed Logic: If equipping a 2H weapon, unequip Off-Hand automatically
- Check
- Swapping:
- If target slot is occupied, move the existing item to Stash (or Swap if source was another slot)
- Stat Recalculation:
- Trigger
unit.recalculateStats()immediately
- Trigger
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
defIdexists. If yes,quantity++. If no, create new entry
C. The Extraction (End of Run)
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 Attacksword 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
InventoryStoragearray 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:
GameLoopdetects collision -> callsInventoryManager.runStash.addItem(foundItem) - Visual:
VoxelManagerremoves 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) orhubStash.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:
- Check
unit.equipment.beltfor the item - Execute Effect (Heal)
- Call
InventoryManager.consumeItem(unit, slotIndex) - Update Unit Inventory state
- Check
D. Persistence (Saving)
- Trigger:
GameStateManager.saveRun()orsaveRoster() - Logic: The
Explorerclass'sequipmentobject and theInventoryManager'srunStashmust be serialized to JSON - Requirement: Ensure
ItemInstanceobjects are saved with their specificuidandquantity, not justdefId