diff --git a/public/index.html b/public/index.html index e9fcc92..a4c132b 100644 --- a/public/index.html +++ b/public/index.html @@ -40,6 +40,13 @@

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).

+ +
+

Cartes disponibles

+
+ +
+
@@ -77,6 +84,59 @@ const qs = `?roomId=${encodeURIComponent(roomId)}${playerId?`&playerId=${encodeURIComponent(playerId)}`:''}`; window.location.href = '/waiting.html' + qs; }); + + // Fetch available cards from server and render as a grid using the same card UI as in-game + async function loadCardsGrid(){ + try{ + const resp = await fetch('/cards'); + if(!resp.ok) return; + const j = await resp.json(); + if(!j || !Array.isArray(j.cards)) return; + const grid = document.getElementById('cardsGrid'); + if(!grid) return; + grid.innerHTML = ''; + j.cards.forEach(c => { + try{ + const cardEl = document.createElement('div'); + cardEl.className = 'pokemon-card'; + cardEl.dataset.cardId = c.cardId || c.id || ''; + cardEl.dataset.cardUuid = c.id || ''; + + const top = document.createElement('div'); top.className = 'pokemon-card-top'; + const art = document.createElement('div'); art.className = 'pokemon-card-art'; + art.textContent = ''; + // artwork mapping (reuse same simple heuristics as in game view) + try{ + const cid = (c.cardId || c.id || '').toString().toLowerCase(); + const title = (c.title || '').toString().toLowerCase(); + let imgSrc = null; + if(cid === 'adoubement' || title.indexOf('adoub') !== -1) imgSrc = '/assets/img/cards/adoubement.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(_){ } + 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.title || c.cardId || 'Carte'; + const p = document.createElement('div'); p.className = 'pokemon-card-desc'; p.textContent = c.description || ''; + mid.appendChild(h); mid.appendChild(p); + + const footer = document.createElement('div'); footer.className = 'pokemon-card-footer'; + const playBtn = document.createElement('button'); playBtn.className = 'card-play-btn'; playBtn.textContent = 'Voir'; playBtn.disabled = true; // homepage view: disabled + footer.appendChild(playBtn); + + cardEl.appendChild(top); + cardEl.appendChild(mid); + cardEl.appendChild(footer); + + grid.appendChild(cardEl); + }catch(e){ console.warn('render card error', e); } + }); + }catch(e){ console.warn('loadCardsGrid error', e); } + } + + function escapeHtml(s){ return String(s||'').replace(/&/g,'&').replace(//g,'>'); } + // populate on load + try{ loadCardsGrid(); }catch(_){ } diff --git a/server.js b/server.js index ed10fec..c0da89e 100644 --- a/server.js +++ b/server.js @@ -13,6 +13,16 @@ app.use(express.static('public')); // Serve provided assets (piece sets, boards, etc.) under /assets app.use('/assets', express.static(path.join(__dirname, 'assets'))); +// Public API: list available cards (uses the same deck builder as the server) +app.get('/cards', (req, res) => { + try{ + const deck = buildDefaultDeck() || []; + // Return a lightweight view (id, cardId, title, description) + const out = deck.map(c => ({ id: c.id, cardId: c.cardId, title: c.title, description: c.description })); + res.json({ ok: true, cards: out }); + }catch(e){ console.error('GET /cards error', e); res.status(500).json({ error: 'server_error' }); } +}); + // In-memory rooms store. For production replace with persistent store. // room = { id, boardState: object|null, players: [{id, socketId, color}], status, size } const rooms = new Map();