From 72d096f0e5f8477b285ede36e99312126bb4b1cc Mon Sep 17 00:00:00 2001 From: Didictateur Date: Sun, 23 Nov 2025 13:00:49 +0100 Subject: [PATCH] la parrure --- public/game.html | 16 ++++++++++++++++ server.js | 27 ++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/public/game.html b/public/game.html index 3d47d03..09c83ac 100644 --- a/public/game.html +++ b/public/game.html @@ -96,6 +96,8 @@ if(id.indexOf('mine') !== -1) return 'empty'; // 'toucher c'est jouer' : require selecting an enemy piece as the card target if(id.indexOf('toucher') !== -1) return 'enemy'; + // la parrure: select an enemy queen to downgrade to a pawn + if(id.indexOf('parrure') !== -1) return 'enemy_queen'; // kamikaz targets one of your pieces (play card, then choose a piece) if(id.indexOf('kamikaz') !== -1) return 'owned'; if(id.indexOf('invis') !== -1 || id.indexOf('invisible') !== -1) return 'owned'; @@ -533,6 +535,17 @@ // keep pendingCard until server response processed (client will call setSelection on success) pendingCard = null; clearTargetHighlights(); return; } + else if(req === 'enemy_queen'){ + 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); } + else { log('card played with target', resp); + try{ setSelection(square); }catch(_){ } + } + }); + pendingCard = null; clearTargetHighlights(); return; + } } // If this square contains a move-marker (either clicked marker or square area), attempt the move. @@ -715,6 +728,9 @@ } else if(type === 'owned_pawn'){ const occ = (currentBoardState.pieces||[]).find(p=>p.square===sq); if(occ && occ.color === myShort && occ.type && occ.type.toLowerCase() === 'p') sqEl.classList.add('targetable'); + } else if(type === 'enemy_queen'){ + const occ = (currentBoardState.pieces||[]).find(p=>p.square===sq); + if(occ && occ.color && occ.color !== myShort && occ.type && occ.type.toLowerCase() === 'q') sqEl.classList.add('targetable'); } else if(type === 'enemy'){ const occ = (currentBoardState.pieces||[]).find(p=>p.square===sq); if(occ && occ.color && occ.color !== myShort) sqEl.classList.add('targetable'); diff --git a/server.js b/server.js index 130524b..5fee77d 100644 --- a/server.js +++ b/server.js @@ -182,7 +182,7 @@ function buildDefaultDeck(){ // ['cluster','Désigne 4 pions formant un rectangle. Tant que ces pions ne bougent pas, aucune pièce ne peut sortir ou rentrer dans ce rectangle.'], // ['vacances','Choisie une pièce qui sort du plateau pendant deux tours. Ce après quoi elle tente de revenir: si la case est occupée, alors la pièce vacancière est capturée par la pièce occupant la case.'], ['mélange','La position de toutes les pièces sont échangées aléatoirement.'], - // ['la parrure','Une reine est dégradée en pion'], + ['la parrure','Une reine est dégradée en pion'], // ['tricherie','Choisis une carte de la pioche parmis trois'], // ['tout ou rien','Une pièce choisie ne peut maintenant se déplacer que si elle capture.'], // ['tous les mêmes','Au yeux de l ennemie, toutes les pièces se ressemblent pendant 2 tours.'], @@ -1387,6 +1387,31 @@ io.on('connection', (socket) => { played.payload = Object.assign({}, payload, { applied: 'toucher', appliedTo: target }); try{ io.to(room.id).emit('card:effect:applied', { roomId: room.id, effect }); }catch(_){ } }catch(e){ console.error('toucher effect error', e); } + } + // la parrure: downgrade an enemy queen to a pawn (selected target must be an enemy queen) + else if((typeof cardId === 'string' && cardId.indexOf('parrure') !== -1) || cardId === 'la_parrure'){ + try{ + const board = room.boardState; + let target = payload && payload.targetSquare; + if(!target){ try{ target = socket.data && socket.data.lastSelectedSquare; }catch(e){ target = null; } } + const roomPlayer = room.players.find(p => p.id === senderId); + const playerColorShort = (roomPlayer && roomPlayer.color && roomPlayer.color[0]) || null; + const targetPiece = (board && board.pieces || []).find(p => p.square === target); + // 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' }); + } + // perform downgrade: change piece type to pawn + try{ + targetPiece.type = 'p'; + if(targetPiece.promoted) delete targetPiece.promoted; + // bump board version so clients react to the board mutation + try{ board.version = (board.version || 0) + 1; }catch(_){ } + played.payload = Object.assign({}, payload, { applied: 'parrure', appliedTo: target }); + try{ io.to(room.id).emit('card:effect:applied', { roomId: room.id, effect: { id: played.id, type: 'parrure', pieceId: targetPiece.id, pieceSquare: targetPiece.square, playerId: senderId } }); }catch(_){ } + }catch(e){ console.error('parrure apply error', e); } + }catch(e){ console.error('parrure effect error', e); } } // teleportation: allow the selected piece to move to any empty square for one turn else if((typeof cardId === 'string' && (cardId.indexOf('teleport') !== -1 || cardId.indexOf('t_l_portation') !== -1 || cardId.indexOf('t_lportation') !== -1)) || cardId === 'teleport'){