import React from 'react' import { Box, Text, type Color } from '@anthropic/ink' import type { BuddyData, SpeciesId } from '../types' import { ALL_SPECIES_IDS } from '../types' import { getSpeciesData } from '../dex/species' const CYAN: Color = 'ansi:cyan' const GREEN: Color = 'ansi:green' const GRAY: Color = 'ansi:white' const YELLOW: Color = 'ansi:yellow' const WHITE: Color = 'ansi:whiteBright' const BAR_WIDTH = 30 /** Gen ranges for stats */ const GEN_RANGES = [ { label: 'Gen I', start: 1, end: 151 }, { label: 'Gen II', start: 152, end: 251 }, { label: 'Gen III', start: 252, end: 386 }, { label: 'Gen IV', start: 387, end: 493 }, { label: 'Gen V', start: 494, end: 649 }, { label: 'Gen VI', start: 650, end: 721 }, { label: 'Gen VII', start: 722, end: 809 }, { label: 'Gen VIII',start: 810, end: 905 }, { label: 'Gen IX', start: 906, end: 1025 }, ] interface PokedexViewProps { buddyData: BuddyData } /** * Pokédex view — shows collection progress, per-gen stats, * and discovered species list. */ export function PokedexView({ buddyData }: PokedexViewProps) { const dexMap = new Map(buddyData.dex.map((d) => [d.speciesId, d])) const collected = buddyData.dex.length const total = ALL_SPECIES_IDS.length const percent = total > 0 ? collected / total : 0 // Build dex number set for quick lookup const collectedNums = new Set() for (const entry of buddyData.dex) { const data = getSpeciesData(entry.speciesId) collectedNums.add(data.dexNumber) } // Per-gen stats const genStats = GEN_RANGES.map(g => { const genTotal = ALL_SPECIES_IDS.filter(id => { const n = getSpeciesData(id).dexNumber return n >= g.start && n <= g.end }).length const genCollected = [...collectedNums].filter(n => n >= g.start && n <= g.end).length return { ...g, total: genTotal, collected: genCollected } }) // Discovered species (for compact display) const discovered = buddyData.dex .map(entry => { const species = getSpeciesData(entry.speciesId) return { entry, species } }) .sort((a, b) => a.species.dexNumber - b.species.dexNumber) return ( {/* Header with percentage */} Pokédex {collected} /{total} {(percent * 100).toFixed(1)}% {/* Fixed-width progress bar */} {'█'.repeat(Math.round(percent * BAR_WIDTH))} {'░'.repeat(BAR_WIDTH - Math.round(percent * BAR_WIDTH))} {Math.floor(percent * 100)}% {/* Per-gen stats */} ─── 分代统计 ─── {genStats.map(g => { const p = g.total > 0 ? g.collected / g.total : 0 const miniBar = '█'.repeat(Math.round(p * 10)) + '░'.repeat(10 - Math.round(p * 10)) return ( {g.label.padEnd(8)} = 1 ? GREEN : p > 0 ? YELLOW : GRAY}>{miniBar} {g.collected}/{g.total} ) })} {/* Discovered species list */} {discovered.length > 0 && ( ─── 已发现 ({discovered.length}) ─── {discovered.map(({ entry, species }) => { const isActive = buddyData.party[0] ? buddyData.creatures.some(c => c.id === buddyData.party[0] && c.speciesId === species.id) : false return ( {isActive ? : ' '} #{String(species.dexNumber).padStart(3, '0')} {species.name} {' '} {species.types.filter((t): t is string => Boolean(t)).map((t, ti) => ( {ti > 0 ? '/' : ''}{t.slice(0, 3).toUpperCase()} ))} Lv.{entry.bestLevel} {entry.caughtCount > 1 && ( x{entry.caughtCount} )} ) })} )} {discovered.length === 0 && ( 还没有发现任何精灵,开始冒险吧! )} {/* Stats row */} ─── Stats ─── Turns: {buddyData.stats.totalTurns} Days: {buddyData.stats.consecutiveDays} Eggs: {buddyData.stats.totalEggsObtained} Evolutions: {buddyData.stats.totalEvolutions} {/* Egg info */} {buddyData.eggs.length > 0 && ( 🥚 Egg: {buddyData.eggs[0].stepsRemaining}/{buddyData.eggs[0].totalSteps} steps )} {buddyData.stats.consecutiveDays < 7 && ( Next egg: {7 - buddyData.stats.consecutiveDays} more days )} ) } /** Type → color mapping */ function getTypeColor(type: string): Color { const colors: Record = { grass: 'ansi:green', poison: 'ansi:magenta', fire: 'ansi:red', flying: 'ansi:cyan', water: 'ansi:blue', electric: 'ansi:yellow', normal: 'ansi:white', } return colors[type] ?? 'ansi:white' }