From fb63b1008e925a89bc03dc2a083b24529328b0ee Mon Sep 17 00:00:00 2001 From: gauvainboiche Date: Tue, 31 Mar 2026 10:04:39 +0200 Subject: [PATCH] fix: Making the scoreBoard clearer --- public/index.html | 114 +++++++++++++++++++++--------------------- public/src/economy.js | 104 +++++++++++++++++++++++++++++++++++++- public/src/game.js | 23 ++++++++- public/style.css | 83 +++++++++++++++++------------- 4 files changed, 232 insertions(+), 92 deletions(-) diff --git a/public/index.html b/public/index.html index 72e711c..d4ddb4c 100644 --- a/public/index.html +++ b/public/index.html @@ -90,30 +90,57 @@
-
- Résistance -
-
- 0 - Tuiles +
+
+
+ Résistance +
+
+ 0 + Tuiles +
+
+ 0 + Points +
+
-
- 0 - Points + +
+ Premier Ordre +
+
+ 0 + Points +
+
+ 0 + Tuiles +
+
-
- -
- Premier Ordre -
-
- 0 - Points + +
+
+ + +0.000/s + + + + +0.000/s +
-
- 0 - Tuiles +
+ + 0.000 + + + + + + 0.000 +
@@ -164,34 +191,6 @@
Les stats sont vides sauf à cliquer sur une tuile exploitable.
- -
- -
- - Résistance - +0.000/s - - - - +0.000/s - Premier Ordre - -
- -
- - 0.000 - - - - - - 0.000 - -
-
-
💰 Ressources @@ -200,10 +199,10 @@
- -
-
⚡ Bonus d'exploration
-
+ +
+ ⚡ Bonus d'exploration +
Résistance 0.00 @@ -215,12 +214,15 @@ % Premier Ordre + + Recharge : + +
-
- Recharge d'équipe : - +
+

Chargement…

-
+
diff --git a/public/src/economy.js b/public/src/economy.js index 20b652a..b75f0bb 100644 --- a/public/src/economy.js +++ b/public/src/economy.js @@ -1,6 +1,6 @@ import { resources, elements } from "./planetEconomy.js"; -// ── Sort state ──────────────────────────────────────────────────────────────── +// ── Sort state (resources) ──────────────────────────────────────────────────── /** 0=Ressource, 1=Rareté, 2=Valeur, 3=Revenu/s */ let _sortCol = 3; @@ -15,6 +15,21 @@ export function getEconSort() { return { col: _sortCol, dir: _sortDir }; } +// ── Sort state (elements) ───────────────────────────────────────────────────── + +/** 0=Élément, 1=Valeur/élément, 2=Bonus % */ +let _elemSortCol = 2; +let _elemSortDir = "desc"; + +export function setElemSort(col, dir) { + _elemSortCol = col; + _elemSortDir = dir; +} + +export function getElemSort() { + return { col: _elemSortCol, dir: _elemSortDir }; +} + // ── Label → resource key lookup ─────────────────────────────────────────────── /** Map from French label string → { cat: "common"|"rare", key: string } */ @@ -102,6 +117,35 @@ export function computeTeamElementBonus(team, cells, elementWorth) { return bonus; } +/** + * Compute per-element bonus breakdown for a team. + * + * @param {string} team + * @param {Map} cells + * @param {object} elementWorth - { common: 1, petrol: 3, ... } + * @returns {{ total: number, byElement: Map }} + * byElement keys are French element label strings, values are cumulative bonus % + */ +export function computeTeamElementBonusDetailed(team, cells, elementWorth) { + const byElement = new Map(); + let total = 0; + for (const [, meta] of cells) { + if (meta.discoveredBy !== team) continue; + if (!meta.hasPlanet || !meta.planet) continue; + const { production } = meta.planet; + if (!production) continue; + for (const [elementLabel, pct] of Object.entries(production)) { + const elementKey = ELEMENT_LABEL_TO_KEY[elementLabel] ?? elementLabel; + const worth = elementWorth?.[elementKey] ?? 0; + if (worth === 0) continue; + const bonus = (pct / 100) * worth; + byElement.set(elementLabel, (byElement.get(elementLabel) ?? 0) + bonus); + total += bonus; + } + } + return { total, byElement }; +} + export { elements }; // ── Resource table for the sidebar ─────────────────────────────────────────── @@ -158,6 +202,64 @@ export function renderResourceTable(resourceWorth, teamByResource) { }) .join(""); + return ` + ${headers} + ${tableRows} +
`; +} + +// ── Element bonus table for the sidebar ────────────────────────────────────── + +/** + * Renders the element bonus breakdown table. + * + * @param {object} elementWorth - { common: 1, petrol: 3, ... } + * @param {Map} teamByElement - bonus per element label for current team + * @returns {string} HTML string + */ +export function renderElementBonusTable(elementWorth, teamByElement) { + const rows = []; + + for (const [key, label] of Object.entries(elements)) { + const worth = elementWorth?.[key] ?? 0; + const bonus = teamByElement?.get(label) ?? 0; + const bonusStr = bonus > 0 ? `+${bonus.toFixed(3)}%` : "—"; + rows.push({ label, worth, bonus, bonusStr }); + } + + const mult = _elemSortDir === "asc" ? 1 : -1; + rows.sort((a, b) => { + if (_elemSortCol === 0) return mult * a.label.localeCompare(b.label, "fr"); + if (_elemSortCol === 1) return mult * (a.worth - b.worth); + if (_elemSortCol === 2) return mult * (a.bonus - b.bonus); + return b.bonus - a.bonus || b.worth - a.worth; + }); + + if (rows.every(r => r.bonus === 0)) { + return `

