Private
Public Access
1
0
Files
star-wars-wild-space/public/src/economy.js
2026-03-30 15:43:43 +02:00

165 lines
6.4 KiB
JavaScript

import { resources, elements } from "./planetEconomy.js";
// ── Sort state ────────────────────────────────────────────────────────────────
/** 0=Ressource, 1=Rareté, 2=Valeur, 3=Revenu/s */
let _sortCol = 3;
let _sortDir = "desc";
export function setEconSort(col, dir) {
_sortCol = col;
_sortDir = dir;
}
export function getEconSort() {
return { col: _sortCol, dir: _sortDir };
}
// ── Label → resource key lookup ───────────────────────────────────────────────
/** Map from French label string → { cat: "common"|"rare", key: string } */
const LABEL_TO_RESOURCE = (() => {
const map = new Map();
for (const [cat, entries] of Object.entries(resources)) {
for (const [key, label] of Object.entries(entries)) {
map.set(label, { cat, key });
}
}
return map;
})();
// ── Income calculation ────────────────────────────────────────────────────────
/**
* Compute income per second for a team based on their discovered planets.
*
* @param {string} team - "blue" or "red"
* @param {Map<string, { discoveredBy: string, hasPlanet: boolean, planet: object|null }>} cells
* @param {object} resourceWorth - { common: { rock: 1, ... }, rare: { rock: 3, ... } }
* @returns {{ total: number, byResource: Map<string, number> }}
* byResource keys are resource label strings (French names), values are credits/sec
*/
export function computeTeamIncome(team, cells, resourceWorth) {
/** @type {Map<string, number>} label → cumulative income/sec */
const byResource = new Map();
let total = 0;
for (const [, meta] of cells) {
if (meta.discoveredBy !== team) continue;
if (!meta.hasPlanet || !meta.planet) continue;
const { naturalResources } = meta.planet;
if (!naturalResources) continue;
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;
const income = (pct / 100) * worth;
byResource.set(label, (byResource.get(label) ?? 0) + income);
total += income;
}
}
return { total, byResource };
}
// ── Element bonus calculation ─────────────────────────────────────────────────
/**
* Reverse map: French element label → config key
* e.g. "Matières premières" → "common", "Hydrocarbures" → "petrol"
*/
const ELEMENT_LABEL_TO_KEY = Object.fromEntries(
Object.entries(elements).map(([key, label]) => [label, key])
);
/**
* Compute cumulative element bonus for a team based on their planets' production.
* planet.production stores French label strings as keys (values from elements const).
* bonus = sum_over_planets( sum_over_elements( elementShare% / 100 * elementWorth[key] ) )
*
* @param {string} team
* @param {Map<string, { discoveredBy: string, hasPlanet: boolean, planet: object|null }>} cells
* @param {object} elementWorth - { common: 1, petrol: 3, ... }
* @returns {number} bonus value (use as %)
*/
export function computeTeamElementBonus(team, cells, elementWorth) {
let bonus = 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)) {
// production keys are French labels; map back to config key
const elementKey = ELEMENT_LABEL_TO_KEY[elementLabel] ?? elementLabel;
const worth = elementWorth?.[elementKey] ?? 0;
if (worth === 0) continue;
bonus += (pct / 100) * worth;
}
}
return bonus;
}
export { elements };
// ── Resource table for the sidebar ───────────────────────────────────────────
/**
* Renders the resource overview table for the economy panel.
*
* @param {object} resourceWorth - { common: {…}, rare: {…} }
* @param {Map<string, number>} teamByResource - income/sec per label for current team
* @returns {string} HTML string
*/
export function renderResourceTable(resourceWorth, teamByResource) {
const rows = [];
for (const [cat, entries] of Object.entries(resources)) {
for (const [key, label] of Object.entries(entries)) {
const worth = resourceWorth?.[cat]?.[key] ?? 0;
const income = teamByResource?.get(label) ?? 0;
const incomeStr = income > 0 ? `+${income.toFixed(3)}/s` : "—";
const catLabel = cat === "rare" ? "Rare" : "Commun";
rows.push({ label, catLabel, worth, income, incomeStr });
}
}
// Sort by selected column
const mult = _sortDir === "asc" ? 1 : -1;
rows.sort((a, b) => {
if (_sortCol === 0) return mult * a.label.localeCompare(b.label, "fr");
if (_sortCol === 1) return mult * a.catLabel.localeCompare(b.catLabel, "fr");
if (_sortCol === 2) return mult * (a.worth - b.worth);
if (_sortCol === 3) return mult * (a.income - b.income);
return b.income - a.income || b.worth - a.worth;
});
const tableRows = rows
.map(({ label, catLabel, worth, incomeStr, income }) => {
const incomeClass = income > 0 ? " econ-income--positive" : "";
return `<tr>
<td class="econ-label">${label}</td>
<td class="econ-cat econ-cat--${catLabel.toLowerCase()}">${catLabel}</td>
<td class="econ-worth">${worth}</td>
<td class="econ-income${incomeClass}">${incomeStr}</td>
</tr>`;
})
.join("");
const thLabels = ["Ressource", "Rareté", "Valeur", "Revenu/s"];
const headers = thLabels
.map((lbl, i) => {
const isActive = i === _sortCol;
const indicator = isActive ? (_sortDir === "asc" ? " ▲" : " ▼") : " ⇅";
const activeClass = isActive ? " econTh--active" : "";
return `<th class="econTh${activeClass}" data-sort-col="${i}">${lbl}<span class="econSortIcon">${indicator}</span></th>`;
})
.join("");
return `<table class="econTable">
<thead><tr>${headers}</tr></thead>
<tbody>${tableRows}</tbody>
</table>`;
}