aether-shards/specs/Marketplace.spec.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

4.9 KiB

Marketplace Specification: The Gilded Bazaar

This document defines the architecture, logic, and UI for the Hub Marketplace. It acts as the primary gold sink and progression accelerator.

1. System Architecture

The Marketplace is managed by a new singleton logic controller: MarketManager.

  • Location: src/managers/MarketManager.js
  • Owner: GameStateManager (instantiated alongside RosterManager).
  • Persistence: The current stock and buyback queue are saved to IndexedDB (market_state) to prevent players from reloading the game to re-roll shop inventory.

Integration Flow

  1. Mission Complete: MissionManager triggers an event. MarketManager listens and sets a needsRefresh flag.
  2. Hub Entry: Player enters Hub. MarketManager checks flag. If true, generates new stock and saves immediately.
  3. UI Render: HubScreen passes the MarketManager instance to the component.

2. TypeScript Interfaces (Data Model)

// src/types/Marketplace.ts

import { ItemType, Rarity, ItemInstance } from "./Inventory";

export type MerchantType = "SMITH" | "TAILOR" | "ALCHEMIST" | "SCAVENGER";

export interface MarketState {
  /** Timestamp or Mission Count when this stock was generated */
  generationId: string;
  /** The active inventory for sale */
  stock: MarketItem[];
  /** Items sold by the player this session (can be bought back) */
  buyback: MarketItem[]; // Price is usually equal to sell price
  /*_ Daily Deal or Special logic */
  specialOffer?: string; // ID of a specific item
}

export interface MarketItem {
  id: string; // Unique Stock ID (e.g. "STOCK_001")
  defId: string; // Reference to ItemRegistry (e.g. "ITEM_RUSTY_BLADE")
  type: ItemType; // Cached for filtering
  rarity: Rarity; // Cached for sorting/styling
  price: number;
  discount: number; // 0.0 to 1.0 (percent off)
  purchased: boolean; // If true, show as "Sold Out"

  /** If this is a specific instance (e.g. Buyback), store the data here */
  instanceData?: ItemInstance;
}

/** _ Configuration for what a merchant sells at a specific Game Stage.
 */
export interface StockTable {
  minItems: number;
  maxItems: number;
  rarityWeights: {
    COMMON: number;
    UNCOMMON: number;
    RARE: number;
    ANCIENT: number;
  };
  allowedTypes: ItemType[];
}

3. Logic & Algorithms

A. Stock Generation (generateStock(tier))

Triggered only when a run is completed.

Tier 1 (Early Game):

  • Smith: 5 Common Weapons, 3 Common Armor.
  • Alchemist: 5 Potions (Stacked), 2 Grenades.

Tier 2 (Mid Game - After Mission 3):

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

Algorithm:

  1. Filter ItemRegistry by allowedTypes and Tier.
  2. Roll RNG against rarityWeights.
  3. Select random Item ID.
  4. Calculate Price: BaseValue * (1 + RandomVariance(0.1)).
  5. Create MarketItem.

B. Transaction Processing (buyItem, sellItem)

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

Buy Logic:

  1. Check Wallet >= Price.
  2. Deduct Currency.
  3. Generate Instance: Create a new ItemInstance with a fresh UID (ITEM_{TIMESTAMP}).
  4. Add to InventoryManager.hubStash.
  5. Mark MarketItem as purchased: true.
  6. Save State.

Sell Logic:

  1. Remove ItemInstance from hubStash.
  2. Calculate Value (BasePrice * 0.25).
  3. Add Currency.
  4. Create Buyback: Convert instance to MarketItem and add to buyback array (Limit 10).
  5. Save State.

4. UI Implementation (LitElement)

Component: src/ui/screens/MarketplaceScreen.js

Visual Layout

  • Grid Container: CSS Grid 250px 1fr.
  • Sidebar (Merchants): Vertical tabs.
    • [⚔️ Smith]
    • [🧥 Tailor]
    • [⚗️ Alchemist]
    • [♻️ Buyback]
  • Main Content:
    • Filter Bar: "Show All", "Weapons Only", etc.
    • Item Grid: Flex-wrap container of Item Cards.
  • Player Panel (Right overlay or bottom slide-up):
    • Shows current Inventory. Drag-and-drop to "Sell Zone" or Right-Click to sell.

Interactive States

  • Affordable: Price Text is White/Green.
  • Unaffordable: Price Text is Red. Button Disabled.
  • Sold Out: Card is dimmed, overlay text "SOLD".

5. Conditions of Acceptance (CoA)

CoA 1: Persistence Integrity

  • Buying an item, saving, and reloading the page must result in the item being in the Inventory and the Shop still showing "Sold Out".
  • The shop stock must not change upon reload.

CoA 2: Currency Math

  • Buying an item costs exactly the listed price.
  • Selling an item refunds exactly the calculated sell price.
  • Buyback allows repurchasing a sold item for the exact amount it was sold for (Undo button logic).

CoA 3: Class Filtering

  • (Optional Polish) The shop should visually flag items that cannot be equipped by anyone in the current Roster (e.g. "No Sapper recruited").