From fd50466e6134ea66832b626cb9ceb812f512819e Mon Sep 17 00:00:00 2001 From: Didictateur Date: Tue, 25 Nov 2025 18:29:37 +0100 Subject: [PATCH] =?UTF-8?q?chang=C3=A9=20le=20message=20d'erreur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/game.html | 24 ++++----- server.js | 132 +++++++++++++++++++++++------------------------ 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/public/game.html b/public/game.html index 5360425..8656d4e 100644 --- a/public/game.html +++ b/public/game.html @@ -409,7 +409,7 @@ }); socket.emit('room:join', { roomId, playerId }, (resp)=>{ - if(resp && resp.error){ log('join error', resp); alert(resp.error); return; } + if(resp && resp.error){ log('join error', resp); alert(resp.message || resp.error); return; } log('Rejoint room', resp); // remember assigned id/color from server if(resp.playerId){ @@ -698,7 +698,7 @@ if(!occ || occ.color === myShort){ log('select an enemy piece (second)'); return; } // emit swap payload: sourceSquare + targetSquare socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { sourceSquare: pendingCard.firstTarget, targetSquare: square } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('inversion played', resp); } }); // cleanup visuals @@ -710,7 +710,7 @@ if(occupied){ log('square not empty for mine placement'); return; } // send mine placement socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played with target', resp); } }); pendingCard = null; clearTargetHighlights(); return; @@ -719,7 +719,7 @@ if(!piece || piece.color !== myShort){ log('select one of your pieces'); return; } if(piece.type && piece.type.toLowerCase() === 'k'){ log('cannot select king'); return; } socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played with target', resp); } }); pendingCard = null; clearTargetHighlights(); return; @@ -736,7 +736,7 @@ pendingCard = null; clearTargetHighlights(); return; } socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square, promotion: promotionChoice } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played with promotion', resp); } }); pendingCard = null; clearTargetHighlights(); @@ -747,7 +747,7 @@ if(!occ || occ.color === myShort){ log('select an enemy piece'); return; } if(occ.type && occ.type.toLowerCase() === 'k'){ log('cannot select king'); return; } socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played with target', resp); // after server applied the card effect, request legal moves for the selected piece so the client // will receive the updated legal moves (including teleport to empty squares) @@ -761,7 +761,7 @@ const occ = currentBoardState && Array.isArray(currentBoardState.pieces) && currentBoardState.pieces.find(p => p.square === square); if(!occ || occ.color === myShort || !occ.type || occ.type.toLowerCase() !== 'q'){ log('select an enemy queen'); return; } socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played with target', resp); try{ setSelection(square); }catch(_){ } } @@ -795,7 +795,7 @@ const myShort = (myColor && myColor[0]) || ''; if(!currentBoardState || currentBoardState.turn !== myShort){ log('not your turn'); return; } socket.emit('game:move', { roomId, from, to }, (resp)=>{ - if(resp && resp.error){ log('move error', resp); alert(resp.error); } + if(resp && resp.error){ log('move error', resp); alert(resp.message || resp.error); } else { log('move sent', resp); } }); return; @@ -1136,7 +1136,7 @@ if(opponents.length === 1){ const targetPlayerId = opponents[0]; socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: cid, payload: { targetPlayerId } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played (steal card)', resp); } }); return; @@ -1145,7 +1145,7 @@ showStealModal(handCounts, (targetPlayerId)=>{ if(!targetPlayerId) return; // canceled socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: cid, payload: { targetPlayerId } }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played (steal card)', resp); } }); }); @@ -1160,7 +1160,7 @@ // immediate-play card (no target required) const payload = {}; socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: cid, payload }, (resp)=>{ - if(resp && resp.error){ log('card:play error', resp); alert(resp.error); } + if(resp && resp.error){ log('card:play error', resp); alert(resp.message || resp.error); } else { log('card played', resp); } }); } @@ -1184,7 +1184,7 @@ if(!socket) return; drawBtn.disabled = true; socket.emit('player:draw', { roomId }, (resp)=>{ - if(resp && resp.error){ log('player:draw error', resp); alert(resp.error); drawBtn.disabled = false; return; } + if(resp && resp.error){ log('player:draw error', resp); alert(resp.message || resp.error); drawBtn.disabled = false; return; } log('player:draw ok', resp); // server will send room:update which will hide/disable the button as needed }); diff --git a/server.js b/server.js index 08f3dac..7d10081 100644 --- a/server.js +++ b/server.js @@ -741,7 +741,7 @@ app.post('/rooms', (req, res) => { app.get('/rooms/:id', (req, res) => { const room = rooms.get(req.params.id); - if (!room) return res.status(404).json({ error: 'room not found' }); + if (!room) return res.status(404).json({ error: 'room not found', message: "Aucune salle trouvée." }); const boardState = room.boardState || null; const size = (room.boardState && room.boardState.width) || room.size; res.json({ id: room.id, boardState, size: size, players: room.players.map(p => ({ id: p.id, color: p.color })), status: room.status, hostId: room.hostId, cards: Object.keys(room.cards || {}), autoDraw: !!room.autoDraw, noRemise: !!room.noRemise }); @@ -752,7 +752,7 @@ io.on('connection', (socket) => { socket.on('room:join', ({ roomId, playerId }, cb) => { const room = rooms.get(roomId); - if (!room) return cb && cb({ error: 'room not found' }); + if (!room) return cb && cb({ error: 'room not found', message: "Aucune salle trouvée." }); const assignedId = playerId || uuidv4(); // If this player was pending removal (disconnect during navigation), cancel removal @@ -809,8 +809,8 @@ io.on('connection', (socket) => { try{ const room = rooms.get(roomId || socket.data.roomId); const playerId = socket.data.playerId; - if(!room) return cb && cb({ error: 'room not found' }); - if(!playerId) return cb && cb({ error: 'not joined' }); + if(!room) return cb && cb({ error: 'room not found', message: "Aucune salle trouvée." }); + if(!playerId) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la salle." }); // remove player immediately room.players = (room.players || []).filter(p => p.id !== playerId); // if the host left, promote the next player @@ -831,10 +831,10 @@ io.on('connection', (socket) => { // Host can toggle automatic drawing in the waiting room. Only the host may change this setting. socket.on('room:auto_draw:set', ({ roomId, enabled }, cb) => { const room = rooms.get(roomId); - if(!room) return cb && cb({ error: 'room not found' }); + 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' }); - if(room.hostId !== sender) return cb && cb({ error: 'only the host can change auto-draw' }); + 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 the host can change auto-draw', message: "Seul l'hôte peut changer le dessin automatique." }); room.autoDraw = !!enabled; // Broadcast updated room state to all participants try{ sendRoomUpdate(room); }catch(_){ } @@ -845,10 +845,10 @@ io.on('connection', (socket) => { // Host can toggle the 'no remise' flag (prevent reshuffling discard into deck). Only the host may change this setting. socket.on('room:no_remise:set', ({ roomId, enabled }, cb) => { const room = rooms.get(roomId); - if(!room) return cb && cb({ error: 'room not found' }); + 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' }); - if(room.hostId !== sender) return cb && cb({ error: 'only the host can change no-remise' }); + 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 the host can change no-remise', message: "Seul l'hôte peut changer le no-remise." }); room.noRemise = !!enabled; // Broadcast updated room state to all participants try{ sendRoomUpdate(room); }catch(_){ } @@ -858,17 +858,17 @@ io.on('connection', (socket) => { socket.on('game:move', ({ roomId, from, to, promotion }, cb) => { const room = rooms.get(roomId); - if (!room) return cb && cb({ error: 'room not found' }); + if (!room) return cb && cb({ error: 'room not found', message: "Aucune salle trouvée." }); // don't allow moves in finished games - if(room.status === 'finished') return cb && cb({ error: 'game_over' }); + if(room.status === 'finished') return cb && cb({ error: 'game_over', message: "La partie est terminée." }); // Basic move handling: validate turn, validate piece ownership, validate target is one of computeLegalMoves, // apply the move to room.boardState, handle captures, flip turn and broadcast the updated board. try{ const senderId = socket.data.playerId; - if(!senderId) return cb && cb({ error: 'not joined' }); + if(!senderId) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la salle." }); const roomPlayer = room.players.find(p => p.id === senderId); - if(!roomPlayer) return cb && cb({ error: 'player not in room' }); - if(!room.boardState) return cb && cb({ error: 'no board state' }); + if(!roomPlayer) return cb && cb({ error: 'player not in room', message: "Vous n'êtes pas dans cette salle." }); + if(!room.boardState) return cb && cb({ error: 'no board state', message: "Aucun état de plateau disponible." }); const board = room.boardState; // determine player's color short form ('w'|'b') @@ -876,13 +876,13 @@ io.on('connection', (socket) => { if(!playerColorShort) return cb && cb({ error: 'invalid player color' }); // must be player's turn - if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn' }); + if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn', message: "Ce n'est pas à votre tour de jouer." }); // find piece at 'from' const pieces = board.pieces || []; const moving = pieces.find(p => p.square === from); if(!moving) return cb && cb({ error: 'no piece at source' }); - if(moving.color !== playerColorShort) return cb && cb({ error: 'not your piece' }); + if(moving.color !== playerColorShort) return cb && cb({ error: 'not your piece', message: "Vous ne pouvez pas déplacer une pièce adverse." }); // Enforce 'toucher' restriction: if there is an active 'toucher' effect targeting this player, // they may ONLY move the specified piece(s). @@ -890,7 +890,7 @@ io.on('connection', (socket) => { const effects = room.activeCardEffects || []; const toucher = effects.find(e => e && e.type === 'toucher' && e.playerId === senderId); if(toucher && toucher.pieceId && moving.id !== toucher.pieceId){ - return cb && cb({ error: 'must_move_restricted_piece' }); + return cb && cb({ error: 'must_move_restricted_piece', message: "Vous devez déplacer la pièce restreinte." }); } }catch(_){ } @@ -898,11 +898,11 @@ io.on('connection', (socket) => { try{ const effects2 = room.activeCardEffects || []; const tout = effects2.find(e => e && e.type === 'tout_ou_rien' && e.pieceId === moving.id); - if(tout){ + if(tout){ // only allow moves that capture an occupied square const targetIndexCheck = pieces.findIndex(p => p.square === to); if(targetIndexCheck === -1){ - return cb && cb({ error: 'must_capture_to_move' }); + return cb && cb({ error: 'must_capture_to_move', message: "Mouvement impossible : cette pièce ne peut se déplacer que pour capturer une pièce adverse." }); } } }catch(_){ } @@ -910,14 +910,14 @@ io.on('connection', (socket) => { // validate that 'to' is among legal moves const legal = computeLegalMoves(room, from) || []; const ok = legal.some(m => m.to === to); - if(!ok) return cb && cb({ error: 'illegal move' }); + if(!ok) return cb && cb({ error: 'illegal move', message: "Mouvement illégal." }); // If any brouillard is active in the room, disallow moving onto squares that the player cannot see try{ const hasBrouillard = (room.activeCardEffects || []).some(e => e && e.type === 'brouillard'); if(hasBrouillard){ const visible = visibleSquaresForPlayer(room, senderId) || new Set(); if(!visible.has(to)){ - return cb && cb({ error: 'destination_not_visible' }); + return cb && cb({ error: 'destination_not_visible', message: "La destination n'est pas visible." }); } } }catch(_){ } @@ -1219,14 +1219,14 @@ io.on('connection', (socket) => { // Host can start the game explicitly socket.on('game:start', ({ roomId }, cb) => { const room = rooms.get(roomId); - if (!room) return cb && cb({ error: 'room not found' }); + if (!room) return cb && cb({ error: 'room not found', message: "La partie n'a pas été trouvée." }); // verify sender is the host (first player) const senderId = socket.data.playerId; - if (!senderId) return cb && cb({ error: 'not joined' }); - if (room.players.length < 2) return cb && cb({ error: 'need 2 players to start' }); + if (!senderId) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la partie." }); + if (room.players.length < 2) return cb && cb({ error: 'need 2 players to start', message: "Il faut 2 joueurs pour commencer." }); // verify sender is the host (explicit hostId) - if (!room.hostId || room.hostId !== senderId) return cb && cb({ error: 'only host can start' }); + if (!room.hostId || room.hostId !== senderId) return cb && cb({ error: 'only host can start', message: "Seul l'hôte peut commencer la partie." }); room.status = 'playing'; // Do not broadcast the raw boardState to all sockets (use per-recipient sendRoomUpdate to enforce invisibility) @@ -1286,20 +1286,20 @@ io.on('connection', (socket) => { // Re-add legalMoves API to compute and return pseudo-legal moves for a square. socket.on('game:legalMoves', ({ roomId, square }, cb) => { const room = rooms.get(roomId); - if (!room) return cb && cb({ error: 'room not found' }); + if (!room) return cb && cb({ error: 'room not found', message: "La partie n'a pas été trouvée." }); 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: [] }); + return cb && cb({ error: 'invalid square', moves: [], message: "La case sélectionnée est invalide." }); } }); // 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' }); + if(!room) return cb && cb({ error: 'room not found', message: "La partie n'a pas été trouvée." }); const playerId = socket.data.playerId || null; // remember last selected square for this socket so cards that need a target can use it try{ socket.data.lastSelectedSquare = square || null; }catch(e){} @@ -1341,29 +1341,29 @@ io.on('connection', (socket) => { // Manual draw by player (consumes the player's turn). Only valid when autoDraw is disabled. socket.on('player:draw', ({ roomId }, cb) => { const room = rooms.get(roomId); - if(!room) return cb && cb({ error: 'room not found' }); - if(room.status === 'finished') return cb && cb({ error: 'game_over' }); + if(!room) return cb && cb({ error: 'room not found', message: "La partie n'a pas été trouvée." }); + if(room.status === 'finished') return cb && cb({ error: 'game_over', message: "La partie est terminée." }); const senderId = socket.data.playerId; - if(!senderId) return cb && cb({ error: 'not joined' }); + if(!senderId) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la partie." }); const board = room.boardState; - if(!board) return cb && cb({ error: 'no board state' }); + if(!board) return cb && cb({ error: 'no board state', message: "L'état du plateau est introuvable." }); const roomPlayer = room.players.find(p => p.id === senderId); - if(!roomPlayer) return cb && cb({ error: 'player not in room' }); + if(!roomPlayer) return cb && cb({ error: 'player not in room', message: "Vous n'êtes pas dans cette partie." }); const playerColorShort = (roomPlayer.color && roomPlayer.color[0]) || null; // must be player's turn - if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn' }); + if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn', message: "Ce n'est pas à votre tour de jouer." }); // manual draw only allowed when autoDraw is disabled - if(room.autoDraw) return cb && cb({ error: 'auto_draw_enabled' }); + if(room.autoDraw) return cb && cb({ error: 'auto_draw_enabled', message: "Le tirage manuel n'est pas autorisé." }); // do not allow drawing if player already played a card this turn room._cardPlayedThisTurn = room._cardPlayedThisTurn || {}; - if(room._cardPlayedThisTurn[senderId]) return cb && cb({ error: 'card_already_played_this_turn' }); + if(room._cardPlayedThisTurn[senderId]) return cb && cb({ error: 'card_already_played_this_turn', message: "Vous avez déjà joué une carte ce tour." }); try{ // perform the draw (this will emit card:drawn privately and send personalized room:update) const drawn = drawCardForPlayer(room, senderId); if(!drawn){ // nothing drawn (hand full or deck empty) - return cb && cb({ error: 'no_card_drawn' }); + return cb && cb({ error: 'no_card_drawn', message: "Aucune carte n'a été tirée." }); } // drawing consumes the player's turn: flip turn and decrement per-turn effects // advance board version @@ -1419,7 +1419,7 @@ io.on('connection', (socket) => { // Simple cards API via sockets: list/play socket.on('card:list', ({ roomId }, cb) => { const room = rooms.get(roomId); - if(!room) return cb && cb({ error: 'room not found' }); + if(!room) return cb && cb({ error: 'room not found', message: "La partie n'a pas été trouvée." }); // return available card types (placeholder) const available = [ { id: 'invert', name: 'Invert Turn', description: 'Swap movement directions for one move' }, @@ -1430,11 +1430,11 @@ io.on('connection', (socket) => { socket.on('card:play', ({ roomId, playerId, cardId, payload }, cb) => { const room = rooms.get(roomId); - if(!room) return cb && cb({ error: 'room not found' }); - if(room.status === 'finished') return cb && cb({ error: 'game_over' }); + if(!room) return cb && cb({ error: 'room not found', message: "La partie n'a pas été trouvée." }); + if(room.status === 'finished') return cb && cb({ error: 'game_over', message: "La partie est terminée." }); // enforce sender identity from socket (don't trust client-supplied playerId) const senderId = socket.data.playerId; - if(!senderId) return cb && cb({ error: 'not joined' }); + if(!senderId) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la partie." }); // enforce one card per player per turn when the game is playing try{ const board = room.boardState; @@ -1442,10 +1442,10 @@ io.on('connection', (socket) => { const roomPlayer = room.players.find(p => p.id === senderId); const playerColorShort = (roomPlayer && roomPlayer.color && roomPlayer.color[0]) || null; // only allow playing a card on your turn - if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn' }); + if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn', message: "Ce n'est pas à votre tour de jouer." }); // track card plays per turn (not per board version) so effects like "double move" don't allow extra card plays room._cardPlayedThisTurn = room._cardPlayedThisTurn || {}; - if(room._cardPlayedThisTurn[senderId]) return cb && cb({ error: 'card_already_played_this_turn' }); + if(room._cardPlayedThisTurn[senderId]) return cb && cb({ error: 'card_already_played_this_turn', message: "Vous avez déjà joué une carte ce tour." }); } }catch(e){ console.error('card play pre-check error', e); } @@ -1469,7 +1469,7 @@ io.on('connection', (socket) => { const targetPiece = (board && board.pieces || []).find(p => p.square === targetCandidate); if(!board || !targetCandidate || !targetPiece || targetPiece.color !== playerColorShort){ // nothing should happen if there's no selected piece or the selected piece isn't owned by the player - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // ensure the payload has the resolved target for downstream handling payload = payload || {}; @@ -1485,7 +1485,7 @@ io.on('connection', (socket) => { // find by unique id or by cardId (first match) const idx = hand.findIndex(c => (c.id && c.id === (payload && payload.id)) || (c.cardId && c.cardId === cardId) || (c.id && c.id === cardId)); if(idx === -1){ - return cb && cb({ error: 'you do not have that card' }); + return cb && cb({ error: 'you do not have that card', message: "Vous ne possedez pas cette carte." }); } const removed = hand.splice(idx,1)[0]; room.hands[senderId] = hand; @@ -1605,7 +1605,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // apply permanent adoubement effect (bind to piece id so it persists when the piece moves) room.activeCardEffects = room.activeCardEffects || []; @@ -1632,7 +1632,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // create a persistent doppelganger effect bound to the selected piece try{ @@ -1664,7 +1664,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // apply permanent folie effect (bind to piece id so it persists when the piece moves) room.activeCardEffects = room.activeCardEffects || []; @@ -1691,7 +1691,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // apply permanent fortification effect (bind to piece id so it persists when the piece moves) room.activeCardEffects = room.activeCardEffects || []; @@ -1718,7 +1718,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // find the owner (player) of the targeted piece const targetOwner = (room.players || []).find(p => (p.color && p.color[0]) === targetPiece.color) || null; @@ -1742,7 +1742,7 @@ io.on('connection', (socket) => { // validate target exists and belongs to the opponent and is a queen if(!board || !target || !targetPiece || targetPiece.color === playerColorShort || !targetPiece.type || targetPiece.type.toLowerCase() !== 'q'){ // invalid target: do NOT restore the removed card (card is consumed). Return error but card remains in discard. - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // perform downgrade: change piece type to pawn try{ @@ -1776,7 +1776,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(_){ } - return cb && cb({ error: 'no valid source selected' }); + return cb && cb({ error: 'no valid source selected', message: "Aucune source valide n'a été sélectionnée." }); } // bind sniper effect to the piece id (one-time use) room.activeCardEffects = room.activeCardEffects || []; @@ -1795,12 +1795,12 @@ io.on('connection', (socket) => { const targetPiece = (board && board.pieces || []).find(p => p.square === target); if(!board || !target || !targetPiece){ // invalid target: do not restore card (consume) and return error - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // do not allow kings to be affected if(targetPiece.type && targetPiece.type.toLowerCase() === 'k'){ // consume card but report invalid - return cb && cb({ error: 'cannot_target_king' }); + return cb && cb({ error: 'cannot_target_king', message: "Vous ne pouvez pas cibler un roi." }); } // find owner of the targeted piece const targetOwner = (room.players || []).find(p => (p.color && p.color[0]) === targetPiece.color) || null; @@ -1831,7 +1831,7 @@ io.on('connection', (socket) => { if(!board || !source || !target || !srcPiece || !tgtPiece || srcPiece.color !== playerColorShort || tgtPiece.color === playerColorShort){ // invalid target(s): restore removed card to hand and abort try{ room.hands = room.hands || {}; room.hands[senderId] = room.hands[senderId] || []; if(removed) room.hands[senderId].push(removed); room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid targets' }); + return cb && cb({ error: 'no valid targets', message: "Aucune cible valide n'a été sélectionnée." }); } // swap their squares try{ @@ -1875,7 +1875,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // apply a temporary teleport effect bound to the piece id for one owner turn room.activeCardEffects = room.activeCardEffects || []; @@ -1898,7 +1898,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ } - return cb && cb({ error: 'no board to flip' }); + return cb && cb({ error: 'no board to flip', message: "Aucun plateau à retourner." }); } // swap piece colors // helper: square <-> coords @@ -1997,7 +1997,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // compute affected squares: target + neighbors function neighbors(sq){ @@ -2083,7 +2083,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid corner piece selected' }); + return cb && cb({ error: 'no valid corner piece selected', message: "Aucun coin valide n'a été sélectionné." }); } // find available destination corners (empty) const emptyCorners = corners.filter(c => { return !(board.pieces || []).some(p => p.square === c); }); @@ -2098,7 +2098,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no empty destination corner available' }); + return cb && cb({ error: 'no empty destination corner available', message: "Aucun coin de destination vide n'est disponible." }); } // instead of teleporting immediately, record an effect that grants this piece the ability // to move to any of the available corner squares for one turn @@ -2124,7 +2124,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(_){ } - return cb && cb({ error: 'no pieces to shuffle' }); + return cb && cb({ error: 'no pieces to shuffle', message: "Aucune pièce à mélanger." }); } // gather pieces and compute a random set of distinct destination squares across the whole board const pieces = board.pieces; @@ -2180,7 +2180,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(_){ } - return cb && cb({ error: 'no pieces to transform' }); + return cb && cb({ error: 'no pieces to transform', message: "Aucune pièce à transformer." }); } const pieces = board.pieces; const transformChoices = ['N','B','R']; @@ -2227,7 +2227,7 @@ io.on('connection', (socket) => { room.discard = room.discard || []; for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // apply invisible effect bound to the piece id so only the owner can see it room.activeCardEffects = room.activeCardEffects || []; @@ -2253,7 +2253,7 @@ io.on('connection', (socket) => { if(!board || !targetPlayer || targetPlayer.id === senderId){ // restore removed card to hand and abort try{ room.hands = room.hands || {}; room.hands[senderId] = room.hands[senderId] || []; if(removed) room.hands[senderId].push(removed); room.discard = room.discard || []; for(let i = room.discard.length-1;i>=0;i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){} - return cb && cb({ error: 'no valid target player' }); + return cb && cb({ error: 'no valid target player', message: "Aucun joueur cible valide n'a été sélectionné." }); } // compute list of all squares (each square veiled individually) const width = board.width || 8; @@ -2505,7 +2505,7 @@ io.on('connection', (socket) => { // try to remove the removed card instance from discard (last occurrence) for(let i = room.discard.length - 1; i >= 0; i--){ if(room.discard[i] && room.discard[i].id === (removed && removed.id)){ room.discard.splice(i,1); break; } } }catch(e){ console.error('restore removed card error', e); } - return cb && cb({ error: 'no valid target' }); + return cb && cb({ error: 'no valid target', message: "Aucune cible valide n'a été sélectionnée." }); } // apply the rebond effect (also record pieceId so it can be identified after moves) room.activeCardEffects = room.activeCardEffects || [];