import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import Image from 'next/image'; import { GameState, GamePhase, Player, GAME_CONFIG, Faction } from '../../../shared/types'; import MissionReveal from './MissionReveal'; import MissionResult from './MissionResult'; import VictoryScreen from './VictoryScreen'; import ExitGameButton from './ExitGameButton'; interface GameBoardProps { gameState: GameState; currentPlayerId: string; actions: any; fullPlayerName: string; } export default function GameBoard({ gameState, currentPlayerId, actions, fullPlayerName }: GameBoardProps) { const [selectedTeam, setSelectedTeam] = useState([]); // Hooks para FASE REVEAL ROLE const [revealCard, setRevealCard] = useState(false); // Orden aleatorio de cartas de misión (se genera una vez) const [cardOrder] = useState(() => Math.random() > 0.5); // Track del voto de misión del jugador const [missionVote, setMissionVote] = useState(null); const [expandedMission, setExpandedMission] = useState(null); // Estado para controlar el colapso del panel de jugadores const [isPlayersCollapsed, setIsPlayersCollapsed] = useState(true); // Estado para controlar el colapso del historial de misiones const [isHistoryCollapsed, setIsHistoryCollapsed] = useState(true); // Timer para avanzar automáticamente en REVEAL_ROLE useEffect(() => { if (gameState.phase === 'reveal_role' as any) { const timer = setTimeout(() => { actions.finishReveal(); }, 10000); return () => clearTimeout(timer); } }, [gameState.phase, actions]); // Reset missionVote cuando cambia la fase useEffect(() => { if (gameState.phase !== GamePhase.MISSION) { setMissionVote(null); } }, [gameState.phase]); // Reset selectedTeam cuando no estamos en TEAM_BUILDING o cambia el líder useEffect(() => { if (gameState.phase !== GamePhase.TEAM_BUILDING) { setSelectedTeam([]); } }, [gameState.phase, gameState.currentLeaderId]); // Estado para controlar cuándo mostrar el tablero const [showBoard, setShowBoard] = useState(false); // Mostrar tablero durante MISSION_RESULT useEffect(() => { if (gameState.phase === GamePhase.MISSION_RESULT) { setShowBoard(true); } else { setShowBoard(false); } }, [gameState.phase]); const currentPlayer = gameState.players.find(p => p.id === currentPlayerId); const isLeader = gameState.currentLeaderId === currentPlayerId; // FIX: Usar currentLeaderId del estado const config = GAME_CONFIG[gameState.players.length as keyof typeof GAME_CONFIG]; const currentQuestSize = config?.quests[gameState.currentRound - 1]; // Manejar selección de equipo const toggleTeamSelection = (playerId: string) => { if (selectedTeam.includes(playerId)) { setSelectedTeam(selectedTeam.filter(id => id !== playerId)); } else { if (selectedTeam.length < currentQuestSize) { setSelectedTeam([...selectedTeam, playerId]); } } }; const handleMissionVote = (vote: boolean) => { setMissionVote(vote); actions.voteMission(vote); }; // Coordenadas porcentuales de los hexágonos de misión en el mapa const missionCoords = [ { left: '18%', top: '60%' }, // Misión 1 - Abajo izquierda { left: '25%', top: '18%' }, // Misión 2 - Arriba izquierda { left: '50%', top: '75%' }, // Misión 3 - Abajo centro { left: '50%', top: '30%' }, // Misión 4 - Centro { left: '80%', top: '45%' }, // Misión 5 - Derecha ]; // Nombres de las misiones const missionNames = [ 'Sabotaje en el Tren', 'Rescate del Prisionero', 'Destrucción del Puente', 'Robo de Documentos', 'Asalto al Cuartel General' ]; // --- UI/Efectos para FASES TEMPRANAS --- const isHost = gameState.hostId === currentPlayerId; // FASE INTRO if (gameState.phase === 'intro' as any) { return (
Battlefield

Traición en París

{/* Audio Auto-Play - Solo para el host */} {isHost && (
); } // FASE REVEAL ROLE NO HOOKS HERE if (gameState.phase === 'reveal_role' as any) { // Determinar imagen basada en el rol // Mapeo actualizado: // Marlene -> good_merlin.png // Capitán Philippe -> good_percival.png // Partisano -> good_soldier_X.png (random) // Francotirador -> evil_assassin.png // Agente Doble -> evil_morgana.png // Comandante Schmidt -> evil_mordred.png // Infiltrado -> evil_oberon.png // Colaboracionista -> evil_minion_X.png let roleImage = '/assets/images/characters/good_soldier_1.png'; // Default const role = currentPlayer?.role; if (role === 'marlene') roleImage = '/assets/images/characters/good_merlin.png'; else if (role === 'francotirador') roleImage = '/assets/images/characters/evil_assassin.png'; else if (role === 'capitan_philippe') roleImage = '/assets/images/characters/good_percival.png'; else if (role === 'agente_doble') roleImage = '/assets/images/characters/evil_morgana.png'; else if (role === 'comandante_schmidt') roleImage = '/assets/images/characters/evil_mordred.png'; else if (role === 'infiltrado') roleImage = '/assets/images/characters/evil_oberon.png'; else if (role === 'partisano') { // Random soldier 1-5 const idx = (currentPlayerId.charCodeAt(0) % 5) + 1; roleImage = `/assets/images/characters/good_soldier_${idx}.png`; } else if (role === 'colaboracionista') { // Random minion 1-3 const idx = (currentPlayerId.charCodeAt(0) % 3) + 1; roleImage = `/assets/images/characters/evil_minion_${idx}.png`; } return (
{/* FONDO (Mismo que Roll Call) */}
Resistance HQ

Tu Identidad Secreta

Desliza hacia arriba para revelar

{/* Carta Revelada (Fondo) */}
Role
{role?.replace('_', ' ')}
{/* Reverso de Carta (Draggable) */} Card Back
); } // FASE ROLL CALL if (gameState.phase === 'roll_call' as any) { return (
Resistance HQ
{/* --- 1. SECCIÓN SUPERIOR: TÍTULO (20-25% altura) --- */}

