import React, { useState, useCallback } from 'react' import { Box, Text, useInput } from '@anthropic/ink' import type { BuddyData, Creature, SpeciesId } from '../types' import { getActiveCreature, getCreatureName } from '../core/creature' import { saveBuddyData } from '../core/storage' import { createBattle, executeTurn, type BattleInit } from '../battle/engine' import { settleBattle, applyMoveLearn, applyEvolution } from '../battle/settlement' import { BattleConfigPanel } from './BattleConfigPanel' import { BattleView } from './BattleView' import { SwitchPanel } from './SwitchPanel' import { ItemPanel } from './ItemPanel' import { BattleResultPanel } from './BattleResultPanel' import { MoveLearnPanel } from './MoveLearnPanel' import type { BattleState, PlayerAction } from '../battle/types' type Phase = | 'config' | 'battle' | 'switch' | 'item' | 'result' | 'learnMoves' | 'evolution' | 'done' interface BattleFlowProps { buddyData: BuddyData onClose: () => void } export function BattleFlow({ buddyData: initialData, onClose }: BattleFlowProps) { const [phase, setPhase] = useState('config') const [buddyData, setBuddyData] = useState(initialData) const [battleInit, setBattleInit] = useState(null) const [battleState, setBattleState] = useState(null) const [opponentSpeciesId, setOpponentSpeciesId] = useState('pikachu') const [opponentLevel, setOpponentLevel] = useState(5) const [pendingMoves, setPendingMoves] = useState<{ creatureId: string; moveId: string; moveName: string }[]>([]) const [pendingEvos, setPendingEvos] = useState<{ creatureId: string; from: SpeciesId; to: SpeciesId }[]>([]) const [replaceIndex, setReplaceIndex] = useState(0) // Evolution phase input — must be at top level (React hooks rule) useInput((_input: string, key: { return?: boolean }) => { if (phase === 'evolution' && key.return) { handleEvolutionConfirm() } }) // Config phase: start battle const handleStartBattle = useCallback((speciesId: SpeciesId, level: number) => { setOpponentSpeciesId(speciesId) setOpponentLevel(level) const creatures = buddyData.party .filter((id): id is string => id !== null) .map(id => buddyData.creatures.find(c => c.id === id)) .filter((c): c is Creature => c !== undefined) if (creatures.length === 0) return const bagItems = buddyData.bag.items const init = createBattle(creatures, speciesId, level, bagItems) setBattleInit(init) setBattleState(init.state) setPhase('battle') }, [buddyData]) // Battle phase: handle action const handleAction = useCallback((action: PlayerAction) => { if (!battleInit) return const state = executeTurn(battleInit, action) setBattleState(state) if (state.finished && state.result) { const participants = buddyData.party.filter((id): id is string => id !== null) const result = { ...state.result, participantIds: participants } const settled = settleBattle(buddyData, result, opponentSpeciesId, opponentLevel) setBuddyData(settled.data) setPendingMoves(settled.learnableMoves) setPendingEvos(settled.pendingEvolutions) setBattleState({ ...state, result }) setPhase('result') } }, [battleInit, buddyData, opponentSpeciesId, opponentLevel]) // Result phase: continue to move learning const handleResultContinue = useCallback(() => { if (pendingMoves.length > 0) { setPhase('learnMoves') } else if (pendingEvos.length > 0) { setPhase('evolution') } else { saveBuddyData(buddyData) setPhase('done') onClose() } }, [pendingMoves, pendingEvos, buddyData, onClose]) // Move learning const handleMoveLearn = useCallback((idx: number) => { if (pendingMoves.length === 0) return const move = pendingMoves[0]! const updated = applyMoveLearn(buddyData, move.creatureId, move.moveId, idx) setBuddyData(updated) const remaining = pendingMoves.slice(1) setPendingMoves(remaining) if (remaining.length === 0) { if (pendingEvos.length > 0) { setPhase('evolution') } else { saveBuddyData(updated) setPhase('done') onClose() } } }, [pendingMoves, pendingEvos, buddyData, onClose]) const handleMoveSkip = useCallback(() => { const remaining = pendingMoves.slice(1) setPendingMoves(remaining) if (remaining.length === 0) { if (pendingEvos.length > 0) { setPhase('evolution') } else { saveBuddyData(buddyData) setPhase('done') onClose() } } }, [pendingMoves, pendingEvos, buddyData, onClose]) // Evolution const handleEvolutionConfirm = useCallback(() => { if (pendingEvos.length === 0) return const evo = pendingEvos[0]! const updated = applyEvolution(buddyData, evo.creatureId, evo.to) setBuddyData(updated) const remaining = pendingEvos.slice(1) setPendingEvos(remaining) if (remaining.length === 0) { saveBuddyData(updated) setPhase('done') onClose() } }, [pendingEvos, buddyData, onClose]) // Switch: convert BattlePokemon to Creature for SwitchPanel const partyCreatures = buddyData.party .filter((id): id is string => id !== null) .map(id => buddyData.creatures.find(c => c.id === id)) .filter((c): c is Creature => c !== undefined) // Render by phase switch (phase) { case 'config': return ( ) case 'battle': { if (!battleState) return null return ( ) } case 'switch': { if (!battleState) return null return ( { handleAction({ type: 'switch', creatureId }) setPhase('battle') }} onCancel={() => setPhase('battle')} /> ) } case 'item': { if (!battleState) return null return ( { handleAction({ type: 'item', itemId }) setPhase('battle') }} onCancel={() => setPhase('battle')} /> ) } case 'result': { if (!battleState?.result) return null return ( ) } case 'learnMoves': { if (pendingMoves.length === 0) return null const move = pendingMoves[0]! const creature = buddyData.creatures.find(c => c.id === move.creatureId) if (!creature) return null return ( ) } case 'evolution': { if (pendingEvos.length === 0) return null const evo = pendingEvos[0]! useInput(() => { handleEvolutionConfirm() }) return ( 进化! {evo.from} 正在进化为 {evo.to}! [Enter] 继续 ) } case 'done': return null default: return null } }