diff --git a/README.md b/README.md
index 2d01a5b..dff652e 100644
--- a/README.md
+++ b/README.md
@@ -234,6 +234,15 @@ non affilié au jeu original de Mountyhall SARL.
## Versions
+- **2.4.2** (2026-06-13) — **Fiche du troll harmonisée** entre le solo et le
+ monde partagé : le mode multi adopte le même **panneau à onglets**
+ « Caractéristiques / Effets » que le solo (avec le badge du nombre d'effets
+ actifs), et son onglet Effets utilise désormais le **même rendu**
+ (cartes buff/malus + « Total des modificateurs ») au lieu de la liste
+ simplifiée d'avant. Les deux modes partagent désormais strictement le même
+ code d'affichage (`fmtStatLine`, `renderEffectsPanel`, `switchLeftTab`) ;
+ seule subsiste la différence de fond entre les modes (compteur de **Tour**
+ en solo, compte à rebours **DLA** en temps réel en multi).
- **2.4.1** (2026-06-13) — Fiche du troll du monde partagé : les **bonus
physiques et magiques** de chaque caractéristique (attaque, esquive, dégâts,
régénération) sont désormais **toujours détaillés** (`phy +X · mag +Y`),
diff --git a/index.html b/index.html
index c27b68f..440e937 100644
--- a/index.html
+++ b/index.html
@@ -159,11 +159,18 @@
diff --git a/js/game.js b/js/game.js
index e72ab34..599318d 100644
--- a/js/game.js
+++ b/js/game.js
@@ -4,7 +4,7 @@
"use strict";
-const APP_VERSION = "2.4.1";
+const APP_VERSION = "2.4.2";
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
const START_COMP_PCT = 90;
@@ -1532,10 +1532,12 @@ function showEnd(victory, killer) {
let selectedRace = "Skrim";
let activeLeftTab = "stats";
-function switchLeftTab(tab) {
+// `prefix` = "" pour le solo, "mp-" pour le monde partagé : les deux modes
+// partagent strictement le même panneau à onglets (Caractéristiques / Effets).
+function switchLeftTab(tab, prefix = "") {
activeLeftTab = tab;
- const statsBtn = $("tab-stats"), fxBtn = $("tab-effects");
- const statsPanel = $("panel-tab-stats"), fxPanel = $("panel-tab-effects");
+ const statsBtn = $(prefix + "tab-stats"), fxBtn = $(prefix + "tab-effects");
+ const statsPanel = $(prefix + "panel-tab-stats"), fxPanel = $(prefix + "panel-tab-effects");
if (!statsBtn || !fxBtn) return;
const isStats = tab === "stats";
statsBtn.classList.toggle("active", isStats);
diff --git a/js/mp-client.js b/js/mp-client.js
index d24c2e7..a19f140 100644
--- a/js/mp-client.js
+++ b/js/mp-client.js
@@ -100,6 +100,7 @@ function mpShowJoin(show) {
async function mpEnter() {
document.getElementById("screen-create").classList.add("hidden");
document.getElementById("screen-mp").classList.remove("hidden");
+ switchLeftTab("stats", "mp-"); // ouvrir sur les caractéristiques
MP.seen = new Set();
try {
const saved = mpLoadIdentity();
@@ -292,10 +293,16 @@ function mpRenderPanels(st) {
improve.appendChild(btn);
}
- const fx = document.getElementById("mp-effects");
- fx.innerHTML = you.effects.length
- ? you.effects.map(ef => `${ef.emoji} ${ef.name}
(${ef.turnsLeft} DLA)${ef.modLines.length ? `
${ef.modLines.join(" · ")}
` : ""}
`).join("")
- : 'Aucun bonus ni malus magique actif.
';
+ // Effets : on réutilise tel quel le panneau du solo (renderEffectsPanel +
+ // badge), pour que les deux modes soient strictement identiques.
+ const fxTroll = { potionEffects: you.potionEffects || [], blockCamoTurns: you.blockCamoTurns || 0 };
+ document.getElementById("mp-effects").innerHTML = renderEffectsPanel(fxTroll);
+ const fxBadge = document.getElementById("mp-fx-badge");
+ if (fxBadge) {
+ const n = countActiveEffects(fxTroll);
+ fxBadge.textContent = n;
+ fxBadge.classList.toggle("hidden", n === 0);
+ }
document.getElementById("mp-pa-bar").textContent = "⚡".repeat(you.pa) + "▫️".repeat(Math.max(0, 6 - you.pa));
@@ -362,6 +369,10 @@ if (typeof document !== "undefined") {
document.getElementById("mp-join-create").onclick = () => mpJoinSubmit(false);
document.getElementById("mp-join-login").onclick = () => mpJoinSubmit(true);
+ // Onglets « Caractéristiques / Effets » — même mécanique que le solo
+ document.getElementById("mp-tab-stats")?.addEventListener("click", () => switchLeftTab("stats", "mp-"));
+ document.getElementById("mp-tab-effects")?.addEventListener("click", () => switchLeftTab("effects", "mp-"));
+
document.addEventListener("keydown", e => {
if (document.getElementById("screen-mp").classList.contains("hidden")) return;
const k = e.key.toLowerCase();
diff --git a/mp.js b/mp.js
index 7ebe715..14c33a9 100644
--- a/mp.js
+++ b/mp.js
@@ -810,7 +810,9 @@ function stateFor(world, t, now = Date.now()) {
comp: { pct: t.comp.pct }, sort: { pct: t.sort.pct },
eff: te,
equip: t.equip, bag: t.bag,
- effects: (t.potionEffects || []).map(e => ({ name: e.name, emoji: e.emoji, turnsLeft: e.turnsLeft, modLines: e.modLines || [] })),
+ // effets bruts : le client réutilise renderEffectsPanel() du solo,
+ // qui a besoin des modificateurs chiffrés (total) en plus des modLines.
+ potionEffects: (t.potionEffects || []).map(e => ({ ...e, modLines: e.modLines || [] })),
blockCamoTurns: t.blockCamoTurns,
},
trolls: Object.values(world.trolls)