import { FlaskConical, Lock, Check, Play, ListOrdered, X } from 'lucide-react'; import { TutorialHint } from '@/components/game/TutorialHint'; import { useGameStore } from '@/store'; import { formatDuration, formatPercent, formatNumber, formatMoney } from '@token-empire/shared'; import { TECH_TREE, getAvailableResearch } from '@token-empire/game-engine'; import type { ResearchNode } from '@token-empire/shared'; const CATEGORY_COLORS: Record = { generation: 'border-purple-500/50 bg-purple-500/10', efficiency: 'border-blue-500/50 bg-blue-500/10', safety: 'border-green-500/50 bg-green-500/10', specialization: 'border-orange-500/50 bg-orange-500/10', infrastructure: 'border-cyan-500/50 bg-cyan-500/10', }; const CATEGORY_LABELS: Record = { generation: 'Model Architecture', efficiency: 'Efficiency', safety: 'Safety & Alignment', specialization: 'Specialization', infrastructure: 'Infrastructure', }; export function ResearchPage() { const completedResearch = useGameStore((s) => s.research.completedResearch); const activeResearch = useGameStore((s) => s.research.activeResearch); const researchQueue = useGameStore((s) => s.research.researchQueue); const researchPoints = useGameStore((s) => s.research.researchPoints); const startResearch = useGameStore((s) => s.startResearch); const queueResearch = useGameStore((s) => s.queueResearch); const removeFromResearchQueue = useGameStore((s) => s.removeFromResearchQueue); const era = useGameStore((s) => s.meta.currentEra); const state = useGameStore.getState(); const available = getAvailableResearch(state); const availableIds = new Set(available.map(n => n.id)); const queuedIds = new Set(researchQueue); const handleStart = (node: ResearchNode) => { if (activeResearch) return; startResearch({ researchId: node.id, progressTicks: 0, totalTicks: node.cost.ticks, allocatedResearchers: state.talent.departments.research.headcount, allocatedCompute: node.cost.compute, moneySpent: 0, }); }; const handleQueue = (node: ResearchNode) => { queueResearch(node.id); }; const categories = [...new Set(TECH_TREE.map(n => n.category))]; return (

Research & Development

Completed: {completedResearch.length}/{TECH_TREE.length}
Research Points: {researchPoints}
Queue up multiple research projects to run in sequence. Complete prerequisites to unlock advanced technologies that improve your models and infrastructure. {activeResearch && (
{TECH_TREE.find(n => n.id === activeResearch.researchId)?.name ?? activeResearch.researchId}
{formatPercent(activeResearch.progressTicks / activeResearch.totalTicks)} complete
ETA: {formatDuration(Math.ceil(activeResearch.totalTicks - activeResearch.progressTicks))}
)} {researchQueue.length > 0 && (
Queue ({researchQueue.length})
{researchQueue.map((id, idx) => { const node = TECH_TREE.find(n => n.id === id); if (!node) return null; return (
{idx + 1}. {node.name} {formatDuration(node.cost.ticks)}
); })}
)} {categories.map(category => { const nodes = TECH_TREE.filter(n => n.category === category); return (

{CATEGORY_LABELS[category] ?? category}

{nodes.map(node => { const isCompleted = completedResearch.includes(node.id); const isActive = activeResearch?.researchId === node.id; const isQueued = queuedIds.has(node.id); const isAvailable = availableIds.has(node.id); const isLocked = !isCompleted && !isActive && !isAvailable && !isQueued; const canStart = isAvailable && !activeResearch; const canQueue = isAvailable && !!activeResearch; const queuePosition = isQueued ? researchQueue.indexOf(node.id) + 1 : -1; return (
canStart ? handleStart(node) : canQueue ? handleQueue(node) : undefined} className={`rounded-xl border p-4 transition-all ${ isCompleted ? 'border-success/50 bg-success/5 opacity-70' : isActive ? 'border-accent/50 bg-accent/5' : isQueued ? 'border-amber-500/50 bg-amber-500/5' : (canStart || canQueue) ? `${CATEGORY_COLORS[category]} hover:brightness-110 cursor-pointer ring-1 ring-transparent hover:ring-accent/30` : 'border-surface-700 bg-surface-900 opacity-50' }`} >

{node.name}

{isCompleted && } {isQueued && #{queuePosition}} {isLocked && }

{node.description}

{formatMoney(node.cost.money)} · {formatDuration(node.cost.ticks)} · {formatNumber(node.cost.compute)} compute {node.cost.researchPoints > 0 && ` · ${node.cost.researchPoints} RP`}
{canStart && ( )} {canQueue && ( )} {isQueued && ( )}
{node.prerequisites.length > 0 && isLocked && (
Requires: {node.prerequisites.map(p => TECH_TREE.find(n => n.id === p)?.name ?? p ).join(', ')}
)}
); })}
); })}
); }