99 lines
3.9 KiB
JavaScript
99 lines
3.9 KiB
JavaScript
// Daily newspaper: headlines picked from templates by how much the indicators
|
|
// moved over the last few weeks. Selection is deterministic (hash of week + key)
|
|
// and never touches the game RNG, so reading the paper can't change the game.
|
|
|
|
import { t, tx, getLang, dateParts } from "../i18n/i18n.js";
|
|
import { IND_IDS, dateOfDay } from "../engine/index.js";
|
|
import { hash, $ } from "./desk.js";
|
|
|
|
const IND_THRESHOLD = 4; // points moved over the window to make news
|
|
const VAR_THRESHOLD = { growth: 0.4, unemployment: 0.4, inflation: 0.5, debtRatio: 3 };
|
|
|
|
// Each entry: [template key, test on the live state, severity score]
|
|
const LEVELS = [
|
|
["unrest.high", (s, f) => s.indicators.unrest >= 50, 1.7],
|
|
["opinion.low", (s, f) => s.indicators.opinion <= 30, 1.6],
|
|
["debt.high", (s, f) => f.debtRatio >= 100, 1.6],
|
|
["stability.low", (s, f) => s.indicators.stability <= 35, 1.6],
|
|
["civilRights.low", (s, f) => s.indicators.civilRights <= 25, 1.5],
|
|
["army.low", (s, f) => s.indicators.armyLoyalty <= 35, 1.7],
|
|
["inflation.high", (s, f) => s.economy.inflation >= 6, 1.5],
|
|
["deficit.high", (s, f) => f.deficit >= 8, 1.5],
|
|
["world.slump", (s) => (s.world?.cycle ?? 0) <= -1.8, 1.4],
|
|
["world.boom", (s) => (s.world?.cycle ?? 0) >= 1.8, 1.2],
|
|
];
|
|
|
|
function candidates(state, tpl) {
|
|
const out = [];
|
|
const h = state.history;
|
|
const n = h.t.length;
|
|
if (n >= 2) {
|
|
const back = Math.min(4, n - 1);
|
|
const consider = (group, id, threshold) => {
|
|
const s = h.series[id];
|
|
const d = s[n - 1] - s[n - 1 - back];
|
|
const dir = d > 0 ? "up" : "down";
|
|
const tpls = tpl[group][id]?.[dir];
|
|
if (tpls && Math.abs(d) / threshold >= 1) out.push({ score: Math.abs(d) / threshold, key: `${group}.${id}.${dir}`, tpls });
|
|
};
|
|
for (const id of IND_IDS) consider("ind", id, IND_THRESHOLD);
|
|
for (const [id, thr] of Object.entries(VAR_THRESHOLD)) consider("var", id, thr);
|
|
}
|
|
const debtRatio = h.series.debtRatio?.[n - 1] ?? 0;
|
|
const deficit = h.series.deficit?.[n - 1] ?? 0;
|
|
for (const [key, test, score] of LEVELS) {
|
|
if (tpl.level[key] && test(state, { debtRatio, deficit })) out.push({ score, key: `level.${key}`, tpls: tpl.level[key] });
|
|
}
|
|
return out.sort((a, b) => b.score - a.score);
|
|
}
|
|
|
|
export function computeHeadlines(state, tpl) {
|
|
const week = Math.floor(state.day / 7);
|
|
const pick = (tpls, key) => tpls[hash(`${key}:${week}`) % tpls.length];
|
|
|
|
if (state.history.t.length < 2 && state.day === 0) return [pick(tpl.intro, "intro")];
|
|
const found = candidates(state, tpl);
|
|
if (!found.length) return [pick(tpl.quiet, "quiet")];
|
|
return found.slice(0, 3).map((c) => pick(c.tpls, c.key));
|
|
}
|
|
|
|
export function createNewspaper(root, tpl) {
|
|
root.innerHTML = `
|
|
<header class="news__mast">
|
|
<h2>${tx(tpl.name)}</h2>
|
|
<div class="news__edition"></div>
|
|
</header>
|
|
<article class="news__lead"><h3></h3><p></p></article>
|
|
<div class="news__side"></div>`;
|
|
|
|
let shown = "";
|
|
|
|
function update(state) {
|
|
// The date line follows the calendar; headlines only change with the weekly history sample.
|
|
$(root, ".news__edition").textContent = t("news.edition", { date: dateParts(dateOfDay(state.startDate, state.day).iso).full });
|
|
|
|
const week = Math.floor(state.day / 7);
|
|
const key = `${week}|${getLang()}|${state.history.t.length}`;
|
|
if (key === shown) return;
|
|
shown = key;
|
|
|
|
const fill = (s) => s.replaceAll("{country}", state.countryName);
|
|
const [lead, ...rest] = computeHeadlines(state, tpl);
|
|
$(root, ".news__lead h3").textContent = fill(tx(lead.h));
|
|
$(root, ".news__lead p").textContent = fill(tx(lead.d));
|
|
|
|
const side = $(root, ".news__side");
|
|
side.replaceChildren(...rest.map((item) => {
|
|
const a = document.createElement("article");
|
|
const h = document.createElement("h4");
|
|
const p = document.createElement("p");
|
|
h.textContent = fill(tx(item.h));
|
|
p.textContent = fill(tx(item.d));
|
|
a.append(h, p);
|
|
return a;
|
|
}));
|
|
}
|
|
|
|
return { update };
|
|
}
|