From edc065ca0cdfe2a158eb5f5030b2da3ae364d239 Mon Sep 17 00:00:00 2001 From: Whykioh Date: Fri, 20 Mar 2026 01:39:04 +0100 Subject: [PATCH] Final push (i hope) --- poker-paf/Css/Config.css | 284 ++++++------ poker-paf/Css/Game.css | 743 ++++++++++++++++++++++++++++--- poker-paf/Css/Index.css | 409 ++++++++--------- poker-paf/Css/gameMobile.css | 152 +++++++ poker-paf/Css/playerSelector.css | 163 +++++++ poker-paf/Js/Config.js | 4 +- poker-paf/Js/Game.js | 452 +++++++++++++------ poker-paf/Js/admin-game.js | 228 ++++++++-- poker-paf/RequestsHandler.php | 453 +++++++++++-------- poker-paf/admin-game.html | 42 +- poker-paf/config.html | 2 +- poker-paf/game.html | 6 +- poker-paf/player-selector.php | 5 +- poker-paf/stream.php | 43 ++ 14 files changed, 2192 insertions(+), 794 deletions(-) create mode 100644 poker-paf/Css/gameMobile.css create mode 100644 poker-paf/Css/playerSelector.css create mode 100644 poker-paf/stream.php diff --git a/poker-paf/Css/Config.css b/poker-paf/Css/Config.css index eef1563..a6fb9bd 100644 --- a/poker-paf/Css/Config.css +++ b/poker-paf/Css/Config.css @@ -1,23 +1,20 @@ -/* config.css */ +/* config.css - Version Poker PAF Harmonisée */ :root { - --poker-green: #0e5d32; + --table-green: radial-gradient(circle, #277d46 0%, #1a5e33 100%); + --poker-border: #2c1b18; --gold: #d4af37; - --wood: #3e2723; + --gold-light: #f9e27d; + --dark-bg: #0a0a0a; --white: #ffffff; + --wood-dark: #1a0f0d; --danger: #b71c1c; } -label.info { - font-size: 0.8rem; - color: rgb(141, 141, 141); - margin-left: 10px; -} - body { - background-color: #121212; - background-image: radial-gradient(circle, #1a1a1a 0%, #000000 100%); + background-color: var(--dark-bg); + background-image: radial-gradient(circle at center, #1a1a1a 0%, #050505 100%); color: var(--white); - font-family: 'Segoe UI', sans-serif; + font-family: 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; @@ -26,23 +23,49 @@ body { padding: 20px; } +/* --- LE CONTENEUR (Exactement comme l'accueil) --- */ .container { - background-color: var(--poker-green); - padding: 30px 50px; - border-radius: 60px; - border: 12px solid var(--wood); - box-shadow: 0 0 50px rgba(0,0,0,0.9), inset 0 0 20px rgba(0,0,0,0.5); + background: var(--table-green); + padding: 40px 50px; + border-radius: 40px; /* Carré arrondi comme l'accueil */ + border: 15px solid var(--poker-border); + box-shadow: 0 25px 50px rgba(0,0,0,0.9), inset 0 0 40px rgba(0,0,0,0.6); width: 100%; max-width: 500px; + position: relative; + outline: 2px solid #3d2b27; +} + +/* --- LE LISERÉ BLANC --- */ +.container::before { + content: ''; + position: absolute; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 30px; + pointer-events: none; + z-index: 1; } h1 { color: var(--gold); - text-align: center; text-transform: uppercase; font-size: 1.8rem; margin-bottom: 30px; - text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + text-align: center; + letter-spacing: 1px; + text-shadow: 2px 2px 0px rgba(0,0,0,0.8); + position: relative; + z-index: 2; +} + +/* --- FORMULAIRE ET INPUTS --- */ +form { + position: relative; + z-index: 2; } label { @@ -50,6 +73,15 @@ label { color: var(--gold); font-weight: bold; margin-bottom: 8px; + text-transform: uppercase; + font-size: 0.9rem; +} + +label.info { + text-transform: none; + font-size: 0.8rem; + color: rgba(255,255,255,0.5); + margin-bottom: 15px; } input[type="number"], @@ -57,14 +89,21 @@ input[type="text"] { width: 100%; padding: 12px; margin-bottom: 20px; - border: 2px solid var(--gold); - border-radius: 8px; - background: rgba(255, 255, 255, 0.9); - box-sizing: border-box; /* Pour que le padding ne dépasse pas */ + border: 2px solid rgba(212, 175, 55, 0.5); + border-radius: 10px; + background: rgba(0, 0, 0, 0.3); + color: white; font-size: 1rem; + transition: border-color 0.3s; } -/* Style spécifique pour la ligne joueur */ +input:focus { + outline: none; + border-color: var(--gold); + background: rgba(0, 0, 0, 0.5); +} + +/* --- LIGNES JOUEURS --- */ .player-row { display: flex; gap: 10px; @@ -72,163 +111,98 @@ input[type="text"] { align-items: center; } -.player-row input { - margin-bottom: 0; /* On annule la marge pour l'alignement */ -} - -/* Boutons */ -button, input[type="submit"] { - cursor: pointer; - font-weight: bold; - transition: all 0.2s; - border: none; -} - -button[type="button"] { - background-color: var(--wood); +.player-row p { color: var(--gold); - border: 1px solid var(--gold); - padding: 10px 15px; - border-radius: 5px; - margin-bottom: 10px; + font-weight: bold; + margin: 0; + min-width: 20px; } -button[type="button"]:hover { - background-color: #4e342e; -} - -/* Bouton Poubelle */ -.player-row button { - background-color: var(--danger); - color: white; - border: none; +.player-row input { margin-bottom: 0; } -/* Bouton Démarrer (le gros bouton doré) */ +/* --- BOUTONS --- */ +button, input[type="submit"] { + cursor: pointer; + font-weight: bold; + transition: all 0.3s; + border: none; +} + +/* Ajouter un joueur */ +button[type="button"] { + background-color: rgba(0,0,0,0.4); + color: var(--white); + border: 1px solid rgba(255,255,255,0.2); + padding: 10px 15px; + border-radius: 8px; + margin: 10px 0 20px 0; + width: 100%; +} + +button[type="button"]:hover { + background-color: rgba(255,255,255,0.1); + border-color: var(--white); +} + +/* Poubelle */ +.player-row button { + background-color: var(--danger); + color: white; + padding: 10px; + border-radius: 8px; + width: auto; + margin: 0; +} + +/* Bouton Démarrer (Gros bouton doré comme l'accueil) */ input[type="submit"] { width: 100%; - background: linear-gradient(135deg, #d4af37 0%, #f9e27d 50%, #d4af37 100%); - color: var(--wood); - padding: 15px; + background: linear-gradient(135deg, var(--gold) 0%, var(--gold-light) 50%, var(--gold) 100%); + color: var(--wood-dark); + padding: 18px; font-size: 1.2rem; - border-radius: 30px; - margin-top: 20px; - box-shadow: 0 4px 15px rgba(0,0,0,0.3); + border-radius: 50px; + margin-top: 10px; + box-shadow: 0 8px 0px #927521, 0 15px 20px rgba(0,0,0,0.4); + text-transform: uppercase; } input[type="submit"]:hover { - transform: scale(1.02); - filter: brightness(1.1); -} - -/* Bouton Debug discret en bas */ -.btn-debug { - background: transparent !important; - color: rgba(255,255,255,0.3) !important; - border: 1px dashed rgba(255,255,255,0.3) !important; - width: 100%; - margin-top: 30px; - font-size: 0.8rem; + transform: translateY(-2px); + box-shadow: 0 10px 0px #927521, 0 20px 25px rgba(0,0,0,0.5); } +/* Bouton Retour */ .btn-back { position: absolute; - top: 25px; /* Ajuste selon tes goûts */ - left: 35px; /* Aligné avec le bord du cadre */ + top: 25px; + left: 30px; color: var(--gold); text-decoration: none; font-weight: bold; - font-size: 0.9rem; - opacity: 0.7; - transition: opacity 0.3s, transform 0.2s; - width: 20px; + font-size: 0.8rem; + background: none !important; + border: none !important; + padding: 0 !important; + z-index: 10; + opacity: 0.8; } .btn-back:hover { opacity: 1; - transform: translateX(-5px); /* Petit effet de glissement vers la gauche */ + transform: translateX(-3px); } - - -/* --- Mode Mobile ---*/ -/* --- ADAPTATION MOBILE POUR LA CONFIGURATION --- */ +/* --- ADAPTATION MOBILE --- */ @media (max-width: 600px) { - - body { - padding: 10px; /* On réduit la marge extérieure */ - align-items: flex-start; /* On aligne en haut pour que le scroll soit naturel */ - } - + body { align-items: flex-start; } .container { - padding: 20px; /* On réduit le padding interne (30px 50px -> 20px) */ - border-width: 8px; /* Bordure bois plus fine */ - border-radius: 30px; /* Moins arrondi pour gagner de la place */ - width: 100%; - box-sizing: border-box; - } - - h1 { - font-size: 1.4rem; - margin-bottom: 20px; - } - - /* Le bouton "Retour" */ - .btn-back { - position: relative; /* On le sort de l'absolute pour qu'il ne chevauche pas le titre */ - top: 0; - left: 0; - display: block; - margin-bottom: 15px; - width: auto; - } - - /* 1. Ligne joueur : Le plus important ! */ - .player-row { - flex-direction: column; /* On empile Nom et Poubelle verticalement */ - align-items: stretch; - background: rgba(0,0,0,0.1); - padding: 10px; - border-radius: 8px; - border: 1px solid rgba(212, 175, 55, 0.3); - } - - .player-row input { - width: 100%; /* Le champ de nom prend toute la largeur */ - } - - .player-row button { - width: 100%; /* Le bouton supprimer devient une large barre rouge */ - padding: 12px; - margin-top: 5px; - } - - /* 2. Inputs plus gros pour le tactile */ - input[type="number"], - input[type="text"] { - padding: 15px; - font-size: 1.1rem; /* Évite le zoom auto sur iPhone */ - } - - /* 3. Boutons d'action */ - button[type="button"] { - width: 100%; /* "Ajouter un joueur" prend toute la largeur */ - padding: 15px; - font-size: 1rem; - } - - input[type="submit"] { - padding: 18px; - font-size: 1.1rem; - border-radius: 15px; /* Un peu moins arrondi pour le look mobile */ - } - - /* Infos de mise (les petits labels gris) */ - label.info { - margin-left: 0; - margin-top: -15px; - margin-bottom: 15px; - display: block; + padding: 30px 20px; + border-width: 10px; + border-radius: 30px; } + .container::before { top: 7px; left: 7px; right: 7px; bottom: 7px; } + .btn-back { position: relative; top: 0; left: 0; margin-bottom: 20px; } } \ No newline at end of file diff --git a/poker-paf/Css/Game.css b/poker-paf/Css/Game.css index a4f67bd..96498c4 100644 --- a/poker-paf/Css/Game.css +++ b/poker-paf/Css/Game.css @@ -1,126 +1,725 @@ -/* --- CONFIGURATION --- */ +/* --- CONFIGURATION DES COULEURS & VARIABLES --- */ :root { --table-green: radial-gradient(circle, #277d46 0%, #1a5e33 100%); - --poker-border: #2c1b18; + --poker-border: #2c1b18; /* Marron cuir très sombre */ --gold: #d4af37; - --dark-bg: #0a0a0a; - --panel-bg: #151515; + --gold-light: #f9e27d; + --dark-bg: #0f0f0f; + --panel-bg: #1a1a1a; --white: #ffffff; + --shadow: 0 10px 30px rgba(0,0,0,0.8); + --green : #4dbf4d; } -/* --- STRUCTURE FIXE --- */ -body, html { - margin: 0; - padding: 0; - height: 100%; +/* --- RESET & BASE --- */ +body { background-color: var(--dark-bg); + background-image: radial-gradient(circle at center, #1a1a1a 0%, #0a0a0a 100%); color: var(--white); - font-family: 'Segoe UI', sans-serif; + font-family: 'Segoe UI', Roboto, sans-serif; + margin: 0; + height: 100vh; + display: flex; + flex-direction: column; overflow: hidden; } .game-container { - display: flex; /* Force l'alignement horizontal : Table à gauche, Panel à droite */ - width: 100vw; - height: 100vh; + display: flex; + flex-direction: row; + height: 100%; } -/* --- TABLE (ZONE GAUCHE) --- */ +/* --- STATS BAR (TOP) --- */ +.stats-bar { + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(10px); + padding: 12px 25px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(212, 175, 55, 0.3); + z-index: 100; +} + +/* --- TABLE DE JEU --- */ .table-container { - flex: 1; /* Prend tout l'espace restant */ - position: relative; + flex-grow: 1; display: flex; justify-content: center; align-items: center; - background: radial-gradient(circle, #1a1a1a 0%, #050505 100%); + perspective: 1000px; /* Petit effet de profondeur */ + padding: 40px; + filter: blur(0); + transition: filter 0.3s ease; +} + +/* On n'applique le flou QUE si la classe est présente */ +.table-container.blur-effect { + filter: blur(8px); + pointer-events: none; /* Empêche de cliquer sur la table quand le panel est ouvert */ } .poker-table { - width: 750px; - height: 380px; + width: 900px; + height: 450px; background: var(--table-green); - border: 15px solid var(--poker-border); - border-radius: 200px; - position: relative; /* Important pour le placement des joueurs */ - box-shadow: inset 0 0 50px rgba(0,0,0,0.5), 0 20px 40px rgba(0,0,0,0.8); + border: 18px solid var(--poker-border); + border-radius: 250px; + position: relative; + /* Ombre interne pour l'effet rebords et ombre externe pour la profondeur */ + box-shadow: + inset 0 0 60px rgba(0,0,0,0.7), + 0 25px 50px rgba(0,0,0,0.9), + 0 0 0 4px #3d2b27; } -/* --- PANEL D'ACTION (ZONE DROITE) --- */ +/* Ligne blanche de démarcation sur la table */ +.poker-table::after { + content: ''; + position: absolute; + top: 25px; left: 25px; right: 25px; bottom: 25px; + border: 2px solid rgba(255,255,255,0.1); + border-radius: 230px; + pointer-events: none; +} + +/* --- ZONE CENTRALE (POT) --- */ +.pot-area { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + z-index: 2; +} + +.total-pot { + font-size: 3.5rem; + font-weight: 900; + color: var(--white); + text-shadow: 0 4px 10px rgba(0,0,0,0.5); + letter-spacing: -1px; +} + +.total-pot::before { + content: 'POT: '; + font-size: 0.9rem; + display: block; + color: var(--gold); + letter-spacing: 2px; +} + +.current-bet-display { + font-size: 1rem; + text-transform: uppercase; + color: rgba(255,255,255,0.6); + margin-top: -5px; +} + +/* --- JOUEURS --- */ +.player-slot { + position: absolute; + width: 150px; + z-index: 10; + transition: transform 0.3s ease; +} + +.player-info { + background: linear-gradient(145deg, #1e1e1e, #111); + border: 2px solid #333; + border-radius: 15px; + padding: 10px; + text-align: center; + box-shadow: var(--shadow); +} + +/* Joueur actif : Effet brillant or */ +.player-info.active { + border-color: var(--gold); + box-shadow: 0 0 20px rgba(212, 175, 55, 0.4); + transform: scale(1.08); + background: linear-gradient(145deg, #2a2a2a, #111); +} + +.player-name { + display: block; + color: var(--gold); + font-weight: 700; + font-size: 0.9rem; + margin-bottom: 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.player-money { + font-size: 1.2rem; + font-weight: bold; + color: #fff; +} + +.player-bet { + font-size: 0.8rem; + color: #888; + background: rgba(0,0,0,0.3); + border-radius: 20px; + padding: 2px 8px; + display: inline-block; + margin-top: 5px; +} + +/* Dealer Badge */ +.dealer-badge { + position: absolute; + top: -12px; + right: -12px; + background: #fff; + color: #000; + width: 28px; + height: 28px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 900; + box-shadow: 0 2px 5px rgba(0,0,0,0.5); + border: 2px solid var(--poker-border); +} + +/* --- POSITIONNEMENT DES SLOTS (AUTO-ADAPTATIF) --- */ +.slot-0 { top: -75px; left: 50%; transform: translateX(-50%); } +.slot-1 { top: 10%; right: -130px; } +.slot-2 { top: 50%; right: -160px; transform: translateY(-50%); } +.slot-3 { bottom: 10%; right: -130px; } +.slot-4 { bottom: -75px; left: 50%; transform: translateX(-50%); } +.slot-5 { bottom: 10%; left: -130px; } +.slot-6 { top: 50%; left: -160px; transform: translateY(-50%); } +.slot-7 { top: 10%; left: -130px; } + +/* --- PANNEAU D'ACTION --- */ .action-panel { - width: 320px; background: var(--panel-bg); border-left: 2px solid var(--gold); - padding: 20px; - display: flex; - flex-direction: column; - box-sizing: border-box; + padding: 25px; + box-shadow: 0 -10px 30px rgba(0,0,0,0.5); +} + +.turn-info { + text-align: center; + margin-bottom: 20px; + font-size: 1.1rem; } .action-buttons { display: flex; + justify-content: center; + gap: 15px; + align-items: center; flex-direction: column; - gap: 12px; } -/* --- BOUTONS --- */ +/* Boutons de base */ .btn { - width: 100%; - padding: 15px; - border-radius: 8px; + padding: 14px 28px; + border-radius: 10px; border: none; font-weight: 800; + cursor: pointer; text-transform: uppercase; + transition: all 0.2s; + font-size: 0.9rem; + width: 80%; +} + +.btn-fold { background: #333; color: #999; } +.btn-fold:hover { background: #444; color: #fff; } + +.btn-call { + background: transparent; + color: var(--green); + border: 2px solid var(--green); +} +.btn-call:hover { background: var(--green); color: #000; } + +.btn-allin { + background: linear-gradient(45deg, #8b0000, #e60000); + color: white; + box-shadow: 0 4px 15px rgba(230, 0, 0, 0.3); +} +.btn-allin:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(230, 0, 0, 0.5); } + +/* Groupe Relance Spécial */ +.raise-group { + display: flex; + background: #000; + border: 2px solid var(--gold); + border-radius: 10px; + overflow: hidden; +} + +#raise-amount { + width: 90px; + background: transparent; + border: none; + color: white; + padding: 10px; + text-align: center; + font-weight: bold; + outline: none; +} + +.btn-validate { + background: var(--gold); + border: none; + padding: 0 20px; + color: #000; + font-weight: bold; cursor: pointer; } -.btn-call { background: #ffffff !important; color: #000 !important; } -.btn-fold { background: #333; color: #999; } -.btn-allin { background: #8b0000; color: #fff; } +/* Boutons utilitaires du haut */ +.btn-spaction, .btn-back, .btn-money { + background: rgba(255,255,255,0.05); + border: 1px solid rgba(212, 175, 55, 0.5); + color: var(--gold); + padding: 8px 15px; + border-radius: 5px; + cursor: pointer; + text-decoration: none; + font-size: 0.8rem; + transition: 0.3s; + margin-left: 5px; +} -.raise-group { +.btn-spaction:hover, .btn-back:hover { + background: var(--gold); + color: #000; +} + +/* Suppression des flèches du type "number" */ +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* --- MENU ADMIN - THÈME BLUEPRINT --- */ + +.admin-toggle { + position: fixed; + top: 20px; + left: 20px; + z-index: 500; + + /* Look rectangulaire et robuste - Bleu */ + background: rgba(13, 27, 42, 0.9); + border: 1px solid #4da6ff; + color: #4da6ff; + padding: 10px 20px; + border-radius: 4px; + + font-weight: 800; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 1.5px; + cursor: pointer; + + /* Ombre portée bleu sombre */ + box-shadow: 4px 4px 0px #060c14; + transition: all 0.2s ease; + backdrop-filter: blur(5px); +} + +.admin-toggle:hover { + background: #4da6ff; + color: #0d1b2a; + transform: translate(-2px, -2px); + box-shadow: 6px 6px 0px #060c14; +} + +.admin-toggle:active { + transform: translate(0, 0); + box-shadow: 2px 2px 0px #060c14; +} + +.admin-overlay { + position: fixed; + inset: 0; + /* Dégradé radial bleu profond pour casser le noir pur */ + background: radial-gradient(circle, rgba(10, 30, 60, 0.9) 0%, rgba(5, 10, 20, 0.95) 100%); + backdrop-filter: blur(8px); + display: none; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.admin-overlay.active { display: flex; - border: 2px solid var(--gold); - border-radius: 8px; - overflow: hidden; } -#raise-amount { - flex: 1; - background: #000; - color: #fff; - border: none; - padding: 10px; - text-align: center; -} -.btn-validate { background: var(--gold); border: none; padding: 0 15px; cursor: pointer; font-weight: bold; } -/* --- PLACEMENT DES JOUEURS (ABSOLU PAR RAPPORT A LA TABLE) --- */ -.player-slot { +.admin-content { + background: #0d1b2a; /* Bleu Navy sombre */ + border: 2px solid #4da6ff; + border-radius: 15px; + padding: 30px; + width: 90%; + max-width: 400px; + position: relative; + text-align: center; + /* Lueur bleue subtile au lieu de l'ombre noire */ + box-shadow: 0 0 50px rgba(77, 166, 255, 0.15); +} + +.admin-content h3 { + color: #4da6ff; + text-transform: uppercase; + letter-spacing: 2px; + margin-bottom: 25px; + font-size: 1.2rem; +} + +.close-admin { position: absolute; - width: 140px; - z-index: 10; + top: 15px; + right: 15px; + background: none; + border: none; + color: #4da6ff; + opacity: 0.5; + font-size: 1.5rem; + cursor: pointer; + transition: opacity 0.2s; } -.player-info { - background: #1e1e1e; - border: 2px solid #333; - border-radius: 10px; +.close-admin:hover { + opacity: 1; +} + +/* --- ÉLÉMENTS INTERNES ADMIN --- */ +.admin-section { + margin: 20px 0; + padding-bottom: 20px; + border-bottom: 1px solid rgba(77, 166, 255, 0.15); +} + +.admin-section label { + display: block; + font-size: 0.75rem; + color: #a3d2ff; + text-transform: uppercase; + margin-bottom: 10px; +} + +.money-group { + display: flex; + gap: 5px; + justify-content: center; + margin-top: 10px; +} + +#money-amount { + background: #050a14; + border: 1px solid #336699; + color: #4da6ff; padding: 8px; + border-radius: 5px; + width: 100px; text-align: center; + font-weight: bold; + outline: none; } -.player-info.active { border-color: var(--gold); box-shadow: 0 0 15px var(--gold); } +#money-amount:focus { + border-color: #4da6ff; +} -/* Positions autour de l'ovale */ -.slot-0 { top: -40px; left: 50%; transform: translateX(-50%); } -.slot-1 { top: 15%; right: -70px; } -.slot-2 { top: 50%; right: -90px; transform: translateY(-50%); } -.slot-3 { bottom: 15%; right: -70px; } -.slot-4 { bottom: -40px; left: 50%; transform: translateX(-50%); } -.slot-5 { bottom: 15%; left: -70px; } -.slot-6 { top: 50%; left: -90px; transform: translateY(-50%); } -.slot-7 { top: 15%; left: -70px; } +.admin-actions { + display: flex; + flex-direction: column; + gap: 10px; + margin: 20px 0; +} -/* --- EFFETS --- */ -.blur-effect, .All-in-Blur { filter: blur(3px) grayscale(1); opacity: 0.5; } -.pot-area { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; } -.total-pot { font-size: 3rem; font-weight: bold; } \ No newline at end of file +/* --- STYLE DU PANNEAU DE VICTOIRE (ADMIN) --- */ + +.win-overlay { + position: absolute; + inset: 0; + /* On garde juste un assombrissement propre */ + background: rgba(0, 0, 0, 0.75); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999 !important; + border-radius: 230px; +} + +.win-panel { + background: #0d1b2a; + border: 2px solid #4da6ff; + border-radius: 20px; + padding: 30px; + width: 90%; + max-width: 400px; + text-align: center; + /* L'ombre bleue suffit largement à détacher le panneau */ + box-shadow: 0 0 50px rgba(77, 166, 255, 0.4); + color: #fff; +} + +/* On neutralise la classe blur-effect pour qu'elle ne fasse plus rien */ +.blur-effect { + filter: none !important; +} + +/* Si tu veux quand même que les joueurs couchés soient différents + mais PAS flous, utilise juste l'opacité */ +.player-slot.is-folded { + opacity: 0.4; + filter: grayscale(0.5); /* Optionnel : les mettre un peu en gris */ +} + +#winner-buttons-area { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + margin-top: 20px; +} + +.btn-win { + background: rgba(77, 166, 255, 0.1); + border: 1px solid rgba(77, 166, 255, 0.4); + color: #fff; + padding: 12px 10px; + border-radius: 8px; + cursor: pointer; + font-weight: bold; + transition: all 0.2s; +} + +.btn-win:hover { + background: #4da6ff; + color: #0d1b2a; +} + +/* Boutons d'action spécifiques bleu */ +.admin-actions .btn-spaction { + background: rgba(77, 166, 255, 0.05); + border: 1px solid #4da6ff; + color: #4da6ff; + font-weight: bold; +} + +.admin-actions .btn-spaction:hover { + background: #4da6ff; + color: #0d1b2a; +} + +.btn-danger { + background: #4a0000 !important; + border: 1px solid #ff4d4d !important; + color: #ff4d4d !important; +} + +.btn-danger:hover { + background: #ff4d4d !important; + color: #fff !important; +} + +.btn-back-home { + display: inline-block; + margin-top: 15px; + color: #4da6ff; + opacity: 0.6; + text-decoration: none; + font-size: 0.8rem; + transition: opacity 0.2s; +} + +.btn-back-home:hover { + opacity: 1; + text-decoration: underline; +} + +.stat-item { + color: #a3d2ff; + background: rgba(0, 0, 0, 0.2); + padding: 12px; + border-radius: 8px; + font-size: 0.9rem; + margin: 15px 0; + border: 1px dashed rgba(77, 166, 255, 0.3); +} + +.stat-item strong { + color: #fff; + font-size: 1.1rem; +} + +.switch { position: relative; display: inline-block; width: 50px; height: 26px; } +.switch input { opacity: 0; width: 0; height: 0; } +.slider { + position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; + background-color: #e74c3c; transition: .4s; border-radius: 34px; +} +.slider:before { + position: absolute; content: ""; height: 18px; width: 18px; left: 4px; bottom: 4px; + background-color: white; transition: .4s; border-radius: 50%; +} +input:checked + .slider { background-color: #2ecc71; } +input:checked + .slider:before { transform: translateX(24px); } + +/* Container du switch */ +.admin-control-group { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background: rgba(255,255,255,0.05); + border-radius: 8px; + margin-bottom: 15px; +} +.switch { position: relative; display: inline-block; width: 50px; height: 26px; } +.switch input { opacity: 0; width: 0; height: 0; } +.slider { + position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; + background-color: #ccc; transition: .4s; border-radius: 34px; +} +.slider:before { + position: absolute; content: ""; height: 18px; width: 18px; left: 4px; bottom: 4px; + background-color: white; transition: .4s; border-radius: 50%; +} +input:checked + .slider { background-color: #2ecc71; } +input:checked + .slider:before { transform: translateX(24px); } + +/* Désactive les couleurs et passe en gris */ +.action-buttons button:disabled, +.raise-group button:disabled { + background: #444 !important; /* Gris foncé */ + border-color: #555 !important; + color: #888 !important; + cursor: not-allowed; + box-shadow: none !important; + filter: grayscale(100%); +} + +/* Désactive aussi l'aspect du champ de saisie */ +.raise-group input:disabled { + background: #222 !important; + border-color: #333 !important; + color: #555 !important; + cursor: not-allowed; +} + +/* Style pour mon propre slot sur la table */ +.player-slot.is-me .player-name { + color: #2ecc71; /* Vert flashy */ + font-weight: bold; + text-shadow: 0 0 5px rgba(46, 204, 113, 0.5); +} + +.player-slot.is-me .player-info { + border: 2px solid rgba(46, 204, 113, 0.4); +} + +.player-slot.is-me .player-info.active { + border-color: #2ecc71 !important; /* Vert */ + box-shadow: 0 0 25px rgba(46, 204, 113, 0.6) !important; /* Ombre verte plus diffuse */ + background: linear-gradient(145deg, #1a2e1f, #0a0a0a) !important; /* Léger reflet vert sombre */ +} + +/* Changement de couleur du panneau d'action si c'est MON tour */ +.action-panel.my-turn-border { + border-top: 4px solid #2ecc71 !important; + background: linear-gradient(to bottom, rgba(46, 204, 113, 0.1), var(--panel-bg)); + transition: all 0.3s ease; +} + +/* --- PANNEAUX DE FIN DE PARTIE (JOUEURS) --- */ + +.player-win-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.9); + display: flex; + justify-content: center; + align-items: center; + z-index: 10000; + text-align: center; + backdrop-filter: blur(5px); +} + +.player-win-content { + background: linear-gradient(145deg, #1a1a1a, #0a0a0a); + border: 3px solid var(--gold); + padding: 40px; + border-radius: 20px; + box-shadow: 0 0 50px rgba(212, 175, 55, 0.3); + max-width: 400px; + width: 90%; +} + +.player-win-content h2 { + color: var(--gold); + text-transform: uppercase; + letter-spacing: 3px; + margin-bottom: 10px; +} + +/* Animation de pulsation pour l'attente */ +.waiting-text { + color: #888; + font-style: italic; + animation: pulse 1.5s infinite; +} + +@keyframes pulse { + 0% { opacity: 0.5; } + 50% { opacity: 1; } + 100% { opacity: 0.5; } +} + +/* État Victoire Finale */ +.victory-title { + color: #2ecc71 !important; + font-size: 2rem; + margin-bottom: 20px; +} + +.win-amount { + font-size: 1.5rem; + font-weight: bold; + display: block; + margin: 15px 0; +} + +.btn-ok { + background: var(--gold); + color: #000; + border: none; + padding: 12px 30px; + border-radius: 8px; + font-weight: 900; + cursor: pointer; + text-transform: uppercase; + margin-top: 20px; + transition: transform 0.2s; +} + +.btn-ok:hover { + transform: scale(1.1); +} + +@keyframes fall { to { transform: translateY(110vh) rotate(360deg); } } + +/* --- ADAPTATION DES SLOTS --- */ +@media (max-width: 1200px) { + .poker-table { + width: 700px; + height: 350px; + } + .slot-1, .slot-7 { right: -80px; left: auto; } + .slot-3, .slot-5 { right: -80px; left: auto; } +} \ No newline at end of file diff --git a/poker-paf/Css/Index.css b/poker-paf/Css/Index.css index 9dd3d5e..a857d70 100644 --- a/poker-paf/Css/Index.css +++ b/poker-paf/Css/Index.css @@ -1,16 +1,19 @@ -/* index.css */ +/* index.css - Version Poker PAF Harmonisée & Aérée */ :root { - --poker-green: #0e5d32; + --table-green: radial-gradient(circle, #277d46 0%, #1a5e33 100%); + --poker-border: #2c1b18; --gold: #d4af37; - --wood: #3e2723; + --gold-light: #f9e27d; + --dark-bg: #0a0a0a; --white: #ffffff; + --wood-dark: #1a0f0d; } body { - background-color: #121212; - background-image: radial-gradient(circle, #1a1a1a 0%, #000000 100%); + background-color: var(--dark-bg); + background-image: radial-gradient(circle at center, #1a1a1a 0%, #050505 100%); color: var(--white); - font-family: 'Segoe UI', sans-serif; + font-family: 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; @@ -18,245 +21,219 @@ body { margin: 0; } +/* --- LE CONTENEUR (Exactement comme la table de l'image 2) --- */ .welcome-container { - background-color: var(--poker-green); - padding: 50px; - border-radius: 80px; - border: 15px solid var(--wood); - box-shadow: 0 0 60px rgba(0,0,0,0.9), inset 0 0 30px rgba(0,0,0,0.5); + background: var(--table-green); + padding: 40px; + border-radius: 40px; /* Moins ovale, plus "carré arrondi" */ + border: 15px solid var(--poker-border); /* Bordure épaisse cuir/bois */ + box-shadow: 0 25px 50px rgba(0,0,0,0.9), inset 0 0 40px rgba(0,0,0,0.6); text-align: center; width: 90%; - max-width: 450px; + max-width: 600px; + position: relative; + /* Petit liseré de finition */ + outline: 2px solid #3d2b27; +} + +/* --- LE LISERÉ BLANC (AJOUTÉ ICI) --- */ +.welcome-container::before { + content: ''; + position: absolute; + /* On le décale de 10px vers l'intérieur par rapport à la bordure marron */ + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; + border: 1px solid rgba(255, 255, 255, 0.15); /* Blanc très subtil et transparent */ + border-radius: 30px; /* Un peu moins que le parent pour suivre la courbe */ + pointer-events: none; /* Pour ne pas gêner les clics sur les boutons */ + z-index: 1; } h1 { color: var(--gold); text-transform: uppercase; - font-size: 2.2rem; - margin-bottom: 40px; - text-shadow: 3px 3px 6px rgba(0,0,0,0.7); + font-size: 2.5rem; + margin-bottom: 30px; + letter-spacing: 2px; + text-shadow: 3px 3px 0px rgba(0,0,0,0.8); } -/* Style pour le lien "Démarrer une partie" */ -.btn-start { - display: inline-block; - background: linear-gradient(135deg, #d4af37 0%, #f9e27d 50%, #d4af37 100%); - color: var(--wood); - text-decoration: none; - padding: 15px 30px; - font-weight: bold; +h2 { + font-size: 1.1rem; + color: rgba(255,255,255,0.6); + text-transform: uppercase; + margin-top: 25px; + letter-spacing: 1px; +} + +/* --- BOUTON PRINCIPAL CRÉER --- */ +.welcome-container > button { + background: linear-gradient(135deg, var(--gold) 0%, var(--gold-light) 50%, var(--gold) 100%); + color: var(--wood-dark); + padding: 18px 40px; + font-weight: 900; font-size: 1.2rem; - border-radius: 40px; - margin-bottom: 40px; - transition: transform 0.2s, box-shadow 0.2s; - box-shadow: 0 5px 15px rgba(0,0,0,0.4); -} - -.btn-start:hover { - transform: scale(1.05); - box-shadow: 0 0 20px rgba(212, 175, 55, 0.6); -} - -/* Section rejoindre */ -.join-section { - border-top: 1px solid rgba(255,255,255,0.2); - padding-top: 30px; -} - -label { - display: block; - color: var(--gold); - margin-bottom: 15px; - font-weight: bold; -} - -input[type="text"] { - background: rgba(255, 255, 255, 0.9); - border: 2px solid var(--gold); - border-radius: 5px; - padding: 12px; - width: 60%; - font-size: 1rem; - margin-bottom: 15px; - outline: none; - text-align: center; -} - -button { - background-color: var(--wood); - color: var(--gold); - border: 2px solid var(--gold); - padding: 10px 20px; - font-weight: bold; - border-radius: 5px; + border-radius: 50px; + border: none; cursor: pointer; - transition: background 0.3s; -} - -button:hover { - background-color: #5d3a3a; -} - -.btn-join-list{ - background-color: transparent; - color: var(--gold); - padding-left: 12px; - border: 1px solid var(--gold); - padding: 8px 12px; - border-radius: 5px; + transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + box-shadow: 0 8px 0px #927521, 0 15px 20px rgba(0,0,0,0.4); + text-transform: uppercase; margin-bottom: 10px; } -.btn-join-list:hover { - background-color: var(--gold); - color: var(--wood); +.welcome-container > button:hover { + transform: translateY(-2px); + box-shadow: 0 10px 0px #927521, 0 20px 25px rgba(0,0,0,0.5); } -li { - display: flex; - justify-content: space-between; - align-items: center; +/* --- LISTE DES PARTIES --- */ +#games_list ul { + list-style: none; + padding: 0; + margin: 25px 0; } - - -/* --- Mode Mobile --- */ -/* --- ADAPTATION MOBILE POUR L'ACCUEIL --- */ -@media (max-width: 600px) { - - body { - /* On permet le scroll si le contenu est plus haut que l'écran */ - height: auto; - padding: 20px 0; - } - - .welcome-container { - /* On réduit les bordures massives et le padding */ - padding: 30px 20px; - border-width: 8px; /* Bordure en bois moins épaisse */ - border-radius: 40px; /* Moins arrondi pour gagner de la place */ - width: 85%; - } - - h1 { - font-size: 1.6rem; /* Titre plus petit */ - margin-bottom: 30px; - } - - /* Le bouton principal doit être bien large pour le pouce */ - .btn-start { - width: 100%; - box-sizing: border-box; /* Pour que le padding n'agrandisse pas la largeur */ - padding: 18px 20px; - font-size: 1.1rem; - } - - /* Section rejoindre */ - input[type="text"] { - width: 100%; /* L'input prend toute la largeur */ - box-sizing: border-box; - padding: 15px; - font-size: 1.1rem; /* Évite le zoom auto de l'iPhone sur les inputs */ - } - - button { - width: 100%; /* Les boutons de validation passent en pleine largeur */ - padding: 15px; - margin-top: 10px; - } - - /* Pour la liste des parties */ - li { - flex-direction: column; /* On empile le nom et le bouton */ - gap: 10px; - background: rgba(0,0,0,0.2); - padding: 15px; - border-radius: 10px; - margin-bottom: 15px; - } - - .btn-join-list { - width: 100%; - text-align: center; - } +#games_list li { + margin-bottom: 20px; } - - - -/* Conteneur principal */ +/* --- L'ACCORDÉON (Design des slots joueurs) --- */ .mon-accordeon { - max-width: 600px; - width: 100%; - margin: 20px auto; - font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; - background-color: #1a1a1a; /* Noir anthracite */ - border-radius: 8px; + background-color: rgba(0,0,0,0.4); /* Fond sombre comme les slots */ + border-radius: 15px; + border: 2px solid #333; overflow: hidden; - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5); - border: 1px solid #333; - } - - .container-parent { - display: flex; - justify-content: space-between; /* Pousse les éléments aux extrémités */ - align-items: center; /* Aligne verticalement au centre (optionnel) */ - width: 100%; /* S'assure que le container prend toute la largeur */ + text-align: left; + transition: border-color 0.3s; } - - /* L'élément details */ - details { - border-bottom: 1px solid #333; - } - - details:last-child { - border-bottom: none; - } - - /* Le titre (Summary) */ - summary { - padding: 15px 20px; - background-color: #1a1a1a; - color: #d4af37; /* Doré sobre */ + +details[open].mon-accordeon { + border-color: var(--gold); +} + +summary { + padding: 18px 25px; + color: var(--gold); font-weight: bold; cursor: pointer; - list-style: none; /* Cache la flèche par défaut sur certains navigateurs */ - transition: background 0.3s ease; + list-style: none; display: flex; justify-content: space-between; align-items: center; - } - - /* Style de survol */ - summary:hover { - background-color: #252525; - color: #f0c644; - } - - /* Flèche personnalisée à droite */ - summary::after { - content: '♣'; /* Trèfle discret */ + outline: none; +} + +summary::-webkit-details-marker { display: none; } + +summary::after { + content: '♣'; font-size: 1.2rem; - transition: transform 0.3s ease; - } - - /* Rotation du trèfle quand c'est ouvert */ - details[open] summary::after { + transition: transform 0.4s ease; +} + +details[open] summary::after { transform: rotate(180deg); - color: #2e7d32; /* Vert poker au clic */ - } - - /* Contenu de l'accordéon */ - .contenu { - padding: 15px 20px; - background-color: #0d3b2e; /* Vert tapis de table sombre */ - color: #e0e0e0; - line-height: 1.6; - border-top: 1px solid #1a1a1a; + color: #4dbf4d; +} + +/* --- CONTENU DÉPLOYÉ (Aération améliorée) --- */ +.container-parent { + display: flex; + justify-content: space-between; + gap: 30px; /* Plus d'espace entre les boutons et les noms */ + padding: 0 25px; + max-height: 0; + opacity: 0; + overflow: hidden; + transition: max-height 0.4s ease, opacity 0.3s ease, padding 0.4s ease; + background: rgba(0,0,0,0.2); +} + +details[open] .container-parent { + max-height: 600px; + opacity: 1; + padding: 25px; /* On redonne du souffle ici */ +} + +/* Zone des noms (Left) */ +.left { + flex: 1.2; + border-left: 2px solid rgba(212, 175, 55, 0.2); + padding-left: 20px; +} + +.left p { + margin: 5px 0; font-size: 0.95rem; - } - - /* Petit effet de bordure gauche pour marquer l'ouverture */ - details[open] { - border-left: 4px solid #2e7d32; - } \ No newline at end of file + color: #eee; + font-weight: 500; +} + +/* Zone des boutons (Right) */ +.right { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; +} + +.right p { + font-size: 0.85rem; + color: var(--gold); + margin-bottom: 15px; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* Boutons Rejoindre */ +.btn-join-list, .btn-admin-join-list { + width: 100%; + margin-bottom: 10px; + padding: 12px; + border-radius: 8px; + border: 1px solid var(--gold); + background: transparent; + color: var(--gold); + cursor: pointer; + font-weight: bold; + text-transform: uppercase; + font-size: 0.75rem; + transition: 0.2s; +} + +.btn-join-list:hover { + background: var(--gold); + color: black; +} + +.btn-admin-join-list { + border-color: rgba(255,255,255,0.3); + color: rgba(255,255,255,0.5); +} + +.btn-admin-join-list:hover { + border-color: white; + color: white; + background: rgba(255,255,255,0.1); +} + +/* --- RESPONSIVE MOBILE --- */ +@media (max-width: 600px) { + .container-parent { + flex-direction: column-reverse; /* Boutons en haut ou en bas selon préférence */ + gap: 20px; + } + .welcome-container { + padding: 20px; + border-width: 10px; + } + .left { + border-left: none; + border-top: 1px solid rgba(212, 175, 55, 0.2); + padding-left: 0; + padding-top: 15px; + } +} \ No newline at end of file diff --git a/poker-paf/Css/gameMobile.css b/poker-paf/Css/gameMobile.css new file mode 100644 index 0000000..5b017ae --- /dev/null +++ b/poker-paf/Css/gameMobile.css @@ -0,0 +1,152 @@ +/* --- gameMobile.css Refondu --- */ + +@media screen and (max-width: 900px) { + + /* 1. Structure Globale : On bloque le défilement et on utilise tout l'écran */ + body { + height: 100dvh; /* Utilise la hauteur réelle dynamique sur mobile */ + overflow: hidden; + position: fixed; + width: 100%; + } + + .game-container { + flex-direction: column; + height: 100%; + } + + /* 2. La Table : Plus ronde (Arène) */ + .table-container { + flex: 1; + padding: 40px 10px; + display: flex; + align-items: center; + justify-content: center; + min-height: 0; + } + + .poker-table { + width: 100%; + max-width: 340px; + height: 240px; /* Un peu plus haute pour l'aspect rond */ + border-width: 10px; + border-radius: 140px; /* Plus grand rayon pour arrondir les coins */ + box-shadow: inset 0 0 30px rgba(0,0,0,0.8), 0 10px 20px rgba(0,0,0,0.5); + } + + /* 3. Le Pot : Ajustement taille */ + .total-pot { + font-size: 1.8rem; + } + .total-pot::before { font-size: 0.7rem; } + .current-bet-display { font-size: 0.8rem; } + + /* 4. Les Joueurs : Positionnement Arène Symétrique */ + .player-slot { + width: 78px; + } + + .player-info { + padding: 4px; + border-radius: 12px; + border-width: 1px; + } + + .player-name { font-size: 0.65rem; margin-bottom: 2px; } + .player-money { font-size: 0.8rem; } + .player-bet { font-size: 0.6rem; padding: 1px 5px; } + + .dealer-badge { + width: 20px; + height: 20px; + font-size: 0.7rem; + top: -8px; + right: -8px; + } + + /* --- POSITIONNEMENT DES SLOTS --- */ + + /* Haut et Bas Milieu */ + .slot-0 { top: -50px; left: 50%; transform: translateX(-50%); } + .slot-4 { bottom: -50px; left: 50%; transform: translateX(-50%); } + + /* Les 4 coins (Diagonales à distance égale) */ + /* En haut à droite */ + .slot-1 { top: 5%; right: -15px; } + /* En bas à droite */ + .slot-3 { bottom: 5%; right: -15px; } + /* En bas à gauche */ + .slot-5 { bottom: 5%; left: -15px; } + /* En haut à gauche */ + .slot-7 { top: 5%; left: -15px; } + + /* Milieux latéraux */ + .slot-2 { top: 50%; right: -10px; transform: translateY(-50%); } + .slot-6 { top: 50%; left: -10px; transform: translateY(-50%); } + + /* 5. Panneau d'Action : Le "Deck" du bas */ + .action-panel { + width: 100% !important; + height: auto; + padding: 15px 10px 25px 10px; + border-left: none; + border-top: 2px solid var(--gold); + background: #151515; + } + + .turn-info { + font-size: 0.9rem; + margin-bottom: 12px; + } + + .action-buttons { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: + "fold call" + "raise raise" + "allin allin" + "quit quit"; + gap: 8px; + width: 100%; + } + + .btn { + width: 100%; + padding: 12px 5px; + font-size: 0.8rem; + } + + .btn-fold { grid-area: fold; } + .btn-call { grid-area: call; } + .raise-group { + grid-area: raise; + display: flex; + width: 100%; + } + .btn-allin { grid-area: allin; } + .btn-back { + grid-area: quit; + margin: 0; + background: rgba(255,0,0,0.1); + color: #ff4d4d; + opacity: 0.8; + } + + #raise-amount { flex: 1; height: 40px; } + .btn-validate { width: 80px; } +} + +/* 6. Optimisation Paysage (Landscape) */ +@media screen and (max-height: 500px) and (orientation: landscape) { + .poker-table { height: 160px; max-width: 480px; border-radius: 100px; } + .player-slot { width: 70px; } + + /* Ajustement slots en paysage pour ne pas sortir de l'écran */ + .slot-1, .slot-7 { top: -10%; } + .slot-3, .slot-5 { bottom: -10%; } + + .action-panel { padding: 5px; } + .action-buttons { grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-areas: none; } + .btn-allin, .raise-group, .btn-back { grid-column: auto; } +} diff --git a/poker-paf/Css/playerSelector.css b/poker-paf/Css/playerSelector.css new file mode 100644 index 0000000..357270f --- /dev/null +++ b/poker-paf/Css/playerSelector.css @@ -0,0 +1,163 @@ +/* main.css - Sélection du Joueur Poker PAF */ +:root { + --table-green: radial-gradient(circle, #277d46 0%, #1a5e33 100%); + --poker-border: #2c1b18; + --gold: #d4af37; + --gold-light: #f9e27d; + --dark-bg: #0a0a0a; + --white: #ffffff; + --wood-dark: #1a0f0d; +} + +body { + background-color: var(--dark-bg); + background-image: radial-gradient(circle at center, #1a1a1a 0%, #050505 100%); + color: var(--white); + font-family: 'Segoe UI', Roboto, sans-serif; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + margin: 0; + padding: 20px; +} + +/* --- LE CONTENEUR TABLE --- */ +.container { + background: var(--table-green); + padding: 40px; + border-radius: 40px; + border: 15px solid var(--poker-border); + box-shadow: 0 25px 50px rgba(0,0,0,0.9), inset 0 0 40px rgba(0,0,0,0.6); + text-align: center; + width: 95%; + max-width: 700px; + position: relative; + outline: 2px solid #3d2b27; +} + +/* --- LE LISERÉ BLANC --- */ +.container::before { + content: ''; + position: absolute; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 30px; + pointer-events: none; + z-index: 1; +} + +h1 { + color: var(--gold); + text-transform: uppercase; + font-size: 1.6rem; + margin-bottom: 10px; + position: relative; + z-index: 2; + text-shadow: 2px 2px 0px rgba(0,0,0,0.8); +} + +h2 { + font-size: 1.1rem; + color: var(--white); + margin-bottom: 5px; + position: relative; + z-index: 2; +} + +p { + color: rgba(255,255,255,0.7); + font-size: 0.9rem; + margin-bottom: 30px; + position: relative; + z-index: 2; +} + +/* --- GRILLE DE SÉLECTION DES JOUEURS --- */ +.player-selection { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 15px; + position: relative; + z-index: 2; +} + +.join-player-btn { + background: rgba(0, 0, 0, 0.4); + color: var(--gold); + border: 2px solid rgba(212, 175, 55, 0.3); + padding: 20px 10px; + border-radius: 12px; + cursor: pointer; + font-weight: bold; + font-size: 1rem; + transition: all 0.2s ease; + display: flex; + justify-content: center; + align-items: center; + min-height: 80px; + box-shadow: 0 4px 10px rgba(0,0,0,0.3); +} + +.join-player-btn:hover { + background: var(--gold); + color: var(--wood-dark); + transform: translateY(-3px); + border-color: var(--gold-light); + box-shadow: 0 8px 15px rgba(0,0,0,0.5); +} + +/* Effet au clic */ +.join-player-btn:active { + transform: translateY(0); +} + +.back-btn { + position: absolute; + top: 25px; + left: 30px; + color: var(--gold); + background: none !important; + border: none !important; + font-weight: bold; + font-size: 0.8rem; + cursor: pointer; + z-index: 10; + opacity: 0.8; + transition: opacity 0.3s, transform 0.2s; + text-transform: uppercase; + display: flex; + align-items: center; + gap: 5px; +} + +.back-btn:hover { + opacity: 1; + transform: translateX(-3px); +} + +/* --- MOBILE --- */ +@media (max-width: 600px) { + .container { + padding: 25px 15px; + border-width: 10px; + } + .player-selection { + grid-template-columns: repeat(2, 1fr); /* 2 colonnes sur mobile */ + gap: 10px; + } + .back-btn { + position: relative; + top: 0; + left: 0; + margin-bottom: 20px; + justify-content: flex-start; + } + .join-player-btn { + padding: 15px 5px; + font-size: 0.9rem; + } +} \ No newline at end of file diff --git a/poker-paf/Js/Config.js b/poker-paf/Js/Config.js index 9c18ea7..0168fdc 100644 --- a/poker-paf/Js/Config.js +++ b/poker-paf/Js/Config.js @@ -4,7 +4,7 @@ async function SqlRequest(action, params = {}) { try { - const response = await fetch('../Php/RequestsHandler.php', { + const response = await fetch('RequestsHandler.php', { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -97,7 +97,7 @@ loginForm.addEventListener('submit', async function(event) { } const result = await SqlRequest('setFirstPlayer', {game_id: gameId}) if (result.success) { - window.location.href = '../Html/Game.html?game_id=' + gameId; + window.location.href = 'admin-login.html?game_id=' + gameId; } else { console.error("Erreur lors de la définition du premier joueur :", result.error); } diff --git a/poker-paf/Js/Game.js b/poker-paf/Js/Game.js index 8053816..5055441 100644 --- a/poker-paf/Js/Game.js +++ b/poker-paf/Js/Game.js @@ -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 = `🔒 Jeu verrouillé. En attente de l'Admin...`; + inputs.forEach(el => el.disabled = true); + } else if (!isMyTurn) { + if (turnInfo && currentPlayer) { + turnInfo.innerHTML = `Attente de ${currentPlayer.name}...`; + } + inputs.forEach(el => el.disabled = true); + } else { + if (turnInfo) turnInfo.innerHTML = `✅ C'est à vous de jouer !`; + 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 = `
${gameData.pot}
Mise: ${gameData.last_bet}
@@ -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 += ` -
-
- ${player.is_dealer ? '
D
' : ''} +
+ +
+ ${isDealer ? '
D
' : ''} - J${index + 1} : ${player.name} + ${isMe ? 'VOUS' : 'J' + (index + 1)} : ${player.name} ${player.money} 🪙
Mise: ${player.current_bet} 🪙
@@ -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 = ` -

🏆 La partie est terminée ! 🏆
Qui a gagné ?

-
- `; + 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 = ` +
+

FIN DE PARTIE

+

Détermination du gagnant en cours...

+
+ `; + 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 = ` - - -

${playersData.find(player => player.id == playerId).name} gagne la partie et remporte ${gameData.last_bet} 🪙 !

- - - `; - } - } 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 = ` +
+

VICTOIRE DE ${winnerName}

+ Il remporte ${amount} 🪙 + +
+ `; + + 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); + } +} \ No newline at end of file diff --git a/poker-paf/Js/admin-game.js b/poker-paf/Js/admin-game.js index 7d28004..c6de5b9 100644 --- a/poker-paf/Js/admin-game.js +++ b/poker-paf/Js/admin-game.js @@ -30,10 +30,40 @@ async function SqlRequest(action, params = {}) { } catch (erreur) { console.error("Erreur de communication :", erreur); } + +} + +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..."); + }; +} + +// Fonction pour ouvrir le menu administrateur +function toggleAdminMenu() { + const menu = document.getElementById('admin-menu'); + menu.classList.toggle('active'); } // Fonctions pour démarrer la page window.onload = async function() { + const urlParams = new URLSearchParams(window.location.search); + const gId = urlParams.get('game_id'); const result = await SqlRequest('is_admin'); console.log("Vérification des droits administrateur :", result); if (!result.is_admin) { @@ -45,15 +75,98 @@ window.onload = async function() { document.getElementById('title_page').textContent = "Vue Administrateur - " + gameData.name + " - PokerPaf"; updateClientInterface(); + + startRealTimeSync(gId); } async function updateClientInterface() { setupPlayers(); getCurrentPlayer(); + refreshAdminPanel(); activePlayerLabel.textContent = `${currentPlayer.name} (${currentPlayer.money} 🪙)`; } +function refreshAdminPanel() { + const adminPanel = document.getElementById('admin-lock-control'); + if (!adminPanel) return; + + let statsItem = document.getElementById('stat-item'); + if (statsItem) { + statsItem.innerHTML = ` + MISE ACTUELLE: ${gameData.last_bet}
+ POT ACTUEL ${gameData.pot} + `; + } + + const isLocked = Number(gameData.is_locked) === 1; + let lockSwitch = document.getElementById('lock-switch'); + + // On ne crée le HTML que s'il n'existe pas encore + if (!lockSwitch) { + adminPanel.innerHTML = ` +
+ Autoriser le jeu : + +
+ `; + } else { + // Si le switch existe, on met à jour son état SEULEMENT si l'admin ne le touche pas + if (document.activeElement !== lockSwitch) { + lockSwitch.checked = !isLocked; + } + } +} + +// 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 @@ -67,10 +180,14 @@ async function setupPlayers() { `; playersData.forEach((player, index) => { + // On s'assure de comparer avec le nombre 1 car la BDD renvoie souvent des strings ou des entiers + const isDealer = Number(player.is_dealer) === 1; + newHtml += `
- ${player.is_dealer ? '
D
' : ''} + + ${isDealer ? '
D
' : ''} J${index + 1} : ${player.name} ${player.money} 🪙
@@ -212,33 +329,39 @@ async function playerAllIn() { // 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; + await SqlRequest('update_game_status', { + game_id: gameData.id, + status: 'deciding' + }); + // 1. On vérifie si le panel existe déjà pour éviter les doublons + if (document.querySelector('.win-overlay')) return; + // 2. Création de l'overlay (on l'appelle win-overlay pour le CSS) const winOverlay = document.createElement('div'); winOverlay.className = 'win-overlay'; + + // 3. Création du panel blanc/bleu const winPanel = document.createElement('div'); winPanel.className = 'win-panel'; winPanel.innerHTML = `

🏆 La partie est terminée ! 🏆
Qui a gagné ?

+ `; winOverlay.appendChild(winPanel); - container.appendChild(winOverlay); + + // IMPORTANCE : On l'attache au BODY pour qu'il soit au-dessus de TOUT (même l'admin) + document.body.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]; + // On récupère le nom proprement + const nameElement = slot.querySelector('.player-name'); + const name = nameElement ? nameElement.textContent.replace('VOUS', '').replace(':', '').trim() : "Joueur " + id; const btn = document.createElement('button'); btn.className = 'btn-win'; @@ -247,33 +370,82 @@ async function endGame() { area.appendChild(btn); }); - document.getElementById('end-game-screen').style.display = 'flex'; - container.classList.add('blur-effect'); + // On supprime la ligne qui cherchait 'end-game-screen' car winOverlay fait déjà le job } - async function declareWinner(playerId) { - const container = document.querySelector('.table-container'); - container.classList.remove('blur-effect'); + console.log("Début de la procédure de victoire..."); - 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 = ` - + // 1. Première requête : On définit le gagnant + const resWinner = await SqlRequest('set_winner', { + game_id: gameData.id, + player_id: playerId + }); -

${playersData.find(player => player.id == playerId).name} gagne la partie et remporte ${gameData.last_bet} 🪙 !

- - - `; + if (resWinner && resWinner.success) { + console.log("✅ Winner ID mis à jour en BDD"); + + // 2. Deuxième requête : On passe le statut à 'finished' + // C'est cette requête qui va déclencher l'écran de victoire chez les joueurs via le SSE + const resStatus = await SqlRequest('update_game_status', { + game_id: gameData.id, + status: 'finished' + }); + + if (resStatus && resStatus.success) { + console.log("✅ Statut passé à 'finished'"); + + // Mise à jour de l'interface Admin + showAdminWinPanel(playerId); } } else { - console.error("Erreur lors de la déclaration du gagnant :", response.error); + alert("Erreur lors de la mise à jour du gagnant."); } } +// Fonction isolée pour l'affichage du panel admin (plus propre) +function showAdminWinPanel(playerId) { + const container = document.querySelector('.table-container'); + if (container) container.classList.remove('blur-effect'); + + const winPanel = document.querySelector('.win-panel'); + if (winPanel) { + const winner = playersData.find(p => p.id == playerId); + winPanel.innerHTML = ` +

🏆 Victoire de ${winner ? winner.name : 'Joueur'}

+

Le pot de ${gameData.pot} 🪙 lui a été attribué.

+ + `; + } +} async function StartNewGame() { - window.location.reload(); + // 1. On force le verrouillage (status: 1) avant de relancer + try { + const response = await SqlRequest('toggle_lock', { + game_id: gameData.id, + status: 1 // 1 pour verrouillé + }); + + if (response.success) { + console.log("Partie verrouillée, relance en cours..."); + // 2. On recharge la page pour démarrer la nouvelle main + const response = await SqlRequest('update_game_status', { + game_id: gameData.id, + status: 'playing' + }); + if(response.success){ + window.location.reload(); + } else { + console.error("Erreur de changement de status :", response.error); + } + } else { + console.error("Erreur de verrouillage :", response.error); + // Optionnel : on recharge quand même ou on affiche une alerte + window.location.reload(); + } + } catch (error) { + console.error("Erreur réseau :", error); + window.location.reload(); + } } async function addMoney() { diff --git a/poker-paf/RequestsHandler.php b/poker-paf/RequestsHandler.php index 93ee1a4..4824efd 100644 --- a/poker-paf/RequestsHandler.php +++ b/poker-paf/RequestsHandler.php @@ -1,6 +1,9 @@ PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - // C'est cette ligne qui fait la différence : PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_STRINGIFY_FETCHES => false, ]; -// charger la BDD try { $pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass, $options); - $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (Exception $e) { - die(json_encode(['error' => 'Connexion échouée'])); + echo json_encode(['success' => false, 'error' => 'Connexion échouée']); + exit; } -// lire les données JSON envoyées par le client +// Lecture de l'input $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data || !isset($data['action'])) { - echo json_encode(['error' => 'Aucune action spécifiée']); + echo json_encode(['success' => false, 'error' => 'Aucune action spécifiée']); exit; } $action = $data['action']; $params = $data['params'] ?? []; -$response = []; - switch ($action) { case 'getGame': $stmt = $pdo->prepare("SELECT * FROM games WHERE id = ?"); - $stmt->execute([$params['game_id']]); - $game = $stmt->fetch(PDO::FETCH_ASSOC); - if ($game) { - $response = ['success' => true, 'game' => $game]; - } else { - $response = ['error' => 'Partie non trouvée']; - } - break; + $stmt->execute([(int)$params['game_id']]); + $game = $stmt->fetch(); + echo json_encode(['success' => true, 'game' => $game]); + exit; case 'getPlayers': $stmt = $pdo->prepare("SELECT * FROM players WHERE game_id = ?"); - $stmt->execute([$params['game_id']]); - $players = $stmt->fetchAll(PDO::FETCH_ASSOC); - $response = ['success' => true, 'players' => $players]; - break; + $stmt->execute([(int)$params['game_id']]); + $players = $stmt->fetchAll(); + echo json_encode(['success' => true, 'players' => $players]); + exit; case 'createGame': $stmt = $pdo->prepare("INSERT INTO games (start_money, start_blind, name) VALUES (?, ?, ?)"); - $stmt->execute([$params['start_money'], $params['blind'], $params['name']]); - $game_id = $pdo->lastInsertId(); - $response = ['success' => true, 'game_id' => $game_id]; - break; + $stmt->execute([(int)$params['start_money'], (int)$params['blind'], $params['name']]); + echo json_encode(['success' => true, 'game_id' => $pdo->lastInsertId()]); + exit; case 'addPlayer': $stmt = $pdo->prepare("INSERT INTO players (name, game_id, money) VALUES (?, ?, ?)"); - $stmt->execute([$params['name'], $params['game_id'], $params['money']]); - $player_id = $pdo->lastInsertId(); - $response = ['success' => true]; - break; - - case 'setFirstPlayer': - $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? ORDER BY id ASC LIMIT 1"); - $stmt->execute([$params['game_id']]); - $first_player = $stmt->fetch(PDO::FETCH_ASSOC); - if ($first_player) { - $first_player_id = $first_player['id']; - $stmt = $pdo->prepare("UPDATE games SET current_player_id = ? WHERE id = ?"); - $stmt->execute([$first_player_id, $params['game_id']]); - $stmt = $pdo->prepare("UPDATE players SET is_dealer = 1 WHERE id = ?"); - $stmt->execute([$first_player_id]); - $response = ['success' => true]; - } else { - $response = ['error' => 'Aucun joueur trouvé pour cette partie']; - } - break; + $stmt->execute([$params['name'], (int)$params['game_id'], (int)$params['money']]); + echo json_encode(['success' => true]); + exit; case 'next_player': - $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? AND is_folded = 0 AND id > ? AND money <> 0 ORDER BY id ASC LIMIT 1"); - $stmt->execute([$params['game_id'], $params['current_player_id']]); - $next_player = $stmt->fetch(); + $game_id = (int)$params['game_id']; + $current_id = (int)$params['current_player_id']; - if (!$next_player) { // Si on est au dernier, on revient au premier pas couché - $stmt = $pdo->prepare("SELECT id FROM players WHERE is_folded = 0 AND game_id = ? ORDER BY id ASC LIMIT 1"); - $stmt->execute([$params['game_id']]); - $next_player = $stmt->fetch(); + // On cherche le prochain joueur (ID plus grand, non couché, pas ruiné) + $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? AND is_folded = 0 AND money > 0 AND id > ? ORDER BY id ASC LIMIT 1"); + $stmt->execute([$game_id, $current_id]); + $next = $stmt->fetch(); + + if (!$next) { + // Boucle : on revient au tout premier de la liste + $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? AND is_folded = 0 AND money > 0 ORDER BY id ASC LIMIT 1"); + $stmt->execute([$game_id]); + $next = $stmt->fetch(); } - // 3. Mise à jour de la BDD - $stmt = $pdo->prepare("UPDATE games SET current_player_id = ? WHERE id = ?"); - $stmt->execute([$next_player['id'], $params['game_id']]); - - $response = (['success' => true, 'next_player_id' => $next_player['id']]); - break; - + if ($next) { + $stmt = $pdo->prepare("UPDATE games SET current_player_id = ? WHERE id = ?"); + $stmt->execute([$next['id'], $game_id]); + echo json_encode(['success' => true, 'next_player_id' => $next['id']]); + } else { + echo json_encode(['success' => false, 'error' => 'Aucun joueur actif trouvé']); + } + exit; + case 'set_current_player': + $game_id = (int)$params['game_id']; + $player_id = (int)$params['player_id']; + $stmt = $pdo->prepare("UPDATE games SET current_player_id = ? WHERE id = ?"); - $stmt->execute([$params['player_id'], $params['game_id']]); - $response = ['success' => true]; - break; + $stmt->execute([$player_id, $game_id]); + echo json_encode(['success' => true]); + + exit; + case 'fold': $stmt = $pdo->prepare("UPDATE players SET is_folded = 1 WHERE id = ?"); - $stmt->execute([$params['player_id']]); - $response = ['success' => true]; - break; + $stmt->execute([(int)$params['player_id']]); + echo json_encode(['success' => true]); + exit; case 'raise': - $stmt = $pdo->prepare("UPDATE players SET money = money - ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['player_id']]); + $game_id = (int)$params['game_id']; + $player_id = (int)$params['player_id']; + $bet_input = (int)$params['bet_input']; - $stmt = $pdo->prepare("UPDATE players SET current_bet = current_bet + ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['player_id']]); + $stmt = $pdo->prepare("SELECT last_bet FROM games WHERE id = ?"); + $stmt->execute([$game_id]); + $last_bet_table = (int)$stmt->fetchColumn(); - $stmt = $pdo->prepare("UPDATE games SET pot = pot + ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['game_id']]); + $stmt = $pdo->prepare("SELECT money, current_bet FROM players WHERE id = ?"); + $stmt->execute([$player_id]); + $player = $stmt->fetch(); + + $target_bet = $last_bet_table + $bet_input; + $to_withdraw = $target_bet - (int)$player['current_bet']; - $stmt = $pdo->prepare("UPDATE games SET last_bet = ? WHERE id = ?"); - $stmt->execute([$params['amount'] + $params['current_bet'], $params['game_id']]); + if ((int)$player['money'] < $to_withdraw) { + echo json_encode(['success' => false, 'error' => 'Fonds insuffisants']); + exit; + } - $response = ['success' => true]; - break; + try { + $pdo->beginTransaction(); + $stmt = $pdo->prepare("UPDATE players SET money = money - ?, current_bet = ? WHERE id = ?"); + $stmt->execute([$to_withdraw, $target_bet, $player_id]); + $stmt = $pdo->prepare("UPDATE games SET pot = pot + ?, last_bet = ? WHERE id = ?"); + $stmt->execute([$to_withdraw, $target_bet, $game_id]); + $pdo->commit(); + echo json_encode(['success' => true]); + } catch (Exception $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + echo json_encode(['success' => false, 'error' => 'Erreur transaction']); + } + exit; case 'follow': - $stmt = $pdo->prepare("UPDATE players SET money = money - ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['player_id']]); + $game_id = (int)$params['game_id']; + $player_id = (int)$params['player_id']; - $stmt = $pdo->prepare("UPDATE players SET current_bet = current_bet + ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['player_id']]); + $stmt = $pdo->prepare("SELECT last_bet FROM games WHERE id = ?"); + $stmt->execute([$game_id]); + $target = (int)$stmt->fetchColumn(); - $stmt = $pdo->prepare("UPDATE games SET pot = pot + ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['game_id']]); + $stmt = $pdo->prepare("SELECT money, current_bet FROM players WHERE id = ?"); + $stmt->execute([$player_id]); + $player = $stmt->fetch(); - $response = ['success' => true]; - break; + $to_pay = $target - (int)$player['current_bet']; - case 'all_in': - $stmt = $pdo->prepare("SELECT money FROM players WHERE id = ?"); - $stmt->execute([$params['player_id']]); - $money = $stmt->fetchColumn(); - - $stmt = $pdo->prepare("UPDATE players SET money = 0, current_bet = current_bet + ? WHERE id = ?"); - $stmt->execute([$money, $params['player_id']]); - - $stmt = $pdo->prepare("UPDATE games SET pot = pot + ?, last_bet = last_bet + ? WHERE id = ?"); - $stmt->execute([$money, $money, $params['game_id']]); - - $response = ['success' => true]; - break; - - case 'declare_winner': - $stmt = $pdo->prepare("SELECT pot FROM games WHERE id = ?"); - $stmt->execute([$params['game_id']]); - $pot = $stmt->fetchColumn(); - - $stmt = $pdo->prepare("UPDATE games SET pot = 0, last_bet = 0 WHERE id = ?"); - $stmt->execute([$params['game_id']]); - - $stmt = $pdo->prepare("UPDATE players SET money = money + ? WHERE id = ?"); - $stmt->execute([$pot, $params['player_id']]); - - $stmt = $pdo->prepare("SELECT * FROM players WHERE game_id = ?"); - $stmt->execute([$params['game_id']]); - $players = $stmt->fetchAll(PDO::FETCH_ASSOC); - - foreach ($players as $player) { - $stmt = $pdo->prepare("UPDATE players SET current_bet = 0, is_folded = 0 WHERE id = ?"); - $stmt->execute([$player['id']]); + if ($to_pay > (int)$player['money']) { + echo json_encode(['success' => false, 'error' => 'Pas assez pour suivre, faites Tapis !']); + exit; } - $stmt = $pdo->prepare("SELECT id FROM players WHERE is_dealer = 1 AND game_id = ?"); - $stmt->execute([$params['game_id']]); - $current_dealer = $stmt->fetchColumn(); - - $stmt = $pdo->prepare("UPDATE players SET is_dealer = 0 WHERE id = ?"); - $stmt->execute([$current_dealer]); - $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? AND id > ? ORDER BY id ASC LIMIT 1"); - $stmt->execute([$params['game_id'], $current_dealer]); - $next_dealer = $stmt->fetchColumn(); - if (!$next_dealer) { - $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? ORDER BY id ASC LIMIT 1"); - $stmt->execute([$params['game_id']]); - $next_dealer = $stmt->fetchColumn(); - } - $stmt = $pdo->prepare("UPDATE players SET is_dealer = 1 WHERE id = ?"); - $stmt->execute([$next_dealer]); - - $response = ['success' => true]; - break; - - case 'add_money': - $stmt = $pdo->prepare("UPDATE players SET money = money + ? WHERE id = ?"); - $stmt->execute([$params['amount'], $params['player_id']]); - $response = ['success' => true]; - break; - - case 'delete_game': try { - // Supprimer les joueurs associés d'abord (intégrité BDD) - $stmt = $pdo->prepare("DELETE FROM players WHERE game_id = ?"); - $stmt->execute([$params['game_id']]); - - // Supprimer la partie - $stmt = $pdo->prepare("DELETE FROM games WHERE id = ?"); - $stmt->execute([$params['game_id']]); - - $response = ['success' => true]; + $pdo->beginTransaction(); + $stmt = $pdo->prepare("UPDATE players SET money = money - ?, current_bet = ? WHERE id = ?"); + $stmt->execute([$to_pay, $target, $player_id]); + $stmt = $pdo->prepare("UPDATE games SET pot = pot + ? WHERE id = ?"); + $stmt->execute([$to_pay, $game_id]); + $pdo->commit(); + echo json_encode(['success' => true]); } catch (Exception $e) { - $response = ['success' => false, 'message' => $e->getMessage()]; + if ($pdo->inTransaction()) $pdo->rollBack(); + echo json_encode(['success' => false, 'error' => 'Erreur follow']); } - break; + exit; - case 'get_all_games': - $stmt = $pdo->query("SELECT * FROM games ORDER BY id ASC"); - $games = $stmt->fetchAll(PDO::FETCH_ASSOC); - $response = ['success' => true, 'games' => $games]; - break; - - case 'adminLogin': - // On vérifie si l'utilisateur est déjà admin - session_start(); - if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) { - $response = ['success' => true, 'message' => 'Déjà connecté en tant qu\'admin']; - break; - } - // On dit que l'utilisateur est admin - $_SESSION['admin_logged_in'] = true; - $response = ['success' => true, 'message' => 'Connexion admin réussie']; - break; + case 'all_in': + $game_id = (int)$params['game_id']; + $player_id = (int)$params['player_id']; + + try { + $pdo->beginTransaction(); + + // 1. Récupérer les jetons restants du joueur + $stmt = $pdo->prepare("SELECT money, current_bet FROM players WHERE id = ?"); + $stmt->execute([$player_id]); + $player = $stmt->fetch(); + $all_in_amount = (int)$player['money']; + $new_player_bet = (int)$player['current_bet'] + $all_in_amount; + + // 2. Le joueur mise TOUT : money tombe à 0 + $stmt = $pdo->prepare("UPDATE players SET money = 0, current_bet = ? WHERE id = ?"); + $stmt->execute([$new_player_bet, $player_id]); + + // 3. Mise à jour de la table : on ajoute l'argent au pot + // Et on met à jour le 'last_bet' SEULEMENT si le tapis est supérieur à la mise actuelle + $stmt = $pdo->prepare("UPDATE games SET pot = pot + ?, last_bet = GREATEST(last_bet, ?) WHERE id = ?"); + $stmt->execute([$all_in_amount, $new_player_bet, $game_id]); + + $pdo->commit(); + echo json_encode(['success' => true]); + } catch (Exception $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + echo json_encode(['success' => false, 'error' => 'Erreur All-in']); + } + exit; + + case 'declare_winner': + $game_id = (int)$params['game_id']; + $winner_id = (int)$params['player_id']; + + try { + $pdo->beginTransaction(); + + // 1. Récupérer le pot total + $stmt = $pdo->prepare("SELECT pot FROM games WHERE id = ?"); + $stmt->execute([$game_id]); + $pot = (int)$stmt->fetchColumn(); + + // 2. Donner le pot au gagnant et remettre ses stats à zéro pour le prochain tour + $stmt = $pdo->prepare("UPDATE players SET money = money + ? WHERE id = ?"); + $stmt->execute([$pot, $winner_id]); + + // 3. Reset de la table (Pot et Mise à suivre) + $stmt = $pdo->prepare("UPDATE games SET pot = 0, last_bet = 0 WHERE id = ?"); + $stmt->execute([$game_id]); + + // 4. Reset de TOUS les joueurs (Mises engagées et Fold) en une seule requête + $stmt = $pdo->prepare("UPDATE players SET current_bet = 0, is_folded = 0 WHERE game_id = ?"); + $stmt->execute([$game_id]); + + // 5. Rotation du Dealer + // On cherche le dealer actuel + $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? AND is_dealer = 1 LIMIT 1"); + $stmt->execute([$game_id]); + $current_dealer = $stmt->fetchColumn(); + + if ($current_dealer) { + // On enlève l'ancien badge + $pdo->prepare("UPDATE players SET is_dealer = 0 WHERE id = ?")->execute([$current_dealer]); + + // On cherche le suivant (ID plus grand) + $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? AND id > ? ORDER BY id ASC LIMIT 1"); + $stmt->execute([$game_id, $current_dealer]); + $next_dealer = $stmt->fetchColumn(); + + // Si pas de suivant, on revient au premier + if (!$next_dealer) { + $stmt = $pdo->prepare("SELECT id FROM players WHERE game_id = ? ORDER BY id ASC LIMIT 1"); + $stmt->execute([$game_id]); + $next_dealer = $stmt->fetchColumn(); + } + + $pdo->prepare("UPDATE players SET is_dealer = 1 WHERE id = ?")->execute([$next_dealer]); + } + + $pdo->commit(); + echo json_encode(['success' => true]); + } catch (Exception $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + echo json_encode(['success' => false, 'error' => 'Erreur lors de la désignation du vainqueur']); + } + exit; + + case 'add_money': + $stmt = $pdo->prepare("UPDATE players SET money = money + ? WHERE id = ?"); + $stmt->execute([(int)$params['amount'], (int)$params['player_id']]); + echo json_encode(['success' => true]); + exit; + + case 'delete_game': + $game_id = (int)$params['game_id']; + try { + $pdo->beginTransaction(); + $pdo->prepare("DELETE FROM players WHERE game_id = ?")->execute([$game_id]); + $pdo->prepare("DELETE FROM games WHERE id = ?")->execute([$game_id]); + $pdo->commit(); + echo json_encode(['success' => true]); + } catch (Exception $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + echo json_encode(['success' => false]); + } + exit; + + case 'get_all_games': + $stmt = $pdo->query("SELECT * FROM games ORDER BY id ASC"); + echo json_encode(['success' => true, 'games' => $stmt->fetchAll()]); + exit; - case 'is_admin': - session_start(); - $response = ['success' => true, 'is_admin' => isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true]; - break; - default: - $response = ['error' => 'Action inconnue']; -} + // Actions d'administration + case 'adminLogin': + $_SESSION['admin_logged_in'] = true; + echo json_encode(['success' => true]); + exit; + + case 'is_admin': + echo json_encode([ + 'success' => true, + 'is_admin' => (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) + ]); + exit; -echo json_encode($response); + case 'toggle_lock': + $game_id = (int)$params['game_id']; + $status = (int)$params['status']; // 1 pour verrouillé, 0 pour ouvert + + $stmt = $pdo->prepare("UPDATE games SET is_locked = ? WHERE id = ?"); + $stmt->execute([$status, $game_id]); + + echo json_encode(['success' => true]); + exit; + + case 'update_game_status': + $game_id = $params['game_id']; + $status = $params['status']; + + $stmt = $pdo->prepare("UPDATE games SET status = ? WHERE id = ?"); + $success = $stmt->execute([$status, $game_id]); + + echo json_encode(['success' => $success]); + exit; // Important pour ne rien envoyer d'autre après + + // --- ACTION 1 : Changer uniquement le statut (ex: 'deciding', 'playing') --- + case 'update_game_status': + $game_id = $params['game_id'] ?? 0; + $status = $params['status'] ?? ''; -?> \ No newline at end of file + $stmt = $pdo->prepare("UPDATE games SET status = ? WHERE id = ?"); + $success = $stmt->execute([$status, $game_id]); + + echo json_encode(['success' => $success]); + exit; + + // --- ACTION 2 : Enregistrer le gagnant --- + case 'set_winner': + $game_id = $params['game_id'] ?? 0; + $player_id = $params['player_id'] ?? 0; + + // Ici on ne change QUE le winner_id + $stmt = $pdo->prepare("UPDATE games SET winner_id = ? WHERE id = ?"); + $success = $stmt->execute([$player_id, $game_id]); + + echo json_encode(['success' => $success]); + exit; + + + default: + echo json_encode(['success' => false, 'error' => 'Action inconnue']); + exit; + } \ No newline at end of file diff --git a/poker-paf/admin-game.html b/poker-paf/admin-game.html index d0dc73b..606643e 100644 --- a/poker-paf/admin-game.html +++ b/poker-paf/admin-game.html @@ -8,23 +8,41 @@ +
-
-
- - + +
+
+ +

Administration de la Table

+ +
+
+
+ +
+ + +
+
+ +
+ MISE ACTUELLE: 0
+ POT ACTUEL 0 +
+ +
+ + + +
+ + ◀️ Quitter la partie
- -
MISE ACTUELLE:
- - - - ⬅ Quitter -
- +
diff --git a/poker-paf/config.html b/poker-paf/config.html index 8a28ea1..bdce4b2 100644 --- a/poker-paf/config.html +++ b/poker-paf/config.html @@ -10,7 +10,7 @@
- +

Configuration de la partie

diff --git a/poker-paf/game.html b/poker-paf/game.html index 7bd37d4..880b9d0 100644 --- a/poker-paf/game.html +++ b/poker-paf/game.html @@ -8,12 +8,11 @@ +
-
-
@@ -34,7 +33,8 @@
- ⬅ Quitter +

+ ◀️ Quitter
diff --git a/poker-paf/player-selector.php b/poker-paf/player-selector.php index 646d545..6f28a84 100644 --- a/poker-paf/player-selector.php +++ b/poker-paf/player-selector.php @@ -39,14 +39,15 @@ $game_name = $stmt->fetchColumn(); Choix du joueur - PokerPaf - +
+

Rejoindre la partie


Choix du joueur

-

Veuillez entrer le nom du joueur pour rejoindre la partie :

+

Veuillez cliquer sur le nom du joueur pour rejoindre la partie :