# Writing scenarios Guide for anyone (human or model) adding a new fictional country or situation to "The Desk". A scenario is one JSON file that sets the start state; the rules, the card pool, the newspaper and the endings are shared. Read `docs/BALANCE.md` and `docs/CARD-WRITING.md` first. Reference scenario: `web/data/scenarios/default.json` (Valdoria). ## 1. What a scenario can change `createGame` (`web/js/engine/game.js`) reads these fields, and nothing else: | Field | What it does | Where it shows | |---|---|---| | `id` | key of the scenario, saved in every save | file name, `index.json`, saves | | `name` `{en, fr}` | default country name (the player may type another) | menu list, save slots, newspaper `{country}`, export file name | | `currency` | text only | "bn VLD" on the dashboard and the ledger | | `startDate` | `YYYY-MM-DD`, day 0 | calendar, card dates, end screen | | `economy` | gdp, debtRatio, growth, unemployment, inflation, otherRevenue, the three start tax rates | everything fiscal | | `indicators` | the 9 start values (0 to 100) | dashboard, and the equilibrium (section 3) | | `sectors` | `need` and `alloc` (% of GDP) for the 15 sectors | ledger, quality, cost of every lever | **What a scenario cannot change without engine work.** All of it lives in shared files: - Tax structure: the three taxes, their yield `coef`, Laffer `peak`, opinion and growth effects (`balance.json` `tax`). A scenario only picks start rates and `otherRevenue`. - The 15 sectors (all must be present, and `balance.json` `sectorTau` / `needGrowth` must match), the 9 indicators, the 3 economy variables. - How things react: indicator weights, drift, gain, world cycle, game-over thresholds, score formula, `needGrowth`. - Characters (`characters.json`: fixed names and roles), the newspaper (`newspaper.json`, called "The Daily Herald"), endings and historian titles (`endgame.json`), the length of the term (1826 days). - **Card text.** About 140 mentions of "Valdoria / Valdorian" (both languages) sit in card text, written literally; card bodies are not run through `{country}` (only the newspaper intro is). A new country therefore plays with Valdorian cards. Write the premise so this is tolerable (a sister state, a Valdorian region or era), or ask for `{country}` substitution in cards as engine work. Scenario-specific cards (section 5) may name the country directly. - A scenario `description` or premise: the menu only reads `name`. What really makes a scenario feel different, given shared rules: 1. **Where the start sits.** Indicators move toward `start value + weighted deviations`, so a country starting at opinion 35 stays near 35 until the player acts. 2. **Need versus alloc.** Quality is `100 * (1 - exp(-0.9 * alloc/need))`; the price of one more point of ratio is `need` % of GDP, so a sector with a small need is a cheap lever and a big need is an expensive one. 3. **The fiscal squeeze:** debt interest and taxes already close to their Laffer peak leave little room to move. 4. **Scenario cards** that stage the central tension. ## 2. Design brief (fill in before writing numbers) Write this in the scenario's pull request or notes; an agent that cannot fill every line is not ready to write JSON. | Question | Example answer | |---|---| | Identity: country, era, one-sentence premise | "A sugar island that borrowed to survive a hurricane." | | Central tension: the two things the player cannot have together | "pay the interest or keep the clinics open" | | Temptation: the easy move that backfires | "print growth with debt, or sell the ports" | | First-year crisis the player will feel by week 8 | "bond auction in month 3" | | Difficulty profile | tighter / same as default / easier, and which of the standard player types should change band (section 6) | | Levers that are cheap and expensive (needs) | health need 9 (expensive), police need 0.8 (cheap) | | Which indicators start high, which low, which near a danger zone | "unrest 44, but opinion 55: no zone" | | Cards: which situations need a scenario-only card | 15 to 40 cards, 3 to 5 chains | Use fiction only: no real country, person, party or brand (same rule as the cards). Prefer neighbours from the existing pool (Torvania, Marenia, Belmora, Kessaly, Draveth). ## 3. The scenario file ```json { "id": "saltmere", "name": { "en": "Free State of Saltmere", "fr": "État libre de Saltmère" }, "startDate": "2028-01-03", "currency": "SLM", "economy": { "gdp": 22, "debtRatio": 100, "growth": 1.5, "unemployment": 9, "inflation": 3, "otherRevenue": 7, "taxes": { "income": 38, "corporate": 28, "vat": 26 } }, "indicators": { "order": 60, "stability": 50, "civilRights": 66, "opinion": 48, "economy": 45, "hardPower": 20, "softPower": 40, "unrest": 28, "armyLoyalty": 62 }, "sectors": { "education": { "need": 4.5, "alloc": 4.0 }, "...": "all 15 sector ids, see default.json" } } ``` | Field | Unit | Sane range | Notes | |---|---|---|---| | `id` | lowercase letters and digits | | must equal the file name; avoid `_` (it is the card id prefix); never change it after release | | `startDate` | ISO date | any | pick a **Monday**: cards arrive every 7th day from day 0 and the calendar shows weekdays. Never change after release (saves refuse a different date) | | `gdp` | bn of currency | 15 to 2000 | display and absolute debt only; must be above 0 | | `debtRatio` | % of GDP | 15 to 105 | see the interest table below; the engine warns at 135 and defaults at 165 | | `growth` | % per year | -1 to 5 | clamped to -15..12 by the engine | | `unemployment` / `inflation` | % | 2 to 15 / 0 to 12 | clamped 1..40 / -2..40 | | `otherRevenue` | % of GDP | 3 to 24 | royalties, fees, aid; the main "resource state" knob | | `taxes.income / corporate / vat` | rate in % | 15 to 45 / 10 to 40 / 10 to 30 | stay under about 75 % of the peaks 60 / 50 / 40, or the player cannot raise them | | `indicators.*` | 0 to 100 | 15 to 85 | all 9 keys required; keep `unrest` 10 to 45, `armyLoyalty` 40 to 85 | | `sectors.*.need` | % of GDP | 0.3 to 14 | greater than 0; the 15 ids as in `default.json` | | `sectors.*.alloc` | % of GDP | 0.5 to 1.5 times need | engine accepts 0 to 3 times need | ### The equilibrium rule `createGame` stores the start in `state.ref` (taxes, indicators, economy variables, sector quality, budget ratios, and the fiscal deviations). Every target is `ref + gain * weight * (change since the start)`, so **the start is an exact equilibrium whatever numbers you choose**: the start deficit is not punished, and a high start inflation does not hurt by itself. A hands-off year still slides (default: order -5, stability -6, opinion -5, economy -6, unrest +8, army loyalty -5) because of the yearly `drift` and growing needs; that slide is the intended pressure. Some rules read **absolute** levels, so the start must respect them: - **Interest and default.** Rate = 3 + 0.0008 * (debtRatio - 40)^2 %, interest = ratio * rate. Default at 165, warning at 135, and the budget must pay the interest every year: | debt / GDP | 40 | 58 | 70 | 80 | 90 | 100 | 110 | 120 | 135 | |---|---|---|---|---|---|---|---|---|---| | interest, % of GDP | 1.2 | 1.9 | 2.6 | 3.4 | 4.5 | 5.9 | 7.6 | 9.7 | 13.8 | Industry quality also loses 0.25 per point of debt above 90. - **Game-over zones**: revolution (unrest above 62 with opinion below 28), coup (army loyalty under 35 with stability under 50, or loyalty under 18), impeachment (civil rights under 10 with opinion under 25, 60 days). Start at least 15 points away from each. - **Quality shaping.** Excess terms start at sector quality 70 (ratio 1.34) and short terms at 40 (ratio 0.57): outside that band the target is offset from the start. Keep ratios in 0.6 to 1.3 for health, social, housing, industry, infrastructure, education, research, police, justice, culture, foreign affairs. - **Score**: debt (ref 60), average growth (ref 2) and unemployment (ref 6.5) are absolute, so a high-debt or slow start scores lower (debt 100 costs 8 points). This is why a scenario may need its own bands (section 6). - Cards gated on `debt` (for example bond panic at 100, rating warning at 62, lenders at 105) fire from day 0 if the start is above them. ### The start fiscal picture All flows are % of GDP per year (`economy.js`): - Tax revenue = `coef * rate * (1 - rate / (2 * peak))`, with `coef` 0.5 / 0.25 / 0.55 and `peak` 60 / 50 / 40 for income / corporate / VAT. Revenue peaks at the peak rate (15 / 6.25 / 11 % of GDP); at the default rates (half the peak) one extra point yields 0.25 / 0.125 / 0.275. - Raising a rate by d points costs opinion (`0.5 / 0.15 / 0.6` times d, then times -1.2 on the opinion target) and growth (`0.03 / 0.06 / 0.025` times d). - `revenue = otherRevenue + sum of the three taxes`; `spending = sum of sector alloc + interest`; `deficit = spending - revenue`. - Default: revenue 35.69, sectors 34.30, interest 1.89, deficit 0.50. - **Aim for a start deficit between -1 and +1.5.** The debt ratio holds when deficit is about `growth * debtRatio / 100` (1.2 for the default), so a start above 2 spirals. Fix a gap by moving `otherRevenue`, tax rates, or sector `alloc`, never by changing a `need` (needs set prices, not budgets). - Order of work: pick debt and taxes, compute revenue and interest, then split the remaining budget over the 15 sectors. ## 4. Registering the scenario 1. Save `web/data/scenarios/.json`. 2. Add the id to `web/data/scenarios/index.json` (`"scenarios": ["default", ""]`). Keep `default` first: it is the pre-selected scenario, the name field's placeholder, and the fallback. 3. Both `name.en` and `name.fr` are required (`TestScenarioIndex`). The menu shows the name only: the New game screen has a scenario `