Introduce the HubScreen as the main interface for managing resources, units, and mission selection, integrating with the GameStateManager for dynamic data binding. Implement the MissionBoard component to display and select available missions, enhancing user interaction with mission details and selection logic. Update the GameStateManager to handle transitions between game states, ensuring a seamless experience for players. Add tests for HubScreen and MissionBoard to validate functionality and integration with the overall game architecture.
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