From 44d741825247f26f2f8b318cbea02f0cb62a8a87 Mon Sep 17 00:00:00 2001 From: Resistencia Dev Date: Fri, 5 Dec 2025 22:09:59 +0100 Subject: [PATCH] =?UTF-8?q?Fix:=20GameBoard.tsx=20limpio=20y=20funcional?= =?UTF-8?q?=20-=20removidos=20componentes=20auxiliares=20problem=C3=A1tico?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/GameBoard.tsx | 152 +---- client/src/components/GameBoard.tsx.backup | 711 +++++++++++++++++++++ client/src/components/GameBoard_temp.tsx | 544 ++++++++++++++++ 3 files changed, 1258 insertions(+), 149 deletions(-) create mode 100644 client/src/components/GameBoard.tsx.backup create mode 100644 client/src/components/GameBoard_temp.tsx diff --git a/client/src/components/GameBoard.tsx b/client/src/components/GameBoard.tsx index ad8c854..bca105d 100644 --- a/client/src/components/GameBoard.tsx +++ b/client/src/components/GameBoard.tsx @@ -533,6 +533,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB {player.name} + ); })} @@ -546,11 +547,10 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB {gameState.missionHistory.map((mission, idx) => (
{mission.round} @@ -563,149 +563,3 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
); } - - -// 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... -
-
- ); -} diff --git a/client/src/components/GameBoard.tsx.backup b/client/src/components/GameBoard.tsx.backup new file mode 100644 index 0000000..ad8c854 --- /dev/null +++ b/client/src/components/GameBoard.tsx.backup @@ -0,0 +1,711 @@ +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... +
+
+ ); +} diff --git a/client/src/components/GameBoard_temp.tsx b/client/src/components/GameBoard_temp.tsx new file mode 100644 index 0000000..3896eae --- /dev/null +++ b/client/src/components/GameBoard_temp.tsx @@ -0,0 +1,544 @@ +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