Private
Public Access
1
0

fix: Making the scoreBoard clearer

This commit is contained in:
gauvainboiche
2026-03-31 10:04:39 +02:00
parent e38997f154
commit fb63b1008e
4 changed files with 232 additions and 92 deletions

View File

@@ -90,30 +90,57 @@
<!-- Team score display -->
<div class="scoreBoard" id="scoreBoard">
<img src="./graphism/logo_resistance.svg" alt="Resistance" class="team-logo" />
<div class="scoreTeam scoreTeam--blue">
<span class="scoreTeamName">Résistance</span>
<div class="scoreStats">
<div class="scoreStat">
<span class="scoreStatVal scoreValue" id="scoreBlue">0</span>
<span class="scoreStatLabel">Tuiles</span>
<div class="scoreBoardContent">
<div class="scoreBoardRow">
<div class="scoreTeam scoreTeam--blue">
<span class="scoreTeamName">Résistance</span>
<div class="scoreStats">
<div class="scoreStat">
<span class="scoreStatVal scoreValue" id="scoreBlue">0</span>
<span class="scoreStatLabel">Tuiles</span>
</div>
<div class="scoreStat">
<span class="scoreStatVal scoreVP" id="vpBlue">0</span>
<span class="scoreStatLabel">Points</span>
</div>
</div>
</div>
<div class="scoreStat">
<span class="scoreStatVal scoreVP" id="vpBlue">0</span>
<span class="scoreStatLabel">Points</span>
<span class="scoreSep"></span>
<div class="scoreTeam scoreTeam--red">
<span class="scoreTeamName">Premier Ordre</span>
<div class="scoreStats">
<div class="scoreStat">
<span class="scoreStatVal scoreVP" id="vpRed">0</span>
<span class="scoreStatLabel">Points</span>
</div>
<div class="scoreStat">
<span class="scoreStatVal scoreValue" id="scoreRed">0</span>
<span class="scoreStatLabel">Tuiles</span>
</div>
</div>
</div>
</div>
</div>
<span class="scoreSep"></span>
<div class="scoreTeam scoreTeam--red">
<span class="scoreTeamName">Premier Ordre</span>
<div class="scoreStats">
<div class="scoreStat">
<span class="scoreStatVal scoreVP" id="vpRed">0</span>
<span class="scoreStatLabel">Points</span>
<!-- Income summary + cumulative economic score (inside scoreBoard) -->
<div class="scoreBoardEcon">
<div class="econSummaryRow">
<span class="econSummaryTeam econSummaryTeam--blue econSummaryTeam--center">
<span class="econSummaryVal" id="incomeBlue">+0.000/s</span>
</span>
<span class="econSummarySep"></span>
<span class="econSummaryTeam econSummaryTeam--red econSummaryTeam--center">
<span class="econSummaryVal" id="incomeRed">+0.000/s</span>
</span>
</div>
<div class="scoreStat">
<span class="scoreStatVal scoreValue" id="scoreRed">0</span>
<span class="scoreStatLabel">Tuiles</span>
<div class="econSummaryRow econSummaryRow--score">
<span class="econSummaryTeam econSummaryTeam--blue econSummaryTeam--center">
<span class="econScoreVal" id="econScoreBlue">0.000</span>
<span class="econDelta" id="econDeltaBlue"></span>
</span>
<span class="econSummarySep"></span>
<span class="econSummaryTeam econSummaryTeam--red econSummaryTeam--center">
<span class="econDelta" id="econDeltaRed"></span>
<span class="econScoreVal" id="econScoreRed">0.000</span>
</span>
</div>
</div>
</div>
@@ -164,34 +191,6 @@
<pre id="details" class="details details--hidden">Les stats sont vides sauf à cliquer sur une tuile exploitable.</pre>
</details>
<!-- Team income summary + cumulative economic score -->
<div class="econSummary">
<!-- Row 1: income per second -->
<div class="econSummaryRow">
<span class="econSummaryTeam econSummaryTeam--blue">
<span class="econSummaryLabel">Résistance</span>
<span class="econSummaryVal" id="incomeBlue">+0.000/s</span>
</span>
<span class="econSummarySep"></span>
<span class="econSummaryTeam econSummaryTeam--red">
<span class="econSummaryVal" id="incomeRed">+0.000/s</span>
<span class="econSummaryLabel">Premier Ordre</span>
</span>
</div>
<!-- Row 2: cumulative economic score -->
<div class="econSummaryRow econSummaryRow--score">
<span class="econSummaryTeam econSummaryTeam--blue">
<span class="econScoreVal" id="econScoreBlue">0.000</span>
<span class="econDelta" id="econDeltaBlue"></span>
</span>
<span class="econSummarySep"></span>
<span class="econSummaryTeam econSummaryTeam--red">
<span class="econDelta" id="econDeltaRed"></span>
<span class="econScoreVal" id="econScoreRed">0.000</span>
</span>
</div>
</div>
<!-- Resources overview (collapsible) -->
<details class="panel panelCollapsible">
<summary class="panelTitle panelTitleSummary">💰 Ressources</summary>
@@ -200,10 +199,10 @@
</div>
</details>
<!-- Element bonus section -->
<div class="elemBonusSection">
<div class="elemBonusSectionTitle">⚡ Bonus d'exploration</div>
<div class="elemBonusRow">
<!-- Element bonus (collapsible) -->
<details class="panel panelCollapsible">
<summary class="panelTitle panelTitleSummary">⚡ Bonus d'exploration</summary>
<div class="elemBonusTotals">
<span class="elemBonusTeam elemBonusTeam--blue">
<span class="elemBonusLabel">Résistance</span>
<span class="elemBonusVal" id="elemBonusBlue">0.00</span>
@@ -215,12 +214,15 @@
<span class="elemBonusUnit">%</span>
<span class="elemBonusLabel">Premier Ordre</span>
</span>
<span class="elemBonusEffective">
<span class="elemBonusDetailLabel">Recharge :</span>
<span class="elemBonusDetailVal" id="effectiveCooldown"></span>
</span>
</div>
<div class="elemBonusDetail">
<span class="elemBonusDetailLabel">Recharge d'équipe :</span>
<span class="elemBonusDetailVal" id="effectiveCooldown"></span>
<div id="elementBonusTableBody" class="econTableWrap">
<p class="econEmpty">Chargement…</p>
</div>
</div>
</details>
<!-- Admin / options section -->
<div class="infoSection infoSection--options">

