affichage du décompte pour le brouillard
This commit is contained in:
parent
01234391f1
commit
43a1c0287e
5 changed files with 153 additions and 20 deletions
BIN
assets/img/cards/medusa.png
Normal file
BIN
assets/img/cards/medusa.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
149
public/game.html
149
public/game.html
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
18
server.js
18
server.js
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue