aether-shards/.cursor/rules/data/Inventory/RULE.md
Matthew Mone 2c86d674f4 Add mission debrief and procedural mission generation features
- Introduce the MissionDebrief component to display after-action reports, including XP, rewards, and squad status.
- Implement the MissionGenerator class to create procedural side missions, enhancing replayability and resource management.
- Update mission schema to include mission objects for INTERACT objectives, improving mission complexity.
- Enhance GameLoop and MissionManager to support new mission features and interactions.
- Add tests for MissionDebrief and MissionGenerator to ensure functionality and integration within the game architecture.
2026-01-01 16:08:54 -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