resurection

This commit is contained in:
Didictateur 2025-11-22 22:28:10 +01:00
parent f4555ed00c
commit 10fc731f5f
2 changed files with 85 additions and 11 deletions

View file

@ -42,7 +42,7 @@ Un pion au choix est promu
### Vole d'une carte ### Vole d'une carte
Vole une carte aléatoirement au joueur adverse Vole une carte aléatoirement au joueur adverse
### Resurection ### Resurection
Choisie une pièce capturée pour la ressuciter dans son camp Ressucite la dernière pièce perdue
### Carte sans effet ### Carte sans effet
N'a aucun effet N'a aucun effet
### Défausse ### Défausse
@ -88,7 +88,7 @@ Si deux pions encadrent parfaitement une pièce adverse, cette dernière change
### Plus on est de fous, plus on rit ### Plus on est de fous, plus on rit
Si le joueur possède deux fous dans la même diagonale, alors toutes les pièces adverses encadrées par ces deux fous sont capturés Si le joueur possède deux fous dans la même diagonale, alors toutes les pièces adverses encadrées par ces deux fous sont capturés
### Cluster ### 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. Désigne 4 pions formant un rectangle. Tant que ces pions ne bougent pas ou que l'une d'elle est capturée, aucune pièce ne peut sortir ou rentrer dans ce rectangle.
### Vacances ### 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. 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 ### Mélange

View file

