274 lines
9.6 KiB
Dart
274 lines
9.6 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,
|
|
assetIconPath: 'assets/ui/generated/actions/action_result_trophy.webp',
|
|
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: IconoFarolero(Icons.visibility),
|
|
onPressed: widget.jugadoresControlados.isEmpty
|
|
? null
|
|
: () => mostrarRevisionPalabraOnline(
|
|
context: context,
|
|
jugadoresControlados: widget.jugadoresControlados,
|
|
pistaCategoria: widget.pistaCategoria,
|
|
),
|
|
),
|
|
IconButton(
|
|
tooltip: l10n.notesTitle,
|
|
icon: IconoFarolero(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();
|
|
}
|
|
}
|