'use client'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Wand2, Scroll, Hammer, Sparkles, Trash2, Plus, Minus, Package, Zap, Clock, ChevronRight, Circle, Anvil } from 'lucide-react'; import { EQUIPMENT_TYPES, type EquipmentType, type EquipmentSlot } from '@/lib/game/data/equipment'; import { ENCHANTMENT_EFFECTS, type EnchantmentEffectDef, calculateEffectCapacityCost } from '@/lib/game/data/enchantment-effects'; import { CRAFTING_RECIPES, canCraftRecipe } from '@/lib/game/data/crafting-recipes'; import { LOOT_DROPS, RARITY_COLORS } from '@/lib/game/data/loot-drops'; import type { EquipmentInstance, EnchantmentDesign, DesignEffect, AppliedEnchantment, LootInventory, EquipmentCraftingProgress } from '@/lib/game/types'; import { fmt, type GameStore } from '@/lib/game/store'; // Slot display names const SLOT_NAMES: Record = { mainHand: 'Main Hand', offHand: 'Off Hand', head: 'Head', body: 'Body', hands: 'Hands', feet: 'Feet', accessory1: 'Accessory 1', accessory2: 'Accessory 2', }; export interface CraftingTabProps { store: GameStore; } export function CraftingTab({ store }: CraftingTabProps) { const equippedInstances = store.equippedInstances; const equipmentInstances = store.equipmentInstances; const enchantmentDesigns = store.enchantmentDesigns; const designProgress = store.designProgress; const preparationProgress = store.preparationProgress; const applicationProgress = store.applicationProgress; const equipmentCraftingProgress = store.equipmentCraftingProgress; const rawMana = store.rawMana; const skills = store.skills; const currentAction = store.currentAction; const unlockedEffects = store.unlockedEffects; const lootInventory = store.lootInventory; const startDesigningEnchantment = store.startDesigningEnchantment; const cancelDesign = store.cancelDesign; const saveDesign = store.saveDesign; const deleteDesign = store.deleteDesign; const startPreparing = store.startPreparing; const cancelPreparation = store.cancelPreparation; const startApplying = store.startApplying; const pauseApplication = store.pauseApplication; const resumeApplication = store.resumeApplication; const cancelApplication = store.cancelApplication; const disenchantEquipment = store.disenchantEquipment; const getAvailableCapacity = store.getAvailableCapacity; const startCraftingEquipment = store.startCraftingEquipment; const cancelEquipmentCrafting = store.cancelEquipmentCrafting; const deleteMaterial = store.deleteMaterial; const [craftingStage, setCraftingStage] = useState<'design' | 'prepare' | 'apply' | 'craft'>('craft'); const [selectedEquipmentType, setSelectedEquipmentType] = useState(null); const [selectedEquipmentInstance, setSelectedEquipmentInstance] = useState(null); const [selectedDesign, setSelectedDesign] = useState(null); // Design creation state const [designName, setDesignName] = useState(''); const [selectedEffects, setSelectedEffects] = useState([]); const enchantingLevel = skills.enchanting || 0; const efficiencyBonus = (skills.efficientEnchant || 0) * 0.05; // Get equipped items as array const equippedItems = Object.entries(equippedInstances) .filter(([, instanceId]) => instanceId && equipmentInstances[instanceId]) .map(([slot, instanceId]) => ({ slot: slot as EquipmentSlot, instance: equipmentInstances[instanceId!], })); // Calculate total capacity cost for current design const designCapacityCost = selectedEffects.reduce( (total, eff) => total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus), 0 ); // Calculate design time const designTime = selectedEffects.reduce((total, eff) => total + 0.5 * eff.stacks, 1); // Add effect to design const addEffect = (effectId: string) => { const existing = selectedEffects.find(e => e.effectId === effectId); const effectDef = ENCHANTMENT_EFFECTS[effectId]; if (!effectDef) return; if (existing) { if (existing.stacks < effectDef.maxStacks) { setSelectedEffects(selectedEffects.map(e => e.effectId === effectId ? { ...e, stacks: e.stacks + 1 } : e )); } } else { setSelectedEffects([...selectedEffects, { effectId, stacks: 1, capacityCost: calculateEffectCapacityCost(effectId, 1, efficiencyBonus), }]); } }; // Remove effect from design const removeEffect = (effectId: string) => { const existing = selectedEffects.find(e => e.effectId === effectId); if (!existing) return; if (existing.stacks > 1) { setSelectedEffects(selectedEffects.map(e => e.effectId === effectId ? { ...e, stacks: e.stacks - 1 } : e )); } else { setSelectedEffects(selectedEffects.filter(e => e.effectId !== effectId)); } }; // Create design const handleCreateDesign = () => { if (!designName || !selectedEquipmentType || selectedEffects.length === 0) return; const success = startDesigningEnchantment(designName, selectedEquipmentType, selectedEffects); if (success) { // Reset form setDesignName(''); setSelectedEquipmentType(null); setSelectedEffects([]); } }; // Complete design after progress const handleCompleteDesign = () => { if (!designProgress || !selectedEquipmentType) return; const design: EnchantmentDesign = { id: designProgress.designId, name: designName || 'Untitled Design', equipmentType: selectedEquipmentType, effects: selectedEffects, totalCapacityUsed: designCapacityCost, designTime, created: Date.now(), }; saveDesign(design); setDesignName(''); setSelectedEquipmentType(null); setSelectedEffects([]); }; // Get available effects for selected equipment type (only unlocked ones) const getAvailableEffects = () => { if (!selectedEquipmentType) return []; const type = EQUIPMENT_TYPES[selectedEquipmentType]; if (!type) return []; return Object.values(ENCHANTMENT_EFFECTS).filter( effect => effect.allowedEquipmentCategories.includes(type.category) && unlockedEffects.includes(effect.id) ); }; // Render design stage const renderDesignStage = () => (
{/* Equipment Type Selection */} 1. Select Equipment Type {designProgress ? (
Designing for: {EQUIPMENT_TYPES[selectedEquipmentType || '']?.name}
{designProgress.progress.toFixed(1)}h / {designProgress.required.toFixed(1)}h
{designProgress.progress >= designProgress.required && ( )}
) : (
{Object.values(EQUIPMENT_TYPES).map(type => (
setSelectedEquipmentType(type.id)} >
{type.name}
Cap: {type.baseCapacity}
))}
)}
{/* Effect Selection */} 2. Select Effects {enchantingLevel < 1 ? (

Learn Enchanting skill to design enchantments

) : designProgress ? (
Design in progress...
{selectedEffects.map(eff => { const def = ENCHANTMENT_EFFECTS[eff.effectId]; return (
{def?.name} x{eff.stacks} {eff.capacityCost} cap
); })}
) : !selectedEquipmentType ? (
Select an equipment type first
) : ( <>
{getAvailableEffects().map(effect => { const selected = selectedEffects.find(e => e.effectId === effect.id); const cost = calculateEffectCapacityCost(effect.id, (selected?.stacks || 0) + 1, efficiencyBonus); return (
{effect.name}
{effect.description}
Cost: {effect.baseCapacityCost} cap | Max: {effect.maxStacks}
{selected && ( )}
{selected && ( {selected.stacks}/{effect.maxStacks} )}
); })}
{/* Selected effects summary */}
setDesignName(e.target.value)} className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm" />
Total Capacity: 100 ? 'text-red-400' : 'text-green-400'}> {designCapacityCost.toFixed(0)}
Design Time: {designTime.toFixed(1)}h
)}
{/* Saved Designs */} Saved Designs ({enchantmentDesigns.length}) {enchantmentDesigns.length === 0 ? (
No saved designs yet
) : (
{enchantmentDesigns.map(design => (
setSelectedDesign(design.id)} >
{design.name}
{EQUIPMENT_TYPES[design.equipmentType]?.name}
{design.effects.length} effects | {design.totalCapacityUsed} cap
))}
)}
); // Render prepare stage const renderPrepareStage = () => (
{/* Equipment Selection */} Select Equipment to Prepare {preparationProgress ? (
Preparing: {equipmentInstances[preparationProgress.equipmentInstanceId]?.name}
{preparationProgress.progress.toFixed(1)}h / {preparationProgress.required.toFixed(1)}h Mana paid: {fmt(preparationProgress.manaCostPaid)}
) : (
{equippedItems.map(({ slot, instance }) => (
setSelectedEquipmentInstance(instance.instanceId)} >
{instance.name}
{SLOT_NAMES[slot]}
{instance.usedCapacity}/{instance.totalCapacity} cap
{instance.enchantments.length} enchants
))} {equippedItems.length === 0 && (
No equipped items
)}
)}
{/* Preparation Details */} Preparation Details {!selectedEquipmentInstance ? (
Select equipment to prepare
) : preparationProgress ? (
Preparation in progress...
) : ( (() => { const instance = equipmentInstances[selectedEquipmentInstance]; const prepTime = 2 + Math.floor(instance.totalCapacity / 50); const manaCost = instance.totalCapacity * 10; return (
{instance.name}
Capacity: {instance.usedCapacity}/{instance.totalCapacity}
Prep Time: {prepTime}h
Mana Cost: {fmt(manaCost)}
); })() )}
); // Render apply stage const renderApplyStage = () => (
{/* Equipment & Design Selection */} Select Equipment & Design {applicationProgress ? (
Applying to: {equipmentInstances[applicationProgress.equipmentInstanceId]?.name}
{applicationProgress.progress.toFixed(1)}h / {applicationProgress.required.toFixed(1)}h Mana spent: {fmt(applicationProgress.manaSpent)}
{applicationProgress.paused ? ( ) : ( )}
) : (
Equipment:
{equippedItems.map(({ slot, instance }) => (
setSelectedEquipmentInstance(instance.instanceId)} > {instance.name} ({instance.usedCapacity}/{instance.totalCapacity} cap)
))}
Design:
{enchantmentDesigns.map(design => (
setSelectedDesign(design.id)} > {design.name} ({design.totalCapacityUsed} cap)
))}
)}
{/* Application Details */} Apply Enchantment {!selectedEquipmentInstance || !selectedDesign ? (
Select equipment and a design
) : applicationProgress ? (
Application in progress...
) : ( (() => { const instance = equipmentInstances[selectedEquipmentInstance]; const design = enchantmentDesigns.find(d => d.id === selectedDesign); if (!design) return null; const availableCap = instance.totalCapacity - instance.usedCapacity; const canFit = availableCap >= design.totalCapacityUsed; const applicationTime = 2 + design.effects.reduce((t, e) => t + e.stacks, 0); const manaPerHour = 20 + design.effects.reduce((t, e) => t + e.stacks * 5, 0); return (
{design.name}
→ {instance.name}
Required Capacity: {design.totalCapacityUsed} / {availableCap} available
Application Time: {applicationTime}h
Mana per Hour: {manaPerHour}
Effects:
    {design.effects.map(eff => (
  • {ENCHANTMENT_EFFECTS[eff.effectId]?.name} x{eff.stacks}
  • ))}
); })() )}
{/* Disenchant Section */} Disenchant Equipment
{equippedItems .filter(({ instance }) => instance.enchantments.length > 0) .map(({ slot, instance }) => { const disenchantLevel = skills.disenchanting || 0; const recoveryRate = 0.1 + disenchantLevel * 0.2; const totalRecoverable = instance.enchantments.reduce( (sum, e) => sum + Math.floor(e.actualCost * recoveryRate), 0 ); return (
{instance.name}
{instance.enchantments.length} enchantments
); })} {equippedItems.filter(({ instance }) => instance.enchantments.length > 0).length === 0 && (
No enchanted equipment to disenchant
)}
); // Render equipment crafting stage const renderCraftStage = () => (
{/* Blueprint Selection */} Available Blueprints {equipmentCraftingProgress ? (
Crafting: {CRAFTING_RECIPES[equipmentCraftingProgress.blueprintId]?.name}
{equipmentCraftingProgress.progress.toFixed(1)}h / {equipmentCraftingProgress.required.toFixed(1)}h Mana spent: {fmt(equipmentCraftingProgress.manaSpent)}
) : (
{lootInventory.blueprints.length === 0 ? (

No blueprints discovered yet.

Defeat guardians to find blueprints!

) : ( lootInventory.blueprints.map(bpId => { const recipe = CRAFTING_RECIPES[bpId]; if (!recipe) return null; const { canCraft, missingMaterials, missingMana } = canCraftRecipe( recipe, lootInventory.materials, rawMana ); const rarityStyle = RARITY_COLORS[recipe.rarity]; return (
{recipe.name}
{recipe.rarity}
{EQUIPMENT_TYPES[recipe.equipmentTypeId]?.category}
{recipe.description}
Materials:
{Object.entries(recipe.materials).map(([matId, amount]) => { const available = lootInventory.materials[matId] || 0; const matDrop = LOOT_DROPS[matId]; const hasEnough = available >= amount; return (
{matDrop?.name || matId} {available} / {amount}
); })}
Mana Cost: = recipe.manaCost ? 'text-green-400' : 'text-red-400'}> {fmt(recipe.manaCost)}
Craft Time: {recipe.craftTime}h
); }) )}
)}
{/* Materials Inventory */} Materials ({Object.values(lootInventory.materials).reduce((a, b) => a + b, 0)}) {Object.keys(lootInventory.materials).length === 0 ? (

No materials collected yet.

Defeat floors to gather materials!

) : (
{Object.entries(lootInventory.materials).map(([matId, count]) => { if (count <= 0) return null; const drop = LOOT_DROPS[matId]; if (!drop) return null; const rarityStyle = RARITY_COLORS[drop.rarity]; return (
{drop.name}
x{count}
); })}
)}
); return (
{/* Stage Tabs */} setCraftingStage(v as typeof craftingStage)}> Craft Design Prepare Apply {renderCraftStage()} {renderDesignStage()} {renderPrepareStage()} {renderApplyStage()} {/* Current Activity Indicator */} {currentAction === 'craft' && equipmentCraftingProgress && (
Crafting equipment...
{((equipmentCraftingProgress.progress / equipmentCraftingProgress.required) * 100).toFixed(0)}%
)} {currentAction === 'design' && designProgress && (
Designing enchantment...
{((designProgress.progress / designProgress.required) * 100).toFixed(0)}%
)} {currentAction === 'prepare' && preparationProgress && (
Preparing equipment...
{((preparationProgress.progress / preparationProgress.required) * 100).toFixed(0)}%
)} {currentAction === 'enchant' && applicationProgress && (
{applicationProgress.paused ? 'Enchantment paused' : 'Applying enchantment...'}
{((applicationProgress.progress / applicationProgress.required) * 100).toFixed(0)}%
{applicationProgress.paused ? ( ) : ( )}
)}
); }