@ -49,6 +49,11 @@ function sendRoomUpdate(room){
payload.minesOwn = mines; payload.minesOwn = mines;
}catch(_){ payload.minesOwn = []; } }catch(_){ payload.minesOwn = []; }
}catch(e){ payload.visibleSquares = []; } }catch(e){ payload.visibleSquares = []; }
// attach captured pieces that belong to this recipient so they may choose one for resurrection
try{
const shortColor = (p.color && p.color[0]) || null;
payload.capturedOwn = (room.captured || []).filter(c => c && c.piece && c.piece.color === shortColor).map(c => ({ id: c.id, type: c.piece.type, originalId: c.piece.id, originalSquare: c.piece.square, capturedBy: c.capturedBy, ts: c.ts }));
}catch(_){ payload.capturedOwn = []; }
if(p.socketId){ if(p.socketId){
io.to(p.socketId).emit('room:update', payload); io.to(p.socketId).emit('room:update', payload);
} }
@ -110,9 +115,8 @@ function buildDefaultDeck(){
['vole d une pièce','Désigne une pièce non roi qui change de camp'], ['vole d une pièce','Désigne une pièce non roi qui change de camp'],
['promotion','Un pion au choix est promu'], ['promotion','Un pion au choix est promu'],
['vole d une carte','Vole une carte aléatoirement au joueur adverse'], ['vole d une carte','Vole une carte aléatoirement au joueur adverse'],
// ['vole d une carte','Vole une carte aléatoirement au joueur adverse'], ['resurection','Ressucite la dernière pièce perdue'],
// ['resurection','Choisie une pièce capturée pour la ressuciter dans son camp'], // ['carte sans effet',"N'a aucun effet"],
// ['carte sans effet','N a aucun effet'],
// ['défausse','Le joueur adverse défausse une carte de son choix'], // ['défausse','Le joueur adverse défausse une carte de son choix'],
// ['immunité à la capture','Désigne une pièce qui ne pourra pas être capturée au prochain tour'], // ['immunité à la capture','Désigne une pièce qui ne pourra pas être capturée au prochain tour'],
// ['kamikaz','Détruit une de ses pièces, détruisant toutes les pièces adjacentes'], // ['kamikaz','Détruit une de ses pièces, détruisant toutes les pièces adjacentes'],
@ -138,7 +142,7 @@ function buildDefaultDeck(){
// ['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.'], // ['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.'], // ['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','Regarde les trois prochaines cartes de la pioche.'], // ['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.'], // ['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.'], // ['tous les mêmes','Au yeux de l ennemie, toutes les pièces se ressemblent pendant 2 tours.'],
// ['petit pion','Le joueur choisit un pion. À partir du prochain tour, il est promu en reine dès qu il capture un pièce non pion.'], // ['petit pion','Le joueur choisit un pion. À partir du prochain tour, il est promu en reine dès qu il capture un pièce non pion.'],
@ -668,8 +672,13 @@ io.on('connection', (socket) => {
// apply move: remove any piece on target (capture) // apply move: remove any piece on target (capture)
const targetIndex = pieces.findIndex(p => p.square === to); const targetIndex = pieces.findIndex(p => p.square === to);
if(targetIndex >= 0){ if(targetIndex >= 0){
// remove captured piece // remove captured piece and record it for potential resurrection
pieces.splice(targetIndex, 1); const capturedPiece = pieces.splice(targetIndex, 1)[0];
try{
room.captured = room.captured || [];
const originalOwner = (room.players || []).find(p => (p.color && p.color[0]) === capturedPiece.color);
room.captured.push({ id: uuidv4().slice(0,8), piece: capturedPiece, originalOwnerId: (originalOwner && originalOwner.id) || null, capturedBy: senderId, ts: Date.now() });
}catch(_){ /* ignore bookkeeping errors */ }
} }
// move the piece // move the piece
moving.square = to; moving.square = to;
@ -702,9 +711,16 @@ io.on('connection', (socket) => {
const e = room.activeCardEffects[i]; const e = room.activeCardEffects[i];
if(e && e.type === 'mine' && e.square === to){ if(e && e.type === 'mine' && e.square === to){
// detonate for any piece that lands on the mine (owner included) // detonate for any piece that lands on the mine (owner included)
// remove the moving piece from the board // remove the moving piece from the board and record it as captured by the mine owner
const rmIdx = pieces.findIndex(p => p.id === moving.id); const rmIdx = pieces.findIndex(p => p.id === moving.id);
if(rmIdx >= 0){ pieces.splice(rmIdx, 1); } if(rmIdx >= 0){
const capturedPiece = pieces.splice(rmIdx, 1)[0];
try{
room.captured = room.captured || [];
const originalOwner = (room.players || []).find(p => (p.color && p.color[0]) === capturedPiece.color);
room.captured.push({ id: uuidv4().slice(0,8), piece: capturedPiece, originalOwnerId: (originalOwner && originalOwner.id) || null, capturedBy: e.playerId, ts: Date.now() });
}catch(_){ }
}
// consume the mine // consume the mine
try{ room.activeCardEffects.splice(i,1); }catch(_){ } try{ room.activeCardEffects.splice(i,1); }catch(_){ }
// broadcast detonation to the room (informational) // broadcast detonation to the room (informational)
@ -1301,7 +1317,65 @@ io.on('connection', (socket) => {
} }
} }
}catch(e){ console.error('steal-card effect error', e); } }catch(e){ console.error('steal-card effect error', e); }
} }
// resurrection: bring back one of your captured pieces and place it on an empty square
else if((typeof cardId === 'string' && cardId.indexOf('resur') !== -1) || (typeof cardId === 'string' && cardId.indexOf('ressur') !== -1)){
try{
const roomPlayer = room.players.find(p => p.id === senderId);
const playerColorShort = (roomPlayer && roomPlayer.color && roomPlayer.color[0]) || null;
// list captured pieces that originally belonged to this player
const available = (room.captured || []).filter(c => c && c.piece && c.piece.color === playerColorShort);
if(!available || available.length === 0){
played.payload = Object.assign({}, payload, { applied: 'resurrection_failed_no_captured' });
try{ const owner = (room.players || []).find(p => p.id === senderId); if(owner && owner.socketId) io.to(owner.socketId).emit('card:effect:applied', { roomId: room.id, effect: { id: played.id, type: 'resurrection_failed_no_captured', playerId: senderId, ts: Date.now() } }); }catch(_){ }
} else {
// allow client to specify which captured entry to resurrect
const selectedId = payload && (payload.captureId || payload.capturedId || payload.targetCapturedId || payload.selectedCapturedId);
let capturedEntry = null;
if(selectedId){
const idx = (room.captured || []).findIndex(c => c && c.id === selectedId && c.piece && c.piece.color === playerColorShort);
if(idx !== -1) capturedEntry = room.captured[idx];
}
if(!capturedEntry){
// fallback: pick the most recently captured of the player's pieces
capturedEntry = available[available.length - 1];
}
if(!capturedEntry){
played.payload = Object.assign({}, payload, { applied: 'resurrection_failed_no_valid' });
try{ const owner = (room.players || []).find(p => p.id === senderId); if(owner && owner.socketId) io.to(owner.socketId).emit('card:effect:applied', { roomId: room.id, effect: { id: played.id, type: 'resurrection_failed_no_valid', playerId: senderId, ts: Date.now() } }); }catch(_){ }
} else {
// placement square
let target = payload && payload.targetSquare;
if(!target){ try{ target = socket.data && socket.data.lastSelectedSquare; }catch(e){ target = null; } }
const board = room.boardState;
const occupied = (board && board.pieces || []).find(p => p.square === target);
if(!board || !target || occupied){
// invalid placement: consume card but report failure to owner
played.payload = Object.assign({}, payload, { applied: 'resurrection_failed_bad_square', attemptedTo: target });
try{ const owner = (room.players || []).find(p => p.id === senderId); if(owner && owner.socketId) io.to(owner.socketId).emit('card:effect:applied', { roomId: room.id, effect: { id: played.id, type: 'resurrection_failed_bad_square', playerId: senderId, square: target, ts: Date.now() } }); }catch(_){ }
} else {
// remove captured entry from the capture log
for(let i = room.captured.length - 1; i >= 0; i--){ if(room.captured[i] && room.captured[i].id === capturedEntry.id){ room.captured.splice(i,1); break; } }
// create a new piece object and place it
const orig = capturedEntry.piece || {};
const newPiece = Object.assign({}, orig);
newPiece.id = ((playerColorShort === 'w') ? 'w_' : 'b_') + (newPiece.type || 'P') + '_' + uuidv4().slice(0,6);
newPiece.square = target;
newPiece.color = playerColorShort;
if(newPiece.promoted) newPiece.promoted = true;
board.pieces = board.pieces || [];
board.pieces.push(newPiece);
// record effect and broadcast
const effect = { id: played.id, type: 'resurrection', pieceId: newPiece.id, pieceType: newPiece.type, placedAt: target, playerId: senderId, ts: Date.now() };
room.activeCardEffects = room.activeCardEffects || [];
room.activeCardEffects.push(effect);
try{ io.to(room.id).emit('card:effect:applied', { roomId: room.id, effect }); }catch(_){ }
played.payload = Object.assign({}, payload, { applied: 'resurrection', appliedTo: target, resurrectedId: newPiece.id, resurrectedType: newPiece.type });
}
}
}
}catch(e){ console.error('resurrection effect error', e); }
}
// rebondir: grant a one-time bounce ability to a specific piece (targetSquare required in payload) // rebondir: grant a one-time bounce ability to a specific piece (targetSquare required in payload)
else if(cardId === 'rebondir_sur_les_bords' || cardId === 'rebondir' || (typeof cardId === 'string' && cardId.indexOf('rebondir') !== -1)){ else if(cardId === 'rebondir_sur_les_bords' || cardId === 'rebondir' || (typeof cardId === 'string' && cardId.indexOf('rebondir') !== -1)){
try{ try{