View File

@@ -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<string, { discoveredBy: string, hasPlanet: boolean, planet: object|null }>} cells
* @param {object} elementWorth - { common: 1, petrol: 3, ... }
* @returns {{ total: number, byElement: Map<string, number> }}
* 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 `<table class="econTable">
<thead><tr>${headers}</tr></thead>
<tbody>${tableRows}</tbody>
</table>`;
}
// ── Element bonus table for the sidebar ──────────────────────────────────────
/**
* Renders the element bonus breakdown table.
*
* @param {object} elementWorth - { common: 1, petrol: 3, ... }
* @param {Map<string, number>} 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 `<p class="econEmpty">Aucune tuile conquise avec des éléments de production.</p>`;
}
const tableRows = rows
.map(({ label, worth, bonus, bonusStr }) => {
const bonusClass = bonus > 0 ? " econ-income--positive" : "";
return `<tr>
<td class="econ-label">${label}</td>
<td class="econ-worth">${worth}</td>
<td class="econ-income${bonusClass}">${bonusStr}</td>
</tr>`;
})
.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 `<th class="econTh${activeClass}" data-elem-sort-col="${i}">${lbl}<span class="econSortIcon">${indicator}</span></th>`;
})
.join("");
return `<table class="econTable">
<thead><tr>${headers}</tr></thead>
<tbody>${tableRows}</tbody>

View File

@@ -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);

View File

@@ -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;
}