{ "_comment": "ECONOMY TUNING (the knobs, not the data). goods.json is WHAT exists; this file is HOW it prices and stocks. MODEL v2 — ONE WORLD, ONE GOOD (full version in docs/ECONOMY.md §5): every tradeable good has exactly one ORIGIN system (seeded strict assignment, 'origin-assign' pass in Economy.buildEconomy) — the single world whose shop sells it, cheap, in a DAILY BATCH. Every other system's shop does NOT sell it. The catalog's remainder (goods with no origin, the HUB POOL) is resold by stations/capitals at standard price (distance measured to the nearest hub). SELL price = baseValue × distanceBand(hops to origin) × (1 + demandBonus if the class wants it), CAPPED at the shop's own sale price for the good (no shop may ever be a positive same-shop spread). The banded distance term is the DOMINANT factor in profit; planet type (demand) is a secondary modifier. Prices are fixed per (good, location), a pure function of the seed; the only state is the day's remaining batch (save: economyStock). Credits are the go-between — buying spends them, selling earns them, no barter.", "shop": { "_comment": "Floor size per settlement KIND (model v2). A WORLD's floor is exactly ONE product — its system's signature good (the strict 1:1 origin assignment; see economy.json → 'stock'). Stations and capitals (hubs) resell from the HUB POOL — wider floors (variety), standard price.", "minStock": 1, "maxStock": 3, "stockByKind": { "capital": { "min": 12, "max": 16 }, "deepSpaceStation": { "min": 10, "max": 16 } } }, "pricing": { "_comment": "Multipliers are dimensionless and multiply baseValue. ORIGIN sale (the good's home world, batch-limited) = baseValue × (1 − originDiscount) × jitter — the only cheap source in the galaxy. HUB resale (station/capital, hub-pool goods only) = baseValue × hubPriceFactor × jitter — standard, never below origin. SELL price (the shop pays you) = baseValue × distanceBand(min(hops to origin, band)) × (1 + demandBonus if the class wants it) × jitter, CAPPED at the shop's own sale price for that good (anti-loop). `distanceBands` are ascending minHops → factor; the band with the largest minHops ≤ hops wins (no cap — the jump tree's diameter ~17 hops is the natural ceiling). The CURRENT factors are the HALVED-MARGIN curve (f′ = 0.25 + f/2 applied to the original ×1.0 / ×1.8 / ×3.2 / ×5.0 / ×7.0): with the origin buy at ×0.5, profit per unit ≈ (factor − 0.5) × base — ≈ 0.25× / 0.65× / 1.35× / 2.25× / 3.25× of base before the demand bonus — so every band nets exactly half the original margin while distance stays the dominant term. `jitter` is a per-good, per-place seeded wobble (±%) so no two shops feel formulated.", "originDiscount": 0.50, "hubPriceFactor": 1.00, "demandBonus": 0.10, "distanceBands": [ { "minHops": 0, "factor": 0.75 }, { "minHops": 2, "factor": 1.15 }, { "minHops": 4, "factor": 1.85 }, { "minHops": 7, "factor": 2.75 }, { "minHops": 11, "factor": 3.75 } ], "jitter": 0.10 }, "stock": { "_comment": "THE TIME-GATED BATCH — the only stateful part of the economy (and the only bookkeeping a shop needs). Each ORIGIN world's signature good comes to market as a daily batch of `dailyBatch` units; a DAY is `dayMs` of wall-clock time past `epoch`, so the batch regenerates while the player is away (the local buy→sell loop in the origin system — buy at the world, sell to the same-system station — is a BOUNDED pocket change, never infinite money, while long-haul SELLs stay unlimited: the market is a sink). The sell-back side is NEVER stock-limited. Save state per system: { day, left } — tiny, save-clone-safe (js/economy/Stock.js).", "dailyBatch": 6, "dayMs": 3600000, "epoch": "2026-01-01T00:00:00Z" }, "cargo": { "_comment": "The hold is a set of fixed BAYS — one good per bay (unitPerGood=1). `capacitySource` is the UNLOCKED-bay count (ship.stats.cargoHold). `maxSlots` is the fixed grid the shop UI always shows (16 slots) — locked bays are just capacity the ship hasn't unlocked yet. You CARRY goods between shops; that's the whole game loop.", "capacitySource": "ship.stats.cargoHold", "maxSlots": 16, "unitPerGood": 1 }, "credits": { "_comment": "The trade currency (the go-between). Displayed next to the minerals HUD (upper-right). Player starts at 0 (startingCredits). Earning credits = selling goods + mission/quest credit rewards + the bounded local batch loop. Spending = builds, upgrades, and buying goods. Buying spends credits; selling earns them.", "startingCredits": 0, "display": { "nextTo": "mineralHud", "anchor": "upper-right" }, "tradingLockedUntilQuest": 3, "earning": ["sellGoods", "questRewards", "localBatchLoop", "miningLaserGems (later)"] }, "restock": { "_comment": "Prices + FLOOR are NON-persistent (re-derived from the seed — no per-player sold/bought counters, 'you bought out their stock' bookkeeping lives ONLY in the day's batch: stock.dailyBatch/remaining). A shop's stock and prices are a pure function of (seed, place, economy version) — the batch remainder is the one saved number per origin system.", "policy": "deterministic-derivation + daily-batch", "stockIsPureFunction": true, "note": "Reopening a shop re-derives identical floor & prices; the origin's batch regenerates on the day clock." }, "reputation": { "_comment": "DEFERRED (decision #3 — wanted, later). Hook: standing with a place's owner faction nudges prices (good standing = better deal). `maxPriceShift` is the max +/- fraction of price. Wired to js/reputation/Reputation.js standingFor(place). OFF for v1.", "enabled": false, "maxPriceShift": 0.08 }, "faction": { "_comment": "DEFERRED (decision #3). Hook: some factions may levy a tariff or patron a good class. `tariffRange` is the +/- fraction. `patronGoods` (a future map of factionId -> [goodId]) lets a faction pay premium for its favored goods. Left empty — populate when factions take an economic role.", "tariffRange": 0.05, "patronGoods": {} }, "version": 2 }