aether-shards/.cursor/rules/data/Inventory/RULE.md
Matthew Mone 5c335b4b3c Add HubScreen and MissionBoard components for campaign management
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.
2025-12-31 10:49:26 -08:00

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:

  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)

// 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)

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