aether-shards/.cursor/rules/logic/Marketplace/RULE.md
Matthew Mone a9d4064dd8 Implement Marketplace system and enhance game state management
- Introduce the Marketplace system, managed by MarketManager, to facilitate buying and selling items, enhancing player engagement and resource management.
- Update GameStateManager to integrate the new MarketManager, ensuring seamless data handling and persistence for market transactions.
- Add specifications for the Marketplace UI, detailing layout, functionality, and conditions of acceptance to ensure a robust user experience.
- Refactor existing components to support the new marketplace features, including dynamic inventory updates and currency management.
- Enhance testing coverage for the MarketManager and MarketplaceScreen to validate functionality and integration within the game architecture.
2025-12-31 13:52:59 -08:00

8.4 KiB

description globs alwaysApply
Marketplace system architecture - the Gilded Bazaar for buying and selling items in the Hub src/managers/MarketManager.js, src/ui/screens/MarketplaceScreen.js, src/core/Persistence.js false

Marketplace System Rule

The Marketplace (The Gilded Bazaar) is the primary gold sink and progression accelerator in the Hub. It allows players to purchase items with currency earned from missions and sell items for buyback.

1. System Architecture

The Marketplace is managed by MarketManager, a singleton logic controller instantiated by GameStateManager.

Integration Flow

  1. Mission Complete: MissionManager dispatches mission-victory event. MarketManager listens and sets needsRefresh flag.
  2. Hub Entry: Player enters Hub. MarketManager checks needsRefresh flag. If true, generates new stock based on tier and saves immediately.
  3. UI Render: HubScreen passes the MarketManager instance to the <marketplace-screen> component.

Persistence

  • Market state (stock, buyback queue) is saved to IndexedDB (market_state store) to prevent players from reloading to re-roll shop inventory.
  • Stock generation is deterministic based on tier and generation timestamp.
  • Each transaction (buy/sell) immediately persists state to prevent duplication or currency desync.

2. Data Model

// src/managers/MarketManager.js (JSDoc types)

/**
 * @typedef {Object} MarketItem
 * @property {string} id - Unique Stock ID (e.g. "STOCK_001")
 * @property {string} defId - Reference to ItemRegistry (e.g. "ITEM_RUSTY_BLADE")
 * @property {string} type - ItemType (cached for filtering)
 * @property {string} rarity - Rarity (cached for sorting/styling)
 * @property {number} price - Purchase price
 * @property {number} discount - 0.0 to 1.0 (percent off)
 * @property {boolean} purchased - If true, show as "Sold Out"
 * @property {Object} [instanceData] - If this is a buyback, store the ItemInstance here
 */

/**
 * @typedef {Object} MarketState
 * @property {string} generationId - Timestamp or Mission Count when this stock was generated
 * @property {MarketItem[]} stock - The active inventory for sale
 * @property {MarketItem[]} buyback - Items sold by the player this session (can be bought back)
 * @property {string} [specialOffer] - ID of a specific item (Daily Deal)
 */

/**
 * @typedef {Object} StockTable
 * @property {number} minItems - Minimum items to generate
 * @property {number} maxItems - Maximum items to generate
 * @property {Object} rarityWeights - Rarity distribution weights
 * @property {string[]} allowedTypes - Item types this merchant can sell
 */

3. Logic & Algorithms

A. Stock Generation (generateStock(tier))

Triggered only when a run is completed and player enters Hub.

Tier 1 (Early Game - Before Mission 3):

  • Smith:** 5 Common Weapons, 3 Common Armor
  • Alchemist: 5 Potions (Stacked), 2 Grenades (when consumables are available)

Tier 2 (Mid Game - After Mission 3):

  • Weights: Common (60%), Uncommon (30%), Rare (10%), Ancient (0%)
  • Scavenger: Unlocks. Sells 3 "Mystery Box" items (Unidentified Relics) - Future feature

Algorithm:

  1. Filter ItemRegistry by allowedTypes and current tier availability
  2. Roll RNG against rarityWeights to determine rarity
  3. Select random Item ID from filtered pool matching selected rarity
  4. Calculate Price: BaseValue * (1 + RandomVariance(±10%))
  5. Create MarketItem with unique stock ID
  6. Save state to IndexedDB immediately

B. Transaction Processing

Transactions must be atomic to prevent item duplication or currency desync.

Buy Logic (buyItem(stockId)):

  1. Check Wallet >= Price (validate currency)
  2. Deduct Currency from hubStash.currency.aetherShards
  3. Generate Instance: Create a new ItemInstance with a fresh UID (ITEM_{TIMESTAMP}_{RANDOM})
  4. Add to InventoryManager.hubStash
  5. Mark MarketItem as purchased: true
  6. Save State to IndexedDB
  7. Return true on success, false on failure

