First commit, done on M6 from the PLAN.MD

This commit is contained in:
gauvainboiche
2026-10-25 17:34:44 +01:00
parent d5dc2a3901
commit 04193e2013
86 changed files with 8396 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
// End of the game: a retrospective front page. It covers both the normal end of
// the term and each way of losing power early.
import { t, tx, tp, tt, getLang, dateParts, fmt } from "../i18n/i18n.js";
import { buildReview, dateOfDay, indexCards } from "../engine/index.js";
import { lineChart } from "./charts.js";
import { $, esc } from "./desk.js";
const MIN_SPAN = 30;
// series id in state.history, label key, show the term average or the final value
const CHARTS = [
["opinion", "ind.opinion", "avg"],
["stability", "ind.stability", "avg"],
["economy", "ind.economy", "avg"],
["debtRatio", "econ.debt", "final"],
];
export function createEndgame(root, { newsName, endgame, cards, onAgain }) {
const cardsById = indexCards(cards);
root.addEventListener("click", (e) => {
if (e.target.closest("button[data-again]")) onAgain();
});
let shown = "";
function chart(state, [id, label, mode], review) {
const series = state.history.series[id] ?? [];
const value = mode === "avg" ? review.metrics[id] : series.at(-1);
const tag = mode === "avg" ? t("end.averages") : t("end.final");
// Zoom on the data (at least MIN_SPAN points tall) so trends are visible; stay within 0-100
// for indicators, and above 0 for debt.
const lo = Math.min(...series);
const hi = Math.max(...series);
const span = Math.max(hi - lo, MIN_SPAN);
let min = Math.max(0, (lo + hi) / 2 - span / 2);
let max = min + span;
if (id !== "debtRatio" && max > 100) {
max = 100;
min = 100 - span;
}
return `
<figure class="end__chart">
<figcaption><span>${t(label)}</span><b>${esc(fmt(value, 0))}</b><small>${tag}</small></figcaption>
${lineChart(series, { min, max })}
</figure>`;
}
function decision(d) {
const card = cardsById.get(d.id);
const date = dateParts(dateOfDay(root.dataset.startDate, d.day).iso);
return `
<li>
<span class="end__when">${esc(date.day)} ${esc(date.month)} ${esc(date.year)}</span>
<b>${esc(tx(card.title))}</b>
<span class="end__chose">${esc(t("end.chose", { label: tx(card[d.choice].label) }))}</span>
</li>`;
}
function update(state, bal) {
if (!state.over) {
if (shown) {
root.hidden = true;
root.innerHTML = "";
shown = "";
}
return;
}
const key = `${state.over.type}:${state.over.day}:${getLang()}`;
if (key === shown) return;
shown = key;
root.dataset.startDate = state.startDate;
const review = buildReview(state, bal, cards, endgame);
const banner = endgame.over[state.over.type];
const entry = endgame.titles.find((x) => x.id === review.titleId);
const date = dateParts(dateOfDay(state.startDate, state.over.day).iso);
const lost = state.over.type !== "term";
root.innerHTML = `
<article class="paper paper--news end ${lost ? "end--lost" : ""}">
<header class="news__mast">
<h2>${esc(tx(newsName))}</h2>
<div class="news__edition">${esc(t("news.edition", { date: date.full }))}</div>
</header>
<div class="end__banner">
<h1>${esc(tx(banner.h))}</h1>
<p>${esc(tx(banner.d))}</p>
</div>
<div class="end__grid">
<section class="end__verdict">
<h3>${t("end.verdict")}</h3>
<div class="end__grade" data-grade="${esc(review.grade)}">
<span class="end__letter">${esc(review.grade)}</span>
<span>${t("end.grade")}</span>
</div>
<p class="end__title">${entry ? esc(tt(entry.title, state.pronouns)) : ""}</p>
<p class="end__text">${entry ? esc(tp(tx(entry.verdict), state.pronouns)) : ""}</p>
<p class="end__score">${t("end.score", { n: Math.round(review.score) })}<br>${t("end.served", { days: state.over.day })}</p>
</section>
<section class="end__charts">${CHARTS.map((c) => chart(state, c, review)).join("")}</section>
<section class="end__decisions">
<h3>${t("end.decisions")}</h3>
${review.topDecisions.length
? `<ol>${review.topDecisions.map(decision).join("")}</ol>`
: `<p class="inbox__idle">${t("end.noDecisions")}</p>`}
</section>
</div>
<footer class="end__foot"><button class="brass brass--wide" data-again>${t("end.again")}</button></footer>
</article>`;
root.hidden = false;
$(root, "button[data-again]").focus();
}
return { update };
}