First commit, done on M6 from the PLAN.MD
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
// Game creation and the daily tick. Pure: data (balance, scenario) is passed in.
|
||||
|
||||
import { STATE_VERSION, IND_IDS, VAR_IDS, qualityFromRatio } from "./state.js";
|
||||
import { computeFiscal, computeDevs, advanceEconomy } from "./economy.js";
|
||||
import { applyShifts, ageBias } from "./modifiers.js";
|
||||
import { stepSectors } from "./sectors.js";
|
||||
import { stepIndicators, stepVars } from "./indicators.js";
|
||||
import { checkGameOver } from "./gameover.js";
|
||||
|
||||
export const PRONOUNS = ["he", "she", "they"];
|
||||
|
||||
// pronouns: omit it and the game opens with the "who are you" question (pending.profile).
|
||||
export function createGame({ scenario, balance, seed = Date.now(), countryName, lang = "en", pronouns }) {
|
||||
const se = scenario.economy;
|
||||
const gdp = se.gdp;
|
||||
|
||||
const sectors = {};
|
||||
for (const [id, s] of Object.entries(scenario.sectors)) {
|
||||
sectors[id] = { need: s.need, alloc: s.alloc, quality: qualityFromRatio(s.alloc / s.need, balance) };
|
||||
}
|
||||
|
||||
const state = {
|
||||
version: STATE_VERSION,
|
||||
seed: seed >>> 0,
|
||||
rngState: seed >>> 0,
|
||||
day: 0,
|
||||
speed: 1, // 0 = paused, otherwise one of balance.time.speeds
|
||||
startDate: scenario.startDate,
|
||||
lang,
|
||||
pronouns: PRONOUNS.includes(pronouns) ? pronouns : "they",
|
||||
countryName: countryName ?? scenario.name[lang] ?? scenario.name.en,
|
||||
scenarioId: scenario.id,
|
||||
economy: {
|
||||
gdp,
|
||||
debt: (gdp * se.debtRatio) / 100,
|
||||
growth: se.growth,
|
||||
unemployment: se.unemployment,
|
||||
inflation: se.inflation,
|
||||
taxes: { ...se.taxes },
|
||||
otherRevenue: se.otherRevenue,
|
||||
extraSpending: 0,
|
||||
},
|
||||
sectors,
|
||||
indicators: { ...scenario.indicators },
|
||||
modifiers: [],
|
||||
flags: {},
|
||||
cardHistory: [],
|
||||
pendingCard: null,
|
||||
followUps: [],
|
||||
// Day 0 opens with the pronoun question, the initial budget review and a first card.
|
||||
// Any true flag here pauses time (see advanceDay).
|
||||
pending: { profile: !PRONOUNS.includes(pronouns), weeklyCard: true, review: true, alert: false },
|
||||
over: null,
|
||||
risk: { revolution: 0, coup: 0, impeachment: 0, defaultZone: false }, // days in each danger zone
|
||||
alerts: [], // urgent telegrams waiting to be read, oldest first
|
||||
history: { t: [], series: {} },
|
||||
log: [],
|
||||
ref: null,
|
||||
};
|
||||
|
||||
state.ref = {
|
||||
taxes: { ...se.taxes },
|
||||
ind: { ...scenario.indicators },
|
||||
vars: { growth: se.growth, unemployment: se.unemployment, inflation: se.inflation },
|
||||
sectors: Object.fromEntries(Object.entries(sectors).map(([id, s]) => [id, s.quality])),
|
||||
ratio: Object.fromEntries(Object.entries(sectors).map(([id, s]) => [id, s.alloc / s.need])),
|
||||
dev: null,
|
||||
};
|
||||
state.ref.dev = computeDevs(state, balance, computeFiscal(state, balance));
|
||||
|
||||
recordHistory(state, balance);
|
||||
return state;
|
||||
}
|
||||
|
||||
function recordHistory(state, bal) {
|
||||
const f = computeFiscal(state, bal);
|
||||
const h = state.history;
|
||||
h.t.push(state.day);
|
||||
const sample = { debtRatio: f.debtRatio, deficit: f.deficit };
|
||||
for (const id of IND_IDS) sample[id] = state.indicators[id];
|
||||
for (const id of VAR_IDS) sample[id] = state.economy[id];
|
||||
for (const [k, v] of Object.entries(sample)) (h.series[k] ??= []).push(round(v));
|
||||
}
|
||||
|
||||
const round = (v) => Math.round(v * 100) / 100;
|
||||
|
||||
export function setSpeed(state, bal, speed) {
|
||||
if (speed !== 0 && !bal.time.speeds.includes(speed)) return false;
|
||||
state.speed = speed;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clears a pause condition once the player has dealt with it.
|
||||
export function clearPending(state, kind) {
|
||||
state.pending[kind] = false;
|
||||
}
|
||||
|
||||
export function setPronouns(state, pronouns) {
|
||||
if (!PRONOUNS.includes(pronouns)) return false;
|
||||
state.pronouns = pronouns;
|
||||
state.pending.profile = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// True while the game is waiting on the player (profile, budget review, weekly card, alert).
|
||||
export const isWaiting = (state) => Object.values(state.pending).some(Boolean);
|
||||
|
||||
// Advances one in-game day unless the game is over or waiting on the player.
|
||||
export function advanceDay(state, bal) {
|
||||
if (state.over) return { advanced: false, blocked: "over", events: null };
|
||||
if (isWaiting(state)) return { advanced: false, blocked: "pending", events: null };
|
||||
|
||||
applyShifts(state, bal);
|
||||
const fiscal = computeFiscal(state, bal);
|
||||
const devs = computeDevs(state, bal, fiscal);
|
||||
|
||||
advanceEconomy(state, fiscal);
|
||||
stepVars(state, bal, devs);
|
||||
stepIndicators(state, bal, devs);
|
||||
stepSectors(state, bal, fiscal);
|
||||
ageBias(state);
|
||||
|
||||
state.day += 1;
|
||||
|
||||
const events = { weeklyCard: false, review: false, termEnd: false, gameOver: null };
|
||||
const fallen = checkGameOver(state, bal);
|
||||
if (fallen) {
|
||||
state.over = { type: fallen, day: state.day };
|
||||
events.gameOver = fallen;
|
||||
recordHistory(state, bal);
|
||||
} else if (state.day >= bal.time.termDays) {
|
||||
state.over = { type: "term", day: state.day };
|
||||
events.termEnd = true;
|
||||
recordHistory(state, bal);
|
||||
} else {
|
||||
if (state.day % bal.time.historyEvery === 0) recordHistory(state, bal);
|
||||
if (state.day % bal.time.daysPerWeek === 0) state.pending.weeklyCard = events.weeklyCard = true;
|
||||
if (state.day % bal.time.daysPerQuarter === 0) state.pending.review = events.review = true;
|
||||
}
|
||||
return { advanced: true, blocked: null, events };
|
||||
}
|
||||
|
||||
// Read-only snapshot for the UI and the sim.
|
||||
export function summarize(state, bal) {
|
||||
const f = computeFiscal(state, bal);
|
||||
return {
|
||||
day: state.day,
|
||||
gdp: state.economy.gdp,
|
||||
debt: state.economy.debt,
|
||||
debtRatio: f.debtRatio,
|
||||
interestRate: f.rate,
|
||||
revenue: f.revenue,
|
||||
spending: f.spending,
|
||||
interest: f.interest,
|
||||
deficit: f.deficit,
|
||||
growth: state.economy.growth,
|
||||
unemployment: state.economy.unemployment,
|
||||
inflation: state.economy.inflation,
|
||||
indicators: { ...state.indicators },
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user