changé le message d'erreur
This commit is contained in:
parent
4ae5abcd82
commit
fd50466e61
2 changed files with 78 additions and 78 deletions
|
|
@ -409,7 +409,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.emit('room:join', { roomId, playerId }, (resp)=>{
|
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);
|
log('Rejoint room', resp);
|
||||||
// remember assigned id/color from server
|
// remember assigned id/color from server
|
||||||
if(resp.playerId){
|
if(resp.playerId){
|
||||||
|
|
@ -698,7 +698,7 @@
|
||||||
if(!occ || occ.color === myShort){ log('select an enemy piece (second)'); return; }
|
if(!occ || occ.color === myShort){ log('select an enemy piece (second)'); return; }
|
||||||
// emit swap payload: sourceSquare + targetSquare
|
// emit swap payload: sourceSquare + targetSquare
|
||||||
socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { sourceSquare: pendingCard.firstTarget, targetSquare: square } }, (resp)=>{
|
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); }
|
else { log('inversion played', resp); }
|
||||||
});
|
});
|
||||||
// cleanup visuals
|
// cleanup visuals
|
||||||
|
|
@ -710,7 +710,7 @@
|
||||||
if(occupied){ log('square not empty for mine placement'); return; }
|
if(occupied){ log('square not empty for mine placement'); return; }
|
||||||
// send mine placement
|
// send mine placement
|
||||||
socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square } }, (resp)=>{
|
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); }
|
else { log('card played with target', resp); }
|
||||||
});
|
});
|
||||||
pendingCard = null; clearTargetHighlights(); return;
|
pendingCard = null; clearTargetHighlights(); return;
|
||||||
|
|
@ -719,7 +719,7 @@
|
||||||
if(!piece || piece.color !== myShort){ log('select one of your pieces'); return; }
|
if(!piece || piece.color !== myShort){ log('select one of your pieces'); return; }
|
||||||
if(piece.type && piece.type.toLowerCase() === 'k'){ log('cannot select king'); 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)=>{
|
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); }
|
else { log('card played with target', resp); }
|
||||||
});
|
});
|
||||||
pendingCard = null; clearTargetHighlights(); return;
|
pendingCard = null; clearTargetHighlights(); return;
|
||||||
|
|
@ -736,7 +736,7 @@
|
||||||
pendingCard = null; clearTargetHighlights(); return;
|
pendingCard = null; clearTargetHighlights(); return;
|
||||||
}
|
}
|
||||||
socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: pendingCard.cardId, payload: { targetSquare: square, promotion: promotionChoice } }, (resp)=>{
|
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); }
|
else { log('card played with promotion', resp); }
|
||||||
});
|
});
|
||||||
pendingCard = null; clearTargetHighlights();
|
pendingCard = null; clearTargetHighlights();
|
||||||
|
|
@ -747,7 +747,7 @@
|
||||||
if(!occ || occ.color === myShort){ log('select an enemy piece'); return; }
|
if(!occ || occ.color === myShort){ log('select an enemy piece'); return; }
|
||||||
if(occ.type && occ.type.toLowerCase() === 'k'){ log('cannot select king'); 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)=>{
|
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);
|
else { log('card played with target', resp);
|
||||||
// after server applied the card effect, request legal moves for the selected piece so the client
|
// 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)
|
// 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);
|
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; }
|
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)=>{
|
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);
|
else { log('card played with target', resp);
|
||||||
try{ setSelection(square); }catch(_){ }
|
try{ setSelection(square); }catch(_){ }
|
||||||
}
|
}
|
||||||
|
|
@ -795,7 +795,7 @@
|
||||||
const myShort = (myColor && myColor[0]) || '';
|
const myShort = (myColor && myColor[0]) || '';
|
||||||
if(!currentBoardState || currentBoardState.turn !== myShort){ log('not your turn'); return; }
|
if(!currentBoardState || currentBoardState.turn !== myShort){ log('not your turn'); return; }
|
||||||
socket.emit('game:move', { roomId, from, to }, (resp)=>{
|
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); }
|
else { log('move sent', resp); }
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
@ -1136,7 +1136,7 @@
|
||||||
if(opponents.length === 1){
|
if(opponents.length === 1){
|
||||||
const targetPlayerId = opponents[0];
|
const targetPlayerId = opponents[0];
|
||||||
socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: cid, payload: { targetPlayerId } }, (resp)=>{
|
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); }
|
else { log('card played (steal card)', resp); }
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
@ -1145,7 +1145,7 @@
|
||||||
showStealModal(handCounts, (targetPlayerId)=>{
|
showStealModal(handCounts, (targetPlayerId)=>{
|
||||||
if(!targetPlayerId) return; // canceled
|
if(!targetPlayerId) return; // canceled
|
||||||
socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: cid, payload: { targetPlayerId } }, (resp)=>{
|
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); }
|
else { log('card played (steal card)', resp); }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -1160,7 +1160,7 @@
|
||||||
// immediate-play card (no target required)
|
// immediate-play card (no target required)
|
||||||
const payload = {};
|
const payload = {};
|
||||||
socket.emit('card:play', { roomId, playerId: myPlayerId, cardId: cid, payload }, (resp)=>{
|
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); }
|
else { log('card played', resp); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1184,7 +1184,7 @@
|
||||||
if(!socket) return;
|
if(!socket) return;
|
||||||
drawBtn.disabled = true;
|
drawBtn.disabled = true;
|
||||||
socket.emit('player:draw', { roomId }, (resp)=>{
|
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);
|
log('player:draw ok', resp);
|
||||||
// server will send room:update which will hide/disable the button as needed
|
// server will send room:update which will hide/disable the button as needed
|
||||||
});
|
});
|
||||||
|
|
|
||||||
130
server.js
130
server.js
|
|
@ -741,7 +741,7 @@ app.post('/rooms', (req, res) => {
|
||||||
|
|
||||||
app.get('/rooms/:id', (req, res) => {
|
app.get('/rooms/:id', (req, res) => {
|
||||||
const room = rooms.get(req.params.id);
|
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 boardState = room.boardState || null;
|
||||||
const size = (room.boardState && room.boardState.width) || room.size;
|
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 });
|
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) => {
|
socket.on('room:join', ({ roomId, playerId }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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();
|
const assignedId = playerId || uuidv4();
|
||||||
|
|
||||||
// If this player was pending removal (disconnect during navigation), cancel removal
|
// If this player was pending removal (disconnect during navigation), cancel removal
|
||||||
|
|
@ -809,8 +809,8 @@ io.on('connection', (socket) => {
|
||||||
try{
|
try{
|
||||||
const room = rooms.get(roomId || socket.data.roomId);
|
const room = rooms.get(roomId || socket.data.roomId);
|
||||||
const playerId = socket.data.playerId;
|
const playerId = socket.data.playerId;
|
||||||
if(!room) return cb && cb({ error: 'room not found' });
|
if(!room) return cb && cb({ error: 'room not found', message: "Aucune salle trouvée." });
|
||||||
if(!playerId) return cb && cb({ error: 'not joined' });
|
if(!playerId) return cb && cb({ error: 'not joined', message: "Vous n'avez pas rejoint la salle." });
|
||||||
// remove player immediately
|
// remove player immediately
|
||||||
room.players = (room.players || []).filter(p => p.id !== playerId);
|
room.players = (room.players || []).filter(p => p.id !== playerId);
|
||||||
// if the host left, promote the next player
|
// 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.
|
// 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) => {
|
socket.on('room:auto_draw:set', ({ roomId, enabled }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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;
|
const sender = socket.data.playerId;
|
||||||
if(!sender) return cb && cb({ error: 'not joined' });
|
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' });
|
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;
|
room.autoDraw = !!enabled;
|
||||||
// Broadcast updated room state to all participants
|
// Broadcast updated room state to all participants
|
||||||
try{ sendRoomUpdate(room); }catch(_){ }
|
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.
|
// 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) => {
|
socket.on('room:no_remise:set', ({ roomId, enabled }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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;
|
const sender = socket.data.playerId;
|
||||||
if(!sender) return cb && cb({ error: 'not joined' });
|
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' });
|
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;
|
room.noRemise = !!enabled;
|
||||||
// Broadcast updated room state to all participants
|
// Broadcast updated room state to all participants
|
||||||
try{ sendRoomUpdate(room); }catch(_){ }
|
try{ sendRoomUpdate(room); }catch(_){ }
|
||||||
|
|
@ -858,17 +858,17 @@ io.on('connection', (socket) => {
|
||||||
|
|
||||||
socket.on('game:move', ({ roomId, from, to, promotion }, cb) => {
|
socket.on('game:move', ({ roomId, from, to, promotion }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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
|
// 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,
|
// 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.
|
// apply the move to room.boardState, handle captures, flip turn and broadcast the updated board.
|
||||||
try{
|
try{
|
||||||
const senderId = socket.data.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 salle." });
|
||||||
const roomPlayer = room.players.find(p => p.id === senderId);
|
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 salle." });
|
||||||
if(!room.boardState) return cb && cb({ error: 'no board state' });
|
if(!room.boardState) return cb && cb({ error: 'no board state', message: "Aucun état de plateau disponible." });
|
||||||
|
|
||||||
const board = room.boardState;
|
const board = room.boardState;
|
||||||
// determine player's color short form ('w'|'b')
|
// 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' });
|
if(!playerColorShort) return cb && cb({ error: 'invalid player color' });
|
||||||
|
|
||||||
// must be player's turn
|
// 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'
|
// find piece at 'from'
|
||||||
const pieces = board.pieces || [];
|
const pieces = board.pieces || [];
|
||||||
const moving = pieces.find(p => p.square === from);
|
const moving = pieces.find(p => p.square === from);
|
||||||
if(!moving) return cb && cb({ error: 'no piece at source' });
|
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,
|
// Enforce 'toucher' restriction: if there is an active 'toucher' effect targeting this player,
|
||||||
// they may ONLY move the specified piece(s).
|
// they may ONLY move the specified piece(s).
|
||||||
|
|
@ -890,7 +890,7 @@ io.on('connection', (socket) => {
|
||||||
const effects = room.activeCardEffects || [];
|
const effects = room.activeCardEffects || [];
|
||||||
const toucher = effects.find(e => e && e.type === 'toucher' && e.playerId === senderId);
|
const toucher = effects.find(e => e && e.type === 'toucher' && e.playerId === senderId);
|
||||||
if(toucher && toucher.pieceId && moving.id !== toucher.pieceId){
|
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(_){ }
|
}catch(_){ }
|
||||||
|
|
||||||
|
|
@ -902,7 +902,7 @@ io.on('connection', (socket) => {
|
||||||
// only allow moves that capture an occupied square
|
// only allow moves that capture an occupied square
|
||||||
const targetIndexCheck = pieces.findIndex(p => p.square === to);
|
const targetIndexCheck = pieces.findIndex(p => p.square === to);
|
||||||
if(targetIndexCheck === -1){
|
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(_){ }
|
}catch(_){ }
|
||||||
|
|
@ -910,14 +910,14 @@ io.on('connection', (socket) => {
|
||||||
// validate that 'to' is among legal moves
|
// validate that 'to' is among legal moves
|
||||||
const legal = computeLegalMoves(room, from) || [];
|
const legal = computeLegalMoves(room, from) || [];
|
||||||
const ok = legal.some(m => m.to === to);
|
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
|
// If any brouillard is active in the room, disallow moving onto squares that the player cannot see
|
||||||
try{
|
try{
|
||||||
const hasBrouillard = (room.activeCardEffects || []).some(e => e && e.type === 'brouillard');
|
const hasBrouillard = (room.activeCardEffects || []).some(e => e && e.type === 'brouillard');
|
||||||
if(hasBrouillard){
|
if(hasBrouillard){
|
||||||
const visible = visibleSquaresForPlayer(room, senderId) || new Set();
|
const visible = visibleSquaresForPlayer(room, senderId) || new Set();
|
||||||
if(!visible.has(to)){
|
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(_){ }
|
}catch(_){ }
|
||||||
|
|
@ -1219,14 +1219,14 @@ io.on('connection', (socket) => {
|
||||||
// Host can start the game explicitly
|
// Host can start the game explicitly
|
||||||
socket.on('game:start', ({ roomId }, cb) => {
|
socket.on('game:start', ({ roomId }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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)
|
// verify sender is the host (first player)
|
||||||
const senderId = socket.data.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." });
|
||||||
if (room.players.length < 2) return cb && cb({ error: 'need 2 players to start' });
|
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)
|
// 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';
|
room.status = 'playing';
|
||||||
// Do not broadcast the raw boardState to all sockets (use per-recipient sendRoomUpdate to enforce invisibility)
|
// 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.
|
// Re-add legalMoves API to compute and return pseudo-legal moves for a square.
|
||||||
socket.on('game:legalMoves', ({ roomId, square }, cb) => {
|
socket.on('game:legalMoves', ({ roomId, square }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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{
|
try{
|
||||||
const moves = computeLegalMoves(room, square) || [];
|
const moves = computeLegalMoves(room, square) || [];
|
||||||
return cb && cb({ ok: true, moves });
|
return cb && cb({ ok: true, moves });
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error('game:legalMoves error', 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
|
// propagate selection made by one client to the other clients in the same room
|
||||||
socket.on('game:select', ({ roomId, square }, cb) => {
|
socket.on('game:select', ({ roomId, square }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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;
|
const playerId = socket.data.playerId || null;
|
||||||
// remember last selected square for this socket so cards that need a target can use it
|
// remember last selected square for this socket so cards that need a target can use it
|
||||||
try{ socket.data.lastSelectedSquare = square || null; }catch(e){}
|
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.
|
// Manual draw by player (consumes the player's turn). Only valid when autoDraw is disabled.
|
||||||
socket.on('player:draw', ({ roomId }, cb) => {
|
socket.on('player:draw', ({ roomId }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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." });
|
||||||
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." });
|
||||||
const senderId = socket.data.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." });
|
||||||
const board = room.boardState;
|
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);
|
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;
|
const playerColorShort = (roomPlayer.color && roomPlayer.color[0]) || null;
|
||||||
// must be player's turn
|
// 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
|
// 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
|
// do not allow drawing if player already played a card this turn
|
||||||
room._cardPlayedThisTurn = room._cardPlayedThisTurn || {};
|
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{
|
try{
|
||||||
// perform the draw (this will emit card:drawn privately and send personalized room:update)
|
// perform the draw (this will emit card:drawn privately and send personalized room:update)
|
||||||
const drawn = drawCardForPlayer(room, senderId);
|
const drawn = drawCardForPlayer(room, senderId);
|
||||||
if(!drawn){
|
if(!drawn){
|
||||||
// nothing drawn (hand full or deck empty)
|
// 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
|
// drawing consumes the player's turn: flip turn and decrement per-turn effects
|
||||||
// advance board version
|
// advance board version
|
||||||
|
|
@ -1419,7 +1419,7 @@ io.on('connection', (socket) => {
|
||||||
// Simple cards API via sockets: list/play
|
// Simple cards API via sockets: list/play
|
||||||
socket.on('card:list', ({ roomId }, cb) => {
|
socket.on('card:list', ({ roomId }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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)
|
// return available card types (placeholder)
|
||||||
const available = [
|
const available = [
|
||||||
{ id: 'invert', name: 'Invert Turn', description: 'Swap movement directions for one move' },
|
{ 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) => {
|
socket.on('card:play', ({ roomId, playerId, cardId, payload }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
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." });
|
||||||
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." });
|
||||||
// enforce sender identity from socket (don't trust client-supplied playerId)
|
// enforce sender identity from socket (don't trust client-supplied playerId)
|
||||||
const senderId = socket.data.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
|
// enforce one card per player per turn when the game is playing
|
||||||
try{
|
try{
|
||||||
const board = room.boardState;
|
const board = room.boardState;
|
||||||
|
|
@ -1442,10 +1442,10 @@ io.on('connection', (socket) => {
|
||||||
const roomPlayer = room.players.find(p => p.id === senderId);
|
const roomPlayer = room.players.find(p => p.id === senderId);
|
||||||
const playerColorShort = (roomPlayer && roomPlayer.color && roomPlayer.color[0]) || null;
|
const playerColorShort = (roomPlayer && roomPlayer.color && roomPlayer.color[0]) || null;
|
||||||
// only allow playing a card on your turn
|
// 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
|
// track card plays per turn (not per board version) so effects like "double move" don't allow extra card plays
|
||||||
room._cardPlayedThisTurn = room._cardPlayedThisTurn || {};
|
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); }
|
}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);
|
const targetPiece = (board && board.pieces || []).find(p => p.square === targetCandidate);
|
||||||
if(!board || !targetCandidate || !targetPiece || targetPiece.color !== playerColorShort){
|
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
|
// 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
|
// ensure the payload has the resolved target for downstream handling
|
||||||
payload = payload || {};
|
payload = payload || {};
|
||||||
|
|
@ -1485,7 +1485,7 @@ io.on('connection', (socket) => {
|
||||||
// find by unique id or by cardId (first match)
|
// 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));
|
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){
|
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];
|
const removed = hand.splice(idx,1)[0];
|
||||||
room.hands[senderId] = hand;
|
room.hands[senderId] = hand;
|
||||||
|
|
@ -1605,7 +1605,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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)
|
// apply permanent adoubement effect (bind to piece id so it persists when the piece moves)
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
@ -1632,7 +1632,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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
|
// create a persistent doppelganger effect bound to the selected piece
|
||||||
try{
|
try{
|
||||||
|
|
@ -1664,7 +1664,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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)
|
// apply permanent folie effect (bind to piece id so it persists when the piece moves)
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
@ -1691,7 +1691,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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)
|
// apply permanent fortification effect (bind to piece id so it persists when the piece moves)
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
@ -1718,7 +1718,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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
|
// find the owner (player) of the targeted piece
|
||||||
const targetOwner = (room.players || []).find(p => (p.color && p.color[0]) === targetPiece.color) || null;
|
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
|
// 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'){
|
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.
|
// 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
|
// perform downgrade: change piece type to pawn
|
||||||
try{
|
try{
|
||||||
|
|
@ -1776,7 +1776,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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(_){ }
|
}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)
|
// bind sniper effect to the piece id (one-time use)
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
@ -1795,12 +1795,12 @@ io.on('connection', (socket) => {
|
||||||
const targetPiece = (board && board.pieces || []).find(p => p.square === target);
|
const targetPiece = (board && board.pieces || []).find(p => p.square === target);
|
||||||
if(!board || !target || !targetPiece){
|
if(!board || !target || !targetPiece){
|
||||||
// invalid target: do not restore card (consume) and return error
|
// 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
|
// do not allow kings to be affected
|
||||||
if(targetPiece.type && targetPiece.type.toLowerCase() === 'k'){
|
if(targetPiece.type && targetPiece.type.toLowerCase() === 'k'){
|
||||||
// consume card but report invalid
|
// 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
|
// find owner of the targeted piece
|
||||||
const targetOwner = (room.players || []).find(p => (p.color && p.color[0]) === targetPiece.color) || null;
|
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){
|
if(!board || !source || !target || !srcPiece || !tgtPiece || srcPiece.color !== playerColorShort || tgtPiece.color === playerColorShort){
|
||||||
// invalid target(s): restore removed card to hand and abort
|
// 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); }
|
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
|
// swap their squares
|
||||||
try{
|
try{
|
||||||
|
|
@ -1875,7 +1875,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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
|
// apply a temporary teleport effect bound to the piece id for one owner turn
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
@ -1898,7 +1898,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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){ }
|
}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
|
// swap piece colors
|
||||||
// helper: square <-> coords
|
// helper: square <-> coords
|
||||||
|
|
@ -1997,7 +1997,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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
|
// compute affected squares: target + neighbors
|
||||||
function neighbors(sq){
|
function neighbors(sq){
|
||||||
|
|
@ -2083,7 +2083,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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)
|
// find available destination corners (empty)
|
||||||
const emptyCorners = corners.filter(c => { return !(board.pieces || []).some(p => p.square === c); });
|
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 || [];
|
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; } }
|
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); }
|
}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
|
// 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
|
// to move to any of the available corner squares for one turn
|
||||||
|
|
@ -2124,7 +2124,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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(_){ }
|
}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
|
// gather pieces and compute a random set of distinct destination squares across the whole board
|
||||||
const pieces = board.pieces;
|
const pieces = board.pieces;
|
||||||
|
|
@ -2180,7 +2180,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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(_){ }
|
}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 pieces = board.pieces;
|
||||||
const transformChoices = ['N','B','R'];
|
const transformChoices = ['N','B','R'];
|
||||||
|
|
@ -2227,7 +2227,7 @@ io.on('connection', (socket) => {
|
||||||
room.discard = room.discard || [];
|
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; } }
|
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); }
|
}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
|
// apply invisible effect bound to the piece id so only the owner can see it
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
@ -2253,7 +2253,7 @@ io.on('connection', (socket) => {
|
||||||
if(!board || !targetPlayer || targetPlayer.id === senderId){
|
if(!board || !targetPlayer || targetPlayer.id === senderId){
|
||||||
// restore removed card to hand and abort
|
// 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){}
|
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)
|
// compute list of all squares (each square veiled individually)
|
||||||
const width = board.width || 8;
|
const width = board.width || 8;
|
||||||
|
|
@ -2505,7 +2505,7 @@ io.on('connection', (socket) => {
|
||||||
// try to remove the removed card instance from discard (last occurrence)
|
// 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; } }
|
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); }
|
}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)
|
// apply the rebond effect (also record pieceId so it can be identified after moves)
|
||||||
room.activeCardEffects = room.activeCardEffects || [];
|
room.activeCardEffects = room.activeCardEffects || [];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue