# Lootbox UX Overhaul

**Date:** 2026-03-18
**Status:** Approved

## Problem

The current lootbox system has three UX issues:
1. **Pull results are visually flat** — a basic embed with plain text like "You found X!" with no visual differentiation between rarities.
2. **Shop loot table formatting is poor** — rewards are dumped as flat text lines grouped by rarity, with no visual hierarchy or scannability.
3. **No personality** — opening a lootbox feels like a database query response, not an event.

## Approach

**Full Components V2** — both pull results and shop loot tables use Discord's Components V2 system (containers, sections, media galleries, accent colors). No canvas image generation. Keeps the rendering approach consistent, simpler to build and maintain.

**Instant reveal** — no two-phase animations or button-driven reveals. The result appears immediately; excitement comes from visual quality and rarity theming.

**Loot table stays in shop only** — not shown in inventory or alongside pull results.

## Design: Pull Result

When a user opens a lootbox, the result is displayed as a Components V2 message (`flags: MessageFlags.IsComponentsV2`) with:

### Container
- **Accent color** driven by reward rarity:
  - `C` (Common): `#95A5A6` (gray)
  - `R` (Rare): `#3498DB` (blue)
  - `SR` (Super Rare): `#9B59B6` (purple)
  - `SSR`: `#F1C40F` (gold)
  - `CURRENCY`: `#2ECC71` (green)
  - `XP`: `#1ABC9C` (aqua)
  - `NOTHING`: `#636363` (dark gray)

### Header
- Subtle context line: source lootbox name (e.g., "Opened: Astral Crate")

### Section (main content)
- **Title format (item rewards):** `🌟 SSR — Celestial Blade` (emoji + rarity + item name)
- **Title format (currency):** `💰 You found 1,250 AU!`
- **Title format (XP):** `🔮 You gained 500 XP!`
- **Title format (nothing):** `💨 Empty...`
- **Description:** Item description for items, contextual message for currency/XP/nothing. For NOTHING results, use the custom `lootResult.message` from the handler (falls back to "You found nothing inside.")
- **Rarity badge:** Shown as text below description for item rewards (e.g., "SSR" + "×1 added to inventory")
- **Thumbnail accessory:** Item icon (via `iconUrl`) when available

### Media Gallery
- If the item has an `imageUrl` different from `iconUrl`, display it in a media gallery below the section for full art showcase.

### Other Effects
- If the lootbox item has non-lootbox effects that also produce results (e.g., a lootbox that also grants XP or a temp role), display these as an additional text display below the main result: "**Other Effects**\n• Gained 100 XP\n• Temporary Role granted for 30m"

### Edge Cases
- **Unknown rarity:** If a reward item's rarity is not in `RARITY_CONFIG`, fall back to Common (`C`) styling.
- **Missing icon:** If no `iconUrl` is available, omit the thumbnail accessory entirely (section without accessory).
- **Missing image:** If no `imageUrl` is available (or same as `iconUrl`), omit the media gallery.

## Design: Shop Loot Table

When viewing a lootbox item in the shop, the listing uses two containers:

### Container 1: Item Info
- **Accent color:** Based on lootbox item's own rarity
- **Section:** Item name (heading), description, price
- **Thumbnail accessory:** Item icon
- **Media gallery:** Item image if different from icon

### Container 2: Loot Table + Purchase
- **Accent color:** Discord blurple (`#5865F2`)
- **Header:** `🎁 Loot Table`
- **Tiers listed in descending rarity order:** SSR → SR → R → C → Currency → XP → Nothing
- **Each tier shows:**
  - Tier header: emoji + rarity label + aggregated chance percentage (sum of all items in that tier)
  - Items listed inline, comma-separated (e.g., "Shadow Dagger ×1, Arcane Focus ×1")
- **Separators** between tiers for visual scannability
- **Tiers with no items are omitted**
- **Purchase button:** Action row inside this container with "🛒 Purchase for {price} 🪙" button (success style)

## Files to Modify

| File | Change |
|------|--------|
| `bot/modules/inventory/inventory.view.ts` | Replace `getItemUseResultEmbed()` with new Components V2 pull result builder |
| `bot/modules/economy/shop.view.ts` | Rework `getShopListingMessage()` loot table section into two-container layout |
| `bot/commands/inventory/use.ts` | Update to send Components V2 message with `flags: MessageFlags.IsComponentsV2` instead of embed |
| `shared/modules/inventory/effect.handlers.ts` | Modify `handleLootbox` ITEM result to return both `iconUrl` and `imageUrl` separately (currently collapses into single `image` field) |

## Shared Constants

The rarity color map and title/emoji map are currently duplicated between `shop.view.ts` and `inventory.view.ts`. Consolidate into a shared location (either a new `shared/lib/rarity.ts` or add to existing `shared/lib/constants.ts`).

Also consolidate the `defaultName` helper (duplicated in both view files) into a shared utility.

Rarity display config:

```typescript
const RARITY_CONFIG: Record<string, { color: number; emoji: string; label: string }> = {
  C:        { color: 0x95A5A6, emoji: "📦", label: "Common" },
  R:        { color: 0x3498DB, emoji: "📦", label: "Rare" },
  SR:       { color: 0x9B59B6, emoji: "✨", label: "Super Rare" },
  SSR:      { color: 0xF1C40F, emoji: "🌟", label: "SSR" },
  CURRENCY: { color: 0x2ECC71, emoji: "💰", label: "Currency" },
  XP:       { color: 0x1ABC9C, emoji: "🔮", label: "Experience" },
  NOTHING:  { color: 0x636363, emoji: "💨", label: "Empty" },
};
```

## Out of Scope

- Loot table visibility in inventory or pull results
- Canvas-based image generation for pulls
- Two-phase or button-driven reveal mechanics
- Lootdrop system changes (channel activity drops are separate)

## Testing

- Existing lootbox tests should continue to pass (effect handler return shape changes are additive)
- Manual testing needed for visual output in Discord (Components V2 rendering)
- Verify all reward types render correctly: ITEM (all rarities), CURRENCY, XP, NOTHING
- Verify shop listing renders cleanly with various loot table sizes (1 tier, all tiers, many items per tier)
- Verify "other effects" display when lootbox item has multiple effect types
- Verify fallback behavior for items with unknown rarity, missing icons, missing images
