Step 2.3: add the three ship models
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
24d9942336
commit
10ded07ea6
|
|
@ -51,3 +51,9 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
||||||
- **Launch multipliers:** Earth 3, Jupiter Cloud Platform 2, Mars 1.5, Moon 0.5, Ceres 0.3.
|
- **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.
|
- **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.
|
- **The cargo-id check uses a placeholder list** of the four cargo ids planned in Step 2.4 until the cargo data exists.
|
||||||
|
|
||||||
|
## Step 2.3
|
||||||
|
|
||||||
|
- **Ranges** (million km): Courier 5 (Moon only; Mars is ~55 at its closest), Freighter 650 (covers Ceres at its farthest, ~600, and Jupiter near its closest, ~590, per decision 0.5.25), Bulk Hauler 1,000 (Jupiter at its farthest, ~970). Step 3.7 checks these against real positions.
|
||||||
|
- **Speeds and overheads** aim at GDD §6 durations at compression 60: Courier 3M km/h with 2 h overhead (Earth→Moon ≈ 2 real minutes, mostly overhead), Freighter 2M km/h with 4 h (Earth→Mars ≈ 1–2 real hours), Bulk Hauler 1.2M km/h with 8 h.
|
||||||
|
- **Prices** (30,000 / 150,000 / 600,000), **capacities** (20 / 100 / 400 t), **fuel costs** (20 / 40 / 80 per million km) and **hull ratings** (1 / 2 / 3) are first guesses; Step 2.5 tunes the Courier price.
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
- [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.
|
- [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.
|
||||||
- [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.
|
- [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
|
- [x] **Step 2.3 — The three ship models.** `src/data/ships.ts`: Courier, Freighter, Bulk Hauler, all atmospheric. Speed, capacity and price orderings tested; range rules are checked in Step 3.7.
|
||||||
- [ ] Step 2.4 — Cargo types and skills
|
- [ ] Step 2.4 — Cargo types and skills
|
||||||
- [ ] Step 2.5 — Default game config
|
- [ ] Step 2.5 — Default game config
|
||||||
- [ ] Step 2.6 — Single content bundle
|
- [ ] Step 2.6 — Single content bundle
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import type { ShipModel } from "../schemas";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base-game ship models (GDD §8). All are atmospheric-rated (decision 0.5.1).
|
||||||
|
* Ranges against approximate Earth distances: Moon ~0.4M km, Mars at closest
|
||||||
|
* ~55M km, Ceres at farthest ~600M km, Jupiter ~590M–970M km.
|
||||||
|
*/
|
||||||
|
export const shipModels: ShipModel[] = [
|
||||||
|
{
|
||||||
|
id: "courier",
|
||||||
|
name: "Courier",
|
||||||
|
archetype: "courier",
|
||||||
|
price: 30000,
|
||||||
|
cruiseSpeedKmPerGameHour: 3_000_000,
|
||||||
|
cargoCapacityTonnes: 20,
|
||||||
|
// Moon only.
|
||||||
|
fuelRangeMillionKm: 5,
|
||||||
|
hullRating: 1,
|
||||||
|
landingClass: "atmospheric",
|
||||||
|
overheadGameHours: 2,
|
||||||
|
fuelCostPerMillionKm: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "freighter",
|
||||||
|
name: "Freighter",
|
||||||
|
archetype: "freighter",
|
||||||
|
price: 150000,
|
||||||
|
cruiseSpeedKmPerGameHour: 2_000_000,
|
||||||
|
cargoCapacityTonnes: 100,
|
||||||
|
// Mars and Ceres at any distance; Jupiter only near its closest (decision 0.5.25).
|
||||||
|
fuelRangeMillionKm: 650,
|
||||||
|
hullRating: 2,
|
||||||
|
landingClass: "atmospheric",
|
||||||
|
overheadGameHours: 4,
|
||||||
|
fuelCostPerMillionKm: 40,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bulk-hauler",
|
||||||
|
name: "Bulk Hauler",
|
||||||
|
archetype: "bulk-hauler",
|
||||||
|
price: 600000,
|
||||||
|
cruiseSpeedKmPerGameHour: 1_200_000,
|
||||||
|
cargoCapacityTonnes: 400,
|
||||||
|
// Jupiter at its farthest.
|
||||||
|
fuelRangeMillionKm: 1000,
|
||||||
|
hullRating: 3,
|
||||||
|
landingClass: "atmospheric",
|
||||||
|
overheadGameHours: 8,
|
||||||
|
fuelCostPerMillionKm: 80,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
export * from "./schemas";
|
export * from "./schemas";
|
||||||
export * from "./data/locations";
|
export * from "./data/locations";
|
||||||
|
export * from "./data/ships";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { shipModelSchema, shipModels } from "../src/index";
|
||||||
|
|
||||||
|
const ship = (id: string) => shipModels.find((s) => s.id === id)!;
|
||||||
|
|
||||||
|
describe("ship models", () => {
|
||||||
|
it("all pass the schema", () => {
|
||||||
|
for (const model of shipModels) {
|
||||||
|
expect(() => shipModelSchema.parse(model)).not.toThrow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has the three archetypes, all atmospheric", () => {
|
||||||
|
expect(shipModels.map((s) => s.id)).toEqual(["courier", "freighter", "bulk-hauler"]);
|
||||||
|
for (const model of shipModels) {
|
||||||
|
expect(model.landingClass).toBe("atmospheric");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("orders speed Courier > Freighter > Bulk Hauler", () => {
|
||||||
|
expect(ship("courier").cruiseSpeedKmPerGameHour).toBeGreaterThan(
|
||||||
|
ship("freighter").cruiseSpeedKmPerGameHour,
|
||||||
|
);
|
||||||
|
expect(ship("freighter").cruiseSpeedKmPerGameHour).toBeGreaterThan(
|
||||||
|
ship("bulk-hauler").cruiseSpeedKmPerGameHour,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("orders capacity Bulk Hauler > Freighter > Courier", () => {
|
||||||
|
expect(ship("bulk-hauler").cargoCapacityTonnes).toBeGreaterThan(
|
||||||
|
ship("freighter").cargoCapacityTonnes,
|
||||||
|
);
|
||||||
|
expect(ship("freighter").cargoCapacityTonnes).toBeGreaterThan(
|
||||||
|
ship("courier").cargoCapacityTonnes,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("orders price Courier < Freighter < Bulk Hauler", () => {
|
||||||
|
expect(ship("courier").price).toBeLessThan(ship("freighter").price);
|
||||||
|
expect(ship("freighter").price).toBeLessThan(ship("bulk-hauler").price);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue