# **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)** ```typescript // 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").