'use client'; import { useState, useMemo } 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 { Anvil, FlaskConical, Hammer, Package, Sparkles, Sword } from 'lucide-react'; import { MaterialRecipeCard } from './MaterialRecipeCard'; import { FABRICATOR_RECIPES, MATERIAL_RECIPES, canCraftRecipe } from '@/lib/game/data/fabricator-recipes'; import { MANA_TYPE_LABELS } from '@/lib/game/data/fabricator-recipe-types'; import { LOOT_DROPS, LOOT_RARITY_COLORS } from '@/lib/game/data/loot-drops'; import { useCraftingStore, useManaStore } from '@/lib/game/stores'; import type { FabricatorRecipe } from '@/lib/game/data/fabricator-recipes'; import { DebugName } from '@/components/game/debug/debug-context'; import { getCraftingCostReduction, applyCostReduction } from '@/lib/game/crafting-fabricator'; const BRANCH_RECIPE_IDS = new Set([ 'oakStaff', 'arcanistStaff', 'battlestaff', 'arcanistCirclet', 'arcanistRobe', 'voidCatalyst', 'arcanistPendant', 'crystalBlade', 'arcanistBlade', 'voidBlade', 'battleHelm', 'battleRobe', 'battleBoots', 'combatGauntlets', ]); function isWizardBranch(recipe: FabricatorRecipe): boolean { return ['oakStaff', 'arcanistStaff', 'battlestaff', 'arcanistCirclet', 'arcanistRobe', 'voidCatalyst', 'arcanistPendant'].includes(recipe.id); } function isPhysicalBranch(recipe: FabricatorRecipe): boolean { return ['crystalBlade', 'arcanistBlade', 'voidBlade', 'battleHelm', 'battleRobe', 'battleBoots', 'combatGauntlets'].includes(recipe.id); } function RecipeCard({ recipe, materials, rawMana, elementalMana, onCraft, isCrafting, costReduction, }: { recipe: FabricatorRecipe; materials: Record; rawMana: number; elementalMana: Record; onCraft: (recipe: FabricatorRecipe) => void; isCrafting: boolean; costReduction: number; }) { const pool = recipe.manaType === 'raw' ? rawMana : (elementalMana[recipe.manaType]?.current ?? 0); const { canCraft } = canCraftRecipe( recipe, materials, pool, recipe.manaType, costReduction, ); const rarityStyle = LOOT_RARITY_COLORS[recipe.rarity]; return (
{recipe.name}
{recipe.rarity}
{MANA_TYPE_LABELS[recipe.manaType] ?? recipe.manaType}
{recipe.description}
{recipe.gearTrait}
Materials: {costReduction > 0 && ( -{costReduction}% cost )}
{Object.entries(recipe.materials).map(([matId, rawAmount]) => { const reducedAmount = applyCostReduction(rawAmount, costReduction); const available = materials[matId] || 0; const hasEnough = available >= reducedAmount; const matDrop = LOOT_DROPS[matId]; return (
{matDrop?.name ?? matId} {available} / {reducedAmount} {costReduction > 0 && rawAmount !== reducedAmount && ( (was {rawAmount}) )}
); })}
{MANA_TYPE_LABELS[recipe.manaType]?.split(' ')[1] ?? recipe.manaType} Mana: = recipe.manaCost ? 'text-green-400' : 'text-red-400'}> {pool} / {recipe.manaCost}
Craft Time: {recipe.craftTime}h
); } type BranchFilter = 'all' | 'elemental' | 'wizard' | 'physical'; export function FabricatorSubTab() { const [selectedManaType, setSelectedManaType] = useState('earth'); const [activeSection, setActiveSection] = useState<'equipment' | 'materials'>('equipment'); const [branchFilter, setBranchFilter] = useState('all'); const lootInventory = useCraftingStore((s) => s.lootInventory); const unlockedRecipes = useCraftingStore((s) => s.unlockedRecipes); const equipmentCraftingProgress = useCraftingStore((s) => s.equipmentCraftingProgress); const rawMana = useManaStore((s) => s.rawMana); const elements = useManaStore((s) => s.elements); const startFabricatorCrafting = useCraftingStore((s) => s.startFabricatorCrafting); const craftMaterial = useCraftingStore((s) => s.craftMaterial); const cancelEquipmentCrafting = useCraftingStore((s) => s.cancelEquipmentCrafting); const availableManaTypes = useMemo(() => { return [...new Set(FABRICATOR_RECIPES.map((r) => r.manaType))]; }, []); const filteredRecipes = useMemo(() => { let recipes = FABRICATOR_RECIPES; if (branchFilter === 'elemental') { recipes = recipes.filter(r => !BRANCH_RECIPE_IDS.has(r.id)); } else if (branchFilter === 'wizard') { recipes = recipes.filter(r => isWizardBranch(r)); } else if (branchFilter === 'physical') { recipes = recipes.filter(r => isPhysicalBranch(r)); } return recipes.filter(r => r.manaType === selectedManaType && unlockedRecipes.includes(r.id)); }, [selectedManaType, branchFilter, unlockedRecipes]); const isCrafting = equipmentCraftingProgress !== null; const costReduction = getCraftingCostReduction(); const materialRecipes = useMemo(() => MATERIAL_RECIPES, []); const handleCraft = (recipe: FabricatorRecipe) => { if (recipe.recipeType === 'material') { craftMaterial(recipe.id); } else { startFabricatorCrafting(recipe.id); } }; return (
{/* Section toggle: Equipment vs Materials */}
{/* Branch filter — only for equipment */} {activeSection === 'equipment' && ( <>
{/* Mana type filter */}
{availableManaTypes.map((mt) => ( ))}
)}
{/* Recipe list */} {activeSection === 'equipment' ? ( <>{MANA_TYPE_LABELS[selectedManaType] ?? selectedManaType} Recipes ) : ( <>Material Recipes )} {isCrafting ? (
Crafting: {equipmentCraftingProgress.blueprintId}
{equipmentCraftingProgress.progress.toFixed(1)}h /{' '} {equipmentCraftingProgress.required.toFixed(1)}h Mana spent: {equipmentCraftingProgress.manaSpent}
) : (
{activeSection === 'equipment' ? ( filteredRecipes.length === 0 ? (

No recipes for this mana type yet.

) : ( filteredRecipes.map((recipe) => ( )) ) ) : ( materialRecipes.length === 0 ? (

No material recipes available.

) : ( materialRecipes.map((recipe) => ( )) ) )}
)}
{/* 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 = LOOT_RARITY_COLORS[drop.rarity]; return (
{drop.name}
x{count}
); })}
)}
); } FabricatorSubTab.displayName = 'FabricatorSubTab';