Files
farolero/lib/pantallas/pantalla_fin_partida_online.dart
T

273 lines
9.5 KiB
Dart

import 'package:confetti/confetti.dart';
import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../modelos/gamificacion_usuario.dart';
import '../modelos/inicio_partida_multijugador.dart';
import '../modelos/jugador.dart';
import '../modelos/palabra.dart';
import '../modelos/snapshot_partida_online.dart';
import '../servicios/servicio_historial_partidas.dart';
import '../servicios/servicio_nearby.dart';
import '../servicios/servicio_perfil_usuario.dart';
import '../tema/componentes_farolero.dart';
import '../tema/componentes_resultado_farolero.dart';
import '../tema/tema_app.dart';
import 'pantalla_notas_online.dart';
import 'pantalla_principal.dart';
import 'pantalla_revision_palabra.dart';
class PantallaFinPartidaOnline extends StatefulWidget {
final SnapshotPartidaOnline snapshot;
final List<JugadorInicioPartida> jugadoresControlados;
final String? pistaCategoria;
const PantallaFinPartidaOnline({
super.key,
required this.snapshot,
required this.jugadoresControlados,
this.pistaCategoria,
});
@override
State<PantallaFinPartidaOnline> createState() =>
_PantallaFinPartidaOnlineState();
}
class _PantallaFinPartidaOnlineState extends State<PantallaFinPartidaOnline> {
bool _guardada = false;
ProgresoGamificacionUsuario? _progreso;
late final ConfettiController _confetti;
@override
void initState() {
super.initState();
_confetti = ConfettiController(duration: const Duration(seconds: 5));
Future.delayed(const Duration(milliseconds: 450), () {
if (mounted) _confetti.play();
});
}
@override
void dispose() {
_confetti.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final snapshot = widget.snapshot;
final ganaronJugadores = snapshot.ganador == 'jugadores';
final color = ganaronJugadores ? TemaApp.colorVerde : TemaApp.colorAcento;
_registrarResultadoSiHaceFalta(context, snapshot);
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: const Color(0xFF05070D),
appBar: AppBar(
title: Text(l10n.gameOver),
automaticallyImplyLeading: false,
backgroundColor: Colors.transparent,
elevation: 0,
actions: _acciones(context, l10n, snapshot),
),
body: FondoFarolero(
intenso: true,
child: Stack(
children: [
Positioned.fill(
child: IgnorePointer(
child: CustomPaint(
painter: EscenarioFinPartidaFaroleroPainter(color: color),
),
),
),
Align(
alignment: Alignment.topCenter,
child: ConfettiWidget(
confettiController: _confetti,
blastDirectionality: BlastDirectionality.explosive,
emissionFrequency: 0.09,
numberOfParticles: 28,
gravity: 0.18,
colors: const [
TemaApp.colorDorado,
TemaApp.colorNaranja,
TemaApp.colorAcento,
Color(0xFFFFECBE),
],
),
),
Positioned.fill(
child: IgnorePointer(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
Colors.black.withValues(alpha: 0.10),
Colors.black.withValues(alpha: 0.52),
],
stops: const [0.0, 0.54, 1.0],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
),
),
),
SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(20, 72, 20, 28),
child: Column(
children: [
HeroFinalPartidaFarolero(
encabezado: l10n.gameOver,
titulo:
ganaronJugadores ? l10n.playersWin : l10n.impostorsWin,
icono: ganaronJugadores
? Icons.emoji_events
: Icons.theater_comedy,
color: color,
),
const SizedBox(height: 12),
if (_progreso == null)
const TarjetaRecompensaCargandoPremium()
else
TarjetaProgresoGamificacionPremium(progreso: _progreso!),
const SizedBox(height: 18),
if (snapshot.palabraSecreta != null)
TarjetaSecretoPremium(
palabra: snapshot.palabraSecreta!,
categoria: BancoPalabras.nombreBonitoCategoria(
snapshot.categoria,
l10n,
),
),
const SizedBox(height: 18),
TarjetaImpostoresPremium(
titulo: snapshot.impostores.length == 1
? l10n.theImpostorWas
: l10n.theImpostorsWere,
impostores: _impostores(snapshot),
),
const SizedBox(height: 18),
if (snapshot.historialVotaciones.isNotEmpty)
TarjetaHistorialVotosPremium(
historial: snapshot.historialVotaciones,
jugadores: snapshot.jugadores,
),
const SizedBox(height: 24),
BotonFarolero.oscuro(
texto: l10n.mainMenu,
icono: Icons.home,
onPressed: () async {
await context.read<ServicioNearby>().desconectar();
if (!context.mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (_) => const PantallaPrincipal(),
),
(route) => false,
);
},
),
const SizedBox(height: 16),
],
),
),
),
],
),
),
);
}
List<Widget> _acciones(
BuildContext context,
AppLocalizations l10n,
SnapshotPartidaOnline snapshot,
) {
return [
IconButton(
tooltip: l10n.seeYourWord,
icon: const Icon(Icons.visibility),
onPressed: widget.jugadoresControlados.isEmpty
? null
: () => mostrarRevisionPalabraOnline(
context: context,
jugadoresControlados: widget.jugadoresControlados,
pistaCategoria: widget.pistaCategoria,
),
),
IconButton(
tooltip: l10n.notesTitle,
icon: const Icon(Icons.edit_note),
onPressed: snapshot.roomId == null || widget.jugadoresControlados.isEmpty
? null
: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PantallaNotasOnline(
partidaId: snapshot.roomId!,
jugadores: snapshot.jugadores,
autoresControlados: widget.jugadoresControlados,
),
),
),
),
];
}
void _registrarResultadoSiHaceFalta(
BuildContext context,
SnapshotPartidaOnline snapshot,
) {
if (_guardada || snapshot.ganador == null) return;
_guardada = true;
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!mounted) return;
final historial = context.read<ServicioHistorialPartidas>();
final perfil = context.read<ServicioPerfilUsuario>();
await historial.guardarSnapshotOnline(snapshot);
final progreso = await perfil.registrarPartidaCompletada(
victoria: _perfilLocalGano(snapshot, widget.jugadoresControlados),
comoImpostor: widget.jugadoresControlados.any((j) => j.esImpostor),
victoriaComoImpostor: snapshot.ganador == 'impostores' &&
widget.jugadoresControlados.any((j) => j.esImpostor),
);
if (!mounted) return;
setState(() => _progreso = progreso);
if (progreso.nuevasMedallas.isNotEmpty || progreso.incrementoFuego > 0) {
_confetti.play();
}
});
}
bool _perfilLocalGano(
SnapshotPartidaOnline snapshot,
List<JugadorInicioPartida> jugadoresControlados,
) {
if (jugadoresControlados.isEmpty) return snapshot.ganador == 'jugadores';
final ganaronImpostores = snapshot.ganador == 'impostores';
return jugadoresControlados.any(
(jugador) => jugador.esImpostor ? ganaronImpostores : !ganaronImpostores,
);
}
List<Jugador> _impostores(SnapshotPartidaOnline snapshot) {
final porNombre = {for (final jugador in snapshot.jugadores) jugador.nombre: jugador};
return snapshot.impostores
.map(
(nombre) =>
porNombre[nombre] ??
Jugador(id: nombre, nombre: nombre, esImpostor: true),
)
.toList();
}
}