'use client'; import { useState, useMemo } from 'react'; import { EQUIPMENT_TYPES, EQUIPMENT_SLOTS, SLOT_NAMES, getEquipmentBySlot, type EquipmentSlot, type EquipmentType, } from '@/lib/game/data/equipment'; import { ENCHANTMENT_EFFECTS } from '@/lib/game/data/enchantment-effects'; import { getUnifiedEffects } from '@/lib/game/effects'; import { fmt } from '@/lib/game/store'; import { Button } from '@/components/ui/button'; import { GameCard } from '@/components/ui/game-card'; import { SectionHeader } from '@/components/ui/section-header'; import { StatRow } from '@/components/ui/stat-row'; import { Badge } from '@/components/ui/badge'; import type { GameStore, EquipmentInstance } from '@/lib/game/types'; import { EquipmentSlotGrid } from './EquipmentSlotGrid'; import { EquipmentInventory } from './EquipmentInventory'; import { EnchantmentsPanel } from './EnchantmentsPanel'; import { useGameToast } from '@/components/game/GameToast'; import { ConfirmDialog } from '@/components/game/ConfirmDialog'; // Rarity color mappings using design system tokens export const RARITY_BORDER_COLORS: Record = { common: 'border-[var(--text-muted)]', uncommon: 'border-[var(--color-success)]', rare: 'border-[var(--mana-water)]', epic: 'border-[var(--mana-stellar)]', legendary: 'border-[var(--mana-light)]', mythic: 'border-[var(--mana-dark)]', }; export const RARITY_BG_COLORS: Record = { common: 'bg-[var(--bg-sunken)]/30', uncommon: 'bg-[var(--color-success)]/10', rare: 'bg-[var(--mana-water)]/10', epic: 'bg-[var(--mana-stellar)]/10', legendary: 'bg-[var(--mana-light)]/10', mythic: 'bg-[var(--mana-dark)]/10', }; export const RARITY_TEXT_COLORS: Record = { common: 'text-[var(--text-secondary)]', uncommon: 'text-[var(--color-success)]', rare: 'text-[var(--mana-water)]', epic: 'text-[var(--mana-stellar)]', legendary: 'text-[var(--mana-light)]', mythic: 'text-[var(--mana-dark)]', }; const SLOT_ICONS: Record = { mainHand: () => ( ), offHand: () => ( ), head: () => ( ), body: () => ( ), hands: () => ( ), feet: () => ( ), accessory1: () => ( ), accessory2: () => ( ), }; // Slot grouping for visual layout type SlotGroup = { label: string; slots: EquipmentSlot[]; }; const SLOT_GROUPS: SlotGroup[] = [ { label: 'Weapon & Shield', slots: ['mainHand', 'offHand'] }, { label: 'Armor', slots: ['head', 'body', 'hands', 'feet'] }, { label: 'Accessories', slots: ['accessory1', 'accessory2'] }, ]; export function EquipmentTab({ store }: { store: GameStore }) { const showToast = useGameToast(); const [selectedSlot, setSelectedSlot] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState<{ instanceId: string; name: string } | null>(null); // Get unequipped items const equippedIds = useMemo(() => new Set(Object.values(store.equippedInstances).filter(Boolean)), [store.equippedInstances] ); const unequippedItems = useMemo(() => Object.values(store.equipmentInstances).filter( (inst) => !equippedIds.has(inst.instanceId) ), [store.equipmentInstances, equippedIds] ); // Equip an item to a slot const handleEquip = (instanceId: string, slot: EquipmentSlot) => { const instance = store.equipmentInstances[instanceId]; store.equipItem(instanceId, slot); setSelectedSlot(null); showToast('success', 'Item Equipped', `${instance?.name || 'Item'} equipped to ${SLOT_NAMES[slot]}`); }; // Unequip from a slot const handleUnequip = (slot: EquipmentSlot) => { const instanceId = store.equippedInstances[slot]; const instance = instanceId ? store.equipmentInstances[instanceId] : null; store.unequipItem(slot); showToast('success', 'Item Unequipped', `${instance?.name || 'Item'} removed from ${SLOT_NAMES[slot]}`); }; // Check if a slot is blocked by a 2-handed weapon const isSlotBlocked = (slot: EquipmentSlot): boolean => { if (slot === 'offHand' && store.equippedInstances.mainHand) { const mainHandInstance = store.equipmentInstances[store.equippedInstances.mainHand]; if (!mainHandInstance) return false; const mainHandType = EQUIPMENT_TYPES[mainHandInstance.typeId]; return mainHandType?.twoHanded === true; } return false; }; // Get items that can be equipped in a slot const getEquippableItems = (slot: EquipmentSlot): EquipmentInstance[] => { const equipmentTypes = getEquipmentBySlot(slot); const typeIds = new Set(equipmentTypes.map((t) => t.id)); return unequippedItems.filter((inst) => typeIds.has(inst.typeId)); }; // Get all items that can go in a slot const getItemsForSlot = (slot: EquipmentSlot): EquipmentInstance[] => { if (isSlotBlocked(slot)) return []; if (slot === 'accessory1' || slot === 'accessory2') { const accessoryTypeIds = Object.values(EQUIPMENT_TYPES) .filter((t) => t.category === 'accessory') .map((t) => t.id); return unequippedItems.filter((inst) => accessoryTypeIds.includes(inst.typeId)); } if (slot === 'offHand') { return getEquippableItems(slot).filter((inst) => { const type = EQUIPMENT_TYPES[inst.typeId]; return !type?.twoHanded; }); } return getEquippableItems(slot); }; // Check if an instance is currently equipped const isEquipped = (instanceId: string): boolean => Object.values(store.equippedInstances).includes(instanceId); // Get all slots an item type can be equipped to const getEquippableSlots = (typeId: string): EquipmentSlot[] => { const equipmentType = EQUIPMENT_TYPES[typeId]; if (!equipmentType) return []; if (equipmentType.category === 'accessory') { return ['accessory1', 'accessory2']; } return [equipmentType.slot]; }; // Handle item deletion const handleDelete = (instanceId: string, name: string) => { setDeleteConfirm({ instanceId, name }); }; const confirmDelete = () => { if (deleteConfirm) { store.deleteEquipmentInstance(deleteConfirm.instanceId); showToast('success', 'Item Discarded', `${deleteConfirm.name} has been removed from inventory`); setDeleteConfirm(null); } }; return (
{/* Equipment Slots */} {Object.values(store.equippedInstances).filter(Boolean).length} / {EQUIPMENT_SLOTS.length} slots filled } /> {/* Equipment Inventory */} {unequippedItems.length === 0 ? (
No unequipped items. Craft new gear in the Crafting tab.
) : ( )}
{/* Equipment Stats Summary */}
{Object.values(store.equipmentInstances).length}
Total Items
{equippedIds.size}
Equipped
{unequippedItems.length}
In Inventory
{Object.values(store.equipmentInstances).reduce( (sum, inst) => sum + inst.enchantments.length, 0 )}
Total Enchantments
{/* Enchantment Power */}

✨ Enchantment Power

{(() => { const unifiedEffects = getUnifiedEffects(store); const enchantPower = unifiedEffects.enchantmentPowerMultiplier || 1; return ( <> 1 ? "success" : "default"} />

Increases the power of all enchantments by {(enchantPower - 1) * 100}%. Multiplier applied to all enchantment effects.

); })()}
{/* Active Effects from Equipment */}
Active Effects from Equipment:
{(() => { const effects = store.getEquipmentEffects(); const effectEntries = Object.entries(effects).filter(([, v]) => v > 0); if (effectEntries.length === 0) { return No active effects; } return effectEntries.map(([stat, value]) => ( {stat}: +{fmt(value)} )); })()}
{/* Delete Confirmation Dialog */} setDeleteConfirm(null)} title="Discard Item?" description={`Discard ${deleteConfirm?.name}? This cannot be undone.`} variant="danger" confirmText="Discard" onConfirm={confirmDelete} />
); } EquipmentTab.displayName = 'EquipmentTab';