Pasando Lista...

{/* --- 2. SECCIÓN INFERIOR: JUGADORES (Resto de altura) --- */}
{isHost && (
); } return (
{/* Botón de Salir de la Partida - No mostrar en pantallas de victoria */} {gameState.phase !== GamePhase.ALLIED_WIN && gameState.phase !== GamePhase.NAZIS_WIN && ( actions.leaveGame()} playerName={fullPlayerName} /> )} {/* Fondo */}
Game Background
{/* Contenedor principal */}
{/* --- MAPA TÁCTICO (TABLERO) O CARTA DE MISIÓN O ASSASSIN_PHASE --- */} {/* No mostrar el tablero en fases de victoria */} {gameState.phase !== GamePhase.ALLIED_WIN && gameState.phase !== GamePhase.NAZIS_WIN && (
{gameState.phase === GamePhase.ASSASSIN_PHASE ? ( /* IMAGEN LASTSHOT PARA ASSASSIN_PHASE */ <> Last Shot {/* Overlay oscuro para mejorar legibilidad */}
{/* Título sobre la imagen - centrado verticalmente */}

¡ÚLTIMA OPORTUNIDAD!

{currentPlayer?.role === 'francotirador' ? (

Francotirador, elige a quién crees que es MARLENE

) : (

El Francotirador está decidiendo...

)}
) : showBoard ? ( <> {/* TABLERO CON TOKENS */} Tactical Map {/* TOKENS SOBRE EL MAPA */} {missionCoords.map((coord, idx) => { const result = gameState.questResults[idx]; const isCurrent = gameState.currentRound === idx + 1; return (
{/* Marcador de Ronda Actual */} {isCurrent && ( Current Round )} {/* Resultado de Misión (Éxito/Fracaso) */} {result === true && (
Success
)} {result === false && (
Fail
)}
); })} ) : ( /* CARTA DE MISIÓN CON TÍTULO */ <> {`Mission {/* Título y subtítulo sobre la carta */}

Misión {gameState.currentRound}

{missionNames[gameState.currentRound - 1]}

)}
)} {/* --- ÁREA DE JUEGO (CARTAS Y ACCIONES) --- */}
{/* FASE: VOTACIÓN DE LÍDER */} {gameState.phase === 'vote_leader' as any && (

Confirmar Líder

¿Aceptas a {gameState.players.find(p => p.id === gameState.currentLeaderId)?.name} como Líder?
{/* Timer visual (solo muestra el tiempo, el servidor controla el timeout) */} {!gameState.leaderVotes?.[currentPlayerId] && ( )}
{gameState.leaderVotes?.[currentPlayerId] === undefined ? (
) : (
VOTO REGISTRADO. ESPERANDO AL RESTO...
)}
)} {/* FASE: CONSTRUCCIÓN DE EQUIPO */} {gameState.phase === GamePhase.TEAM_BUILDING && ( {/* Información del líder */} {/* Información del líder - SOLO para NO líderes */} {!isLeader && (
Líder Actual
{gameState.players.find(p => p.id === gameState.currentLeaderId)?.name || 'Desconocido'}
)} {/* Mensaje para el líder o para los demás */} {/* Mensaje para el líder o para los demás */}

{isLeader ? '🎯 TU TURNO: ELIGE TU EQUIPO' : '⏳ ESPERANDO AL LÍDER...'}

{isLeader && (

Se necesitan {currentQuestSize} agentes para la misión #{gameState.currentRound}.

)} {/* Contador de seleccionados */} {isLeader && (
Seleccionados: {selectedTeam.length} / {currentQuestSize}
)} {isLeader && ( )} {!isLeader && (
El líder está seleccionando el equipo de misión...
)}
)} {/* FASE: VOTACIÓN DE EQUIPO */} {gameState.phase === GamePhase.VOTING_TEAM && (

PROPUESTA DE MISIÓN

{gameState.proposedTeam.map(id => { const p = gameState.players.find(pl => pl.id === id); return (
{p?.name}
); })}
{gameState.teamVotes[currentPlayerId] === undefined ? (
) : (
VOTO REGISTRADO. ESPERANDO AL RESTO... ```
)}
)} {/* FASE: MISIÓN */} {gameState.phase === GamePhase.MISSION && ( {gameState.proposedTeam.includes(currentPlayerId) ? (

🎯 REALIZA LA MISIÓN

Elige si quieres un éxito o un fracaso

{/* Cartas en orden aleatorio */}
{cardOrder ? ( <> {/* Carta de Éxito primero */} {/* Carta de Sabotaje segundo (solo para alemanes) */} {currentPlayer?.faction === Faction.ALEMANES && ( )} ) : ( <> {/* Carta de Sabotaje primero (solo para alemanes) */} {currentPlayer?.faction === Faction.ALEMANES && ( )} {/* Carta de Éxito segundo */} )}
) : (
La misión está en curso...
Esperando a que el equipo complete su votación.
)}
)} {/* FASE: REVELACIÓN DE CARTAS */} {gameState.phase === 'mission_reveal' as any && ( actions.finishMissionReveal()} /> )} {/* FASE: RESULTADO DE MISIÓN */} {gameState.phase === 'mission_result' as any && ( isHost && actions.finishMissionResult()} /> )} {/* FASE: ASESINO (FRANCOTIRADOR) */} {gameState.phase === GamePhase.ASSASSIN_PHASE && ( {currentPlayer?.role === 'francotirador' && (
{gameState.players .filter(p => p.faction === Faction.ALIADOS) // Solo jugadores Aliados .map(player => ( actions.assassinKill(player.id)} className="bg-black/60 hover:bg-red-600/70 border-2 border-white/30 hover:border-red-500 p-3 rounded-lg transition-all backdrop-blur-sm flex flex-col items-center gap-2" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} >
{player.name}

{player.name}

))}
)} {currentPlayer?.role !== 'francotirador' && (
El Francotirador está decidiendo...
)}
)} {/* FASE: VICTORIA NAZIS */} {gameState.phase === GamePhase.NAZIS_WIN && ( actions.restartGame()} onFinalize={() => actions.finalizeGame()} /> )} {/* FASE: VICTORIA ALIADOS */} {gameState.phase === GamePhase.ALLIED_WIN && ( actions.restartGame()} onFinalize={() => actions.finalizeGame()} /> )}
{/* JUGADORES - POSICIONADOS ABSOLUTAMENTE EN EL FONDO */} {/* Botón de colapso/expansión */}
{gameState.players.map((player) => { const isSelected = selectedTeam.includes(player.id); const isMe = player.id === currentPlayerId; // Avatar logic const avatarSrc = `/assets/images/characters/${player.avatar}`; return (
isLeader && gameState.phase === GamePhase.TEAM_BUILDING && toggleTeamSelection(player.id)} className={` relative flex flex-col items-center cursor-pointer transition-all duration-300 group ${isSelected ? 'scale-110 z-10' : 'scale-100 opacity-70 hover:opacity-100 hover:scale-105'} `} > {/* Avatar */}
{player.name} {/* Icono de Líder */} {gameState.currentLeaderId === player.id && (
L
)} {/* Icono de Miembro del Equipo de Misión */} {gameState.proposedTeam.includes(player.id) && ( gameState.phase === GamePhase.VOTING_TEAM || gameState.phase === GamePhase.MISSION || gameState.phase === 'mission_reveal' as any || gameState.phase === 'mission_result' as any ) && (
)}
{/* Nombre */} {player.name}
); })}
{/* HISTÓRICO DE MISIONES (Esquina superior derecha) */} {gameState.missionHistory.length > 0 && ( {/* Botón de colapso/expansión */} setIsHistoryCollapsed(!isHistoryCollapsed)} className="absolute top-0 bg-gradient-to-l from-yellow-600 to-yellow-700 hover:from-yellow-500 hover:to-yellow-600 text-white rounded-l-lg px-2 py-3 shadow-lg border-2 border-yellow-500 border-r-0 transition-all hover:shadow-yellow-500/50 flex items-center" initial={false} animate={{ right: isHistoryCollapsed ? '0px' : '100%' }} transition={{ type: "spring", stiffness: 300, damping: 30 }} > {/* Panel del historial */}
Historial
{gameState.missionHistory.map((mission, idx) => { const isExpanded = expandedMission === idx; return (
{ e.stopPropagation(); console.log('Click en misión', idx); setExpandedMission(prev => prev === idx ? null : idx); }} > {mission.round}
{/* Lista de participantes */} {isExpanded && (
{mission.team.map((playerId) => { const player = gameState.players.find(p => p.id === playerId); return (
{player?.name || playerId}
); })}
)}
); })}
)}
); } // Subcomponente para el Timer de Votación (solo visual, el servidor controla el timeout real) function VotingTimer() { const [timeLeft, setTimeLeft] = useState(10); useEffect(() => { if (timeLeft <= 0) { return; // El servidor se encargará de forzar la resolución } const interval = setInterval(() => setTimeLeft(t => t - 1), 1000); return () => clearInterval(interval); }, [timeLeft]); return (
{timeLeft}
); }