feat: Fix victory screens background images

- Changed GameBoard background to show mission_success.png for ALLIED_WIN and mission_fail.png for NAZIS_WIN
- Hidden board area completely during victory phases
- Removed redundant background image from VictoryScreen component
- Fixed image extensions from .jpg to .png for victory backgrounds
This commit is contained in:
Resistencia Dev
2025-12-08 22:39:05 +01:00
parent f09e14f99a
commit 6e65152648
2 changed files with 158 additions and 166 deletions

View File

@@ -291,113 +291,156 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
<div className="relative w-full h-screen flex flex-col items-center overflow-hidden"> <div className="relative w-full h-screen flex flex-col items-center overflow-hidden">
<div className="absolute inset-0 z-0 opacity-40"> <div className="absolute inset-0 z-0 opacity-40">
<Image src="/assets/images/ui/bg_game.png" alt="Game Background" fill className="object-cover" /> <Image
src={
gameState.phase === GamePhase.ALLIED_WIN
? "/assets/images/tokens/mission_success.png"
: gameState.phase === GamePhase.NAZIS_WIN
? "/assets/images/tokens/mission_fail.png"
: "/assets/images/ui/bg_game.png"
}
alt="Game Background"
fill
className="object-cover"
/>
<div className="absolute inset-0 bg-black/60" /> <div className="absolute inset-0 bg-black/60" />
</div> </div>
<div className="relative z-10 w-full flex flex-col items-center"> <div className="relative z-10 w-full flex flex-col items-center">
{/* --- MAPA TÁCTICO (TABLERO) O CARTA DE MISIÓN --- */} {/* --- MAPA TÁCTICO (TABLERO) O CARTA DE MISIÓN O ASSASSIN_PHASE --- */}
<div className="relative w-full max-w-5xl aspect-video mt-4 shadow-2xl border-4 border-gray-800 rounded-lg overflow-hidden bg-[#2a2a2a]"> {/* No mostrar el tablero en fases de victoria */}
{showBoard ? ( {gameState.phase !== GamePhase.ALLIED_WIN && gameState.phase !== GamePhase.NAZIS_WIN && (
<> <div className="relative w-full max-w-5xl aspect-video mt-4 shadow-2xl border-4 border-gray-800 rounded-lg overflow-hidden bg-[#2a2a2a]">
{/* TABLERO CON TOKENS */} {gameState.phase === GamePhase.ASSASSIN_PHASE ? (
<Image /* IMAGEN LASTSHOT PARA ASSASSIN_PHASE */
src="/assets/images/ui/board_map.jpg" <>
alt="Tactical Map" <Image
fill src="/assets/images/tokens/lastshot.jpg"
className="object-contain" alt="Last Shot"
/> fill
className="object-cover"
priority
/>
{/* Overlay oscuro para mejorar legibilidad */}
<div className="absolute inset-0 bg-black/40" />
{/* TOKENS SOBRE EL MAPA */} {/* Título sobre la imagen */}
{missionCoords.map((coord, idx) => { <div className="absolute top-4 left-0 right-0 flex flex-col items-center z-10">
const result = gameState.questResults[idx]; <h1 className="text-5xl font-bold text-red-600 mb-2 drop-shadow-[0_4px_8px_rgba(0,0,0,0.9)]">
const isCurrent = gameState.currentRound === idx + 1; ¡ÚLTIMA OPORTUNIDAD!
</h1>
return ( {currentPlayer?.role === 'francotirador' ? (
<div <p className="text-xl text-white drop-shadow-[0_2px_4px_rgba(0,0,0,0.9)] font-bold">
key={idx} Francotirador, elige a quién crees que es <span className="text-yellow-400">MARLENE</span>
className="absolute w-[10%] aspect-square flex items-center justify-center" </p>
style={{ ) : (
left: coord.left, <p className="text-xl text-gray-300 drop-shadow-[0_2px_4px_rgba(0,0,0,0.9)] font-bold">
top: coord.top, El Francotirador está decidiendo...
transform: 'translate(-50%, -50%)' </p>
}} )}
>
{/* Marcador de Ronda Actual */}
{isCurrent && (
<motion.div
layoutId="round-marker"
className="absolute inset-0 z-10"
initial={{ scale: 1.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<Image
src="/assets/images/tokens/marker_round.png"
alt="Current Round"
fill
className="object-contain drop-shadow-lg"
/>
</motion.div>
)}
{/* Resultado de Misión (Éxito/Fracaso) */}
{result === true && (
<motion.div
initial={{ scale: 0 }} animate={{ scale: 1 }}
className="absolute inset-0 z-20 flex items-center justify-center"
>
<div className="w-[80%] h-[80%] relative">
<Image src="/assets/images/tokens/marker_score_blue.png" alt="Success" fill className="object-contain drop-shadow-lg" />
</div>
</motion.div>
)}
{result === false && (
<motion.div
initial={{ scale: 0 }} animate={{ scale: 1 }}
className="absolute inset-0 z-20 flex items-center justify-center"
>
<div className="w-[80%] h-[80%] relative">
<Image src="/assets/images/tokens/marker_score_red.png" alt="Fail" fill className="object-contain drop-shadow-lg" />
</div>
</motion.div>
)}
</div>
);
})}
{/* TRACK DE VOTOS FALLIDOS */}
<div className="absolute bottom-[5%] left-[2%] bg-black/60 p-2 rounded border border-white/20">
<div className="text-[10px] text-gray-300 uppercase mb-1 text-center">Votos Rechazados</div>
<div className="flex gap-1">
{[...Array(5)].map((_, i) => (
<div key={i} className={`w-3 h-3 rounded-full border border-gray-500 ${i < gameState.failedVotesCount ? 'bg-red-500' : 'bg-transparent'}`} />
))}
</div> </div>
</div> </>
</> ) : showBoard ? (
) : ( <>
/* CARTA DE MISIÓN CON TÍTULO */ {/* TABLERO CON TOKENS */}
<> <Image
<Image src="/assets/images/ui/board_map.jpg"
src={`/assets/images/missions/mission${gameState.currentRound}.png`} alt="Tactical Map"
alt={`Mission ${gameState.currentRound}`} fill
fill className="object-contain"
className="object-contain" />
/>
{/* Título y subtítulo sobre la carta */} {/* TOKENS SOBRE EL MAPA */}
<div className="absolute top-4 left-0 right-0 flex flex-col items-center z-10"> {missionCoords.map((coord, idx) => {
<h2 className="text-4xl font-bold text-white drop-shadow-[0_4px_8px_rgba(0,0,0,0.8)] mb-2 uppercase tracking-wider"> const result = gameState.questResults[idx];
Misión {gameState.currentRound} const isCurrent = gameState.currentRound === idx + 1;
</h2>
<h3 className="text-2xl font-semibold text-yellow-400 drop-shadow-[0_4px_8px_rgba(0,0,0,0.8)] uppercase tracking-wide"> return (
{missionNames[gameState.currentRound - 1]} <div
</h3> key={idx}
</div> className="absolute w-[10%] aspect-square flex items-center justify-center"
</> style={{
)} left: coord.left,
</div> top: coord.top,
transform: 'translate(-50%, -50%)'
}}
>
{/* Marcador de Ronda Actual */}
{isCurrent && (
<motion.div
layoutId="round-marker"
className="absolute inset-0 z-10"
initial={{ scale: 1.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<Image
src="/assets/images/tokens/marker_round.png"
alt="Current Round"
fill
className="object-contain drop-shadow-lg"
/>
</motion.div>
)}
{/* Resultado de Misión (Éxito/Fracaso) */}
{result === true && (
<motion.div
initial={{ scale: 0 }} animate={{ scale: 1 }}
className="absolute inset-0 z-20 flex items-center justify-center"
>
<div className="w-[80%] h-[80%] relative">
<Image src="/assets/images/tokens/marker_score_blue.png" alt="Success" fill className="object-contain drop-shadow-lg" />
</div>
</motion.div>
)}
{result === false && (
<motion.div
initial={{ scale: 0 }} animate={{ scale: 1 }}
className="absolute inset-0 z-20 flex items-center justify-center"
>
<div className="w-[80%] h-[80%] relative">
<Image src="/assets/images/tokens/marker_score_red.png" alt="Fail" fill className="object-contain drop-shadow-lg" />
</div>
</motion.div>
)}
</div>
);
})}
{/* TRACK DE VOTOS FALLIDOS */}
<div className="absolute bottom-[5%] left-[2%] bg-black/60 p-2 rounded border border-white/20">
<div className="text-[10px] text-gray-300 uppercase mb-1 text-center">Votos Rechazados</div>
<div className="flex gap-1">
{[...Array(5)].map((_, i) => (
<div key={i} className={`w-3 h-3 rounded-full border border-gray-500 ${i < gameState.failedVotesCount ? 'bg-red-500' : 'bg-transparent'}`} />
))}
</div>
</div>
</>
) : (
/* CARTA DE MISIÓN CON TÍTULO */
<>
<Image
src={`/assets/images/missions/mission${gameState.currentRound}.png`}
alt={`Mission ${gameState.currentRound}`}
fill
className="object-contain"
/>
{/* Título y subtítulo sobre la carta */}
<div className="absolute top-4 left-0 right-0 flex flex-col items-center z-10">
<h2 className="text-4xl font-bold text-white drop-shadow-[0_4px_8px_rgba(0,0,0,0.8)] mb-2 uppercase tracking-wider">
Misión {gameState.currentRound}
</h2>
<h3 className="text-2xl font-semibold text-yellow-400 drop-shadow-[0_4px_8px_rgba(0,0,0,0.8)] uppercase tracking-wide">
{missionNames[gameState.currentRound - 1]}
</h3>
</div>
</>
)}
</div>
)}
{/* --- ÁREA DE JUEGO (CARTAS Y ACCIONES) --- */} {/* --- ÁREA DE JUEGO (CARTAS Y ACCIONES) --- */}
<div className="flex-1 w-full max-w-6xl relative mt-4 px-4"> <div className="flex-1 w-full max-w-6xl relative mt-4 px-4">
@@ -677,68 +720,23 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
{/* FASE: ASESINO (FRANCOTIRADOR) */} {/* FASE: ASESINO (FRANCOTIRADOR) */}
{gameState.phase === GamePhase.ASSASSIN_PHASE && ( {gameState.phase === GamePhase.ASSASSIN_PHASE && (
<motion.div <motion.div
className="fixed inset-0 flex flex-col items-center justify-center z-50 relative" className="w-full flex flex-col items-center gap-6"
initial={{ opacity: 0 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1 }} animate={{ opacity: 1, y: 0 }}
> >
{/* Imagen de fondo solo para el Francotirador */}
{currentPlayer?.role === 'francotirador' && ( {currentPlayer?.role === 'francotirador' && (
<div className="absolute inset-0 z-0"> <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 max-w-4xl">
<Image
src="/assets/images/tokens/lastshot.jpg"
alt="Last Shot"
fill
className="object-cover"
priority
/>
{/* Overlay oscuro para mejorar legibilidad */}
<div className="absolute inset-0 bg-black/50" />
</div>
)}
{/* Fondo oscuro para los demás jugadores */}
{currentPlayer?.role !== 'francotirador' && (
<div className="absolute inset-0 z-0 bg-black/90" />
)}
<motion.div
className="text-center mb-8 relative z-10"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 200 }}
>
<h1 className="text-6xl font-bold text-red-600 mb-4">
¡ÚLTIMA OPORTUNIDAD!
</h1>
{currentPlayer?.role === 'francotirador' ? (
<p className="text-2xl text-white drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)]">
Francotirador, elige a quién crees que es <span className="text-yellow-400 font-bold">MARLENE</span>
</p>
) : (
<p className="text-2xl text-gray-400">
El Francotirador está decidiendo...
</p>
)}
</motion.div>
{currentPlayer?.role === 'francotirador' && (
<motion.div
className="grid grid-cols-3 gap-6 max-w-4xl relative z-10"
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.5 }}
>
{gameState.players {gameState.players
.filter(p => p.faction === Faction.ALIADOS) // Solo jugadores Aliados .filter(p => p.faction === Faction.ALIADOS) // Solo jugadores Aliados
.map(player => ( .map(player => (
<motion.button <motion.button
key={player.id} key={player.id}
onClick={() => actions.assassinKill(player.id)} onClick={() => actions.assassinKill(player.id)}
className="bg-black/60 hover:bg-red-600/70 border-4 border-white/30 hover:border-red-500 p-6 rounded-xl transition-all backdrop-blur-sm" 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.1 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
> >
<div className="w-32 h-32 mx-auto mb-4 rounded-full border-4 border-white/50 overflow-hidden bg-black relative shadow-2xl"> <div className="w-20 h-20 rounded-full border-2 border-white/50 overflow-hidden bg-black relative shadow-xl">
<Image <Image
src={`/assets/images/characters/${player.avatar}`} src={`/assets/images/characters/${player.avatar}`}
alt={player.name} alt={player.name}
@@ -746,10 +744,16 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
className="object-cover" className="object-cover"
/> />
</div> </div>
<p className="text-white font-bold text-xl drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)]">{player.name}</p> <p className="text-white font-bold text-sm drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)]">{player.name}</p>
</motion.button> </motion.button>
))} ))}
</motion.div> </div>
)}
{currentPlayer?.role !== 'francotirador' && (
<div className="text-white text-xl font-mono bg-black/50 px-6 py-3 rounded-full border border-white/20 animate-pulse">
El Francotirador está decidiendo...
</div>
)} )}
</motion.div> </motion.div>
)} )}

View File

@@ -36,18 +36,6 @@ export default function VictoryScreen({ gameState, isHost, onRestart, onFinalize
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
> >
{/* Imagen de fondo a pantalla completa */}
<div className="absolute inset-0 z-0">
<Image
src={isNazisWin ? '/assets/images/tokens/mission_fail.png' : '/assets/images/tokens/mission_success.png'}
alt={isNazisWin ? 'Victoria Nazi' : 'Victoria Aliada'}
fill
className="object-cover"
priority
/>
{/* Overlay oscuro para mejorar legibilidad */}
<div className="absolute inset-0 bg-black/60" />
</div>
{/* Título de victoria */} {/* Título de victoria */}
<motion.div <motion.div
className="text-center mb-12 relative z-10" className="text-center mb-12 relative z-10"