carte travesti

This commit is contained in:
Didictateur 2026-01-03 16:08:54 +01:00
parent 9b5ce433e0
commit 3b5f37403c
5 changed files with 212 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

View file

@ -386,6 +386,7 @@ logs...</pre
if (id.indexOf("medusa") !== -1) return "enemy";
if (id.indexOf("parrure") !== -1) return "enemy_queen";
if (id.indexOf("tout") !== -1) return "piece";
if (id.indexOf("travesti") !== -1) return "piece";
if (id.indexOf("kamikaz") !== -1) return "owned";
if (id.indexOf("invisible") !== -1) return "owned";
if (id.indexOf("rebond") !== -1) return "owned";
@ -2437,6 +2438,8 @@ logs...</pre
imgSrc = "/assets/img/cards/medusa.png";
if (cid === "cachotier")
imgSrc = "/assets/img/cards/cachotier.png";
if (cid === "travesti")
imgSrc = "/assets/img/cards/travesti.png";
if (imgSrc) {
const img = document.createElement("img");

View file

@ -236,6 +236,8 @@
imgSrc = "/assets/img/cards/medusa.png";
if (cid === "cachotier")
imgSrc = "/assets/img/cards/cachotier.png";
if (cid === "travesti")
imgSrc = "/assets/img/cards/travesti.png";
if (imgSrc) {
const img = document.createElement("img");

View file

@ -651,6 +651,8 @@
imgSrc = "/assets/img/cards/medusa.png";
if (cid === "cachotier")
imgSrc = "/assets/img/cards/cachotier.png";
if (cid === "travesti")
imgSrc = "/assets/img/cards/travesti.png";
if (imgSrc) {
const img = document.createElement("img");

207
server.js
View file

@ -253,6 +253,61 @@ function recordPlayerDrewPrev(room, playerId) {
} catch (_) {}
}
// Helper: apply travesti effect at the start of each turn
// Changes the type of affected pieces randomly to P, N, B, R, or Q
function applyTraverstiEffects(room) {
try {
if (!room || !room.boardState) return;
const board = room.boardState;
const pieces = board.pieces || [];
const effects = room.activeCardEffects || [];
const travestiEffects = effects.filter((e) => e && e.type === "travesti");
if (!travestiEffects.length) return;
const possibleTypes = ["P", "N", "B", "R", "Q"];
travestiEffects.forEach((eff) => {
const piece = pieces.find((p) => p.id === eff.pieceId);
if (!piece) {
// Piece was captured, remove the effect
try {
room.activeCardEffects = effects.filter((e) => e !== eff);
io.to(room.id).emit("card:effect:removed", {
roomId: room.id,
effectId: eff.id,
type: "travesti",
playerId: eff.playerId,
});
} catch (_) {}
return;
}
// Choose a random type different from current (if possible)
const currentType = piece.type;
const availableTypes = possibleTypes.filter((t) => t !== currentType);
const newType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
piece.type = newType;
// Update the effect to track the current type
eff.currentType = newType;
try {
io.to(room.id).emit("card:effect:updated", {
roomId: room.id,
effect: eff,
pieceChanged: { pieceId: piece.id, newType, square: piece.square },
});
} catch (_) {}
});
// Bump version so clients pick up the change
try {
board.version = (board.version || 0) + 1;
} catch (_) {}
} catch (e) {
console.error("applyTraverstiEffects error", e);
}
}
// Helper: at the start of a player's turn, either perform an automatic draw
// if room.autoDraw is enabled, or simply broadcast the room state so clients
// can update UI. This centralizes the auto-draw toggle behavior.
@ -409,6 +464,10 @@ function endTurnAfterCard(room, senderId) {
(p) => (p.color && p.color[0]) === nextColor,
);
if (nextPlayer) {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} else {
sendRoomUpdate(room);
@ -659,17 +718,21 @@ function buildDefaultDeck() {
"cachotier"
],
[
"Travesti",
"La pièce désignée change aléatoirement de type à chaque tour.",
"travesti"
]
// facile et intéresssant
// ["intrication quantique","Deux pièces sont intriquées. Quand l'une bouge, l'autre bouge de la même manière."],
// ['kurby','Choisis une pièce. À sa prochaine capture, récupère tous les mouvements de la pièce capturée.'],
// ['défausse','Le joueur adverse défausse une carte de son choix'],
// ['glue','Toutes les pièces autour de la pièce désignée ne peuvent pas bouger tant que cette dernière ne bouge pas'],
// ['immunité à la capture','Désigne une pièce qui ne pourra pas être capturée au prochain tour'],
// ['marécage','Pendant X tours, toutes les pièces ne peuvent se déplacer que comme un roi'],
// ['tricherie','Choisis une carte de la pioche parmis trois'],
// ['trêve','Aucun capture ne peut avoir lieu pendant 4 tours'],
// ['traversti','La pièce désignée change aléatoirement de type à chaque tour']
// ['pièce berserk','Une pièce choisie doit capturer dans les trois prochains tours, sinon elle est capturée'],
// moyen facile mais intéressant
@ -2095,6 +2158,10 @@ io.on("connection", (socket) => {
(p) => (p.color && p.color[0]) === nextColor,
);
if (nextPlayer) {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} else {
sendRoomUpdate(room);
@ -2552,6 +2619,10 @@ io.on("connection", (socket) => {
);
if (nextPlayer) {
try {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} catch (_) {
sendRoomUpdate(room);
@ -2667,6 +2738,8 @@ io.on("connection", (socket) => {
typeof cardId === "string" && cardId.indexOf("folie") !== -1;
const isFort =
typeof cardId === "string" && cardId.indexOf("fortification") !== -1;
const isTraversti =
typeof cardId === "string" && cardId.indexOf("travesti") !== -1;
const isTargetCard = isRebond || isAdoub || isFolie || isFort;
if (isTargetCard) {
const board = room.boardState;
@ -2702,6 +2775,40 @@ io.on("connection", (socket) => {
payload.targetSquare = targetCandidate;
played.payload = Object.assign({}, payload);
}
// travesti: any piece except king
if (isTraversti) {
const board = room.boardState;
let targetCandidate = payload && payload.targetSquare;
if (!targetCandidate) {
try {
targetCandidate = socket.data && socket.data.lastSelectedSquare;
} catch (e) {
targetCandidate = null;
}
}
const targetPiece = ((board && board.pieces) || []).find(
(p) => p.square === targetCandidate,
);
if (
!board ||
!targetCandidate ||
!targetPiece ||
targetPiece.type === "K"
) {
return (
cb &&
cb({
error: "no valid target",
message: targetPiece && targetPiece.type === "K"
? "Vous ne pouvez pas cibler un roi."
: "Sélectionnez une pièce à affecter.",
})
);
}
payload = payload || {};
payload.targetSquare = targetCandidate;
played.payload = Object.assign({}, payload);
}
} catch (e) {
console.error("target card pre-check error", e);
}
@ -3816,6 +3923,10 @@ io.on("connection", (socket) => {
);
if (nextPlayer) {
try {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} catch (_) {}
}
@ -3995,6 +4106,10 @@ io.on("connection", (socket) => {
);
if (nextPlayer) {
try {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} catch (_) {}
}
@ -4739,6 +4854,10 @@ io.on("connection", (socket) => {
);
if (nextPlayer) {
try {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} catch (_) {}
}
@ -5113,6 +5232,86 @@ io.on("connection", (socket) => {
console.error("cachotier effect error", e);
}
}
// travesti
else if (cardId === "travesti") {
try {
const board = room.boardState;
let target = payload && payload.targetSquare;
if (!target) {
try {
target = socket.data && socket.data.lastSelectedSquare;
} catch (e) {
target = null;
}
}
const targetPiece = ((board && board.pieces) || []).find(
(p) => p.square === target,
);
// Must select a piece that is not a king
if (
!board ||
!target ||
!targetPiece ||
targetPiece.type === "K"
) {
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 target",
message:
targetPiece && targetPiece.type === "K"
? "Vous ne pouvez pas cibler un roi."
: "Aucune cible valide n'a été sélectionnée.",
})
);
}
room.activeCardEffects = room.activeCardEffects || [];
const effect = {
id: played.id,
type: "travesti",
pieceId: targetPiece.id,
pieceSquare: target,
originalType: targetPiece.type,
currentType: targetPiece.type,
playerId: senderId,
targetColor: targetPiece.color,
ts: Date.now(),
};
room.activeCardEffects.push(effect);
try {
io.to(room.id).emit("card:effect:applied", {
roomId: room.id,
effect,
});
} catch (_) {}
played.payload = Object.assign({}, payload, {
applied: "travesti",
appliedTo: target,
pieceId: targetPiece.id,
});
} catch (e) {
console.error("travesti effect error", e);
}
}
} catch (e) {
console.error("card:play effect error", e);
}
@ -5269,6 +5468,10 @@ io.on("connection", (socket) => {
(p) => (p.color && p.color[0]) === nextColor,
);
if (nextPlayer) {
// Apply travesti effect at the start of the new turn
try {
applyTraverstiEffects(room);
} catch (_) {}
maybeDrawAtTurnStart(room, nextPlayer.id);
} else {
sendRoomUpdate(room);