Release v1.0 - Primera versión estable de Francia Ocupada
- Implementación completa del juego La Resistencia - Sistema de roles: Aliados y Nazis (incluyendo Francotirador) - Fases del juego: Selección de equipo, votación, misión, asesinato - Interfaz de usuario con imágenes temáticas - Sistema de WebSockets para multijugador en tiempo real - Configuración Docker para desarrollo y producción - Dockerfiles optimizados para cliente y servidor - docker-compose.yml para desarrollo local - docker-compose_prod.yml para despliegue en producción con Nginx Proxy Manager - Base de datos PostgreSQL integrada - Documentación de cambios y fases del juego
This commit is contained in:
@@ -23,6 +23,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
||||
|
||||
// Track del voto de misión del jugador
|
||||
const [missionVote, setMissionVote] = useState<boolean | null>(null);
|
||||
const [expandedMission, setExpandedMission] = useState<number | null>(null);
|
||||
|
||||
|
||||
// Timer para avanzar automáticamente en REVEAL_ROLE
|
||||
@@ -628,7 +629,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
||||
{/* Carta de Éxito primero */}
|
||||
<button
|
||||
onClick={() => handleMissionVote(true)}
|
||||
className={`group transition-opacity ${missionVote !== null && missionVote !== true ? 'opacity-50' : 'opacity-100'}`}
|
||||
className={`group transition-opacity ${missionVote === true ? 'opacity-100' : 'opacity-50'}`}
|
||||
disabled={missionVote !== null}
|
||||
>
|
||||
<motion.div
|
||||
@@ -643,7 +644,11 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
||||
|
||||
{/* Carta de Sabotaje segundo (solo para alemanes) */}
|
||||
{currentPlayer?.faction === Faction.ALEMANES && (
|
||||
<button onClick={() => handleMissionVote(false)} className="group transition-opacity" style={{ opacity: missionVote !== null && missionVote !== false ? 0.5 : 1 }} disabled={missionVote !== null}>
|
||||
<button
|
||||
onClick={() => handleMissionVote(false)}
|
||||
className={`group transition-opacity ${missionVote === false ? 'opacity-100' : 'opacity-50'}`}
|
||||
disabled={missionVote !== null}
|
||||
>
|
||||
<motion.div
|
||||
className="w-64 h-96 bg-gradient-to-br from-red-600 to-red-900 rounded-2xl shadow-2xl border-4 border-red-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:-rotate-3 hover:shadow-red-500/50"
|
||||
whileHover={{ scale: 1.1, rotate: -3 }}
|
||||
@@ -659,7 +664,11 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
||||
<>
|
||||
{/* Carta de Sabotaje primero (solo para alemanes) */}
|
||||
{currentPlayer?.faction === Faction.ALEMANES && (
|
||||
<button onClick={() => handleMissionVote(false)} className="group transition-opacity" style={{ opacity: missionVote !== null && missionVote !== false ? 0.5 : 1 }} disabled={missionVote !== null}>
|
||||
<button
|
||||
onClick={() => handleMissionVote(false)}
|
||||
className={`group transition-opacity ${missionVote === false ? 'opacity-100' : 'opacity-50'}`}
|
||||
disabled={missionVote !== null}
|
||||
>
|
||||
<motion.div
|
||||
className="w-64 h-96 bg-gradient-to-br from-red-600 to-red-900 rounded-2xl shadow-2xl border-4 border-red-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:-rotate-3 hover:shadow-red-500/50"
|
||||
whileHover={{ scale: 1.1, rotate: -3 }}
|
||||
@@ -674,7 +683,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
||||
{/* Carta de Éxito segundo */}
|
||||
<button
|
||||
onClick={() => handleMissionVote(true)}
|
||||
className={`group transition-opacity ${missionVote !== null && missionVote !== true ? 'opacity-50' : 'opacity-100'}`}
|
||||
className={`group transition-opacity ${missionVote === true ? 'opacity-100' : 'opacity-50'}`}
|
||||
disabled={missionVote !== null}
|
||||
>
|
||||
<motion.div
|
||||
@@ -848,18 +857,42 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
||||
<div className="absolute top-4 right-4 bg-black/80 p-3 rounded-lg border border-white/20 backdrop-blur-sm">
|
||||
<div className="text-[10px] text-gray-400 uppercase mb-2 text-center font-bold tracking-wider">Historial</div>
|
||||
<div className="flex gap-2">
|
||||
{gameState.missionHistory.map((mission, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold border-2 ${mission.isSuccess
|
||||
? 'bg-blue-600 border-blue-400 text-white'
|
||||
: 'bg-red-600 border-red-400 text-white'
|
||||
}`}
|
||||
title={`Misión ${mission.round}: ${mission.isSuccess ? 'Éxito' : 'Fracaso'} (${mission.successes}✓ ${mission.fails}✗)`}
|
||||
>
|
||||
{mission.round}
|
||||
</div>
|
||||
))}
|
||||
{gameState.missionHistory.map((mission, idx) => {
|
||||
const isExpanded = expandedMission === idx;
|
||||
|
||||
return (
|
||||
<div key={idx} className="relative">
|
||||
<div
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold border-2 cursor-pointer transition-all hover:scale-110 ${mission.isSuccess
|
||||
? 'bg-blue-600 border-blue-400 text-white'
|
||||
: 'bg-red-600 border-red-400 text-white'
|
||||
} ${isExpanded ? 'ring-2 ring-yellow-400' : ''}`}
|
||||
title={`Misión ${mission.round}: ${mission.isSuccess ? 'Éxito' : 'Fracaso'} (${mission.successes}✓ ${mission.fails}✗)`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
console.log('Click en misión', idx, 'Estado actual:', expandedMission);
|
||||
setExpandedMission(isExpanded ? null : idx);
|
||||
}}
|
||||
>
|
||||
{mission.round}
|
||||
</div>
|
||||
|
||||
{/* Lista de participantes */}
|
||||
{isExpanded && (
|
||||
<div className="absolute top-10 right-0 bg-black/95 p-2 rounded border border-white/30 min-w-max z-[100]">
|
||||
{mission.team.map((playerId) => {
|
||||
const player = gameState.players.find(p => p.id === playerId);
|
||||
return (
|
||||
<div key={playerId} className="text-xs text-white whitespace-nowrap">
|
||||
{player?.name || playerId}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user