ChessNut/public/index.html
2025-11-24 14:06:17 +01:00

142 lines
6.8 KiB
HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>ChessNut</title>
<link rel="stylesheet" href="/styles/style.css">
</head>
<body>
<div class="page">
<header class="hero card">
<div>
<div class="title">ChessNut</div>
<div class="subtitle">Créez ou rejoignez une partie</div>
</div>
<div style="text-align:right">
<img class="decor" src="/assets/chess-pieces/boards/8x8_pink_1Kbyte_gambit.svg" alt="decor" />
</div>
<svg class="floating-piece" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#0b1220" d="M10 90 L30 10 L50 90 Z" /></svg>
</header>
<section class="card lobby">
<div>
<button id="create" class="btn-cta">Créer une room</button>
</div>
<div class="controls">
<input id="roomId" class="input" placeholder="ex: ab12cd34" />
<button id="join" class="btn-ghost">Rejoindre</button>
</div>
</section>
<section class="card rules" id="cardRules" style="margin-top:16px">
<h3>Règles des cartes</h3>
<p>Les cartes ajoutent des effets spéciaux au-dessus des règles d'échecs classiques. Voici l'essentiel pour jouer :</p>
<ul>
<li><strong>Jouer une carte :</strong> 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.</li>
<li><strong>Carte mouvement :</strong> certaines cartes comptent déjà comme un mouvement à part entière. Il faut donc en tenir compte lors de votre tour.</li>
<li><strong>Durée des effets :</strong> 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.</li>
<li><strong>Fin de partie :</strong> 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.</li>
</ul>
<p style="margin-top:8px;font-size:0.95em;color:#444">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).</p>
</section>
<section class="card" id="availableCards" style="margin-top:16px">
<h3>Cartes disponibles</h3>
<div id="cardsGrid" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:12px">
<!-- cards will be injected here -->
</div>
</section>
</div>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script>
function log(...args){ console.log(...args); }
document.getElementById('create').addEventListener('click', async ()=>{
const r = await fetch('/rooms', { method:'POST' });
const j = await r.json();
document.getElementById('roomId').value = j.roomId;
log('Room créée', j.roomId);
// redirect creator to waiting page
window.location.href = `/waiting.html?roomId=${encodeURIComponent(j.roomId)}`;
});
document.getElementById('join').addEventListener('click', async ()=>{
const roomId = document.getElementById('roomId').value.trim();
if(!roomId){ alert('Entrez roomId'); return; }
// verify room exists before redirecting
try{
const resp = await fetch(`/rooms/${encodeURIComponent(roomId)}`);
if(!resp.ok){
const j = await resp.json().catch(()=>({ error: 'room not found' }));
alert(j.error || 'room not found');
return;
}
}catch(e){
alert('Erreur de connexion au serveur');
return;
}
const playerIdEl = document.getElementById('playerId');
const playerId = (playerIdEl && playerIdEl.value) ? playerIdEl.value.trim() : '';
// redirect to waiting page; playerId optional
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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
// populate on load
try{ loadCardsGrid(); }catch(_){ }
</script>
</body>
</html>