Files
adrien aa726ff431 Made queued buildings gather their materials and rise ten times faster
City building orders now keep their production-queue slot but open in a materials phase: the order reserves the market value of its steel and high-tech, buys from the nation's own stores and reachable suppliers once a day, and only joins a construction line once the bill is settled, so a city with no supplier simply waits. The queue, the city panel and the quick view show the gathering progress instead of a stalled bar.

Buildings and tile improvements now take a tenth of the old time; unit training is untouched. Any material money left over is returned when the work completes.
2026-09-22 21:43:31 +02:00

124 lines
5.6 KiB
JavaScript

// National economy constants and the pure formulas the server and the browser
// both use to derive production, training time, budget income and population
// growth.
// Trade taxes are the government's only revenue; there is no GDP-based tax.
// Every region buying from another region inside the country pays a national
// sales tax on top of the price. A foreign buyer pays an export tariff to the
// seller's government, and a region buying from abroad pays an import tariff to
// its own government on the national currency it spent acquiring the foreign
// currency. Rates are per nation and may be overridden per resource.
export const TAXES = {
defaultSales: 0.2,
defaultExport: 0.05,
defaultImport: 0.05,
// The highest rate a player may set, per kind and resource.
maxRate: 1.0,
// The three tax kinds, in the order the panels list them.
kinds: ["sales", "export", "import"],
};
// The buckets the monthly budget report groups a nation's spending into. Each
// row carries a signed cash movement (positive is money into the treasury) and,
// for the upkeep rows, the market value of the materials consumed whether they
// were drawn from stores or bought. `group` is the heading the table files the
// row under.
export const BUDGET_EXPENSE_CATEGORIES = [
{ id: "buildings", label: "Building upkeep", group: "Upkeep" },
{ id: "units", label: "Unit upkeep", group: "Upkeep" },
{ id: "works", label: "Tile works", group: "Upkeep" },
{ id: "transport", label: "Transport", group: "Upkeep" },
{ id: "campaign", label: "Campaign", group: "Government" },
{ id: "construction", label: "Construction", group: "Investment" },
{ id: "training", label: "Training", group: "Investment" },
{ id: "tilebuild", label: "Tile improvements", group: "Investment" },
{ id: "provisioning", label: "Provisioning", group: "Resources" },
{ id: "energy", label: "Power", group: "Resources" },
{ id: "military", label: "Military supplies", group: "Resources" },
];
// The monetary system: each nation issues its own currency and runs a central
// bank. Regions are the private sector, each holding a basket of currencies.
// When their cash runs out they borrow from the central bank, and the same bank
// lets the government run an overdraft. The interest rate is the cost of that
// borrowing, not a direct lever on inflation.
export const MONEY = {
// The central bank's opening lending rate, charged on every debt each year,
// and the ceiling a player may set.
defaultInterestRate: 0.02,
maxInterestRate: 0.5,
// Private-sector cash a region opens with, as a share of its yearly GDP.
privateCashFraction: 0.5,
// Each day a region converts this share of every foreign holding back into its
// own currency.
repatriationPerDay: 0.05,
// How much a currency's value may move from par, either way.
maxRateDrift: 0.9,
// How hard a day's net foreign demand pushes a currency's value.
demandSensitivity: 0.5,
// When several regions chase the same scarce surplus, each further buyer
// raises the price it pays by this share, capped at `maxBidMultiplier`.
bidPremium: 0.3,
maxBidMultiplier: 2,
// The central bank's foreign-exchange desk. It works the reserves the nation
// earned abroad: it retires the national money foreign banks hold, sells more
// reserves to defend a weak rate, and pays part of the nation's foreign
// purchases out of reserves to keep food and energy imports cheap.
fx: {
// Share of the bank's foreign reserves spent redeeming the national money
// held abroad on an ordinary day.
redemptionPerDay: 0.004,
// Extra share sold on a day the national currency sits below `weakBelow`.
interventionPerDay: 0.05,
// The value against par the bank treats as a weak rate to defend.
weakBelow: 0.98,
// The share of a foreign purchase of each resource the bank covers from its
// reserves, and the most of its reserves it will spend subsidising imports
// in one day.
importSubsidy: { food: 0.5, energy: 0.5, steel: 0.25, luxury: 0, hightech: 0.25 },
importSubsidyPerDay: 0.02,
},
};
export const ECONOMY = {
basePopulation: 100_000,
cityTileProductionMultiplier: 2.0,
capitalTileProductionMultiplier: 2.5,
citySurroundingProductionBonus: 0.3,
gdpPerProductionPoint: 100_000,
// Building and tile-improvement construction runs this much faster than
// training: a building takes a tenth of the hours a unit of the same cost
// would. Unit training is untouched.
buildingTimeMultiplier: 0.1,
// A nation opens the game on 1 January 2000 with a tenth of its starting GDP.
startingBudgetFraction: 0.1,
startingBudget: 1_000_000_000,
basePopulationGrowthRate: 0.005,
// Natural growth of production: every day a tile gains this share of the
// production it opened the game with. The amount is constant, so the relative
// growth rate naturally tapers off as the years pass.
naturalProductionGrowthPerDay: 0.0001,
productionCapacity(gdp) {
if (this.gdpPerProductionPoint <= 0) return 0;
return gdp / this.gdpPerProductionPoint;
},
trainingHours(gdp, cost) {
const capacity = this.productionCapacity(gdp);
if (capacity <= 0) return 0;
return cost / capacity;
},
// The hours a building or tile improvement of `cost` takes to rise, ten times
// quicker than training something of the same cost.
buildingHours(gdp, cost) {
return this.trainingHours(gdp, cost) * this.buildingTimeMultiplier;
},
populationGrowthFactor(hoursPerYear, hours = 1) {
if (this.basePopulationGrowthRate <= 0 || hoursPerYear <= 0 || hours <= 0) return 0;
return Math.pow(1 + this.basePopulationGrowthRate, hours / hoursPerYear) - 1;
},
};