From 4cf1aca7e4edd56fda129d7933527601256b7eca Mon Sep 17 00:00:00 2001 From: Didictateur Date: Tue, 25 Nov 2025 21:57:56 +0100 Subject: [PATCH] selection fonctionnelle --- public/waiting.html | 57 +++++++++++++++++++++++++++++++++++++++++++-- server.js | 36 +++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/public/waiting.html b/public/waiting.html index bcb678c..c9222a4 100644 --- a/public/waiting.html +++ b/public/waiting.html @@ -267,11 +267,26 @@ connectAndJoin(); - // Start button handler (host) + // Start button handler (host) — ensure custom deck is applied before starting document.getElementById('startBtn').addEventListener('click', ()=>{ if(!socket) return; + // If custom draw is enabled and host selected cards, send the deck first + try{ + if(customDrawToggleEl && customDrawToggleEl.checked && selectedCards.size && myPlayerId && roomHostId && myPlayerId === roomHostId){ + socket.emit('room:deck:set', { roomId, selected: Array.from(selectedCards) }, (deckResp)=>{ + if(deckResp && deckResp.error){ showToast(deckResp.message || deckResp.error, { background: 'rgba(200,60,60,0.95)' }); return; } + // now start the game + socket.emit('game:start', { roomId }, (resp)=>{ + if(resp && resp.error){ log('start error', resp); showToast(resp.error || resp.message || 'Erreur', { background: 'rgba(200,60,60,0.95)' }); return; } + log('game:start acknowledged'); + }); + }); + return; + } + }catch(_){ } + // fallback: start immediately socket.emit('game:start', { roomId }, (resp)=>{ - if(resp && resp.error){ log('start error', resp); showToast(resp.error || resp.message || 'Erreur', { background: 'rgba(200,60,60,0.95)' }); return; } + if(resp && resp.error){ log('start error', resp); showToast(resp.error || resp.message || 'Erreur', { background: 'rgba(200,60,60,0.95)' }); return; } log('game:start acknowledged'); }); }); @@ -311,6 +326,26 @@ const checked = !!customDrawToggleEl.checked; log('custom-draw toggled (minimal)', { checked }); if(customCardsSection) customCardsSection.style.display = checked ? 'block' : 'none'; + // When toggling the checkbox, inform server: if unchecked, clear custom deck on server so default deck is used. + try{ + if(socket && socket.connected && myPlayerId && roomHostId && myPlayerId === roomHostId){ + if(!checked){ + // inform server to revert to default deck + socket.emit('room:deck:set', { roomId, selected: [] }, (resp)=>{ + if(resp && resp.error) showToast(resp.message || resp.error, { background: 'rgba(200,60,60,0.95)' }); + else showToast('Pioche personnalisée désactivée — pioche par défaut restaurée', { background: 'rgba(40,160,40,0.95)' }); + }); + } else { + // if checked and we already have a selection, push it to server + if(selectedCards.size){ + socket.emit('room:deck:set', { roomId, selected: Array.from(selectedCards) }, (resp)=>{ + if(resp && resp.error) showToast(resp.message || resp.error, { background: 'rgba(200,60,60,0.95)' }); + else showToast('Pioche personnalisée activée', { background: 'rgba(40,160,40,0.95)' }); + }); + } + } + } + }catch(_){ } if(checked){ // fetch card list and render simple names if(customCardsContainer){ @@ -379,6 +414,15 @@ selectMark.style.display = 'flex'; } saveSelection(); + // if we're the host and connected AND custom-draw is enabled, update the server's custom deck selection + try{ + if(socket && socket.connected && myPlayerId && roomHostId && myPlayerId === roomHostId && customDrawToggleEl && customDrawToggleEl.checked){ + socket.emit('room:deck:set', { roomId, selected: Array.from(selectedCards) }, (resp)=>{ + if(resp && resp.error){ showToast(resp.message || resp.error, { background: 'rgba(200,60,60,0.95)' }); } + else { showToast('Pioche personnalisée mise à jour', { background: 'rgba(40,160,40,0.95)' }); } + }); + } + }catch(_){ } }); grid.appendChild(cardEl); @@ -400,6 +444,15 @@ else { selectedCards.delete(id); el.classList.remove('selected'); if(mark) mark.style.display = 'none'; } }); saveSelection(); + // send selection to server if host AND custom-draw enabled + try{ + if(socket && socket.connected && myPlayerId && roomHostId && myPlayerId === roomHostId && customDrawToggleEl && customDrawToggleEl.checked){ + socket.emit('room:deck:set', { roomId, selected: Array.from(selectedCards) }, (resp)=>{ + if(resp && resp.error) showToast(resp.message || resp.error, { background: 'rgba(200,60,60,0.95)' }); + else showToast('Pioche personnalisée mise à jour', { background: 'rgba(40,160,40,0.95)' }); + }); + } + }catch(_){ } } if(customSelectAllBtn) customSelectAllBtn.addEventListener('click', (e)=>{ e.preventDefault(); setAllVisible(true); }); if(customDeselectAllBtn) customDeselectAllBtn.addEventListener('click', (e)=>{ e.preventDefault(); setAllVisible(false); }); diff --git a/server.js b/server.js index 7d10081..14717c6 100644 --- a/server.js +++ b/server.js @@ -303,7 +303,9 @@ function buildDefaultDeck(){ let ascii = normalized; try{ ascii = ascii.normalize('NFD').replace(/\p{Diacritic}/gu, ''); }catch(e){ /* ignore if normalize unsupported */ } const cardId = ascii.replace(/[^a-z0-9]+/gi,'_').toLowerCase(); - return { id: uuidv4().slice(0,8), cardId, title: cap(normalized), description: desc }; + // Use deterministic id equal to the slug so clients and server agree on identifiers across requests + const id = cardId; + return { id, cardId, title: cap(normalized), description: desc }; }); } @@ -1251,6 +1253,38 @@ io.on('connection', (socket) => { cb && cb({ ok: true }); }); + // Host can set a custom deck for the room (selected list of card ids). Only host may change it. + socket.on('room:deck:set', ({ roomId, selected }, cb) => { + const room = rooms.get(roomId); + if(!room) return cb && cb({ error: 'room not found', message: "Aucune salle trouvée." }); + const sender = socket.data.playerId; + if(!sender) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la salle." }); + if(room.hostId !== sender) return cb && cb({ error: 'only host can set deck', message: "Seul l'hôte peut définir la pioche." }); + try{ + // selected is expected to be an array of card identifiers (either id or cardId) + const sel = Array.isArray(selected) ? selected : []; + // Build a lookup from the canonical default deck and current room deck + const master = buildDefaultDeck(); + const pool = (room.deck && Array.isArray(room.deck) && room.deck.length) ? room.deck.concat(master) : master; + const byId = {}; + pool.forEach(c => { if(c && c.id) byId[c.id] = c; if(c && c.cardId) byId[c.cardId] = c; }); + // Map selected ids to card objects, preserving order. If a selected id is unknown, ignore it. + const newDeck = []; + sel.forEach(sid => { const c = byId[sid]; if(c) newDeck.push(Object.assign({}, c)); }); + if(newDeck.length === 0){ + // empty selection -> revert to default deck + room.deck = buildDefaultDeck(); + } else { + room.deck = newDeck; + } + // reset discard and hands so the new deck applies for the upcoming game + room.discard = room.discard || []; + // inform players + sendRoomUpdate(room); + return cb && cb({ ok: true, deckCount: room.deck.length }); + }catch(err){ console.error('room:deck:set error', err); return cb && cb({ error: 'server_error' }); } + }); + socket.on('disconnect', () => { const roomId = socket.data.roomId; if (!roomId) return;