diff --git a/public/index.html b/public/index.html
index 8ed6966..cdb8ba5 100644
--- a/public/index.html
+++ b/public/index.html
@@ -30,13 +30,13 @@
- Règles des cartes
+ Règles du jeu
Les cartes ajoutent des effets spéciaux au-dessus des règles d'échecs classiques. Voici l'essentiel pour jouer :
Jouer une carte : cliquez sur une carte dans votre main puis suivez l'invite (sélection d'une pièce, d'une case). Certaines cartes n'exigent pas de cible.
Carte mouvement : certaines cartes comptent déjà comme un mouvement à part entière. Il faut donc en tenir compte lors de votre tour.
Durée des effets : certaines cartes sont temporaires (durée mesurée en tours), d'autres restent actives. Un tour correspond aux actions d'un seul joueur. Un cycle vaut donc pour deux tours.
- Fin de partie : si un roi est retiré, la partie s'arrête automatiquement et un message indique victoire/défaite ou égalité. Les joueurs peuvent choisir de rester en spectateur ou de quitter la partie.
+ Fin de partie : si un roi est capturé, la partie s'arrête sur une victoire, une défaite ou peut-être même une égalité. Les joueurs peuvent choisir de rester en spectateur ou de quitter la partie.
Remarque : ces règles couvrent uniquement les mécaniques des cartes — pour le jeu d'échecs standard, les mouvements habituels s'appliquent (pions, tours, cavaliers, fous, dames, rois).
diff --git a/public/waiting.html b/public/waiting.html
index c84f9c1..f2083a0 100644
--- a/public/waiting.html
+++ b/public/waiting.html
@@ -4,6 +4,12 @@
ChessNut - Salle d'attente
+
@@ -35,6 +41,25 @@
+
+
+
+ Pioche personnalisée
+
+
+
+
+
+
+
Cartes disponibles
+
+ Tout sélectionner
+ Tout déselectionner
+
+
+
(Cochez "Pioche personnalisée" pour charger la liste)
+
+
Commencer la partie
Copier le lien d'invitation
@@ -63,6 +88,14 @@
const autoDrawToggleEl = document.getElementById('autoDrawToggle');
const noRemiseLabelEl = document.getElementById('no-remise');
const noRemiseToggleEl = document.getElementById('no-remiseToggle');
+ const customDrawLabelEl = document.getElementById('customDrawLabel');
+ const customDrawToggleEl = document.getElementById('customDrawToggle');
+
+ // Selection state for custom draw (persisted per room)
+ let selectedCards = new Set();
+ function storageKey(){ return `customDrawSelection:${roomId}`; }
+ function saveSelection(){ try{ localStorage.setItem(storageKey(), JSON.stringify(Array.from(selectedCards))); }catch(_){} }
+ function loadSelection(){ try{ const v = localStorage.getItem(storageKey()); if(v){ const arr = JSON.parse(v); selectedCards = new Set(arr || []); } }catch(_){} }
function log(...args){ console.log(...args); }
@@ -137,6 +170,18 @@
}
}
}catch(_){ }
+ // show custom draw checkbox to host (UI-only for now)
+ try{
+ if(customDrawLabelEl && customDrawToggleEl){
+ if(myPlayerId && roomHostId && myPlayerId === roomHostId){
+ customDrawLabelEl.style.display = 'inline-flex';
+ // do not override the user's local choice; leave checked as-is
+ customDrawToggleEl.disabled = false;
+ } else {
+ customDrawLabelEl.style.display = 'none';
+ }
+ }
+ }catch(_){ }
});
// register game started handler early so we don't miss the broadcast
@@ -195,6 +240,18 @@
}
}
}catch(_){ }
+ // show custom draw checkbox to host on initial fetch as well
+ try{
+ if(customDrawLabelEl && customDrawToggleEl){
+ if(myPlayerId && roomHostId && myPlayerId === roomHostId){
+ customDrawLabelEl.style.display = 'inline-flex';
+ // preserve local UI state for this control; do not force-assign checked
+ customDrawToggleEl.disabled = false;
+ } else {
+ customDrawLabelEl.style.display = 'none';
+ }
+ }
+ }catch(_){ }
}catch(_){ }
if(roomInfo && roomInfo.status === 'playing'){
// server already marked room as playing; redirect immediately
@@ -243,6 +300,113 @@
});
}
+ // Custom draw toggle (minimal behavior)
+ // When checked, show a new section and fetch /cards to display a simple list.
+ if(customDrawToggleEl){
+ const customCardsSection = document.getElementById('customCardsSection');
+ const customCardsContainer = document.getElementById('customCardsContainer');
+ const customSelectAllBtn = document.getElementById('customSelectAll');
+ const customDeselectAllBtn = document.getElementById('customDeselectAll');
+ customDrawToggleEl.addEventListener('change', async (ev)=>{
+ const checked = !!customDrawToggleEl.checked;
+ log('custom-draw toggled (minimal)', { checked });
+ if(customCardsSection) customCardsSection.style.display = checked ? 'block' : 'none';
+ if(checked){
+ // fetch card list and render simple names
+ if(customCardsContainer){
+ customCardsContainer.textContent = 'Chargement...';
+ try{
+ const r = await fetch('/cards');
+ const data = await r.json();
+ const cards = Array.isArray(data) ? data : (data && Array.isArray(data.cards) ? data.cards : []);
+ if(cards.length === 0){ customCardsContainer.textContent = 'Aucune carte trouvée.'; return; }
+ // restore previous selection for this room
+ try{ loadSelection(); }catch(_){}
+ const grid = document.createElement('div');
+ grid.style.display = 'grid';
+ grid.style.gridTemplateColumns = 'repeat(auto-fill,minmax(220px,1fr))';
+ grid.style.gap = '10px';
+
+ cards.forEach(c => {
+ // create the same visual structure as in game.renderHands -> .pokemon-card
+ const cardEl = document.createElement('div');
+ cardEl.className = 'pokemon-card';
+ const cardId = (c.id || c.cardId || c.name || c.title || JSON.stringify(c)).toString();
+ cardEl.dataset.cardId = cardId;
+
+ const top = document.createElement('div'); top.className = 'pokemon-card-top';
+ const art = document.createElement('div'); art.className = 'pokemon-card-art'; art.textContent = '';
+ try{
+ const cid = (c.cardId || c.id || '').toString().toLowerCase();
+ const titleRaw = (c.title || '').toString().toLowerCase();
+ let imgSrc = null;
+ if(cid === 'adoubement' || titleRaw.indexOf('adoub') !== -1) imgSrc = '/assets/img/cards/adoubement.png';
+ if(cid === 'folie' || titleRaw.indexOf('folie') !== -1 || titleRaw.indexOf('fou') !== -1) imgSrc = '/assets/img/cards/folie.png';
+ if(cid === 'fortification' || titleRaw.indexOf('fortification') !== -1 || titleRaw.indexOf('fortif') !== -1) imgSrc = '/assets/img/cards/fortification.png';
+ if(imgSrc){ const img = document.createElement('img'); img.src = imgSrc; img.alt = c.title || cid || 'card'; img.className = 'card-art-img'; img.style.width='100%'; img.style.height='100%'; img.style.objectFit='cover'; art.appendChild(img); }
+ }catch(e){}
+ top.appendChild(art);
+
+ const mid = document.createElement('div'); mid.className = 'pokemon-card-mid';
+ const h = document.createElement('div'); h.className = 'pokemon-card-title'; h.textContent = c.name || c.title || c.cardId || c.id || 'Carte';
+ const p = document.createElement('div'); p.className = 'pokemon-card-desc'; p.textContent = c.short || c.description || '';
+ mid.appendChild(h); mid.appendChild(p);
+
+ const footer = document.createElement('div'); footer.className = 'pokemon-card-footer';
+
+ // add a small select mark (hidden unless selected)
+ const selectMark = document.createElement('div'); selectMark.className = 'select-mark'; selectMark.textContent = '✓'; selectMark.style.display = 'none';
+ cardEl.appendChild(selectMark);
+
+ cardEl.appendChild(top); cardEl.appendChild(mid); cardEl.appendChild(footer);
+
+ // apply selection state if previously selected
+ if(selectedCards.has(cardId)){
+ cardEl.classList.add('selected');
+ selectMark.style.display = 'flex';
+ }
+
+ // click toggles selection (no other action in waiting room)
+ cardEl.addEventListener('click', (ev)=>{
+ ev.stopPropagation();
+ if(selectedCards.has(cardId)){
+ selectedCards.delete(cardId);
+ cardEl.classList.remove('selected');
+ selectMark.style.display = 'none';
+ } else {
+ selectedCards.add(cardId);
+ cardEl.classList.add('selected');
+ selectMark.style.display = 'flex';
+ }
+ saveSelection();
+ });
+
+ grid.appendChild(cardEl);
+ });
+ customCardsContainer.innerHTML = '';
+ customCardsContainer.appendChild(grid);
+ }catch(e){ customCardsContainer.textContent = 'Erreur lors du chargement des cartes.'; }
+ }
+ }
+ });
+ // wire up select all / deselect all
+ function setAllVisible(yes){
+ // find card elements in the container grid
+ const els = customCardsContainer ? customCardsContainer.querySelectorAll('.pokemon-card') : [];
+ els.forEach(el => {
+ const id = el.dataset.cardId;
+ const mark = el.querySelector('.select-mark');
+ if(yes){ selectedCards.add(id); el.classList.add('selected'); if(mark) mark.style.display = 'flex'; }
+ else { selectedCards.delete(id); el.classList.remove('selected'); if(mark) mark.style.display = 'none'; }
+ });
+ saveSelection();
+ }
+ if(customSelectAllBtn) customSelectAllBtn.addEventListener('click', (e)=>{ e.preventDefault(); setAllVisible(true); });
+ if(customDeselectAllBtn) customDeselectAllBtn.addEventListener('click', (e)=>{ e.preventDefault(); setAllVisible(false); });
+ // ensure initial state: hide unless checkbox is already checked
+ if(customDrawToggleEl.checked && customCardsSection){ customCardsSection.style.display = 'block'; customDrawToggleEl.dispatchEvent(new Event('change')); }
+ }
+
// Copy invite link (hidden link, no display)
document.getElementById('copyInviteBtn').addEventListener('click', async ()=>{
const link = `${window.location.origin}/waiting.html?roomId=${encodeURIComponent(roomId)}`;