diff --git a/dev/economy.test.mjs b/dev/economy.test.mjs index b577dd4..5d459d0 100644 --- a/dev/economy.test.mjs +++ b/dev/economy.test.mjs @@ -189,6 +189,25 @@ const classFor = (sysId, good, list) => (list ?? []).find((c) => g.economy.class const cshop = E.shopFor('capital', capSys, g); check('a capital buys back EVERYTHING (universal sink)', cshop.sell.length === goods.length); check('a capital floor is 8–16 goods', cshop.buy.length >= 8 && cshop.buy.length <= 16); + + // THE WORLD'S JEWEL (§4/§6): every shop carries ITS class's signature gem — + // a guaranteed premium good + a guaranteed local buy in every shop. Sweep + // every system of every class (a gem that's only present by catalog luck + // would fail here: the floor must always include the class's gemstone). + let gemWorlds = 0; + let gemMissing = 0; + for (const [id, set] of g.economy.classBySystem) { + for (const cls of set) { + const gem = goods.find((x) => x.category === 'gemstone' && Array.isArray(x.producer) && x.producer.includes(cls)); + if (!gem) continue; + gemWorlds++; + const shop = E.shopFor(cls, id, g); + const row = shop.buy.find((b) => b.id === gem.id); + // the jewel is a premium good (tier 3–4) + a local buy (produced). + if (!row || row.produced !== true || (Number(gem.tier) || 0) < 3) gemMissing++; + } + } + check(`every shop carries its class's signature gem (${gemWorlds - gemMissing}/${gemWorlds} worlds)`, gemWorlds > 0 && gemMissing === 0); } // --------------------------------------------------------------------------- diff --git a/js/economy/Economy.js b/js/economy/Economy.js index 92874d8..1973056 100644 --- a/js/economy/Economy.js +++ b/js/economy/Economy.js @@ -184,6 +184,8 @@ function seededSelect(items, n, seed, cls, systemId) { * · plain world: its produced goods (abundant, cheap) * · station: produced (≤ half) + a seeded selection of resold goods (variety) * · capital: produced (≤ half) + a seeded selection of premium goods (tier 3–4) + * · the class's SIGNATURE GEM is ALWAYS on the floor (the world's + * "jewel" — a guaranteed premium good + a guaranteed local buy) * sell — goods the shop BUYS BACK (what you can sell), each { id, price } * · plain world / station: the goods the class wants * · capital: EVERYTHING (the universal sink) @@ -227,6 +229,20 @@ export function shopFor(cls, systemId, galaxy) { } } + // THE WORLD'S JEWEL (goods.json gemstones, §4/§6): the class's signature + // gem is ALWAYS on the shelf — a guaranteed premium good (tier 3–4) and a + // guaranteed local buy in every shop, whatever the seeded floor picked. + // It's a produced good of `cls`, so it prices at the producer discount; + // swap it into the least-important slot (a hub's resold filler, or an + // extra local good on a plain world) keeping the 8–16 floor size + no dups. + const gem = goods.find( + (g) => g.category === 'gemstone' && Array.isArray(g.producer) && g.producer.includes(cls), + ); + if (gem && !buyIds.includes(gem.id)) { + if (buyIds.length < maxStock) buyIds.push(gem.id); + else buyIds[buyIds.length - 1] = gem.id; + } + const buy = buyIds.map((id) => ({ id, name: goodById(id)?.name ?? id,