'use client'; import { useState } from 'react'; import { useGameStore, fmt, fmtDec } from '@/lib/game/store'; import { SKILLS_DEF, SKILL_CATEGORIES, getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants'; import { SKILL_EVOLUTION_PATHS, getUpgradesForSkillAtMilestone, getNextTierSkill, getTierMultiplier } from '@/lib/game/skill-evolution'; import { computeEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/upgrade-effects'; import { useStudyStats } from '@/lib/game/hooks/useGameDerived'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { BookOpen, X } from 'lucide-react'; import type { SkillUpgradeChoice } from '@/lib/game/types'; // Format study time function formatStudyTime(hours: number): string { if (hours < 1) return `${Math.round(hours * 60)}m`; return `${hours.toFixed(1)}h`; } export function SkillsTab() { const store = useGameStore(); const { studySpeedMult, studyCostMult, hasParallelStudy } = useStudyStats(); const [upgradeDialogSkill, setUpgradeDialogSkill] = useState(null); const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5); const [pendingUpgradeSelections, setPendingUpgradeSelections] = useState([]); const upgradeEffects = computeEffects(store.skillUpgrades || {}, store.skillTiers || {}); // Check if skill has milestone available const hasMilestoneUpgrade = (skillId: string, level: number): { 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; if (level >= 5) { const upgrades5 = getUpgradesForSkillAtMilestone(skillId, 5, store.skillTiers); const selected5 = (store.skillUpgrades[skillId] || []).filter(id => id.includes('_l5')); if (upgrades5.length > 0 && selected5.length < 2) { return { milestone: 5, hasUpgrades: true, selectedCount: selected5.length }; } } if (level >= 10) { const upgrades10 = getUpgradesForSkillAtMilestone(skillId, 10, store.skillTiers); const selected10 = (store.skillUpgrades[skillId] || []).filter(id => id.includes('_l10')); if (upgrades10.length > 0 && selected10.length < 2) { return { milestone: 10, hasUpgrades: true, selectedCount: selected10.length }; } } return null; }; // Render upgrade selection dialog const renderUpgradeDialog = () => { if (!upgradeDialogSkill) return null; const skillDef = SKILLS_DEF[upgradeDialogSkill]; const level = store.skills[upgradeDialogSkill] || 0; const { available, selected: alreadySelected } = store.getSkillUpgradeChoices(upgradeDialogSkill, upgradeDialogMilestone); const currentSelections = pendingUpgradeSelections.length > 0 ? pendingUpgradeSelections : alreadySelected; const toggleUpgrade = (upgradeId: string) => { if (currentSelections.includes(upgradeId)) { setPendingUpgradeSelections(currentSelections.filter(id => id !== upgradeId)); } else if (currentSelections.length < 2) { setPendingUpgradeSelections([...currentSelections, upgradeId]); } }; const handleDone = () => { if (currentSelections.length === 2 && upgradeDialogSkill) { store.commitSkillUpgrades(upgradeDialogSkill, currentSelections, upgradeDialogMilestone); } setPendingUpgradeSelections([]); setUpgradeDialogSkill(null); }; const handleCancel = () => { setPendingUpgradeSelections([]); setUpgradeDialogSkill(null); }; return ( { if (!open) { setPendingUpgradeSelections([]); setUpgradeDialogSkill(null); } }}> Choose Upgrade - {skillDef?.name || upgradeDialogSkill} Level {upgradeDialogMilestone} Milestone - Select 2 upgrades ({currentSelections.length}/2 chosen)
{available.map((upgrade) => { const isSelected = currentSelections.includes(upgrade.id); const canToggle = currentSelections.length < 2 || isSelected; return (
{ if (canToggle) { toggleUpgrade(upgrade.id); } }} >
{upgrade.name}
{isSelected && Selected}
{upgrade.desc}
{upgrade.effect.type === 'multiplier' && (
+{Math.round((upgrade.effect.value! - 1) * 100)}% {upgrade.effect.stat}
)} {upgrade.effect.type === 'bonus' && (
+{upgrade.effect.value} {upgrade.effect.stat}
)} {upgrade.effect.type === 'special' && (
⚡ {upgrade.effect.specialDesc || 'Special effect'}
)}
); })}
); }; // Render study progress const renderStudyProgress = () => { if (!store.currentStudyTarget) return null; const target = store.currentStudyTarget; const progressPct = Math.min(100, (target.progress / target.required) * 100); const def = SKILLS_DEF[target.id] || SKILLS_DEF[target.id.split('_t')[0]]; return (
{def?.name}
{formatStudyTime(target.progress)} / {formatStudyTime(target.required)} {studySpeedMult.toFixed(1)}x speed
); }; return (
{/* Upgrade Selection Dialog */} {renderUpgradeDialog()} {/* Current Study Progress */} {store.currentStudyTarget && store.currentStudyTarget.type === 'skill' && ( {renderStudyProgress()} )} {SKILL_CATEGORIES.map((cat) => { const skillsInCat = Object.entries(SKILLS_DEF).filter(([, def]) => def.cat === cat.id); if (skillsInCat.length === 0) return null; return ( {cat.icon} {cat.name}
{skillsInCat.map(([id, def]) => { const currentTier = store.skillTiers?.[id] || 1; const tieredSkillId = currentTier > 1 ? `${id}_t${currentTier}` : id; const tierMultiplier = getTierMultiplier(tieredSkillId); const level = store.skills[tieredSkillId] || store.skills[id] || 0; const maxed = level >= def.max; const isStudying = (store.currentStudyTarget?.id === id || store.currentStudyTarget?.id === tieredSkillId) && store.currentStudyTarget?.type === 'skill'; const savedProgress = store.skillProgress[tieredSkillId] || store.skillProgress[id] || 0; 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 studyEffects = computeEffects(store.skillUpgrades || {}, store.skillTiers || {}); const effectiveSpeedMult = studySpeedMult * studyEffects.studySpeedMultiplier; const tierStudyTime = def.studyTime * currentTier; const effectiveStudyTime = tierStudyTime / effectiveSpeedMult; const baseCost = def.base * (level + 1) * currentTier; const cost = Math.floor(baseCost * studyCostMult); const canStudy = !maxed && prereqMet && store.rawMana >= cost && !isStudying; const milestoneInfo = hasMilestoneUpgrade(tieredSkillId, level); const nextTierSkill = getNextTierSkill(tieredSkillId); const canTierUp = maxed && nextTierSkill; 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{studyCostMult < 1 && ({Math.round(studyCostMult * 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 */} {hasParallelStudy && store.currentStudyTarget && !store.parallelStudyTarget && store.currentStudyTarget.id !== tieredSkillId && canStudy && (

Study in parallel (50% speed)

)}
)}
); })}
); })}
); }