import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import Image from 'next/image'; import { GameState, GamePhase, Player, GAME_CONFIG } from '../../../shared/types'; interface GameBoardProps { gameState: GameState; currentPlayerId: string; actions: any; } export default function GameBoard({ gameState, currentPlayerId, actions }: GameBoardProps) { const [selectedTeam, setSelectedTeam] = useState([]); // Hooks para FASE REVEAL ROLE const [revealCard, setRevealCard] = useState(false); // 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]); const currentPlayer = gameState.players.find(p => p.id === currentPlayerId); const isLeader = currentPlayer?.isLeader; 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]); } } }; // Coordenadas porcentuales de los hexágonos de misión en el mapa const missionCoords = [ { left: '12%', top: '55%' }, // Misión 1 { left: '28%', top: '15%' }, // Misión 2 { left: '52%', top: '25%' }, // Misión 3 { left: '42%', top: '70%' }, // Misión 4 { left: '82%', top: '40%' }, // Misión 5 ]; // --- UI/Efectos para FASES TEMPRANAS --- const isHost = gameState.hostId === currentPlayerId; // FASE INTRO if (gameState.phase === 'intro' as any) { return (
Battlefield

Guerra Total

{/* Audio Auto-Play */}
); } // FASE REVEAL ROLE NO HOOKS HERE if (gameState.phase === 'reveal_role' as any) { // Determinar imagen basada en el rol // Mapeo básico: // Merlin -> good_merlin.png // Percival -> good_percival.png // Servant -> good_soldier_X.png (random) // Assassin -> evil_assassin.png // Morgana -> evil_morgana.png // Mordred -> evil_mordred.png // Oberon -> evil_oberon.png // Minion -> evil_minion_X.png let roleImage = '/assets/images/characters/good_soldier_1.png'; // Default const role = currentPlayer?.role; if (role === 'merlin') roleImage = '/assets/images/characters/good_merlin.png'; else if (role === 'assassin') roleImage = '/assets/images/characters/evil_assassin.png'; else if (role === 'percival') roleImage = '/assets/images/characters/good_percival.png'; else if (role === 'morgana') roleImage = '/assets/images/characters/evil_morgana.png'; else if (role === 'mordred') roleImage = '/assets/images/characters/evil_mordred.png'; else if (role === 'oberon') roleImage = '/assets/images/characters/evil_oberon.png'; else if (role === 'loyal_servant') { // Random soldier 1-5 const idx = (currentPlayerId.charCodeAt(0) % 5) + 1; roleImage = `/assets/images/characters/good_soldier_${idx}.png`; } else if (role === 'minion') { // 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) */} { // Reducir umbral a -50 para facilitar if (info.offset.y < -50) { setRevealCard(true); } }} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98, cursor: 'grabbing' }} animate={revealCard ? { y: -1000, opacity: 0 } : { y: 0, opacity: 1 }} className="absolute inset-0 w-full h-full rounded-xl overflow-hidden shadow-2xl z-20 cursor-grab active:cursor-grabbing hover:ring-2 hover:ring-white/50 transition-all" > Card Back
); } // FASE ROLL CALL if (gameState.phase === 'roll_call' as any) { return (
Resistance HQ

Pasando Lista...

