From 5446352cf9861636988775625e206bdfb41d34c2 Mon Sep 17 00:00:00 2001 From: dbvictor2002 Date: Wed, 30 Sep 2026 10:21:43 +0200 Subject: [PATCH 1/4] Fix inicia partida todos los usuarios --- js/buscadorSala.js | 75 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/js/buscadorSala.js b/js/buscadorSala.js index c258d97..47e0730 100644 --- a/js/buscadorSala.js +++ b/js/buscadorSala.js @@ -3,6 +3,43 @@ let usuarioYaEstaEnEquipo = false; $(document).ready(function () { comprobarSalaUsuario(); }); +// ========================================================================== +// DETECTOR DE INICIO DE PARTIDA PARA JUGADORES INVITADOS (1 VS 1) +// ========================================================================== +$(document).ready(function () { + // Solo activamos el vigilante si estamos en la página del buscador de salas + if (window.location.pathname.includes("buscadorSala.php")) { + setInterval(function () { + // 1. Preguntamos al servidor en qué partida está metido el usuario actual + $.ajax({ + url: "controladores/controladorEquipoxUsuario.php", + type: "POST", + dataType: "json", + data: { orden: "obtenerSalaUsuario" }, + success: function (sala) { + // Si el usuario está metido en una sala/partida activa... + if (sala.asociado && sala.idPartida) { + // 2. Comprobamos si el anfitrión ya ha iniciado esa partida concreta + $.ajax({ + url: "controladores/controladorPartida.php", + type: "POST", + dataType: "json", + data: { orden: "buscar", idPartida: sala.idPartida }, + success: function (partida) { + // Si 'fechaInicio' ya no es null, significa que el host le dio a "Jugar" + if (partida && partida.fechaInicio !== null) { + // Redirigimos instantáneamente al invitado a la pantalla del juego + window.location.href = + "juego.php?idPartida=" + sala.idPartida; + } + }, + }); + } + }, + }); + }, 2000); // Comprueba el estado de la partida automáticamente cada 2 segundos + } +}); // 1. Comprobar si el usuario actual ya pertenece a una sala function comprobarSalaUsuario() { @@ -179,12 +216,12 @@ function mostrarSala(partida, equipos, jugadores) { // Solo el host puede iniciar la partida if (esHost) { + // Así debe renderizarse el botón del host dentro de tu función mostrarSala() para que el AJAX funcione: botonIniciarHTML = ` - - ¡Iniciar Partida! - - `; + +`; } accionesSalaHTML = ` @@ -266,7 +303,8 @@ let botonAbandonarReferencia = null; $(document).on("click", ".btn-abandonar-sala", function () { botonAbandonarReferencia = $(this); - const esHost = $(this).data("es-host") === true || $(this).data("es-host") === "true"; + const esHost = + $(this).data("es-host") === true || $(this).data("es-host") === "true"; const titulo = $("#modalAbandonarSalaLabel"); const mensaje = $("#modalAbandonarSalaMensaje"); const botonConfirmar = $("#btnConfirmarAbandonar"); @@ -279,9 +317,7 @@ $(document).on("click", ".btn-abandonar-sala", function () { botonConfirmar.text("Sí, abandonar"); } else { titulo.text("¿Abandonar la sala?"); - mensaje.text( - "Si sales de la sala, perderás tu lugar en el equipo.", - ); + mensaje.text("Si sales de la sala, perderás tu lugar en el equipo."); botonConfirmar.text("Sí, salir"); } @@ -292,7 +328,6 @@ $(document).on("click", ".btn-abandonar-sala", function () { modal.show(); }); - // B. Al hacer clic en "Sí, salir" dentro del modal Kahoot $(document).on("click", "#btnConfirmarAbandonar", function () { const modalEl = document.getElementById("modalAbandonarSala"); @@ -403,6 +438,7 @@ $("#btnCrearSala").on("click", function () { if (respuesta.exito) { $("#modalCrearSala").modal("hide"); + location.reload(); } }, @@ -433,3 +469,22 @@ $(document).on("input", ".input-kahoot", function () { $(this).removeClass("is-invalid"); $(this).next(".mensaje-error-sala").remove(); }); + +$(document).on("click", "#btnInicioJuego", function () { + // 1. Capturamos el id de la partida de forma dinámica desde el botón que se ha pulsado + const idPartida = $(this).data("id-partida"); + + // 2. Enviamos la petición AJAX adecuada con TODOS los datos que el controlador espera recibir + $.ajax({ + url: "controladores/controladorPartida.php", + type: "POST", + dataType: "json", + data: { + orden: "iniciar", + idPartida: idPartida, + }, + error: function (error) { + console.error("Error al iniciar el juego por AJAX:", error); + }, + }); +}); From 83545152bb4dd2e98e699dd5dac14441a870bed2 Mon Sep 17 00:00:00 2001 From: dbvictor2002 Date: Thu, 1 Oct 2026 10:46:12 +0200 Subject: [PATCH 2/4] Subir archivos a "/" --- resultados.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/resultados.php b/resultados.php index 1f571e3..6b04e5a 100644 --- a/resultados.php +++ b/resultados.php @@ -1,6 +1,6 @@ $a['puntos']; }); usort($equiposFinales, function($a, $b) { return $b['puntos'] <=> $a['puntos']; }); ?> @@ -89,7 +87,7 @@ usort($equiposFinales, function($a, $b) { return $b['puntos'] <=> $a['puntos']; Resultados - Trivial - + @@ -146,11 +144,13 @@ usort($equiposFinales, function($a, $b) { return $b['puntos'] <=> $a['puntos']; - + + + - \ No newline at end of file + From eed77db62f65b68b3029072df8bc8cf82b785f62 Mon Sep 17 00:00:00 2001 From: dbvictor2002 Date: Thu, 1 Oct 2026 10:46:36 +0200 Subject: [PATCH 3/4] Subir archivos a "controladores" --- controladores/controladorResultados.php | 138 ++++++++++++++++++------ 1 file changed, 103 insertions(+), 35 deletions(-) diff --git a/controladores/controladorResultados.php b/controladores/controladorResultados.php index c952229..ec6071b 100644 --- a/controladores/controladorResultados.php +++ b/controladores/controladorResultados.php @@ -1,6 +1,6 @@ prepare(" SELECT COUNT(exu.idUsuario) AS total_jugadores FROM equipoxpartida exp @@ -38,7 +38,7 @@ switch ($orden) { $preguntasTotalesEsperadas = $totalJugadores * 10; - // Contar respuestas dadas en la partida + // 2. Contar cuántas respuestas reales se han guardado $stmtRespuestas = $conexion->prepare(" SELECT COUNT(*) FROM respuestaalmacenada ra @@ -50,17 +50,16 @@ switch ($orden) { $terminada = ($respuestasRespondidas >= $preguntasTotalesEsperadas && $preguntasTotalesEsperadas > 0); - // Cuando la partida finaliza, se actualizan los históricos + // 3. Si ha terminado, guardamos los datos históricos una sola vez if ($terminada && !isset($_SESSION['partidas_procesadas'][$idPartida])) { $conexion->beginTransaction(); - // CÁLCULO DE VELOCIDAD PREGUNTA A PREGUNTA EN SESIÓN + // --- CÁLCULO DE VELOCIDAD --- if (!isset($_SESSION['primeros_en_acertar'][$idPartida])) { $_SESSION['primeros_en_acertar'][$idPartida] = []; } - // Vemos quién fue el primero $stmtVelocidad = $conexion->prepare(" SELECT ra.idPartidaxPregunta, ra.idUsuario FROM respuestaalmacenada ra @@ -81,8 +80,7 @@ switch ($orden) { } } - // Actualizamos la base de datos - // Buscamos los aciertos de cada jugador en la partida actual + // Consulta de aciertos $stmtAciertos = $conexion->prepare(" SELECT ra.idUsuario, exu.idEquipo, COUNT(*) as aciertos_partida FROM respuestaalmacenada ra @@ -90,12 +88,12 @@ switch ($orden) { INNER JOIN respuesta r ON ra.idRespuesta = r.idRespuesta INNER JOIN equipoxusuario exu ON ra.idUsuario = exu.idUsuario WHERE pxp.idPartida = :idPartida AND r.correcta = 1 - GROUP BY ra.idUsuario + GROUP BY ra.idUsuario, exu.idEquipo "); $stmtAciertos->execute([':idPartida' => $idPartida]); $jugadoresPartida = $stmtAciertos->fetchAll(PDO::FETCH_ASSOC); - $updateAciertosGlobales = $conexion->prepare(" + $updateAciertos = $conexion->prepare(" UPDATE usuario SET aciertos = aciertos + :nuevos_aciertos WHERE idUsuario = :idUsuario @@ -107,7 +105,6 @@ switch ($orden) { $idE = (int)$jp['idEquipo']; $nuevosAciertos = (int)$jp['aciertos_partida']; - // Sumamos los aciertos al perfil del usuario $updateAciertos->execute([ ':nuevos_aciertos' => $nuevosAciertos, ':idUsuario' => $idU @@ -119,34 +116,42 @@ switch ($orden) { $puntosEquipos[$idE] += $nuevosAciertos; } - // Determinamos qué equipo ha obtenido más aciertos en la partida - arsort($puntosEquipos); - $idEquipoGanador = key($puntosEquipos); + if (!empty($puntosEquipos)) { + arsort($puntosEquipos); + $idEquipoGanador = key($puntosEquipos); - // Obtenemos todos los usuarios de la partida para asignar victorias/derrotas - $stmtMiembros = $conexion->prepare(" - SELECT exu.idUsuario, exu.idEquipo - FROM equipoxpartida exp - INNER JOIN equipoxusuario exu ON exp.idEquipo = exu.idEquipo - WHERE exp.idPartida = :idPartida - "); - $stmtMiembros->execute([':idPartida' => $idPartida]); - $todosLosMiembros = $stmtMiembros->fetchAll(PDO::FETCH_ASSOC); + $stmtMiembros = $conexion->prepare(" + SELECT exu.idUsuario, exu.idEquipo + FROM equipoxpartida exp + INNER JOIN equipoxusuario exu ON exp.idEquipo = exu.idEquipo + WHERE exp.idPartida = :idPartida + "); + $stmtMiembros->execute([':idPartida' => $idPartida]); + $todosLosMiembros = $stmtMiembros->fetchAll(PDO::FETCH_ASSOC); - $updateVictoria = $conexion->prepare("UPDATE usuario SET victorias = victorias + 1 WHERE idUsuario = :idUsuario"); - $updateDerrota = $conexion->prepare("UPDATE usuario SET derrotas = derrotas + 1 WHERE idUsuario = :idUsuario"); + $updateVictoria = $conexion->prepare("UPDATE usuario SET victorias = victorias + 1 WHERE idUsuario = :idUsuario"); + $updateDerrota = $conexion->prepare("UPDATE usuario SET derrotas = derrotas + 1 WHERE idUsuario = :idUsuario"); - foreach ($todosLosMiembros as $miembro) { - $idU = (int)$miembro['idUsuario']; - $idE = (int)$miembro['idEquipo']; + foreach ($todosLosMiembros as $miembro) { + $idU = (int)$miembro['idUsuario']; + $idE = (int)$miembro['idEquipo']; - if ($idE === $idEquipoGanador) { - $updateVictoria->execute([':idUsuario' => $idU]); - } else { - $updateDerrota->execute([':idUsuario' => $idU]); + if ($idE === $idEquipoGanador) { + $updateVictoria->execute([':idUsuario' => $idU]); + } else { + $updateDerrota->execute([':idUsuario' => $idU]); + } } } + // Marcar fecha fin de la partida para los históricos globales del grupo + $stmtFinalizar = $conexion->prepare(" + UPDATE partida + SET fechaFin = NOW() + WHERE idPartida = :idPartida AND fechaFin IS NULL + "); + $stmtFinalizar->execute([':idPartida' => $idPartida]); + $conexion->commit(); $_SESSION['partidas_procesadas'][$idPartida] = true; } @@ -160,7 +165,70 @@ switch ($orden) { } break; - default: - echo json_encode(['error' => 'Acción no permitida']); + case 'registrarSalidaUsuario': + try { + $idUsuario = (int)$_SESSION['idUsuario']; + $conexion->beginTransaction(); + + // A. LIBERACIÓN INMEDIATA DEL JUGADOR ACTUAL: Lo borramos de su equipo + $stmtSalir = $conexion->prepare("DELETE FROM equipoxusuario WHERE idUsuario = :idUsuario"); + $stmtSalir->execute([':idUsuario' => $idUsuario]); + + // B. CONTADOR DE ALUMNOS QUEDAN: Contamos cuántas personas quedan en los equipos de esta partida + $stmtContarQuedan = $conexion->prepare(" + SELECT COUNT(exu.idUsuario) + FROM equipoxpartida exp + INNER JOIN equipoxusuario exu ON exp.idEquipo = exu.idEquipo + WHERE exp.idPartida = :idPartida + "); + $stmtContarQuedan->execute([':idPartida' => $idPartida]); + $jugadoresRestantes = (int)$stmtContarQuedan->fetchColumn(); + + // C. DESTRUCCIÓN EN CADENA AUTOMÁTICA ATÓMICA (Si da 0, somos los últimos en salir) + if ($jugadoresRestantes === 0) { + // 1. Limpiar las respuestas almacenadas + $stmtBorrarRespuestas = $conexion->prepare(" + DELETE ra FROM respuestaalmacenada ra + INNER JOIN partidaxpregunta pxp ON ra.idPartidaxPregunta = pxp.idPartidaxPregunta + WHERE pxp.idPartida = :idPartida + "); + $stmtBorrarRespuestas->execute([':idPartida' => $idPartida]); + + // 2. Limpiar las preguntas vinculadas a la partida + $stmtBorrarPreguntas = $conexion->prepare("DELETE FROM partidaxpregunta WHERE idPartida = :idPartida"); + $stmtBorrarPreguntas->execute([':idPartida' => $idPartida]); + + // 3. NUEVO: Obtener los IDs de los dos equipos que jugaban en esta partida antes de borrar la relación + $stmtObtenerEquipos = $conexion->prepare("SELECT idEquipo FROM equipoxpartida WHERE idPartida = :idPartida"); + $stmtObtenerEquipos->execute([':idPartida' => $idPartida]); + $equiposDeLaPartida = $stmtObtenerEquipos->fetchAll(PDO::FETCH_COLUMN); + + // 4. Limpiar la relación de equipos de la partida (tabla intermedia) + $stmtBorrarEquiposPartida = $conexion->prepare("DELETE FROM equipoxpartida WHERE idPartida = :idPartida"); + $stmtBorrarEquiposPartida = $conexion->prepare("DELETE FROM equipoxpartida WHERE idPartida = :idPartida"); + $stmtBorrarEquiposPartida->execute([':idPartida' => $idPartida]); + + // 5. NUEVO: Borrar físicamente los equipos de la tabla 'equipo' si existen + if (!empty($equiposDeLaPartida)) { + // Creamos los marcadores (?, ?) de forma dinámica según los equipos que devuelva + $inQuery = implode(',', array_fill(0, count($equiposDeLaPartida), '?')); + $stmtBorrarEquiposFisicos = $conexion->prepare("DELETE FROM equipo WHERE idEquipo IN ($inQuery)"); + $stmtBorrarEquiposFisicos->execute($equiposDeLaPartida); + } + + // 6. Borrar físicamente la fila de la partida principal + $stmtBorrarSala = $conexion->prepare("DELETE FROM partida WHERE idPartida = :idPartida"); + $stmtBorrarSala->execute([':idPartida' => $idPartida]); + } + + $conexion->commit(); + echo json_encode(['exito' => true, 'usuarios_quedan' => $jugadoresRestantes]); + } catch (PDOException $e) { + if ($conexion->inTransaction()) { + $conexion->rollBack(); + } + echo json_encode(['error' => $e->getMessage()]); + } break; -} \ No newline at end of file + +} From a66ac7086d5705873b6042d12e916056ba5dd21b Mon Sep 17 00:00:00 2001 From: dbvictor2002 Date: Thu, 1 Oct 2026 10:47:05 +0200 Subject: [PATCH 4/4] Subir archivos a "js" --- js/resultados.js | 102 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/js/resultados.js b/js/resultados.js index 267d676..eea804f 100644 --- a/js/resultados.js +++ b/js/resultados.js @@ -1,38 +1,80 @@ $(document).ready(function () { - const idPartida = $("body").data("id-partida"); - - comprobarEstadoPartida(idPartida); + // Captura segura del ID de la partida desde el body de la página + const idPartida = + $("body").attr("data-id-partida") || $("body").data("id-partida"); - const intervalo = setInterval(function () { - comprobarEstadoPartida(idPartida, intervalo); - }, 3000); + comprobarEstadoPartida(idPartida); + + const intervalo = setInterval(function () { + comprobarEstadoPartida(idPartida, intervalo); + }, 3000); }); function comprobarEstadoPartida(idPartida, intervalo = null) { - $.ajax({ - url: "controladores/controladorResultados.php", - type: "POST", - dataType: "json", - data: { - orden: "comprobarEstado", - idPartida: idPartida - }, - success: function (respuesta) { - if (respuesta.error) { - console.error("Error:", respuesta.error); - return; - } + if (!idPartida || idPartida <= 0) return; - if (respuesta.terminada) { - if (intervalo !== null) clearInterval(intervalo); - - $("#pantalla-espera").fadeOut(400, function() { - location.reload(); - }); - } - }, - error: function () { - console.log("Error de comunicación con el controlador."); + $.ajax({ + url: "controladores/controladorResultados.php", + type: "POST", + dataType: "json", + data: { + orden: "comprobarEstado", + idPartida: idPartida, + }, + success: function (respuesta) { + if (respuesta.error) { + console.error("Error del controlador:", respuesta.error); + return; + } + + if (respuesta.terminada === true || respuesta.terminada === "true") { + if (intervalo !== null) { + clearInterval(intervalo); } - }); + + $("#pantalla-espera").hide(); + $("#pantalla-resultados").show(); + } + }, + error: function () { + console.log("Esperando conexión con el servidor del Trivial..."); + }, + }); } + +// INTERCEPTOR DE SALIDA: Saca al usuario y destruye la sala en cascada +$(document).on("click", "#btnVolverInicio", function (e) { + e.preventDefault(); // Congelamos un instante la redirección + + const urlDestino = $(this).attr("href"); + // Leemos el ID de forma científica directamente desde el botón pulsado + const idPartida = $(this).attr("data-id"); + + if (!idPartida) { + window.location.href = urlDestino; + return; + } + + $.ajax({ + url: "controladores/controladorResultados.php", + type: "POST", + dataType: "json", + data: { + orden: "registrarSalidaUsuario", + idPartida: idPartida, + }, + success: function (response) { + console.log( + "Servidor procesó la salida. Usuarios restantes: " + + response.usuarios_quedan, + ); + }, + error: function (error) { + console.error("Error técnico al salir de la sala:", error); + }, + complete: function () { + // Rompemos la pausa y mandamos al jugador al menú de inicio limpio + window.location.href = urlDestino; + }, + }); +});