'use client'; import { useState } from 'react'; import { SKILLS_DEF, SKILL_CATEGORIES, getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants'; import { SKILL_EVOLUTION_PATHS, getUpgradesForSkillAtMilestone, getNextTierSkill, getTierMultiplier } from '@/lib/game/skill-evolution'; import { getUnifiedEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects'; import { getAvailableSkillCategories } from '@/lib/game/data/attunements'; import { fmt, fmtDec } from '@/lib/game/store'; import { formatStudyTime } from '@/lib/game/formatting'; import type { SkillUpgradeChoice, GameStore } from '@/lib/game/types'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { StudyProgress } from './StudyProgress'; import { UpgradeDialog } from './UpgradeDialog'; import { ChevronDown, ChevronRight } from 'lucide-react'; export interface SkillsTabProps { store: GameStore; } // Check if skill has milestone available function hasMilestoneUpgrade( skillId: string, level: number, skillTiers: Record, skillUpgrades: Record ): { milestone: 5 | 10; hasUpgrades: boolean; selectedCount: number } | null { const baseSkillId = skillId.includes('_t') ? skillId.split('_t')[0] : skillId; const path = SKILL_EVOLUTION_PATHS[baseSkillId]; if (!path) return null; // Check level 5 milestone if (level >= 5) { const upgrades5 = getUpgradesForSkillAtMilestone(skillId, 5, skillTiers); const selected5 = (skillUpgrades[skillId] || []).filter(id => id.includes('_l5')); if (upgrades5.length > 0 && selected5.length < 2) { return { milestone: 5, hasUpgrades: true, selectedCount: selected5.length }; } } // Check level 10 milestone if (level >= 10) { const upgrades10 = getUpgradesForSkillAtMilestone(skillId, 10, skillTiers); const selected10 = (skillUpgrades[skillId] || []).filter(id => id.includes('_l10')); if (upgrades10.length > 0 && selected10.length < 2) { return { milestone: 10, hasUpgrades: true, selectedCount: selected10.length }; } } return null; } export function SkillsTab({ store }: SkillsTabProps) { const [upgradeDialogSkill, setUpgradeDialogSkill] = useState(null); const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5); const [pendingUpgradeSelections, setPendingUpgradeSelections] = useState([]); const [collapsedCategories, setCollapsedCategories] = useState>(new Set()); const studySpeedMult = getStudySpeedMultiplier(store.skills); const upgradeEffects = getUnifiedEffects(store); // Toggle category collapse const toggleCategory = (categoryId: string) => { setCollapsedCategories(prev => { const newSet = new Set(prev); if (newSet.has(categoryId)) { newSet.delete(categoryId); } else { newSet.add(categoryId); } return newSet; }); }; // Get upgrade choices for dialog const getUpgradeChoices = () => { if (!upgradeDialogSkill) return { available: [] as SkillUpgradeChoice[], selected: [] as string[] }; return store.getSkillUpgradeChoices(upgradeDialogSkill, upgradeDialogMilestone); }; const { available, selected: alreadySelected } = getUpgradeChoices(); // Toggle selection const toggleUpgrade = (upgradeId: string) => { const currentSelections = pendingUpgradeSelections.length > 0 ? pendingUpgradeSelections : alreadySelected; if (currentSelections.includes(upgradeId)) { setPendingUpgradeSelections(currentSelections.filter(id => id !== upgradeId)); } else if (currentSelections.length < 2) { setPendingUpgradeSelections([...currentSelections, upgradeId]); } }; // Commit selections and close const handleConfirm = () => { const currentSelections = pendingUpgradeSelections.length > 0 ? pendingUpgradeSelections : alreadySelected; if (currentSelections.length === 2 && upgradeDialogSkill) { store.commitSkillUpgrades(upgradeDialogSkill, currentSelections, upgradeDialogMilestone); } setPendingUpgradeSelections([]); setUpgradeDialogSkill(null); }; // Cancel and close const handleCancel = () => { setPendingUpgradeSelections([]); setUpgradeDialogSkill(null); }; return (
{/* Upgrade Selection Dialog */} { if (!open) { setPendingUpgradeSelections([]); setUpgradeDialogSkill(null); } }} /> {/* Current Study Progress */} {store.currentStudyTarget && store.currentStudyTarget.type === 'skill' && ( )} {/* Get available skill categories based on attunements */} {(() => { const availableCategories = getAvailableSkillCategories(store.attunements || {}); return SKILL_CATEGORIES .filter(cat => availableCategories.includes(cat.id)) .map((cat) => { const skillsInCat = Object.entries(SKILLS_DEF).filter(([, def]) => def.cat === cat.id); if (skillsInCat.length === 0) return null; const isCollapsed = collapsedCategories.has(cat.id); return ( toggleCategory(cat.id)}> {cat.icon} {cat.name}
{skillsInCat.length} skills {isCollapsed ? : }
{!isCollapsed && (
{skillsInCat.map(([id, def]) => { // Get tier info const currentTier = store.skillTiers?.[id] || 1; const tieredSkillId = currentTier > 1 ? `${id}_t${currentTier}` : id; const tierMultiplier = getTierMultiplier(tieredSkillId); // Get the actual level from the tiered skill const level = store.skills[tieredSkillId] || store.skills[id] || 0; const maxed = level >= def.max; // Check if studying this skill const isStudying = (store.currentStudyTarget?.id === id || store.currentStudyTarget?.id === tieredSkillId) && store.currentStudyTarget?.type === 'skill'; // Get tier name for display const tierDef = SKILL_EVOLUTION_PATHS[id]?.tiers.find(t => t.tier === currentTier); const skillDisplayName = tierDef?.name || def.name; // Check prerequisites let prereqMet = true; if (def.req) { for (const [r, rl] of Object.entries(def.req)) { if ((store.skills[r] || 0) < rl) { prereqMet = false; break; } } } // Apply skill modifiers const costMult = getStudyCostMultiplier(store.skills); const speedMult = getStudySpeedMultiplier(store.skills); const studyEffects = getUnifiedEffects(store); const effectiveSpeedMult = speedMult * studyEffects.studySpeedMultiplier; // Study time scales with tier const tierStudyTime = def.studyTime * currentTier; const effectiveStudyTime = tierStudyTime / effectiveSpeedMult; // Cost scales with tier const baseCost = def.base * (level + 1) * currentTier; const cost = Math.floor(baseCost * costMult); // Can start studying? const canStudy = !maxed && prereqMet && store.rawMana >= cost && !isStudying; // Check for milestone upgrades const milestoneInfo = hasMilestoneUpgrade(tieredSkillId, level, store.skillTiers || {}, store.skillUpgrades); // Check for tier up const nextTierSkill = getNextTierSkill(tieredSkillId); const canTierUp = maxed && nextTierSkill; // Get selected upgrades const selectedUpgrades = store.skillUpgrades[tieredSkillId] || []; const selectedL5 = selectedUpgrades.filter(u => u.includes('_l5')); const selectedL10 = selectedUpgrades.filter(u => u.includes('_l10')); return (
{skillDisplayName} {currentTier > 1 && ( Tier {currentTier} ({fmtDec(tierMultiplier, 0)}x) )} {level > 0 && Lv.{level}} {selectedUpgrades.length > 0 && (
{selectedL5.length > 0 && ( L5: {selectedL5.length} )} {selectedL10.length > 0 && ( L10: {selectedL10.length} )}
)}
{def.desc}{currentTier > 1 && ` (Tier ${currentTier}: ${fmtDec(tierMultiplier, 0)}x effect)`}
{!prereqMet && def.req && (
Requires: {Object.entries(def.req).map(([r, rl]) => `${SKILLS_DEF[r]?.name} Lv.${rl}`).join(', ')}
)}
1 ? 'text-green-400' : ''}> Study: {formatStudyTime(effectiveStudyTime)}{effectiveSpeedMult > 1 && ({Math.round(effectiveSpeedMult * 100)}% speed)} {' • '} Cost: {fmt(cost)} mana{costMult < 1 && ({Math.round(costMult * 100)}% cost)}
{milestoneInfo && (
⭐ Level {milestoneInfo.milestone} milestone: {milestoneInfo.selectedCount}/2 upgrades selected
)}
{/* Level dots */}
{Array.from({ length: def.max }).map((_, i) => (
))}
{isStudying ? (
{formatStudyTime(store.currentStudyTarget?.progress || 0)}/{formatStudyTime(tierStudyTime)}
) : milestoneInfo ? ( ) : canTierUp ? ( ) : maxed ? ( Maxed ) : (
{/* Parallel Study button */} {hasSpecial(upgradeEffects, SPECIAL_EFFECTS.PARALLEL_STUDY) && store.currentStudyTarget && !store.parallelStudyTarget && store.currentStudyTarget.id !== tieredSkillId && canStudy && (

Study in parallel (50% speed)

)}
)}
); })}
)} ); }); })()}
); }