affichage du décompte pour le brouillard

This commit is contained in:
Didictateur 2025-12-08 13:33:20 +01:00
parent 01234391f1
commit 43a1c0287e
5 changed files with 153 additions and 20 deletions

BIN
assets/img/cards/medusa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

View file

@ -46,6 +46,20 @@
#log { #log {
display: none; display: none;
} }
/* effects feed for user-facing logs (card plays) */
#effectsFeed {
max-height: 140px;
overflow: auto;
padding: 8px;
background: #fff;
border: 1px solid #eee;
border-radius: 6px;
font-size: 14px;
color: #222;
}
#log {
display: none;
}
</style> </style>
</head> </head>
<body> <body>
@ -139,10 +153,14 @@
logs...</pre logs...</pre
> >
</div> </div>
<div style="margin-top: 8px"> <div style="margin-top: 8px; display:flex; gap:8px; align-items:flex-start;">
<div><strong>Activité</strong></div> <div style="flex:1; min-width:0">
<div id="activityFeed" style="margin-top: 8px"> <div><strong>Activité</strong></div>
Aucune activité pour l'instant. <div id="activityFeed" style="margin-top: 8px">Aucune activité pour l'instant.</div>
</div>
<div style="width:200px; min-width:160px">
<div><strong>Effets actifs</strong></div>
<div id="effectsFeed" style="margin-top: 8px">Aucun effet actif pour l'instant.</div>
</div> </div>
</div> </div>
</section> </section>
@ -183,6 +201,54 @@ logs...</pre
const logEl = document.getElementById("log"); const logEl = document.getElementById("log");
const playersEl = document.getElementById("players"); const playersEl = document.getElementById("players");
const effectsFeedEl = document.getElementById("effectsFeed");
// simple client-side registry of active effects we know about (keyed by effect.id)
const activeEffects = {};
function refreshEffectsFeed() {
try {
const dest = document.getElementById("effectsFeed");
if (!dest) return;
const effects = Object.values(activeEffects).filter(
(e) => e && e.type === "brouillard",
);
if (!effects || !effects.length) {
dest.textContent = "Aucun effet actif pour l'instant.";
return;
}
dest.innerHTML = "";
effects.forEach((e) => {
try {
const row = document.createElement("div");
row.style.padding = "4px 6px";
row.style.borderBottom = "1px solid #f3f3f3";
const rem = typeof e.remainingTurns !== "undefined" ? e.remainingTurns : "?";
row.textContent = `${e.title} — ${rem+1} tour(s) restant(s)`;
dest.appendChild(row);
} catch (_) {}
});
} catch (_) {}
}
// Apply a local visual tick when any player plays a card or moves.
// This updates the displayed remainingTurns immediately for UX, but
// does not replace server authority — incoming card:effect:updated or
// room:update will resync the true values.
function applyLocalPlayTick() {
try {
let changed = false;
Object.keys(activeEffects).forEach((id) => {
try {
const e = activeEffects[id];
if (e && typeof e.remainingTurns === "number") {
// decrement visually by one (but never below zero)
e.remainingTurns = Math.max(0, (e.remainingTurns || 0) - 1);
changed = true;
}
} catch (_) {}
});
if (changed) refreshEffectsFeed();
} catch (_) {}
}
const boardEl = document.getElementById("board"); const boardEl = document.getElementById("board");
const myColorEl = document.getElementById("myColor"); const myColorEl = document.getElementById("myColor");
let socket = null; let socket = null;
@ -490,6 +556,29 @@ logs...</pre
removeSpectatorQuitButton(); removeSpectatorQuitButton();
} }
} catch (_) {} } catch (_) {}
// sync activeEffects from server-provided activeCardEffects (update remainingTurns and remove expired)
try {
if (Array.isArray(data.activeCardEffects)) {
const seen = new Set();
data.activeCardEffects.forEach((eff) => {
try {
if (eff && eff.type === "brouillard" && eff.id) {
activeEffects[eff.id] = eff;
seen.add(eff.id);
}
} catch (_) {}
});
// remove stale brouillard entries not present in server list
Object.keys(activeEffects).forEach((id) => {
try {
if (!seen.has(id) && activeEffects[id] && activeEffects[id].type === "brouillard") {
delete activeEffects[id];
}
} catch (_) {}
});
refreshEffectsFeed();
}
} catch (_) {}
try { try {
myMines = data.minesOwn || []; myMines = data.minesOwn || [];
} catch (e) { } catch (e) {
@ -564,10 +653,11 @@ logs...</pre
const actor = const actor =
pid === myPlayerId pid === myPlayerId
? "Vous" ? "Vous"
: playerColors[pid]
? playerColors[pid]
: "Adversaire"; : "Adversaire";
appendActivity(`${actor} : a pioché une carte`); appendActivity(`${actor} : a pioché une carte`);
try {
applyLocalPlayTick();
} catch (_) {}
} catch (_) {} } catch (_) {}
}); });
socket.on("card:played", (data) => { socket.on("card:played", (data) => {
@ -607,8 +697,6 @@ logs...</pre
const actor = const actor =
owner === myPlayerId owner === myPlayerId
? "Vous" ? "Vous"
: playerColors[owner]
? playerColors[owner]
: "Adversaire"; : "Adversaire";
const isHidden = const isHidden =
(cardObj && cardObj.hidden) || (cardObj && cardObj.hidden) ||
@ -618,6 +706,10 @@ logs...</pre
} else { } else {
appendActivity(`${actor} : a joué la carte "${title}"`); appendActivity(`${actor} : a joué la carte "${title}"`);
} }
try {
// Visual tick: decrement displayed counters immediately when any card is played
applyLocalPlayTick();
} catch (_) {}
} catch (_) {} } catch (_) {}
} catch (e) { } catch (e) {
console.warn("card:played handler error", e); console.warn("card:played handler error", e);
@ -701,6 +793,13 @@ logs...</pre
} }
} }
} catch (e) {} } catch (e) {}
// track brouillard effects for the sidebar feed
try {
if (effect.type === "brouillard") {
activeEffects[effect.id] = effect;
refreshEffectsFeed();
}
} catch (_) {}
// If empathie was applied, explicitly request a room refresh as a reliable fallback // If empathie was applied, explicitly request a room refresh as a reliable fallback
try { try {
if (effect.type === "empathie") { if (effect.type === "empathie") {
@ -722,6 +821,21 @@ logs...</pre
} }
}); });
socket.on("card:effect:updated", (data) => {
try {
const eff = data && (data.effect || data);
if (!eff || !eff.id) return;
try {
activeEffects[eff.id] = eff;
} catch (_) {}
try {
refreshEffectsFeed();
} catch (_) {}
} catch (e) {
console.warn("card:effect:updated handler error", e);
}
});
// toucher / medusa removals // toucher / medusa removals
socket.on("card:effect:removed", (data) => { socket.on("card:effect:removed", (data) => {
try { try {
@ -763,6 +877,17 @@ logs...</pre
} }
} catch (_) {} } catch (_) {}
} }
// brouillard removal from sidebar feed
try {
if (type === "brouillard") {
try {
delete activeEffects[effId];
} catch (_) {}
try {
refreshEffectsFeed();
} catch (_) {}
}
} catch (_) {}
if (type === "medusa") { if (type === "medusa") {
try { try {
const sq = const sq =
@ -915,6 +1040,10 @@ logs...</pre
fromBeforeEl.querySelector("img.piece") fromBeforeEl.querySelector("img.piece")
) )
hasPieceFromBefore = true; hasPieceFromBefore = true;
try {
// moving a piece is an in-game action — apply visual tick immediately
applyLocalPlayTick();
} catch (_) {}
} catch (_) {} } catch (_) {}
try { try {
const toBeforeEl = const toBeforeEl =
@ -2231,7 +2360,7 @@ logs...</pre
imgSrc = "/assets/img/cards/inversion.png"; imgSrc = "/assets/img/cards/inversion.png";
if (cid === "invisible") if (cid === "invisible")
imgSrc = "/assets/img/cards/invisible.png"; imgSrc = "/assets/img/cards/invisible.png";
if (cid === "kamikaze") imgSrc = "/assets/img/cards/kamikaz.png"; if (cid === "kamikaze") imgSrc = "/assets/img/cards/kamikaze.png";
if (cid === "melange") imgSrc = "/assets/img/cards/mélange.png"; if (cid === "melange") imgSrc = "/assets/img/cards/mélange.png";
if (cid === "mine") imgSrc = "/assets/img/cards/mine.png"; if (cid === "mine") imgSrc = "/assets/img/cards/mine.png";
if (cid === "promotion") if (cid === "promotion")
@ -2259,6 +2388,8 @@ logs...</pre
if (cid === "tout") imgSrc = "/assets/img/cards/tout.png"; if (cid === "tout") imgSrc = "/assets/img/cards/tout.png";
if (cid === "vole_carte") if (cid === "vole_carte")
imgSrc = "/assets/img/cards/vole_carte.png"; imgSrc = "/assets/img/cards/vole_carte.png";
if (cid === "medusa")
imgSrc = "/assets/img/cards/medusa.png";
if (imgSrc) { if (imgSrc) {
const img = document.createElement("img"); const img = document.createElement("img");

View file

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

View file

@ -613,7 +613,7 @@
if (cid === "invisible") if (cid === "invisible")
imgSrc = "/assets/img/cards/invisible.png"; imgSrc = "/assets/img/cards/invisible.png";
if (cid === "kamikaze") if (cid === "kamikaze")
imgSrc = "/assets/img/cards/kamikaz.png"; imgSrc = "/assets/img/cards/kamikaze.png";
if (cid === "melange") if (cid === "melange")
imgSrc = "/assets/img/cards/mélange.png"; imgSrc = "/assets/img/cards/mélange.png";
if (cid === "mine") imgSrc = "/assets/img/cards/mine.png"; if (cid === "mine") imgSrc = "/assets/img/cards/mine.png";
@ -647,6 +647,8 @@
if (cid === "tout") imgSrc = "/assets/img/cards/tout.png"; if (cid === "tout") imgSrc = "/assets/img/cards/tout.png";
if (cid === "vole_carte") if (cid === "vole_carte")
imgSrc = "/assets/img/cards/vole_carte.png"; imgSrc = "/assets/img/cards/vole_carte.png";
if (cid === "medusa")
imgSrc = "/assets/img/cards/medusa.png";
if (imgSrc) { if (imgSrc) {
const img = document.createElement("img"); const img = document.createElement("img");

View file

@ -52,7 +52,6 @@ function sendRoomUpdate(room) {
previousDraws: room._playerDrewPrev || {}, previousDraws: room._playerDrewPrev || {},
playerDrew: room._playerDrew || {}, playerDrew: room._playerDrew || {},
}; };
const handCounts = {}; const handCounts = {};
(room.players || []).forEach((p) => { (room.players || []).forEach((p) => {
handCounts[p.id] = handCounts[p.id] =
@ -440,7 +439,7 @@ function checkAndHandleVictory(room) {
function buildDefaultDeck() { function buildDefaultDeck() {
const cards = [ const cards = [
[ [
'médusa', 'Médusa',
'La pièce désignée ne peut plus bouger pendant 4 tours', 'La pièce désignée ne peut plus bouger pendant 4 tours',
'medusa', 'medusa',
], ],
@ -1841,11 +1840,12 @@ io.on("connection", (socket) => {
// Update brouillard play counts // Update brouillard play counts
try { try {
room.activeCardEffects = room.activeCardEffects || [];
for (let ei = room.activeCardEffects.length - 1; ei >= 0; ei--) { for (let ei = room.activeCardEffects.length - 1; ei >= 0; ei--) {
const ev = room.activeCardEffects[ei]; const ev = room.activeCardEffects[ei];
if (!ev || ev.type !== "brouillard") continue; if (!ev || ev.type !== "brouillard") continue;
try { try {
// only consider play-count based expiry if the effect explicitly enabled it
if (!ev.countByPlay) continue;
ev.playCounts = ev.playCounts || {}; ev.playCounts = ev.playCounts || {};
ev.playCounts[senderId] = (ev.playCounts[senderId] || 0) + 1; ev.playCounts[senderId] = (ev.playCounts[senderId] || 0) + 1;
try { try {
@ -4403,20 +4403,18 @@ io.on("connection", (socket) => {
} }
} }
room.activeCardEffects = room.activeCardEffects || []; room.activeCardEffects = room.activeCardEffects || [];
const playCounts = {}; // legacy: optionally expire brouillard by play-counts. Default: disabled.
try { const countByPlay = !!(payload && payload.countByPlay);
(room.players || []).forEach((pl) => {
if (pl && pl.id) playCounts[pl.id] = 0;
});
} catch (_) {}
const effect = { const effect = {
id: played.id, id: played.id,
title: "Brouillard de guerre",
type: "brouillard", type: "brouillard",
playerId: targetPlayer.id, playerId: targetPlayer.id,
ts: Date.now(), ts: Date.now(),
remainingTurns: (payload && payload.turns) || 6, remainingTurns: (payload && payload.turns) || 6,
decrementOn: "both",
veiledSquares: all, veiledSquares: all,
playCounts, countByPlay,
}; };
room.activeCardEffects.push(effect); room.activeCardEffects.push(effect);
try { try {