Aucune tuile conquise avec des éléments de production.

`; + } + + const tableRows = rows + .map(({ label, worth, bonus, bonusStr }) => { + const bonusClass = bonus > 0 ? " econ-income--positive" : ""; + return ` + ${label} + ${worth} + ${bonusStr} + `; + }) + .join(""); + + const thLabels = ["Élément", "Val./élément", "Bonus %"]; + const headers = thLabels + .map((lbl, i) => { + const isActive = i === _elemSortCol; + const indicator = isActive ? (_elemSortDir === "asc" ? " ▲" : " ▼") : " ⇅"; + const activeClass = isActive ? " econTh--active" : ""; + return `${lbl}${indicator}`; + }) + .join(""); + return `${headers}${tableRows} diff --git a/public/src/game.js b/public/src/game.js index c1c07e7..ffc022e 100644 --- a/public/src/game.js +++ b/public/src/game.js @@ -1,7 +1,7 @@ import { fnv1a32, hash2u32, mulberry32 } from "./rng.js"; import { formatPlanet, generatePlanet } from "./planetGeneration.js"; import { apiFetchConfig, apiFetchScores, apiFetchGrid, apiRevealCell, apiFetchEconScores, apiTickEconScores, apiFetchElementBonus, apiTickElementBonus, apiFetchDbInfo, apiFetchVictoryPoints } from "./api.js"; -import { computeTeamIncome, computeTeamElementBonus, renderResourceTable, setEconSort, getEconSort } from "./economy.js"; +import { computeTeamIncome, computeTeamElementBonus, computeTeamElementBonusDetailed, renderResourceTable, renderElementBonusTable, setEconSort, getEconSort, setElemSort, getElemSort } from "./economy.js"; // ── Constants ───────────────────────────────────────────────────────────────── @@ -163,6 +163,7 @@ const econScoreBlueEl = document.getElementById("econScoreBlue"); const econScoreRedEl = document.getElementById("econScoreRed"); const econDeltaBlueEl = document.getElementById("econDeltaBlue"); const econDeltaRedEl = document.getElementById("econDeltaRed"); +const elemBonusTableEl = document.getElementById("elementBonusTableBody"); const teamCorner = document.getElementById("teamCorner"); const teamTrack = document.getElementById("teamSegmentedTrack"); const teamBlueBtn = document.getElementById("teamBlue"); @@ -333,6 +334,14 @@ export function updateEconomyDisplay() { if (resourceTableEl) { resourceTableEl.innerHTML = renderResourceTable(worth, teamIncome.byResource); } + + const elemWorth = GAME_CONFIG.elementWorth; + const teamElemBonus = currentTeam === "blue" + ? computeTeamElementBonusDetailed("blue", cells, elemWorth) + : computeTeamElementBonusDetailed("red", cells, elemWorth); + if (elemBonusTableEl) { + elemBonusTableEl.innerHTML = renderElementBonusTable(elemWorth, teamElemBonus.byElement); + } } // ── Economic score ──────────────────────────────────────────────────────────── @@ -718,6 +727,18 @@ resourceTableEl?.addEventListener("click", (ev) => { updateEconomyDisplay(); }); +elemBonusTableEl?.addEventListener("click", (ev) => { + const th = ev.target.closest("th[data-elem-sort-col]"); + if (!th) return; + const col = Number(th.dataset.elemSortCol); + const { col: curCol, dir: curDir } = getElemSort(); + const newDir = col === curCol + ? (curDir === "asc" ? "desc" : "asc") + : (col === 0 ? "asc" : "desc"); + setElemSort(col, newDir); + updateEconomyDisplay(); +}); + canvas.addEventListener("mousemove", (ev) => { lastPointerEvent = ev; refreshCursor(ev); }); canvas.addEventListener("mouseleave", () => { lastPointerEvent = null; canvas.style.cursor = "default"; }); canvas.addEventListener("click", onCanvasClick); diff --git a/public/style.css b/public/style.css index 9599175..cc1d5d4 100644 --- a/public/style.css +++ b/public/style.css @@ -276,8 +276,8 @@ body { .scoreBoard { display: flex; + flex-direction: row; align-items: center; - justify-content: space-between; gap: 8px; padding: 10px 12px; border-radius: 14px; @@ -287,6 +287,29 @@ body { font-variant-numeric: tabular-nums; } +.scoreBoardContent { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; +} + +.scoreBoardRow { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.scoreBoardEcon { + display: flex; + flex-direction: column; + gap: 6px; + padding: 8px 0 0; + border-top: 1px solid rgba(255, 255, 255, 0.08); + margin-top: 8px; +} + .team-logo { width: 54px; height: 54px; @@ -676,15 +699,6 @@ button:hover { /* ── Team income summary ──────────────────────────────────────────────────── */ -.econSummary { - display: flex; - flex-direction: column; - gap: 6px; - padding: 8px 12px; - font-family: "Courier New", Courier, monospace; - font-size: 12px; -} - .econSummaryTeam { display: flex; align-items: center; @@ -700,10 +714,12 @@ button:hover { justify-content: flex-start; } -.econSummaryTeam--blue .econSummaryLabel { color: rgba(90, 200, 255, 0.85); } -.econSummaryTeam--blue .econSummaryVal { color: rgba(90, 200, 255, 1); font-weight: 700; } -.econSummaryTeam--red .econSummaryLabel { color: rgba(220, 75, 85, 0.85); } -.econSummaryTeam--red .econSummaryVal { color: rgba(220, 75, 85, 1); font-weight: 700; } +.econSummaryTeam--blue .econSummaryLabel { color: rgba(90, 200, 255, 0.85); font-size: 12px; } +.econSummaryTeam--blue .econSummaryVal { color: rgba(90, 200, 255, 1); font-weight: 700; font-size: 12px; } +.econSummaryTeam--red .econSummaryLabel { color: rgba(220, 75, 85, 0.85); font-size: 12px; } +.econSummaryTeam--red .econSummaryVal { color: rgba(220, 75, 85, 1); font-weight: 700; font-size: 12px; } + +.econSummaryTeam--center { justify-content: center; } .econSummarySep { opacity: 0.3; @@ -872,20 +888,29 @@ button:hover { /* ── Element bonus section ────────────────────────────────────────────────── */ -.elemBonusSection { - display: flex; - flex-direction: column; - gap: 6px; - padding: 8px 12px; - font-family: "Courier New", Courier, monospace; - font-size: 12px; -} - -.elemBonusRow { +.elemBonusTotals { display: flex; align-items: center; justify-content: center; gap: 0; + padding: 8px 12px 4px; + font-family: "Courier New", Courier, monospace; + font-size: 12px; + flex-wrap: wrap; + row-gap: 4px; +} + +.elemBonusEffective { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + gap: 5px; + border-top: 1px solid rgba(255, 255, 255, 0.06); + padding-top: 5px; + margin-top: 2px; + font-size: 11px; + font-family: "Courier New", Courier, monospace; } .elemBonusTeam { @@ -919,16 +944,6 @@ button:hover { margin: 0 6px; } -.elemBonusDetail { - display: flex; - align-items: center; - gap: 6px; - border-top: 1px solid rgba(255, 255, 255, 0.06); - padding-top: 6px; - margin-top: 2px; - font-size: 11px; -} - .elemBonusDetailLabel { opacity: 0.55; }