Rolled the central bank's commodity chart over the last two years
The index is still 100 at January 2000's prices, but the chart now plots only the last twenty-four monthly samples and its end labels follow the window, so the graph stops growing as the game runs. A test covers the 24-month window and its end labels.
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
// Generated by scripts/generate-devlog.js from `git log`; do not edit.
|
||||
// The pre-commit hook refreshes it so the main menu shows the latest commits.
|
||||
export const DEVLOG = [
|
||||
{
|
||||
"hash": "2d36685",
|
||||
"date": "2026-09-22",
|
||||
"subject": "Moved tile works into a management panel and put every building on one card",
|
||||
"body": "The city buildings tab and the HUD's tile panel now draw the same card: an icon, name and status, a bottom-anchored button carrying the estimated price and build time, a quiet remove control and a hatched locked state, with the produce/consume/cost detail in a hover tooltip. The shared card lives in client/js/ui/card.js.\n\nThe tile dock is now a summary — description, production, people and their ethnic makeup, GDP and the improvements list — with a Management button that opens a full-size panel like the city and nation panels. Its Improvements tab holds the grouped Military, Transportation and Economic cards, so Road and Railway sit with the other works. City buildings sit in a two-row horizontal scroller with the built ones first, and the city panel now matches the nation panel's size.\n\nAGENTS.md now asks for a minimal visual preview to be shown before a player-visible change is committed."
|
||||
},
|
||||
{
|
||||
"hash": "7ea5373",
|
||||
"date": "2026-09-22",
|
||||
@@ -54,11 +60,5 @@ export const DEVLOG = [
|
||||
"date": "2026-09-22",
|
||||
"subject": "Let central banks work their reserves and cleared the landlocked famines",
|
||||
"body": "Central banks now redeem the national money foreign banks hold, sell reserves to defend a weak rate, and cover part of strategic imports from reserves. Five repeatable technologies raise each commodity's output per unit of energy. Hostile troops on a road or sea lane sever trade, power and unit supply. Middlemen buy food to resell to neighbours at a markup, and starving regions buy before those topping up their buffers, which clears the landlocked famines. The economic map's trade graph gains a colour legend."
|
||||
},
|
||||
{
|
||||
"hash": "3e429cc",
|
||||
"date": "2026-09-22",
|
||||
"subject": "Gave production buildings their own economy and fed island regions",
|
||||
"body": "Production buildings are now independent private-sector agents: they keep what their output sells for, run only to the demand they can actually sell, and spend that cash on steel and high-tech upgrades when their demand grows. Grid power is a paid private flow between plants and their consumers, and regions synthesise the food their own harvest and the reachable market cannot supply.\n\nIsland regions no longer famine: a region with no reachable supplier grows its whole shortfall from energy, and grid power now feeds cities and food before the converters, so a famine never waits on a factory.\n\nBundles the in-progress tree cleanup that was already present in the working tree."
|
||||
}
|
||||
];
|
||||
|
||||
@@ -26,6 +26,10 @@ import { currencySymbol, currencyFor, rateToNational } from "../currency.js";
|
||||
import { HOURS_PER_DAY } from "../../../shared/game_state/constants.js";
|
||||
import { MONTH_NAMES } from "../../../shared/game_clock.js";
|
||||
|
||||
// The central bank charts a rolling window of the last two years, so its
|
||||
// monthly commodity index stays 24 points instead of growing with the game.
|
||||
const PRICE_INDEX_WINDOW_MONTHS = 24;
|
||||
|
||||
export class NationModal {
|
||||
constructor() {
|
||||
this.$modal = $("#modal-nation");
|
||||
@@ -746,7 +750,8 @@ export class NationModal {
|
||||
|
||||
// The commodity price index chart and the per-commodity inflation table. Each
|
||||
// series is a commodity's world price rebased to 100 in January 2000, with the
|
||||
// live point appended so the current month reads to today.
|
||||
// live point appended so the current month reads to today. Only the last two
|
||||
// years are plotted, so the window rolls instead of the chart growing.
|
||||
_renderCommodityChart(money) {
|
||||
const $container = this.$economyCentralBank.find(".central-bank-prices");
|
||||
if (!$container.length) return;
|
||||
@@ -754,6 +759,8 @@ export class NationModal {
|
||||
const inflation = new Map((money.resourceInflation || []).map((entry) => [entry.id, entry]));
|
||||
const series = [];
|
||||
const rows = [];
|
||||
// The month the plotted window starts in, as an offset from January 2000.
|
||||
let windowStart = 0;
|
||||
for (const [id, prices] of history) {
|
||||
const resource = resourceById(id);
|
||||
if (!resource || !prices || prices.length === 0) continue;
|
||||
@@ -761,9 +768,11 @@ export class NationModal {
|
||||
const values = prices.map((price) => (price / base) * 100);
|
||||
const info = inflation.get(id) || { index: 100, monthly: 0, yearly: 0 };
|
||||
if (Math.abs(info.index - values[values.length - 1]) > 1e-6) values.push(info.index);
|
||||
windowStart = Math.max(windowStart, values.length - PRICE_INDEX_WINDOW_MONTHS);
|
||||
series.push({ label: resource.name, colour: resource.colour, values });
|
||||
rows.push({ resource, info });
|
||||
}
|
||||
for (const entry of series) entry.values = entry.values.slice(windowStart);
|
||||
$container.empty();
|
||||
$container.append($("<div class='subhead'></div>").text("Commodity prices"));
|
||||
$container.append(
|
||||
@@ -773,7 +782,9 @@ export class NationModal {
|
||||
);
|
||||
if (series.length === 0) return;
|
||||
const options = {
|
||||
xLabel: (i) => `${MONTH_NAMES[i % 12].slice(0, 3)} ${2000 + Math.floor(i / 12)}`,
|
||||
xLabel: (i) =>
|
||||
`${MONTH_NAMES[(windowStart + i) % 12].slice(0, 3)} ` +
|
||||
`${2000 + Math.floor((windowStart + i) / 12)}`,
|
||||
};
|
||||
// The panel scrolls, but the chart must not swallow it: size it to the
|
||||
// panel's width and cap it at half the visible height, so the inflation
|
||||
|
||||
@@ -1055,6 +1055,57 @@ export class NationModalTest extends TestCase {
|
||||
teardownDom(env);
|
||||
}
|
||||
}
|
||||
|
||||
async test_central_bank_chart_rolls_a_two_year_window() {
|
||||
const env = await setupDom();
|
||||
try {
|
||||
const modal = new NationModal();
|
||||
// Thirty monthly openings, so six must fall off the twenty-four month
|
||||
// window. The live index matches the last sample, so no point is appended.
|
||||
const energy = Array.from({ length: 30 }, (_, i) => 100 + i * 10);
|
||||
const money = {
|
||||
viewerCiv: 0,
|
||||
currency: { code: "FRF", symbol: "₣", name: "French Franc" },
|
||||
currencies: [{ civ: 0, code: "FRF", symbol: "₣", name: "French Franc", value: 1 }],
|
||||
exchangeRates: [{ civ: 0, code: "FRF", symbol: "₣", name: "French Franc", rate: 1 }],
|
||||
centralBank: { rate: 0.02, reserves: [], loans: 0 },
|
||||
taxes: {
|
||||
sales: { rate: 0.2, resources: {} },
|
||||
export: { rate: 0.05, resources: {} },
|
||||
import: { rate: 0.05, resources: {} },
|
||||
},
|
||||
takeTax: { sales: 0, export: 0, import: 0 },
|
||||
commodityInflation: 0,
|
||||
currencyHoldings: null,
|
||||
privateCash: 0,
|
||||
cities: [],
|
||||
resourcePriceHistory: [["energy", energy]],
|
||||
resourceInflation: [{ id: "energy", index: 390, monthly: 0.03, yearly: 0.25 }],
|
||||
};
|
||||
const resourcesInfo = {
|
||||
summary: {
|
||||
stock: {}, need: {}, production: {}, prices: {},
|
||||
consumed: { steel: 0, food: 0, luxury: 0, hightech: 0, energy: 0 },
|
||||
external: { buy: {}, sell: {} },
|
||||
},
|
||||
market: [],
|
||||
trade: [],
|
||||
};
|
||||
modal.show(
|
||||
GOVERNMENTS, 0, TECHNOLOGIES, new Set(), 1.0e9, 1.0e15, 1.0e6,
|
||||
{ months: [], cities: [] }, null, [], null, null, resourcesInfo, money
|
||||
);
|
||||
env.$("#modal-nation .tab[data-tab=economy]").click();
|
||||
env.$("#economy-subtabs .subtab[data-view=centralbank]").click();
|
||||
const points = env.$("#economy-central-bank .line-chart-line").attr("points") || "";
|
||||
this.assertEqual(points.trim().split(/\s+/).length, 24, "the window holds 24 months");
|
||||
const labels = env.$("#economy-central-bank .line-chart-label");
|
||||
this.assertEqual(labels.eq(-2).text(), "Jul 2000", "the window starts two years back");
|
||||
this.assertEqual(labels.eq(-1).text(), "Jun 2002", "the window ends at the latest month");
|
||||
} finally {
|
||||
teardownDom(env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ResourceTrendTest extends TestCase {
|
||||
|
||||
Reference in New Issue
Block a user