feat: Implement multiplayer game session management
Some checks failed
Build & Deploy Farolero / Análisis de código (push) Has been cancelled
Build & Deploy Farolero / Build APK + AAB release (push) Has been cancelled

- Add models for managing player assignments and game session initialization in `inicio_partida_multijugador.dart`.
- Create a multiplayer room state management system in `sala_multijugador.dart`, including user registration, selection, and session validation.
- Develop a UI screen for displaying player words sequentially in `pantalla_palabras_cliente.dart`.
- Implement unit tests for the multiplayer session management and player assignment logic in `inicio_partida_multijugador_test.dart` and `sala_multijugador_test.dart`.
This commit is contained in:
Javier Bautista Fernández
2026-04-27 14:02:33 +02:00
parent 4a1abd0be0
commit a8d5b0f002
14 changed files with 1779 additions and 421 deletions

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:provider/provider.dart';
import '../estado/estado_juego.dart';
import '../modelos/inicio_partida_multijugador.dart';
import '../modelos/palabra.dart';
import '../modelos/partida.dart';
import '../modelos/usuario.dart';
@@ -153,15 +154,21 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
onIniciar: () {
// Cuando el host toca "Iniciar" con suficientes jugadores
final estado = context.read<EstadoJuego>();
final sala = nearby.estadoSala;
if (sala == null) return;
final validacion = sala.iniciarPartida();
if (!validacion.exitoso) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'No se puede iniciar: ${validacion.codigo ?? "sala inválida"}',
),
),
);
return;
}
// Set host local player first (required for host-included game)
estado.setHostJugador(nombre.trim());
final jugadoresMulti = [
nombre.trim(),
...nearby.jugadores.map((j) => j.nombre),
];
estado.crearPartida(
estado.crearPartidaDesdeSala(
config: ConfigPartida(
modoMultimovil: true,
categoria: _categoria,
@@ -169,24 +176,41 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
pistaImpostor: _pistaImpostor,
tiempoDebateSegundos: _tiempoDebate,
),
nombresJugadores: jugadoresMulti,
sala: sala,
);
// Enviar palabras a cada jugador via Nearby
final partida = estado.partida!;
final impostores = <String, bool>{};
for (int i = 0; i < nearby.jugadores.length; i++) {
final jugadorNearby = nearby.jugadores[i];
// El jugador [0] es el host, los de nearby son [1..n]
final jugadorPartida = partida.jugadores[i + 1];
impostores[jugadorNearby.endpointId] =
jugadorPartida.esImpostor;
}
final asignaciones = partida.jugadores.map((jugador) {
final usuarioSala = sala.usuarios[jugador.id];
final clientId = usuarioSala?.clienteIdSeleccionado;
final cliente = clientId == null ? null : sala.clientes[clientId];
return AsignacionJugador(
jugadorId: jugador.id,
nombre: jugador.nombre,
clientId: clientId ?? sala.hostClientId,
endpointId: cliente?.endpointId,
);
}).toList();
final impostores = {
for (final jugador in partida.jugadores)
jugador.id: jugador.esImpostor,
};
final jugadoresTodos = partida.jugadores
.map(
(jugador) => {
'id': jugador.id,
'nombre': jugador.nombre,
'eliminado': jugador.eliminado,
},
)
.toList();
nearby.enviarInicioPartida(
nearby.enviarInicioPartidaMulti(
asignaciones: asignaciones,
palabraSecreta: partida.palabraSecreta,
categoria: _categoria,
impostores: impostores,
impostoresPorJugadorId: impostores,
jugadoresTodos: jugadoresTodos,
);
Navigator.pushReplacement(