Sell Logic (sellItem(itemUid)):

  1. Find ItemInstance in hubStash by UID
  2. Remove ItemInstance from hubStash
  3. Calculate Value: BasePrice * 0.25 (25% of base value)
  4. Add Currency to hubStash.currency.aetherShards
  5. Create Buyback: Convert instance to MarketItem and add to buyback array (Limit 10, oldest removed if full)
  6. Save State to IndexedDB
  7. Return true on success, false on failure

C. Merchant Types

Merchants filter stock by item type:

  • SMITH: ["WEAPON", "ARMOR"]
  • TAILOR: ["ARMOR"]
  • ALCHEMIST: ["CONSUMABLE", "UTILITY"]
  • SCAVENGER: ["RELIC", "UTILITY"] (Tier 2+)
  • BUYBACK: Shows all items in buyback array

4. UI Implementation (LitElement)

Component: src/ui/screens/MarketplaceScreen.js

Visual Layout

  • Grid Container: CSS Grid 250px 1fr (Sidebar | Main Content)
  • Sidebar (Merchants): Vertical tabs with icons
    • [⚔️ Smith]
    • [🧥 Tailor]
    • [⚗️ Alchemist]
    • [♻️ Buyback]
  • Main Content:
    • Filter Bar: "All", "Weapons", "Armor", "Utility", "Consumables"
    • Item Grid: Flex-wrap container of Item Cards with voxel-style borders
  • Modal: Purchase confirmation dialog ("Buy for 50?")

Interactive States

  • Affordable: Price Text is Gold/Green, button enabled
  • Unaffordable: Price Text is Red, button disabled
  • Sold Out: Card is dimmed (opacity 0.5), overlay text "SOLD", cursor not-allowed

Rarity Styling

Item cards use border colors to indicate rarity:

  • COMMON: #888 (Gray)
  • UNCOMMON: #00ff00 (Green)
  • RARE: #0088ff (Blue)
  • ANCIENT: #ff00ff (Magenta)

Events

  • market-closed: Dispatched when player clicks close button. HubScreen listens and closes overlay.

5. Integration Points

A. GameStateManager

  • MarketManager is instantiated in GameStateManager constructor
  • Requires: persistence, itemRegistry, hubInventoryManager, missionManager
  • Initialized in GameStateManager.init() after persistence is ready
  • checkRefresh() is called when transitioning to STATE_MAIN_MENU

B. HubScreen

  • When activeOverlay === "MARKET", renders <marketplace-screen> component
  • Passes marketManager instance as property
  • Listens for market-closed event to close overlay

C. Persistence

  • Market state stored in IndexedDB Market store (version 3+)
  • Key: "market_state"
  • Saved after every transaction (buy/sell) and stock generation

6. Conditions of Acceptance (CoA)

CoA 1: Persistence Integrity

  • Buying an item, saving, and reloading the page must result in:
    • Item being in hubStash inventory
    • Shop showing "Sold Out" for that item
    • Stock must not change upon reload (same generationId)
  • Test: Buy item → Save → Reload → Verify item in stash and stock unchanged

CoA 2: Currency Math

  • Buying an item costs exactly the listed price (no rounding errors)
  • Selling an item refunds exactly BasePrice * 0.25 (rounded down)
  • Buyback allows repurchasing a sold item for the exact amount it was sold for (undo logic)
  • Test: Buy for 50 → Wallet decreases by 50. Sell for 12 → Wallet increases by 12. Buyback costs 12.

CoA 3: Atomic Transactions

  • If currency deduction fails, item should not be added to inventory
  • If item removal fails, currency should not be added
  • State must be consistent after any transaction (success or failure)
  • Test: Attempt buy with insufficient funds → Verify no item added, currency unchanged

CoA 4: Stock Generation

  • Tier 1 stock must contain only Common items
  • Tier 2 stock must follow rarity weights (60% Common, 30% Uncommon, 10% Rare)
  • Stock must be generated only when needsRefresh is true and player enters Hub
  • Test: Complete mission → Enter Hub → Verify new stock generated with correct tier distribution

CoA 5: Buyback Limit

  • Buyback array must not exceed 10 items
  • When adding 11th item, oldest item must be removed
  • Test: Sell 11 items → Verify buyback contains only last 10 items

7. Future Enhancements (Optional)

  • Class Filtering: Visually flag items that cannot be equipped by anyone in current Roster
  • Daily Deals: Special offers with discounts on specific items
  • Scavenger Merchant: Sells unidentified relics (Mystery Boxes) that must be identified
  • Price Negotiation: Skill-based haggling system (future feature)