{isHost && (
); } return (
Game Background
{/* --- MAPA TÁCTICO (TABLERO) --- */}
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 )}
); })} {/* TRACK DE VOTOS FALLIDOS (Pequeño indicador en la esquina inferior izquierda del mapa) */}
Votos Rechazados
{[...Array(5)].map((_, i) => (
))}
{/* --- Á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 */} {!gameState.leaderVotes?.[currentPlayerId] && ( actions.voteLeader(null)} /> )}
{gameState.leaderVotes?.[currentPlayerId] === undefined ? (
) : (
VOTO REGISTRADO. ESPERANDO AL RESTO...
)}
)} {/* FASE: CONSTRUCCIÓN DE EQUIPO */} {gameState.phase === GamePhase.TEAM_BUILDING && (

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

Se necesitan {currentQuestSize} agentes para esta misión.

{isLeader && ( )}
)} {/* 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}
); })}
{!currentPlayer?.hasVoted ? (
) : (
VOTO REGISTRADO. ESPERANDO AL RESTO...
)}
)} {/* FASE: MISIÓN */} {gameState.phase === GamePhase.MISSION && ( {gameState.proposedTeam.includes(currentPlayerId) ? (

¡ESTÁS EN LA MISIÓN!

{/* Solo los malos pueden sabotear */} {currentPlayer?.faction === 'spies' && ( )}
) : (
La misión está en curso...
Rezando por el éxito.
)}
)} {/* FASE: REVELACIÓN DE CARTAS */} {gameState.phase === 'mission_reveal' as any && ( isHost && actions.finishMissionReveal()} /> )} {/* FASE: RESULTADO DE MISIÓN */} {gameState.phase === 'mission_result' as any && ( isHost && actions.finishMissionResult()} /> )}
{/* JUGADORES (TIENDA DE CAMPAÑA) */}
{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 ${isSelected ? 'scale-110' : 'scale-100 opacity-80 hover:opacity-100'} `} > {/* Avatar */}
{player.name} {/* Icono de Líder */} {player.isLeader && (
L
)}
{/* Nombre */} {player.name} ); })}
{/* HISTÓRICO DE MISIONES (Esquina superior derecha) */} {gameState.missionHistory.length > 0 && (
Historial
{gameState.missionHistory.map((mission, idx) => (
{mission.round}
))}
)}
); } // Subcomponente para el Timer de Votación function VotingTimer({ onTimeout }: { onTimeout: () => void }) { const [timeLeft, setTimeLeft] = useState(10); useEffect(() => { if (timeLeft <= 0) { onTimeout(); return; } const interval = setInterval(() => setTimeLeft(t => t - 1), 1000); return () => clearInterval(interval); }, [timeLeft, onTimeout]); return (
{timeLeft}
); } // Componente para revelar cartas una a una function MissionReveal({ votes, onComplete }: { votes: boolean[], onComplete: () => void }) { const [revealedCount, setRevealedCount] = useState(0); useEffect(() => { if (revealedCount < votes.length) { const timer = setTimeout(() => { setRevealedCount(c => c + 1); }, 2000); // 2 segundos entre carta y carta return () => clearTimeout(timer); } else if (revealedCount === votes.length && votes.length > 0) { // Todas reveladas, esperar 2s más y avanzar const timer = setTimeout(() => { onComplete(); }, 2000); return () => clearTimeout(timer); } }, [revealedCount, votes.length, onComplete]); return (

Revelando Votos...

{votes.slice(0, revealedCount).map((vote, idx) => ( {vote ))}
{revealedCount} / {votes.length} cartas reveladas
); } // Componente para mostrar el resultado de la misión function MissionResult({ gameState, onContinue }: { gameState: any, onContinue: () => void }) { const currentMission = gameState.missionHistory[gameState.missionHistory.length - 1]; const isHost = gameState.hostId === gameState.players[0]?.id; // Simplificado useEffect(() => { // Auto-avanzar después de 5 segundos const timer = setTimeout(() => { onContinue(); }, 5000); return () => clearTimeout(timer); }, [onContinue]); if (!currentMission) return null; const { isSuccess, successes, fails, team, round } = currentMission; return ( {/* Título */}

{isSuccess ? '✓ MISIÓN EXITOSA' : '✗ MISIÓN FALLIDA'}

Misión #{round}

{/* Estadísticas */}
{successes}
Éxitos
{fails}
Sabotajes
{/* Equipo */}

Equipo de Misión:

{team.map((playerId: string) => { const player = gameState.players.find((p: any) => p.id === playerId); return (
{player?.name || 'Desconocido'}
); })}
{/* Mensaje */}
Continuando en breve...
); }