diff --git a/public/game.html b/public/game.html index 03f4c2d..b95565a 100644 --- a/public/game.html +++ b/public/game.html @@ -17,10 +17,7 @@
Outils / Logs
- - - - +
logs...
@@ -44,6 +41,12 @@ const myColorEl = document.getElementById('myColor'); let socket = null; let myColor = null; + // selection state for this client + let selectedSquare = null; // algebraic name like 'e2' + let selectedSquareEl = null; + let myPlayerId = playerId || null; + // track remote selections: { playerId: square } + const remoteSelections = {}; // safe logger: prefer on-page log if present, otherwise console function log(...args){ @@ -84,15 +87,31 @@ } if(data.boardState) renderBoardFromState(data.boardState); }); - socket.on('move:moved', (data)=>{ log('move:moved', data); if(data.boardState) renderBoardFromState(data.boardState); }); socket.on('game:over', (data)=>{ log('game:over', data); if(data.boardState) renderBoardFromState(data.boardState); }); socket.on('game:started', (data)=>{ log('game:started', data); if(data.boardState) renderBoardFromState(data.boardState); }); + // selection events broadcasted by other clients + socket.on('game:select', (data) => { + try{ + const pid = data && data.playerId; + const square = data && data.square; + // ignore our own (some transports might echo) + if(pid === myPlayerId) return; + if(!pid) return; + if(!square){ + // clear remote selection for that player + clearRemoteSelection(pid); + return; + } + setRemoteSelection(pid, square); + }catch(e){ console.warn('game:select handler error', e); } + }); socket.emit('room:join', { roomId, playerId }, (resp)=>{ if(resp && resp.error){ log('join error', resp); alert(resp.error); return; } log('Rejoint room', resp); // remember assigned id/color from server if(resp.playerId){ + myPlayerId = resp.playerId; if(playerIdEl) playerIdEl.textContent = resp.playerId; } if(resp.color){ @@ -109,26 +128,59 @@ connectAndJoin(); - // show markers for legal moves when clicking any square (any piece color) - function clearLegalMarkers(){ - const prev = boardEl.querySelectorAll('.move-marker'); - prev.forEach(p=>p.remove()); + // movement UI removed: legalMoves and move markers are disabled + + // selection helpers (client-side) + function clearSelection(emit = true){ + if(selectedSquareEl){ + selectedSquareEl.classList.remove('selected'); + } + selectedSquare = null; + selectedSquareEl = null; + const fromEl = document.getElementById('from'); + if(fromEl) fromEl.value = ''; + // notify others that we cleared selection + if(emit && socket && myPlayerId){ + socket.emit('game:select', { roomId, square: null }, ()=>{}); + } } - function showLegalMoves(moves){ - clearLegalMarkers(); - (moves||[]).forEach(m => { - const to = m.to || (m.move && m.move.to); - if(!to) return; - const target = boardEl.querySelector(`.square[data-square="${to}"]`); - if(!target) return; - const marker = document.createElement('div'); - marker.className = 'move-marker'; - // store metadata if needed later - marker.dataset.from = m.from || ''; - marker.dataset.to = to; - target.appendChild(marker); - }); + function setSelection(squareName, emit = true){ + if(!squareName) return clearSelection(emit); + if(selectedSquareEl) selectedSquareEl.classList.remove('selected'); + const sqEl = boardEl.querySelector(`.square[data-square="${squareName}"]`); + if(!sqEl){ selectedSquare = null; selectedSquareEl = null; return; } + selectedSquare = squareName; + selectedSquareEl = sqEl; + sqEl.classList.add('selected'); + const fromEl = document.getElementById('from'); + if(fromEl) fromEl.value = squareName; + // notify others of our selection + if(emit && socket && myPlayerId){ + socket.emit('game:select', { roomId, square: squareName }, ()=>{}); + } + } + + // remote selection helpers: mark a square as selected by another player + function setRemoteSelection(playerId, squareName){ + // clear previous for that player + clearRemoteSelection(playerId); + const sqEl = boardEl.querySelector(`.square[data-square="${squareName}"]`); + if(!sqEl) return; + remoteSelections[playerId] = squareName; + sqEl.classList.add('selected-remote'); + sqEl.dataset.selectedBy = playerId; + } + + function clearRemoteSelection(playerId){ + const prev = remoteSelections[playerId]; + if(!prev) return; + const prevEl = boardEl.querySelector(`.square[data-square="${prev}"]`); + if(prevEl){ + prevEl.classList.remove('selected-remote'); + delete prevEl.dataset.selectedBy; + } + delete remoteSelections[playerId]; } if(boardEl){ @@ -137,32 +189,20 @@ if(!sqEl) return; const square = sqEl.getAttribute('data-square'); if(!square) return; - if(socket){ - socket.emit('game:legalMoves', { roomId, square }, (resp)=>{ - if(resp && resp.error){ log('legalMoves error', resp); clearLegalMarkers(); return; } - log('legalMoves for', square, resp.moves || resp); - showLegalMoves(resp.moves || []); - }); + + // clicked a piece -> select it (no legal moves requested) + const pieceImg = ev.target.closest && ev.target.closest('img.piece'); + if(pieceImg){ + setSelection(square); + return; } + + // default: clicked empty square -> clear selection + clearSelection(); }); } - const moveBtn = document.getElementById('move'); - if(moveBtn){ - moveBtn.addEventListener('click', ()=>{ - if(!socket){ alert('non connecté'); return; } - const fromEl = document.getElementById('from'); - const toEl = document.getElementById('to'); - const promoEl = document.getElementById('promo'); - const from = fromEl ? fromEl.value.trim() : ''; - const to = toEl ? toEl.value.trim() : ''; - const promotion = promoEl ? promoEl.value.trim() || undefined : undefined; - socket.emit('game:move', { roomId, from, to, promotion }, (resp)=>{ - if(resp && resp.error) { log('move error', resp); alert(resp.error); return; } - log('move accepted', resp); - }); - }); - } + // move button removed // Board rendering using provided SVG assets and `boardState` JSON. const pieceSetPath = '/assets/chess-pieces/chess_maestro_bw'; @@ -187,6 +227,17 @@ img.className = 'piece'; sqEl.appendChild(img); }); + + // re-apply selection if the previously selected square still exists + if(selectedSquare){ + // re-apply local selection without re-emitting + setSelection(selectedSquare, false); + } + // re-apply remote selections + Object.entries(remoteSelections).forEach(([pid, sq]) => { + const el = boardEl.querySelector(`.square[data-square="${sq}"]`); + if(el) el.classList.add('selected-remote'); + }); } // build grid with independent square elements and data-square attributes diff --git a/public/styles/style.css b/public/styles/style.css index 3ae6f1e..4616fde 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -49,12 +49,23 @@ a:hover{text-decoration:underline} /* marker shown on target squares for legal moves */ .square{position:relative} -.square .move-marker{ - position:absolute; - left:50%;top:50%;transform:translate(-50%,-50%); - width:18%;height:18%;border-radius:50%;pointer-events:none;background:rgba(96,165,250,0.95); - box-shadow:0 2px 6px rgba(2,6,23,0.6);z-index:3 +/* move markers removed */ + +/* highlighted/selected square when a player selects a piece */ +.square.selected{ + outline: 3px solid rgba(245,158,11,0.95); /* amber */ + box-shadow: inset 0 0 0 3px rgba(245,158,11,0.08), 0 8px 24px rgba(245,158,11,0.06); + z-index: 4; } +.square.selected .piece{transform: scale(1.06); transition: transform 140ms ease} + +/* remote selection (other player's selection) */ +.square.selected-remote{ + outline: 3px solid rgba(59,130,246,0.95); /* blue */ + box-shadow: inset 0 0 0 3px rgba(59,130,246,0.06), 0 6px 16px rgba(59,130,246,0.04); + z-index: 3; +} +.square.selected-remote .piece{transform: scale(1.02); transition: transform 140ms ease} /* basic checkerboard coloring */ .square.light{background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));} diff --git a/server.js b/server.js index 5fdf259..9998197 100644 --- a/server.js +++ b/server.js @@ -200,16 +200,16 @@ io.on('connection', (socket) => { }); // delegates to `computeLegalMoves` which is a stub you should implement. - socket.on('game:legalMoves', ({ roomId, square }, cb) => { + // legalMoves API removed (movement UI disabled) + + // propagate selection made by one client to the other clients in the same room + socket.on('game:select', ({ roomId, square }, cb) => { const room = rooms.get(roomId); - if (!room) return cb && cb({ error: 'room not found' }); - try{ - const moves = computeLegalMoves(room, square) || []; - return cb && cb({ ok: true, moves }); - }catch(e){ - console.error('game:legalMoves error', e); - return cb && cb({ error: 'invalid square', moves: [] }); - } + if(!room) return cb && cb({ error: 'room not found' }); + const playerId = socket.data.playerId || null; + // broadcast to other sockets in the room (exclude sender) + socket.to(roomId).emit('game:select', { playerId, square }); + cb && cb({ ok: true }); }); // Simple cards API via sockets: list/play