From 026947ebe94b04081608bbd3604e1db7f905a35f Mon Sep 17 00:00:00 2001 From: fireshaper Date: Sat, 10 Oct 2026 13:37:39 -0400 Subject: [PATCH] Step 16.2: content: goods Adds Atmospheric Data Cores, Refined Sulfur, Volcanic Isotopes and Subsurface Ocean Samples, and the new producers and destinations for existing goods at Venus, Io and Europa. Co-Authored-By: Claude Opus 5.5 --- memory-bank/progress.md | 2 +- packages/content/src/data/goods.ts | 67 +++++++++++++++++++++++------ packages/content/test/goods.test.ts | 55 +++++++++++++++++++++++ 3 files changed, 111 insertions(+), 13 deletions(-) diff --git a/memory-bank/progress.md b/memory-bank/progress.md index b9bd874..e197f34 100644 --- a/memory-bank/progress.md +++ b/memory-bank/progress.md @@ -394,7 +394,7 @@ Spec: `memory-bank/change-requests/CR-015-fixed-map-fuel-tanks.md` (received as Spec: `memory-bank/change-requests/CR-016-venus-io-europa-belt.md` (received as "CR-015 … Phase 15"). New decisions recorded as plan §0.5.100–0.5.105. - [x] **Step 16.1 — Content: new locations.** Venus Cloud City (cloud platform, atmospheric, 108M km at 110°, range factor 1.0, fuel × 1.3, bay 20,000), Io Sulfur Works (new type `orbital-station`, `vacuum` landing so any ship can land, 0.42M km out from Jupiter, factor 1.2, fuel × 1.0, bay 70,000) and Europa Ice Station (new type `surface-base`, `vacuum`, 0.67M km out from Jupiter, factor 1.3, fuel × 0.7, bay 70,000); all undiscovered, selling fuel, with the CR's shippers and class mixes. Pulled forward so the game keeps working: each new place's Standard good (Cloud-Grown Botanicals at Venus, a new good; Mail & Parcels also produced at Io and Europa), since boards can't be generated where nothing Standard is made; and the three body sprites in the art manifest, with their generated art (the belt is still 16.5). The balance check's first Market purchase moved from 64 to ~92 min (the Clipper's landings decide it, and Venus changed which first contacts boards offer); the user widened the window to 45–95. Tests: `packages/content/test/fixed-map.test.ts` (Earth/Moon–Venus ≈ 200, Venus–Mars ≈ 332, Venus–Ceres ≈ 520, Venus–Jupiter ≈ 885, Ceres–Io/Europa ≈ 393, all within 2 %; the Jupiter system within 1.1M km; Io and Europa ride with Jupiter at their offsets; the three undiscovered, selling fuel, not Hardened; only Earth and the Moon start discovered); `locations.test.ts` (eight locations; fuel prices ordered Jupiter < Europa < Ceres < Io < Moon < Venus < Mars < Earth). Updated: bundle count, first contacts may go to Mars or Venus, the CR-007 class-mix baseline measured on its original five places, orbit rings for every body but those riding a parent, the Market reach line now listing Venus. -- [ ] Step 16.2 — Content: goods +- [x] **Step 16.2 — Content: goods.** New goods: Atmospheric Data Cores (High-Value tier 2, Venus → Earth), Refined Sulfur (Heavy, Io → Earth, Mars, Venus), Volcanic Isotopes (High-Value tier 3, Io → Earth), Subsurface Ocean Samples (High-Value tier 3, Priority, Europa → Earth); Cloud-Grown Botanicals came in 16.1. Water Ice and Liquid Oxygen are also made at Europa, Industrial Chemicals at Venus; Water Ice goes to Io and Venus, Liquid Oxygen to Io, Habitat Prefabs to Io and Europa, and Packaged Food, Consumer Goods, Mail & Parcels, Medical Supplies, Electronics and Machine Parts to all three new places. Balance medians: first purchase 92.3, discovery 48.9, loan 160.5, four ships 147 vs 156. Tests (`packages/content/test/goods.test.ts`): the five new goods with their classes, tiers and routes (Sulfur has no tier of its own); every location makes a Standard good and wants at least one; each new place is a destination for at least five goods; the new producers on existing goods. - [ ] Step 16.3 — Content tuning: reach - [ ] Step 16.4 — Engine: save migration and boards - [ ] Step 16.5 — Assets: sprites and belt rocks diff --git a/packages/content/src/data/goods.ts b/packages/content/src/data/goods.ts index 411a3eb..f1f2bc0 100644 --- a/packages/content/src/data/goods.ts +++ b/packages/content/src/data/goods.ts @@ -24,7 +24,7 @@ export const goods: Good[] = [ payMultiplier: 1.2, priorityEligible: true, producedAt: [EARTH, MARS, IO, EUROPA], - demandedAt: [MOON, MARS, CERES, JUPITER, EARTH], + demandedAt: [MOON, MARS, CERES, JUPITER, EARTH, VENUS, IO, EUROPA], }, { id: "packaged-food", @@ -34,7 +34,7 @@ export const goods: Good[] = [ payMultiplier: 1, priorityEligible: false, producedAt: [EARTH], - demandedAt: [MOON, MARS, CERES, JUPITER], + demandedAt: [MOON, MARS, CERES, JUPITER, VENUS, IO, EUROPA], }, { id: "consumer-goods", @@ -44,7 +44,7 @@ export const goods: Good[] = [ payMultiplier: 1, priorityEligible: false, producedAt: [EARTH], - demandedAt: [MOON, MARS, CERES, JUPITER], + demandedAt: [MOON, MARS, CERES, JUPITER, VENUS, IO, EUROPA], }, { id: "optical-glass", @@ -170,7 +170,7 @@ export const goods: Good[] = [ tier: 1, priorityEligible: true, producedAt: [EARTH], - demandedAt: [MOON, MARS, CERES, JUPITER], + demandedAt: [MOON, MARS, CERES, JUPITER, VENUS, IO, EUROPA], }, { id: "electronics", @@ -181,7 +181,7 @@ export const goods: Good[] = [ tier: 1, priorityEligible: false, producedAt: [EARTH], - demandedAt: [MOON, MARS, CERES, JUPITER], + demandedAt: [MOON, MARS, CERES, JUPITER, VENUS, IO, EUROPA], }, { id: "seed-stock", @@ -238,6 +238,39 @@ export const goods: Good[] = [ producedAt: [CERES], demandedAt: [EARTH], }, + { + id: "atmo-data-cores", + name: "Atmospheric Data Cores", + cargoClassId: "high-value", + massTonnes: { min: 0.5, max: 4 }, + payMultiplier: 1.4, + tier: 2, + priorityEligible: false, + producedAt: [VENUS], + demandedAt: [EARTH], + }, + { + id: "volcanic-isotopes", + name: "Volcanic Isotopes", + cargoClassId: "high-value", + massTonnes: { min: 0.5, max: 5 }, + payMultiplier: 1.7, + tier: 3, + priorityEligible: false, + producedAt: [IO], + demandedAt: [EARTH], + }, + { + id: "ocean-samples", + name: "Subsurface Ocean Samples", + cargoClassId: "high-value", + massTonnes: { min: 0.2, max: 2 }, + payMultiplier: 1.8, + tier: 3, + priorityEligible: true, + producedAt: [EUROPA], + demandedAt: [EARTH], + }, // Heavy: Heavy Cargo skill; tier follows the generator's Heavy rule. { @@ -249,7 +282,7 @@ export const goods: Good[] = [ priorityEligible: true, priorityName: "Emergency Parts", producedAt: [EARTH, MARS], - demandedAt: [MOON, MARS, CERES, JUPITER], + demandedAt: [MOON, MARS, CERES, JUPITER, VENUS, IO, EUROPA], }, { id: "habitat-prefabs", @@ -259,7 +292,7 @@ export const goods: Good[] = [ payMultiplier: 1, priorityEligible: false, producedAt: [EARTH], - demandedAt: [MOON, MARS, CERES], + demandedAt: [MOON, MARS, CERES, IO, EUROPA], }, { id: "lunar-titanium", @@ -291,6 +324,16 @@ export const goods: Good[] = [ producedAt: [CERES], demandedAt: [EARTH, MARS, MOON], }, + { + id: "sulfur", + name: "Refined Sulfur", + cargoClassId: "heavy", + massTonnes: { min: 40, max: 200 }, + payMultiplier: 0.9, + priorityEligible: false, + producedAt: [IO], + demandedAt: [EARTH, MARS, VENUS], + }, { id: "water-ice", name: "Water Ice", @@ -298,8 +341,8 @@ export const goods: Good[] = [ massTonnes: { min: 60, max: 250 }, payMultiplier: 0.8, priorityEligible: false, - producedAt: [CERES], - demandedAt: [MARS, MOON, JUPITER], + producedAt: [CERES, EUROPA], + demandedAt: [MARS, MOON, JUPITER, IO, VENUS], }, // Dangerous: Dangerous Cargo skill; tier is the hazard class. @@ -311,7 +354,7 @@ export const goods: Good[] = [ payMultiplier: 1, tier: 1, priorityEligible: false, - producedAt: [EARTH], + producedAt: [EARTH, VENUS], demandedAt: [MOON, MARS, CERES, JUPITER], }, { @@ -322,8 +365,8 @@ export const goods: Good[] = [ payMultiplier: 1, tier: 1, priorityEligible: false, - producedAt: [MOON], - demandedAt: [MARS, CERES, JUPITER], + producedAt: [MOON, EUROPA], + demandedAt: [MARS, CERES, JUPITER, IO], }, { id: "mining-charges", diff --git a/packages/content/test/goods.test.ts b/packages/content/test/goods.test.ts index 6fecc44..d34045d 100644 --- a/packages/content/test/goods.test.ts +++ b/packages/content/test/goods.test.ts @@ -77,3 +77,58 @@ describe("goods catalog (CR-007)", () => { expect(goods.some((g) => g.priorityEligible)).toBe(true); }); }); + +describe("goods for Venus, Io and Europa (Step 16.2, decision 0.5.103)", () => { + const newPlaces = ["venus-cloud-city", "io-sulfur-works", "europa-ice-station"]; + + it("adds the five new goods with the CR's classes, tiers and routes", () => { + const good = (id: string) => goods.find((g) => g.id === id)!; + expect(good("cloud-botanicals")).toMatchObject({ + cargoClassId: "standard", + priorityEligible: true, + producedAt: ["venus-cloud-city"], + }); + expect(good("atmo-data-cores")).toMatchObject({ cargoClassId: "high-value", tier: 2 }); + expect(good("sulfur")).toMatchObject({ + cargoClassId: "heavy", + producedAt: ["io-sulfur-works"], + }); + expect(good("sulfur").tier).toBeUndefined(); + expect(good("volcanic-isotopes")).toMatchObject({ cargoClassId: "high-value", tier: 3 }); + expect(good("ocean-samples")).toMatchObject({ + cargoClassId: "high-value", + tier: 3, + priorityEligible: true, + producedAt: ["europa-ice-station"], + }); + }); + + it("has every location produce a Standard good and demand at least one good", () => { + for (const location of locations) { + const standard = goods.filter( + (g) => g.cargoClassId === "standard" && g.producedAt.includes(location.id), + ); + expect(standard.length, location.id).toBeGreaterThan(0); + expect( + goods.some((g) => g.demandedAt.includes(location.id)), + location.id, + ).toBe(true); + } + }); + + it("makes each new place a destination for at least five goods", () => { + for (const id of newPlaces) { + expect(goods.filter((g) => g.demandedAt.includes(id)).length, id).toBeGreaterThanOrEqual(5); + } + }); + + it("adds the new producers to existing goods", () => { + const at = (id: string) => goods.find((g) => g.id === id)!; + expect(at("water-ice").producedAt).toContain("europa-ice-station"); + expect(at("liquid-oxygen").producedAt).toContain("europa-ice-station"); + expect(at("industrial-chemicals").producedAt).toContain("venus-cloud-city"); + expect(at("mail").producedAt).toEqual( + expect.arrayContaining(["io-sulfur-works", "europa-ice-station"]), + ); + }); +});