Step 2.2: add the five locations
Also fixes Prettier formatting in DECISIONS.md from Step 2.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8ff2df976e
commit
24d9942336
10
DECISIONS.md
10
DECISIONS.md
|
|
@ -40,6 +40,14 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
|||
- **Epoch fields are ISO 8601 UTC strings** (`epoch.realStart`, `epoch.ephemerisDate`), not `Date` objects, so content stays plain data.
|
||||
- **`tutorialBoardSize`** is a GameConfig field in addition to `contractBoardSize`, because decision 0.5.21 sets the tutorial board's size separately.
|
||||
- **XP per delivery inputs** are `base`, `perMillionKm` and `perCargoTier`. Step 3.16 decides how they combine.
|
||||
- **`pilotLevelXpThresholds`** lists the *total* XP needed for level 1, 2, 3, ...; it and the Long Haul cut-offs must be strictly increasing.
|
||||
- **`pilotLevelXpThresholds`** lists the _total_ XP needed for level 1, 2, 3, ...; it and the Long Haul cut-offs must be strictly increasing.
|
||||
- **`tierPayMultiplier`** is one number per cargo type. Step 3.8 decides how it scales with tier.
|
||||
- **Imports omit the `.ts` extension** (`moduleResolution: "Bundler"`).
|
||||
|
||||
## Step 2.2
|
||||
|
||||
- **Location ids:** `earth`, `moon`, `mars`, `ceres-station`, `jupiter-cloud-platform`.
|
||||
- **Cargo flows** follow GDD §9: Earth and Mars produce standard and high-value goods and demand ore (heavy) and fuel (dangerous); the Moon produces standard and heavy; Ceres produces heavy; Jupiter produces dangerous (fuel). The outposts import high-value equipment.
|
||||
- **Launch multipliers:** Earth 3, Jupiter Cloud Platform 2, Mars 1.5, Moon 0.5, Ceres 0.3.
|
||||
- **Bay lease prices are first guesses** (Earth 5,000 up to Jupiter 15,000). Step 2.5 tunes the Earth price against the tutorial.
|
||||
- **The cargo-id check uses a placeholder list** of the four cargo ids planned in Step 2.4 until the cargo data exists.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
## Phase 2 — Content Package
|
||||
|
||||
- [x] **Step 2.1 — Content schemas.** Zod 4 schemas in `packages/content/src/schemas.ts` for Location, ShipModel, CargoType, Skill and GameConfig, with inferred types. Tests check that each schema accepts a valid example and rejects it with any one field missing.
|
||||
- [ ] Step 2.2 — The five locations
|
||||
- [x] **Step 2.2 — The five locations.** `src/data/locations.ts`: Earth, Moon, Mars, Ceres Station, Jupiter Cloud Platform. Earth and Moon start discovered; Earth has the highest launch multiplier.
|
||||
- [ ] Step 2.3 — The three ship models
|
||||
- [ ] Step 2.4 — Cargo types and skills
|
||||
- [ ] Step 2.5 — Default game config
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import type { Location } from "../schemas";
|
||||
|
||||
/**
|
||||
* Base-game destinations (GDD §9). Cargo flows: Earth and Mars make goods, the
|
||||
* Moon and Ceres mine ore (heavy), Jupiter refines fuel (dangerous); remote
|
||||
* outposts import high-value equipment.
|
||||
*/
|
||||
export const locations: Location[] = [
|
||||
{
|
||||
id: "earth",
|
||||
name: "Earth",
|
||||
type: "atmospheric-world",
|
||||
landingRequirement: "atmospheric",
|
||||
body: "earth",
|
||||
launchCostMultiplier: 3,
|
||||
startsDiscovered: true,
|
||||
bayLeasePrice: 5000,
|
||||
produces: ["standard", "high-value"],
|
||||
demands: ["heavy", "dangerous"],
|
||||
},
|
||||
{
|
||||
id: "moon",
|
||||
name: "Moon",
|
||||
type: "airless-body",
|
||||
landingRequirement: "vacuum",
|
||||
body: "moon",
|
||||
launchCostMultiplier: 0.5,
|
||||
startsDiscovered: true,
|
||||
bayLeasePrice: 3000,
|
||||
produces: ["standard", "heavy"],
|
||||
demands: ["standard", "high-value", "dangerous"],
|
||||
},
|
||||
{
|
||||
id: "mars",
|
||||
name: "Mars",
|
||||
type: "atmospheric-world",
|
||||
landingRequirement: "atmospheric",
|
||||
body: "mars",
|
||||
launchCostMultiplier: 1.5,
|
||||
startsDiscovered: false,
|
||||
bayLeasePrice: 8000,
|
||||
produces: ["standard", "high-value"],
|
||||
demands: ["heavy", "dangerous"],
|
||||
},
|
||||
{
|
||||
id: "ceres-station",
|
||||
name: "Ceres Station",
|
||||
type: "belt-station",
|
||||
landingRequirement: "vacuum",
|
||||
body: "ceres",
|
||||
launchCostMultiplier: 0.3,
|
||||
startsDiscovered: false,
|
||||
bayLeasePrice: 10000,
|
||||
produces: ["heavy"],
|
||||
demands: ["standard", "high-value", "dangerous"],
|
||||
},
|
||||
{
|
||||
id: "jupiter-cloud-platform",
|
||||
name: "Jupiter Cloud Platform",
|
||||
type: "cloud-platform",
|
||||
landingRequirement: "atmospheric",
|
||||
body: "jupiter",
|
||||
launchCostMultiplier: 2,
|
||||
startsDiscovered: false,
|
||||
bayLeasePrice: 15000,
|
||||
produces: ["dangerous"],
|
||||
demands: ["standard", "high-value", "heavy"],
|
||||
},
|
||||
];
|
||||
|
|
@ -1 +1,2 @@
|
|||
export * from "./schemas";
|
||||
export * from "./data/locations";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { locationSchema, locations } from "../src/index";
|
||||
|
||||
// The cargo types planned for Step 2.4; replaced by the real data once it exists.
|
||||
const cargoTypeIds = ["standard", "heavy", "high-value", "dangerous"];
|
||||
|
||||
describe("locations", () => {
|
||||
it("all pass the schema", () => {
|
||||
for (const location of locations) {
|
||||
expect(() => locationSchema.parse(location)).not.toThrow();
|
||||
}
|
||||
});
|
||||
|
||||
it("has exactly five locations with unique ids", () => {
|
||||
expect(locations).toHaveLength(5);
|
||||
expect(new Set(locations.map((l) => l.id)).size).toBe(5);
|
||||
});
|
||||
|
||||
it("starts with exactly Earth and Moon discovered", () => {
|
||||
const discovered = locations.filter((l) => l.startsDiscovered).map((l) => l.id);
|
||||
expect(discovered.sort()).toEqual(["earth", "moon"]);
|
||||
});
|
||||
|
||||
it("references only existing cargo types", () => {
|
||||
for (const location of locations) {
|
||||
for (const cargoId of [...location.produces, ...location.demands]) {
|
||||
expect(cargoTypeIds).toContain(cargoId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("gives Earth the highest launch multiplier", () => {
|
||||
const earth = locations.find((l) => l.id === "earth")!;
|
||||
for (const location of locations) {
|
||||
if (location !== earth) {
|
||||
expect(earth.launchCostMultiplier).toBeGreaterThan(location.launchCostMultiplier);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue