refacto: Changing display of revenues per planet by default + fixing WS for revenues

This commit is contained in:
gauvainboiche
2026-04-02 15:10:51 +02:00
parent a746662db4
commit 5ce2ae6c98
7 changed files with 417 additions and 97 deletions
+23 -7
View File
@@ -191,9 +191,16 @@
</details>
<!-- Resources overview (collapsible) -->
<details class="panel panelCollapsible">
<details class="panel panelCollapsible" id="resourcesDetails">
<summary class="panelTitle panelTitleSummary">💰 Ressources</summary>
<div id="resourceTableBody" class="econTableWrap">
<div class="resTabs">
<button type="button" class="resTabBtn resTabBtn--active" data-res-tab="planet">Par planète</button>
<button type="button" class="resTabBtn" data-res-tab="type">Par type</button>
</div>
<div id="resourcePlanetTableBody" class="econTableWrap">
<p class="econEmpty">Chargement…</p>
</div>
<div id="resourceTableBody" class="econTableWrap hidden">
<p class="econEmpty">Chargement…</p>
</div>
</details>
@@ -267,6 +274,20 @@
</div>
</details>
<!-- Players list -->
<details class="panel panelCollapsible">
<summary class="panelTitle panelTitleSummary">👥 Joueurs actifs</summary>
<div class="playerListPanel">
<div class="playerListFilter">
<input type="text" id="playerListSearch" class="playerListSearchInput" placeholder="Filtrer par nom…" autocomplete="off" />
<button type="button" id="playerListSortBtn" class="playerListSortBtn" title="Trier">AZ ↑</button>
</div>
<div id="playerListTableWrap" class="playerListTableWrap">
<p class="econEmpty">Chargement…</p>
</div>
</div>
</details>
<!-- Credits -->
<details class="panel panelCollapsible">
<summary class="panelTitle panelTitleSummary">🏷️ Crédits</summary>
@@ -287,11 +308,6 @@
</aside>
<!-- Player list popup (shown on click of joueur count) -->
<div id="playerListPopup" class="playerListPopup hidden" role="tooltip">
<div id="playerListContent"></div>
</div>
<!-- ── Galaxy (square, 1000×1000, fixed ratio) ────────────────────────── -->
<main class="galaxyMain">
<!-- Mobile burger button -->
+91 -1
View File
@@ -30,6 +30,21 @@ export function getElemSort() {
return { col: _elemSortCol, dir: _elemSortDir };
}
// ── Sort state (per-planet resources) ────────────────────────────────────────
/** 0=Planète, 1=Revenu/s */
let _planetSortCol = 0;
let _planetSortDir = "asc";
export function setPlanetSort(col, dir) {
_planetSortCol = col;
_planetSortDir = dir;
}
export function getPlanetSort() {
return { col: _planetSortCol, dir: _planetSortDir };
}
// ── Label → resource key lookup ───────────────────────────────────────────────
/** Map from French label string → { cat: "common"|"rare", key: string } */
@@ -79,6 +94,34 @@ export function computeTeamIncome(team, cells, resourceWorth) {
return { total, byResource };
}
/**
* Compute income per second per planet for a team.
*
* @param {string} team - "blue" or "red"
* @param {Map<string, { controlledBy: string|null, hasPlanet: boolean, planet: object|null }>} cells
* @param {object} resourceWorth - { common: {…}, rare: {…} }
* @returns {Array<{ name: string, income: number }>}
*/
export function computeTeamIncomeByPlanet(team, cells, resourceWorth) {
const rows = [];
for (const [, meta] of cells) {
if (meta.controlledBy !== team) continue;
if (!meta.hasPlanet || !meta.planet) continue;
const { name, naturalResources } = meta.planet;
if (!naturalResources) continue;
let income = 0;
for (const [label, pct] of Object.entries(naturalResources)) {
const info = LABEL_TO_RESOURCE.get(label);
if (!info) continue;
const worth = resourceWorth?.[info.cat]?.[info.key] ?? 0;
if (worth === 0) continue;
income += (pct / 100) * worth;
}
if (income > 0) rows.push({ name: name ?? "?", income });
}
return rows;
}
// ── Element bonus calculation ─────────────────────────────────────────────────
/**
@@ -148,6 +191,51 @@ export function computeTeamElementBonusDetailed(team, cells, elementWorth) {
export { elements };
// ── Per-planet resource table for the sidebar ─────────────────────────────────
/**
* Renders the per-planet income table.
*
* @param {Array<{ name: string, income: number }>} rows - output of computeTeamIncomeByPlanet
* @returns {string} HTML string
*/
export function renderResourceByPlanetTable(rows) {
if (!rows || rows.length === 0) {
return `<p class="econEmpty">Aucune planète sous contrôle de votre équipe.</p>`;
}
const sorted = [...rows];
const mult = _planetSortDir === "asc" ? 1 : -1;
sorted.sort((a, b) => {
if (_planetSortCol === 0) return mult * a.name.localeCompare(b.name, "fr");
return mult * (a.income - b.income);
});
const tableRows = sorted
.map(({ name, income }) =>
`<tr>
<td class="econ-label">${name}</td>
<td class="econ-income econ-income--positive">+${income.toFixed(3)}/s</td>
</tr>`
)
.join("");
const thLabels = ["Planète", "Revenu/s"];
const headers = thLabels
.map((lbl, i) => {
const isActive = i === _planetSortCol;
const indicator = isActive ? (_planetSortDir === "asc" ? " ▲" : " ▼") : " ⇅";
const activeClass = isActive ? " econTh--active" : "";
return `<th class="econTh${activeClass}" data-planet-sort-col="${i}">${lbl}<span class="econSortIcon">${indicator}</span></th>`;
})
.join("");
return `<table class="econTable">
<thead><tr>${headers}</tr></thead>
<tbody>${tableRows}</tbody>
</table>`;
}
// ── Resource table for the sidebar ───────────────────────────────────────────
/**
@@ -276,7 +364,9 @@ for (const [, subgroup] of Object.entries(population)) {
}
}
/** Sort state for military table: 0=Type, 1=% Mil., 2=Soldats */
// ── Sort state (military) ─────────────────────────────────────────────────────
/** 0=Type, 1=% Mil., 2=Soldats */
let _milSortCol = 2;
let _milSortDir = "desc";
+124 -52
View File
@@ -1,7 +1,7 @@
import { fnv1a32, hash2u32, mulberry32 } from "./rng.js";
import { formatPlanet, generatePlanet } from "./planetGeneration.js";
import { apiFetchConfig, apiFetchGrid, apiRevealCell, apiFetchEconScores, apiTickEconScores, apiFetchElementBonus, apiTickElementBonus, apiFetchDbInfo, apiFetchVictoryPoints, apiFetchActivePlayers, apiFetchActivePlayerNames, apiFetchMilitaryDeductions, apiMilitaryAttack, apiCaptureCell } from "./api.js";
import { computeTeamIncome, computeTeamElementBonus, computeTeamElementBonusDetailed, renderResourceTable, renderElementBonusTable, setEconSort, getEconSort, setElemSort, getElemSort, computeTeamMilitaryDetailed, renderMilitaryTable, setMilSort, getMilSort } from "./economy.js";
import { computeTeamIncome, computeTeamElementBonus, computeTeamElementBonusDetailed, renderResourceTable, renderElementBonusTable, setEconSort, getEconSort, setElemSort, getElemSort, computeTeamMilitaryDetailed, renderMilitaryTable, setMilSort, getMilSort, computeTeamIncomeByPlanet, renderResourceByPlanetTable, setPlanetSort, getPlanetSort } from "./economy.js";
// ── Constants ─────────────────────────────────────────────────────────────────
@@ -214,6 +214,9 @@ const effectiveCooldownEl = document.getElementById("effectiveCooldown");
const incomeBlueEl = document.getElementById("incomeBlue");
const incomeRedEl = document.getElementById("incomeRed");
const resourceTableEl = document.getElementById("resourceTableBody");
const resourcePlanetTableEl = document.getElementById("resourcePlanetTableBody");
/** @type {'planet'|'type'} */
let _resActiveTab = "planet";
const econScoreBlueEl = document.getElementById("econScoreBlue");
const econScoreRedEl = document.getElementById("econScoreRed");
const econDeltaBlueEl = document.getElementById("econDeltaBlue");
@@ -234,8 +237,9 @@ const captureModalYesEl = document.getElementById("captureModalYes");
const captureModalNoEl = document.getElementById("captureModalNo");
const teamQuotaEl = document.getElementById("teamActionsRemaining");
const captorInfoEl = document.getElementById("captorInfo");
const playerListPopupEl = document.getElementById("playerListPopup");
const playerListContentEl = document.getElementById("playerListContent");
const playerListTableWrapEl = document.getElementById("playerListTableWrap");
const playerListSearchEl = document.getElementById("playerListSearch");
const playerListSortBtnEl = document.getElementById("playerListSortBtn");
const mapAnimEl = document.getElementById("mapAnim");
// ── Cell helpers ──────────────────────────────────────────────────────────────
@@ -324,6 +328,13 @@ export async function fetchAndApplyActivePlayers() {
} catch { /* ignore */ }
}
export async function loadPlayerNames() {
try {
const names = await apiFetchActivePlayerNames();
applyPlayerNamesUpdate(names);
} catch { /* ignore */ }
}
function setVictoryPointsDisplay(blue, red) {
if (vpBlueEl) vpBlueEl.textContent = String(blue ?? 0);
if (vpRedEl) vpRedEl.textContent = String(red ?? 0);
@@ -335,64 +346,70 @@ function setActivePlayersDisplay(blue, red) {
if (activeCountRedEl) activeCountRedEl.textContent = fmt(red ?? 0);
}
// ── Player list popup (click on joueur count) ─────────────────────────────────
// ── Player list panel ─────────────────────────────────────────────────────────
function closePlayerListPopup() {
if (playerListPopupEl) playerListPopupEl.classList.add("hidden");
/** Last known player names from the server, sorted ascending. */
let playerNamesCache = { blue: [], red: [] };
/** Current sort direction: "asc" or "desc". */
let playerListSortDir = "asc";
function renderPlayerListTable() {
if (!playerListTableWrapEl) return;
const filter = playerListSearchEl?.value.trim().toLowerCase() ?? "";
const blue = playerNamesCache.blue.filter(n => n.toLowerCase().includes(filter));
const red = playerNamesCache.red.filter(n => n.toLowerCase().includes(filter));
if (playerListSortDir === "desc") {
blue.reverse();
red.reverse();
}
async function openPlayerListPopup(anchorEl, team) {
if (!playerListPopupEl || !playerListContentEl) return;
try {
const names = await apiFetchActivePlayerNames();
const list = names[team] ?? [];
if (!list.length) {
playerListContentEl.innerHTML = `<span class="playerListEmpty">Aucun joueur actif</span>`;
} else {
const teamClass = `playerListName--${team}`;
playerListContentEl.innerHTML =
list.map(u => `<div class="${escHtml(teamClass)}">${escHtml(u)}</div>`).join("");
}
// Position popup below the anchor
const rect = anchorEl.getBoundingClientRect();
const parentRect = anchorEl.closest(".infoColumn, aside")?.getBoundingClientRect() ?? { left: 0, top: 0 };
playerListPopupEl.style.left = `${rect.left - parentRect.left}px`;
playerListPopupEl.style.top = `${rect.bottom - parentRect.top + 4}px`;
playerListPopupEl.classList.remove("hidden");
playerListPopupEl.dataset.team = team;
} catch { /* ignore */ }
const maxRows = Math.max(blue.length, red.length);
if (maxRows === 0) {
playerListTableWrapEl.innerHTML = `<p class="econEmpty">Aucun joueur actif</p>`;
return;
}
if (activeCountBlueEl) {
activeCountBlueEl.style.cursor = "pointer";
activeCountBlueEl.addEventListener("click", (ev) => {
ev.stopPropagation();
if (playerListPopupEl && !playerListPopupEl.classList.contains("hidden") && playerListPopupEl.dataset.team === "blue") {
closePlayerListPopup();
} else {
openPlayerListPopup(activeCountBlueEl, "blue");
const rows = Array.from({ length: maxRows }, (_, i) => {
const b = blue[i] ? `<td class="playerListCell--blue">${escHtml(blue[i])}</td>` : `<td></td>`;
const r = red[i] ? `<td class="playerListCell--red">${escHtml(red[i])}</td>` : `<td></td>`;
return `<tr>${b}${r}</tr>`;
}).join("");
playerListTableWrapEl.innerHTML = `
<table class="playerListTable">
<thead>
<tr>
<th class="playerListTh--blue">Résistance (${blue.length})</th>
<th class="playerListTh--red">Premier Ordre (${red.length})</th>
</tr>
</thead>
<tbody>${rows}</tbody>
</table>`;
}
export function applyPlayerNamesUpdate(names) {
if (!names || typeof names !== "object") return;
// Server sends pre-sorted asc; store as-is and sort desc lazily when rendering
playerNamesCache = {
blue: Array.isArray(names.blue) ? [...names.blue] : [],
red: Array.isArray(names.red) ? [...names.red] : [],
};
renderPlayerListTable();
}
if (playerListSearchEl) {
playerListSearchEl.addEventListener("input", renderPlayerListTable);
}
if (playerListSortBtnEl) {
playerListSortBtnEl.addEventListener("click", () => {
playerListSortDir = playerListSortDir === "asc" ? "desc" : "asc";
playerListSortBtnEl.textContent = playerListSortDir === "asc" ? "AZ ↑" : "ZA ↓";
renderPlayerListTable();
});
}
if (activeCountRedEl) {
activeCountRedEl.style.cursor = "pointer";
activeCountRedEl.addEventListener("click", (ev) => {
ev.stopPropagation();
if (playerListPopupEl && !playerListPopupEl.classList.contains("hidden") && playerListPopupEl.dataset.team === "red") {
closePlayerListPopup();
} else {
openPlayerListPopup(activeCountRedEl, "red");
}
});
}
document.addEventListener("click", (ev) => {
if (playerListPopupEl && !playerListPopupEl.contains(ev.target)) {
closePlayerListPopup();
}
});
// ── Element bonus ─────────────────────────────────────────────────────────────
let elemBonusBlue = 0;
@@ -482,6 +499,10 @@ export function updateEconomyDisplay() {
if (resourceTableEl) {
resourceTableEl.innerHTML = renderResourceTable(worth, teamIncome.byResource);
}
if (resourcePlanetTableEl) {
const planetRows = computeTeamIncomeByPlanet(currentTeam, cells, worth);
resourcePlanetTableEl.innerHTML = renderResourceByPlanetTable(planetRows);
}
const elemWorth = GAME_CONFIG.elementWorth;
const teamElemBonus = currentTeam === "blue"
@@ -580,6 +601,10 @@ export function applyRealtimeSnapshot(snapshot) {
setActivePlayersDisplay(snapshot.activePlayers.blue ?? 0, snapshot.activePlayers.red ?? 0);
}
if (snapshot.playerNames && typeof snapshot.playerNames === "object") {
applyPlayerNamesUpdate(snapshot.playerNames);
}
if (snapshot.victoryPoints && typeof snapshot.victoryPoints === "object") {
setVictoryPointsDisplay(snapshot.victoryPoints.blue ?? 0, snapshot.victoryPoints.red ?? 0);
}
@@ -587,6 +612,20 @@ export function applyRealtimeSnapshot(snapshot) {
if (shouldUpdateEconomy) {
updateEconomyDisplay();
}
// Override income and military displays with authoritative server values,
// which reflect ALL team cells (not just locally-visible ones).
if (snapshot.incomePerSecond && typeof snapshot.incomePerSecond === "object") {
if (incomeBlueEl) incomeBlueEl.textContent = `+${Number(snapshot.incomePerSecond.blue ?? 0).toFixed(3)}/s`;
if (incomeRedEl) incomeRedEl.textContent = `+${Number(snapshot.incomePerSecond.red ?? 0).toFixed(3)}/s`;
}
if (snapshot.militaryPowerGross && typeof snapshot.militaryPowerGross === "object") {
const blueMilNet = Number(snapshot.militaryPowerGross.blue ?? 0) - milDeductBlue;
const redMilNet = Number(snapshot.militaryPowerGross.red ?? 0) - milDeductRed;
if (milTotalBlueEl) milTotalBlueEl.textContent = (blueMilNet * 1000).toFixed(1);
if (milTotalRedEl) milTotalRedEl.textContent = (redMilNet * 1000).toFixed(1);
}
}
/** Trigger the delta fade animation on an element. */
@@ -1214,6 +1253,39 @@ resourceTableEl?.addEventListener("click", (ev) => {
updateEconomyDisplay();
});
// ── Per-planet resource table sort (delegated, set up once) ──────────────────
resourcePlanetTableEl?.addEventListener("click", (ev) => {
const th = ev.target.closest("th[data-planet-sort-col]");
if (!th) return;
const col = Number(th.dataset.planetSortCol);
const { col: curCol, dir: curDir } = getPlanetSort();
const newDir = col === curCol
? (curDir === "asc" ? "desc" : "asc")
: (col === 0 ? "asc" : "desc");
setPlanetSort(col, newDir);
updateEconomyDisplay();
});
// ── Resource section tab switching ────────────────────────────────────────────
document.getElementById("resourcesDetails")?.addEventListener("click", (ev) => {
const btn = ev.target.closest(".resTabBtn[data-res-tab]");
if (!btn) return;
const tab = btn.dataset.resTab;
if (tab === _resActiveTab) return;
_resActiveTab = tab;
// Update button active state
document.querySelectorAll(".resTabBtn[data-res-tab]").forEach(b => {
b.classList.toggle("resTabBtn--active", b.dataset.resTab === tab);
});
// Show/hide panes
const planetPane = document.getElementById("resourcePlanetTableBody");
const typePane = document.getElementById("resourceTableBody");
if (planetPane) planetPane.classList.toggle("hidden", tab !== "planet");
if (typePane) typePane.classList.toggle("hidden", tab !== "type");
});
elemBonusTableEl?.addEventListener("click", (ev) => {
const th = ev.target.closest("th[data-elem-sort-col]");
if (!th) return;
+2
View File
@@ -5,6 +5,7 @@ import {
fetchConfig,
fetchGridForSeed,
fetchAndApplyActivePlayers,
loadPlayerNames,
updateEconomyDisplay,
loadEconScores,
loadVictoryPoints,
@@ -83,6 +84,7 @@ function scheduleScorePoll() {
clearTimeout(scorePollTimer);
scorePollTimer = window.setTimeout(async () => {
await fetchAndApplyActivePlayers();
await loadPlayerNames();
await loadEconScores();
await loadElementBonus();
await loadMilitaryDeductions();
+127 -31
View File
@@ -1,5 +1,7 @@
/* ── Reset & base ─────────────────────────────────────────────────────────── */
.hidden { display: none !important; }
*,
*::before,
*::after {
@@ -772,6 +774,41 @@ button:hover {
animation: econDeltaFade 3s ease forwards;
}
/* ── Resource section tabs ────────────────────────────────────────────────── */
.resTabs {
display: flex;
gap: 2px;
padding: 6px 8px 0;
}
.resTabBtn {
flex: 1;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 4px 4px 0 0;
color: rgba(233, 238, 246, 0.55);
font-family: "Courier New", Courier, monospace;
font-size: 11px;
font-weight: 600;
cursor: pointer;
letter-spacing: 0.03em;
transition: color 0.15s, background 0.15s, border-color 0.15s;
text-transform: uppercase;
}
.resTabBtn:hover {
color: rgba(233, 238, 246, 0.85);
background: rgba(255, 255, 255, 0.09);
}
.resTabBtn--active {
background: rgba(113, 199, 255, 0.12);
border-color: rgba(113, 199, 255, 0.35);
color: rgba(113, 199, 255, 0.95);
}
/* ── Economy resource table ───────────────────────────────────────────────── */
.econTableWrap {
@@ -1254,47 +1291,106 @@ canvas {
color: rgba(220, 75, 85, 0.9);
}
/* ── Player list popup ─────────────────────────────────────────────────────── */
/* ── Players list panel ───────────────────────────────────────────────────── */
.playerListPopup {
position: absolute;
z-index: 30;
min-width: 120px;
max-width: 200px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.13);
background: rgba(12, 18, 38, 0.97);
backdrop-filter: blur(8px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
padding: 8px 10px;
.playerListPanel {
padding: 10px 10px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.playerListFilter {
display: flex;
gap: 6px;
align-items: center;
}
.playerListSearchInput {
flex: 1;
padding: 6px 10px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.06);
color: #e9eef6;
font-size: 12px;
outline: none;
font-family: "Courier New", Courier, monospace;
transition: border-color 0.15s;
}
.playerListSearchInput:focus {
border-color: rgba(113, 199, 255, 0.5);
}
.playerListSearchInput::placeholder {
color: rgba(233, 238, 246, 0.35);
}
.playerListSortBtn {
padding: 5px 10px;
font-size: 11px;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif;
pointer-events: auto;
font-weight: 700;
font-family: "Courier New", Courier, monospace;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.14);
background: rgba(255, 255, 255, 0.07);
color: rgba(233, 238, 246, 0.75);
cursor: pointer;
white-space: nowrap;
transition: background 0.15s;
}
.playerListPopup.hidden {
display: none;
.playerListSortBtn:hover {
background: rgba(255, 255, 255, 0.13);
color: #e9eef6;
}
.playerListName--blue {
.playerListTableWrap {
overflow-x: auto;
}
.playerListTable {
width: 100%;
border-collapse: collapse;
font-size: 11px;
font-family: "Courier New", Courier, monospace;
}
.playerListTable thead th {
padding: 5px 8px;
text-align: left;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.playerListTable thead th.playerListTh--blue {
color: rgba(90, 200, 255, 0.85);
}
.playerListTable thead th.playerListTh--red {
color: rgba(220, 75, 85, 0.85);
}
.playerListTable tbody tr:hover {
background: rgba(255, 255, 255, 0.04);
}
.playerListTable td {
padding: 3px 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
vertical-align: top;
}
.playerListCell--blue {
color: rgba(90, 200, 255, 0.9);
padding: 2px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.playerListName--red {
.playerListCell--red {
color: rgba(220, 75, 85, 0.9);
padding: 2px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.playerListEmpty {
color: rgba(233, 238, 246, 0.4);
font-style: italic;
}
/* ── Map action animation ─────────────────────────────────────────────────── */
+22 -1
View File
@@ -1,7 +1,7 @@
import { pool } from "./pools.js";
import { loadConfigFile, getConfig } from "../configLoader.js";
import { computeWorldSeedState } from "../worldSeed.js";
import { nextNoonUtc, resetAllUserActions } from "./usersDb.js";
import { nextNoonUtc, resetAllUserActions, getUsersByIds } from "./usersDb.js";
let lastSeedSlot = null;
@@ -455,6 +455,27 @@ export async function getActivePlayerIds(worldSeed) {
return result;
}
/**
* Returns per-team sorted lists of usernames who have been active this seed epoch.
* Uses a two-step lookup because users live in a separate database.
*/
export async function getActivePlayerNames(worldSeed) {
const playerIds = await getActivePlayerIds(worldSeed);
const allIds = [...playerIds.blue, ...playerIds.red];
const users = await getUsersByIds(allIds);
const teamOf = {};
for (const id of playerIds.blue) teamOf[id] = "blue";
for (const id of playerIds.red) teamOf[id] = "red";
const result = { blue: [], red: [] };
for (const user of users) {
const team = teamOf[user.id];
if (team) result[team].push(user.username);
}
result.blue.sort((a, b) => a.localeCompare(b));
result.red.sort((a, b) => a.localeCompare(b));
return result;
}
// ── Team action quota (daily, independent of world seed) ─────────────────────
export async function getTeamActionsRow(team) {
+24 -1
View File
@@ -3,24 +3,47 @@ import {
getElementBonus,
getMilitaryDeductions,
getActivePlayerCounts,
getActivePlayerNames,
getVictoryPoints,
getGridCells,
} from "./db/gameDb.js";
import { getConfig } from "./configLoader.js";
import { computeTeamIncome, computeTeamMilitaryPower } from "./helpers/economy.js";
export async function buildRealtimeSnapshot(worldSeed) {
const [scores, elementBonus, militaryDeductions, activePlayers, victoryPoints] = await Promise.all([
const [scores, elementBonus, militaryDeductions, activePlayers, playerNames, victoryPoints, rows] = await Promise.all([
getEconScores(worldSeed),
getElementBonus(worldSeed),
getMilitaryDeductions(worldSeed),
getActivePlayerCounts(worldSeed),
getActivePlayerNames(worldSeed),
getVictoryPoints(),
getGridCells(worldSeed),
]);
const cfg = getConfig();
const resourceWorth = cfg.resourceWorth ?? { common: {}, rare: {} };
const militaryPowerCfg = cfg.militaryPower ?? {};
const incomePerSecond = {
blue: computeTeamIncome("blue", rows, resourceWorth),
red: computeTeamIncome("red", rows, resourceWorth),
};
const militaryPowerGross = {
blue: computeTeamMilitaryPower("blue", rows, militaryPowerCfg),
red: computeTeamMilitaryPower("red", rows, militaryPowerCfg),
};
return {
worldSeed,
scores,
elementBonus,
militaryDeductions,
activePlayers,
playerNames,
victoryPoints,
incomePerSecond,
militaryPowerGross,
};
}