Final push (i hope)

This commit is contained in:
2026-03-20 01:39:04 +01:00
parent ff67c541bc
commit edc065ca0c
14 changed files with 2192 additions and 794 deletions
+327 -125
View File
@@ -33,27 +33,169 @@ async function SqlRequest(action, params = {}) {
}
// Fonctions pour démarrer la page
// Remplace ton window.onload par ceci :
window.onload = async function() {
gameData = await getGame();
playersData = await getPlayers();
const urlParams = new URLSearchParams(window.location.search);
const gId = urlParams.get('game_id');
// On récupère les données initiales
gameData = await getGame(gId);
playersData = await getPlayers();
document.getElementById('title_page').textContent = gameData.name + " - Poker PAF";
updateClientInterface();
// --- LANCEMENT DU TEMPS RÉEL (SSE) ---
startRealTimeSync(gId);
}
function startRealTimeSync(gameId) {
const evtSource = new EventSource(`stream.php?game_id=${gameId}`);
evtSource.onmessage = function(event) {
const data = JSON.parse(event.data);
// On met à jour nos variables globales avec les données fraîches du serveur
gameData = data.game;
playersData = data.players;
console.log("🔄 Table synchronisée");
// On rafraîchit l'affichage sans recharger la page
updateClientInterface();
};
evtSource.onerror = function() {
console.log("⚠️ Connexion perdue, tentative de reconnexion...");
};
}
// Dans game.js
let lastDataHash = "";
async function updateClientInterface() {
if (!gameData) return;
const urlParams = new URLSearchParams(window.location.search);
const viewerId = Number(urlParams.get('player_id'));
const actionPanel = document.querySelector('.action-panel');
// --- 1. GESTION DES PANNEAUX DE VICTOIRE (SYNCHRO SSE) ---
console.log("DEBUG STATUS SSE:", gameData.status);
if (gameData.status === 'deciding') {
// L'admin est en train de choisir : on affiche "Attente"
showWaitingForWinner();
}
else if (gameData.status === 'finished' && gameData.winner_id) {
// Un gagnant a été validé en BDD
const winner = playersData.find(p => Number(p.id) === Number(gameData.winner_id));
const winnerName = winner ? winner.name : "Un joueur";
// showVictoryScreen s'occupe maintenant de supprimer l'ancien panneau
// avant d'afficher le nouveau avec les confettis.
showVictoryScreen(winnerName, gameData.pot);
}
else if (gameData.status === 'playing') {
// Si l'admin a relancé la partie (StartNewGame), on nettoie les panneaux
// pour ceux qui n'auraient pas cliqué sur "OK"
closeVictoryScreen();
}
// --- 2. GESTION DU HASH POUR L'INTERFACE DE TABLE ---
const currentHash = `${gameData.pot}-${gameData.current_player_id}-${gameData.last_bet}-${gameData.is_locked}-${gameData.status}`;
if (currentHash === lastDataHash) return;
lastDataHash = currentHash;
setupPlayers();
getCurrentPlayer();
// --- 3. GESTION DES BOUTONS ET DU TOUR ---
activePlayerLabel.textContent = `${currentPlayer.name} (${currentPlayer.money} 🪙)`;
const isLocked = Number(gameData.is_locked) === 1;
const isMyTurn = Number(gameData.current_player_id) === viewerId;
const turnInfo = document.querySelector('.turn-info');
const inputs = document.querySelectorAll('.action-buttons button, .raise-group input, .raise-group button');
// Bordure de tour
if (!isLocked && isMyTurn && gameData.status === 'playing') {
actionPanel.classList.add('my-turn-border');
} else {
actionPanel.classList.remove('my-turn-border');
}
if (isLocked) {
if (turnInfo) turnInfo.innerHTML = `<span style="color: #f1c40f; font-weight: bold;">🔒 Jeu verrouillé. En attente de l'Admin...</span>`;
inputs.forEach(el => el.disabled = true);
} else if (!isMyTurn) {
if (turnInfo && currentPlayer) {
turnInfo.innerHTML = `Attente de <strong style="color: #e74c3c;">${currentPlayer.name}</strong>...`;
}
inputs.forEach(el => el.disabled = true);
} else {
if (turnInfo) turnInfo.innerHTML = `<span style="color: #2ecc71; font-weight: bold;">✅ C'est à vous de jouer !</span>`;
inputs.forEach(el => el.disabled = false);
}
// Mise à jour du label du haut
if (currentPlayer && activePlayerLabel) {
activePlayerLabel.textContent = `${currentPlayer.name} (${currentPlayer.money} 🪙)`;
}
}
// Fonction pour envoyer l'ordre au serveur
async function toggleGameLock(checkbox) {
const status = checkbox.checked ? 0 : 1;
// 1. On change d'abord le statut de verrouillage
const response = await SqlRequest('toggle_lock', {
game_id: gameData.id,
status: status
});
if (response && response.success) {
// 2. Si on vient de DÉVERROUILLER (status 0), on reset le joueur actif
if (status === 0) {
console.log("🔓 Déverrouillage : Réinitialisation au joueur après le Dealer...");
await resetToPostDealerPlayer();
}
} else {
alert("Erreur lors du changement de statut");
checkbox.checked = !checkbox.checked;
}
}
async function resetToPostDealerPlayer() {
// On cherche l'index du dealer dans playersData
const dealerIndex = playersData.findIndex(p => Number(p.is_dealer) === 1);
// Le premier joueur à parler est (dealer + 1), mais on doit gérer la boucle du tableau
// et sauter les joueurs qui se sont couchés (is_folded)
let nextIndex = (dealerIndex + 1) % playersData.length;
// Sécurité : on cherche le prochain qui n'est pas couché
let attempts = 0;
while (playersData[nextIndex].is_folded && attempts < playersData.length) {
nextIndex = (nextIndex + 1) % playersData.length;
attempts++;
}
const firstPlayerId = playersData[nextIndex].id;
// On envoie l'ordre au serveur de mettre ce joueur en actif
await SqlRequest('set_current_player', {
game_id: gameData.id,
player_id: firstPlayerId
});
}
async function setupPlayers() {
const PokerTable = document.getElementById('table');
PokerTable.innerHTML = ''; // Clear existing players
let newHtml = ``;
const urlParams = new URLSearchParams(window.location.search);
const viewerId = Number(urlParams.get('player_id')); // On récupère qui regarde la page
newHtml += `
const PokerTable = document.getElementById('table');
PokerTable.innerHTML = '';
let newHtml = `
<div class="pot-area">
<div class="total-pot">${gameData.pot}</div>
<div id="Mise" class="current-bet-display">Mise: ${gameData.last_bet}</div>
@@ -61,12 +203,21 @@ async function setupPlayers() {
`;
playersData.forEach((player, index) => {
const isDealer = Number(player.is_dealer) === 1;
const isMe = Number(player.id) === viewerId; // Est-ce moi ?
const isActive = Number(gameData.current_player_id) === Number(player.id);
newHtml += `
<div class="player-slot slot-${index}${player.is_folded ? ' blur-effect' : ''}${player.money <= 0 ? ' All-in-Blur' : ''}" onclick="changePlayer(${player.id})" data-id="${player.id}">
<div class="player-info${gameData.current_player_id == player.id ? ' active' : ''}">
${player.is_dealer ? '<div class="dealer-badge">D</div>' : ''}
<div class="player-slot slot-${index}
${player.is_folded ? ' blur-effect' : ''}
${player.money <= 0 ? ' All-in-Blur' : ''}
${isMe ? 'is-me' : ''}"
data-id="${player.id}">
<div class="player-info ${isActive ? ' active' : ''}">
${isDealer ? '<div class="dealer-badge">D</div>' : ''}
<span class="player-name">J${index + 1} : ${player.name}</span>
<span class="player-name">${isMe ? 'VOUS' : 'J' + (index + 1)} : ${player.name}</span>
<span class="player-money">${player.money} 🪙</span><br>
<span class="player-bet">Mise: ${player.current_bet} 🪙</span>
</div>
@@ -105,8 +256,13 @@ async function getPlayers() {
}
}
async function getCurrentPlayer() {
currentPlayer = playersData.find(player => player.id === gameData.current_player_id);
function getCurrentPlayer() {
if (!playersData || !gameData) return null;
// On cherche le joueur dans le tableau par son ID
currentPlayer = playersData.find(player => Number(player.id) === Number(gameData.current_player_id));
return currentPlayer;
}
// -----------------------------------------------------
@@ -134,6 +290,14 @@ async function changePlayer(id = null) {
}
async function playerFold() {
const urlParams = new URLSearchParams(window.location.search);
const viewerId = Number(urlParams.get('player_id'));
// Sécurité : si l'ID de l'URL n'est pas celui du joueur actif, on stoppe tout
if (Number(gameData.current_player_id) !== viewerId) {
alert("Ce n'est pas votre tour !");
return;
}
const response = await SqlRequest('fold', { player_id: gameData.current_player_id });
if (response.success) {
playersData = await getPlayers();
@@ -144,52 +308,121 @@ async function playerFold() {
}
async function playerRaise() {
const betAmount = parseInt(document.getElementById('raise-amount').value);
const urlParams = new URLSearchParams(window.location.search);
const viewerId = Number(urlParams.get('player_id'));
if (betAmount <= 0) {
alert("Veuillez entrer un montant de mise valide.");
// Sécurité : si l'ID de l'URL n'est pas celui du joueur actif, on stoppe tout
if (Number(gameData.current_player_id) !== viewerId) {
alert("Ce n'est pas votre tour !");
return;
}
const amount = betAmount + gameData.last_bet - currentPlayer.current_bet;
const betInput = document.getElementById('raise-amount');
const betValue = parseInt(betInput.value);
if (currentPlayer.money < amount) {
alert("Vous n'avez pas assez d'argent pour cette mise.");
// Validation basique du champ de saisie
if (isNaN(betValue) || betValue <= 0) {
alert("Veuillez saisir un montant valide.");
return;
}
const response = await SqlRequest('raise', { game_id: gameData.id, player_id: gameData.current_player_id, amount: amount, current_bet: currentPlayer.current_bet });
if (response.success) {
gameData.last_bet = currentPlayer.current_bet + amount;
gameData.pot += amount;
playersData = await getPlayers();
changePlayer();
// Envoi de la requête de relance au serveur
const response = await SqlRequest('raise', {
game_id: gameData.id,
player_id: gameData.current_player_id,
bet_input: betValue
});
if (response && response.success) {
betInput.value = ''; // Réinitialisation du champ
await changePlayer(); // Passage au joueur suivant
} else {
console.error("Erreur lors du raise :", response.error);
// Affichage de l'erreur retournée par le PHP (ex: "Fonds insuffisants")
alert("Erreur : " + (response ? response.error : "Serveur injoignable"));
}
}
async function playerFollow() {
if (currentPlayer.current_bet >= gameData.last_bet) {
const urlParams = new URLSearchParams(window.location.search);
const viewerId = Number(urlParams.get('player_id'));
// Sécurité : si l'ID de l'URL n'est pas celui du joueur actif, on stoppe tout
if (Number(gameData.current_player_id) !== viewerId) {
alert("Ce n'est pas votre tour !");
return;
}
// Si le joueur a déjà mis la somme requise
if (Number(currentPlayer.current_bet) >= Number(gameData.last_bet)) {
// AU LIEU DE changePlayer(), on vérifie si le tour est fini
const activePlayers = playersData.filter(p => Number(p.is_folded) === 0);
const allMatched = activePlayers.every(p => Number(p.current_bet) === Number(gameData.last_bet));
if (allMatched) {
await SqlRequest('toggle_lock', { game_id: gameData.id, status: 1 });
return; // On s'arrête là, le SSE fera le reste
}
await changePlayer();
return;
}
// 1. On force la récupération du joueur actuel s'il est manquant
if (!currentPlayer) {
await getCurrentPlayer();
}
// 2. Si après ça il est toujours introuvable, on arrête pour éviter l'erreur
if (!currentPlayer) {
console.error("Erreur : Impossible de trouver les données du joueur actif.");
return;
}
// 3. Logique de suivi
if (Number(currentPlayer.current_bet) >= Number(gameData.last_bet)) {
changePlayer();
return;
}
let delta_amount = gameData.last_bet - currentPlayer.current_bet;
if (currentPlayer.money < delta_amount) {
delta_amount = currentPlayer.money;
// Calcul du montant à ajouter
let delta_amount = Math.max(0, Number(gameData.last_bet) - Number(currentPlayer.current_bet));
if (Number(currentPlayer.money) < delta_amount) {
delta_amount = Number(currentPlayer.money);
}
const response = await SqlRequest('follow', { game_id: gameData.id, player_id: gameData.current_player_id, amount: delta_amount });
if (response.success) {
gameData.pot += delta_amount;
playersData = await getPlayers();
changePlayer();
} else {
console.error("Erreur lors du follow :", response.error);
const response = await SqlRequest('follow', {
game_id: gameData.id,
player_id: gameData.current_player_id,
amount: delta_amount
});
if (response && response.success) {
// --- LOGIQUE DE VÉRIFICATION DU TOUR ---
// On récupère les données fraîches pour savoir si c'était le dernier à suivre
const refreshedPlayers = await getPlayers();
// On vérifie si tout le monde a mis la même somme (et n'est pas couché)
const activePlayers = refreshedPlayers.filter(p => Number(p.is_folded) === 0);
const allMatched = activePlayers.every(p => Number(p.current_bet) === Number(gameData.last_bet));
if (allMatched) {
// On demande au serveur de verrouiller la partie (si tu as l'action prévue)
await SqlRequest('toggle_lock', { game_id: gameData.id, status: 1 });
console.log("Tour terminé, table verrouillée.");
} else {
// Sinon on passe juste au suivant
await changePlayer();
}
}
}
async function playerAllIn() {
const urlParams = new URLSearchParams(window.location.search);
const viewerId = Number(urlParams.get('player_id'));
// Sécurité : si l'ID de l'URL n'est pas celui du joueur actif, on stoppe tout
if (Number(gameData.current_player_id) !== viewerId) {
alert("Ce n'est pas votre tour !");
return;
}
const response = await SqlRequest('all_in', { game_id: gameData.id, player_id: gameData.current_player_id });
if (response.success) {
gameData = await getGame(gameData.id);
@@ -200,102 +433,71 @@ async function playerAllIn() {
}
}
// -----------------------------------------------------
// Étape 1 : Afficher l'attente quand l'admin termine la partie
function showWaitingForWinner() {
if (document.getElementById('player-modal')) return;
// Fonctions pour les actions administratives
async function endGame() {
const container = document.querySelector('.table-container');
if (!container) {
console.error("Conteneur .table-container introuvable");
return;
}
if (document.querySelector('.win-panel')) return;
const winOverlay = document.createElement('div');
winOverlay.className = 'win-overlay';
const winPanel = document.createElement('div');
winPanel.className = 'win-panel';
winPanel.innerHTML = `
<h2>🏆 La partie est terminée ! 🏆<br>Qui a gagné ?</h2>
<div id="winner-buttons-area"></div>
`;
const overlay = document.createElement('div');
overlay.id = 'player-modal';
overlay.className = 'player-win-overlay';
winOverlay.appendChild(winPanel);
container.appendChild(winOverlay);
const area = document.getElementById('winner-buttons-area');
const playerElements = document.querySelectorAll('.player-slot');
playerElements.forEach(slot => {
const id = slot.getAttribute('data-id');
const name = slot.querySelector('.player-name').textContent.split(': ')[1];
const btn = document.createElement('button');
btn.className = 'btn-win';
btn.innerText = name;
btn.onclick = () => declareWinner(id);
area.appendChild(btn);
});
document.getElementById('end-game-screen').style.display = 'flex';
container.classList.add('blur-effect');
overlay.innerHTML = `
<div class="player-win-content">
<h2>FIN DE PARTIE</h2>
<p class="waiting-text">Détermination du gagnant en cours...</p>
</div>
`;
document.body.appendChild(overlay);
}
async function declareWinner(playerId) {
const container = document.querySelector('.table-container');
container.classList.remove('blur-effect');
let victoryDisplayedFor = null; // Variable globale
const response = await SqlRequest('declare_winner', { game_id: gameData.id, player_id: playerId });
if (response.success) {
const winPanel = document.querySelector('.win-panel');
if (winPanel) {
winPanel.innerHTML = `
<h2>${playersData.find(player => player.id == playerId).name} gagne la partie et remporte ${gameData.last_bet} 🪙 !</h2>
<button class="btn-back" onclick="window.location.href='index.html'">Retour à l'accueil</button>
<button class="btn-replay" onclick="StartNewGame()">Rejouer</button>
`;
}
} else {
console.error("Erreur lors de la déclaration du gagnant :", response.error);
}
}
async function StartNewGame() {
window.location.reload();
}
async function addMoney() {
let amount = parseInt(document.getElementById('money-amount').value);
if (isNaN(amount)) {
alert("Veuillez entrer un montant valide.");
return;
function showVictoryScreen(winnerName, amount) {
// 1. On cherche s'il y a déjà un panneau ouvert (celui d'attente par exemple)
const existingModal = document.getElementById('player-modal');
if (existingModal) {
existingModal.remove(); // On le supprime proprement
}
const response = await SqlRequest('add_money', { player_id: gameData.current_player_id, amount: amount });
if (response.success) {
playersData = await getPlayers();
updateClientInterface();
} else {
console.error("Erreur lors de l'ajout d'argent :", response.error);
}
// 2. On crée le nouveau panneau de victoire
const modal = document.createElement('div');
modal.id = 'player-modal';
modal.className = 'player-win-overlay';
modal.innerHTML = `
<div class="player-win-content">
<h2 class="victory-title">VICTOIRE DE ${winnerName}</h2>
<span class="win-amount">Il remporte ${amount} 🪙</span>
<button class="btn-ok" onclick="closeVictoryScreen()">OK</button>
</div>
`;
document.body.appendChild(modal);
// On lance les confettis
if (typeof startConfetti === "function") startConfetti();
}
async function deleteGame() {
const confirmation = confirm("Êtes-vous sûr de vouloir supprimer cette partie ? Cette action est irréversible.");
if (!confirmation) return;
const response = await SqlRequest('delete_game', { game_id: gameData.id });
if (response.success) {
console.log("Partie supprimée avec succès.", response.success);
window.location.replace('index.html');
} else {
console.error("Erreur lors de la suppression du jeu :", response.error);
}
// Et dans ton closeVictoryScreen
function closeVictoryScreen() {
const modal = document.getElementById('player-modal');
if (modal) modal.remove();
victoryDisplayedFor = null; // Reset pour la prochaine partie
}
function startConfetti() {
for (let i = 0; i < 50; i++) {
const confetti = document.createElement('div');
confetti.style.cssText = `
position: fixed; width: 10px; height: 10px;
background: ${['#d4af37','#ffffff','#2ecc71'][Math.floor(Math.random()*3)]};
top: -10px; left: ${Math.random() * 100}vw;
z-index: 10001; pointer-events: none;
border-radius: 50%;
animation: fall ${2 + Math.random() * 3}s linear forwards;
`;
document.body.appendChild(confetti);
setTimeout(() => confetti.remove(), 5000);
}
}