Step 20.1: content and config
CI / check (push) Has been cancelled
Details
CI / check (push) Has been cancelled
Details
GameConfig.independentPilots: Dispatch Office price, first-look window, turnaround, empty periods before heading home, catch-up cap and spending floor (CR-020). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f7bbc6c1ea
commit
83cb2ae306
|
|
@ -1669,3 +1669,8 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
||||||
|
|
||||||
- **Numbering:** received as "CR-019 — Independent Pilots" for "Phase 19"; stored as **CR-020**, built as **Phase 20** (its 19.n = 20.n). Its decisions CR-019.D1–D9 are **0.5.118–0.5.126**. Its references: CR-005 → CR-007, CR-007 → CR-010, CR-011 → CR-014 (its 10K.6 → Step 14.6), CR-014 → CR-015, CR-016 → CR-017, CR-017 → CR-018, CR-018 → CR-019; CR-001 and CR-004 are unchanged.
|
- **Numbering:** received as "CR-019 — Independent Pilots" for "Phase 19"; stored as **CR-020**, built as **Phase 20** (its 19.n = 20.n). Its decisions CR-019.D1–D9 are **0.5.118–0.5.126**. Its references: CR-005 → CR-007, CR-007 → CR-010, CR-011 → CR-014 (its 10K.6 → Step 14.6), CR-014 → CR-015, CR-016 → CR-017, CR-017 → CR-018, CR-018 → CR-019; CR-001 and CR-004 are unchanged.
|
||||||
- **Plan 0.3:** automation/standing routes is struck from the deferred list; standing orders, pilot traits, decision events and a hiring market stay deferred.
|
- **Plan 0.3:** automation/standing routes is struck from the deferred list; standing orders, pilot traits, decision events and a hiring market stay deferred.
|
||||||
|
|
||||||
|
## Step 20.1
|
||||||
|
|
||||||
|
- **The settings live in one `independentPilots` group** in GameConfig.
|
||||||
|
- **The Dispatch Office costs exactly a Lighter (36,000 cr)**, the CR's "start around the price of a Lighter", until Step 20.8's balance pass says otherwise.
|
||||||
|
|
|
||||||
|
|
@ -462,7 +462,7 @@ Spec: `memory-bank/change-requests/CR-020-bridge-dashboard.md`. New decisions re
|
||||||
|
|
||||||
Spec: `memory-bank/change-requests/CR-020-independent-pilots.md` (received as "CR-019 … Phase 19"). New decisions recorded as plan §0.5.118–0.5.126.
|
Spec: `memory-bank/change-requests/CR-020-independent-pilots.md` (received as "CR-019 … Phase 19"). New decisions recorded as plan §0.5.118–0.5.126.
|
||||||
|
|
||||||
- [ ] Step 20.1 — Content and config
|
- [x] **Step 20.1 — Content and config.** `config.independentPilots`: `dispatchOfficePrice` 36,000 (a Lighter), `firstLookRealMinutes` 3, `turnaroundGameMinutes` 15 (15 real seconds at ×60), `emptyPeriodsBeforeHome` 3, `maxCatchUpRealDays` 7 and `defaultSpendingFloor` 500. All must be positive, and the first-look window must be shorter than the board period. Two stale quarter-tank comments from CR-015 are gone. Tests (`packages/content/test/config.test.ts`): the defaults, with the Dispatch Office at the Lighter's price; zero and negative values rejected; a first-look window as long as the board period rejected.
|
||||||
- [ ] Step 20.2 — Sim: eligibility and job choice
|
- [ ] Step 20.2 — Sim: eligibility and job choice
|
||||||
- [ ] Step 20.3 — Engine: Dispatch Office, modes, orders
|
- [ ] Step 20.3 — Engine: Dispatch Office, modes, orders
|
||||||
- [ ] Step 20.4 — Engine: event-queue catch-up
|
- [ ] Step 20.4 — Engine: event-queue catch-up
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,7 @@ export const gameConfig: GameConfig = {
|
||||||
loanRepaymentShare: 0.25,
|
loanRepaymentShare: 0.25,
|
||||||
// The deal (CR-013): Halvorsen's Courier for the avatar, his Clipper for the
|
// The deal (CR-013): Halvorsen's Courier for the avatar, his Clipper for the
|
||||||
// veteran, and the Earth bay. The loan is computed from their prices.
|
// veteran, and the Earth bay. The loan is computed from their prices.
|
||||||
// The ships come with a quarter tank (CR-015, user decision): the Clipper earns
|
// The deal's ships come with full tanks (CR-018, decision 0.5.111).
|
||||||
// locally before the player can fuel a Mars run, as before fuel tanks.
|
|
||||||
deal: {
|
deal: {
|
||||||
avatarShipModelId: "courier",
|
avatarShipModelId: "courier",
|
||||||
veteranShipModelId: "clipper",
|
veteranShipModelId: "clipper",
|
||||||
|
|
@ -87,4 +86,15 @@ export const gameConfig: GameConfig = {
|
||||||
pilotHireCost: 2000,
|
pilotHireCost: 2000,
|
||||||
// A quarter of each contract's profit (payout − fuel) goes to a hired pilot (CR-010).
|
// A quarter of each contract's profit (payout − fuel) goes to a hired pilot (CR-010).
|
||||||
pilotProfitShare: 0.25,
|
pilotProfitShare: 0.25,
|
||||||
|
// Independent pilots (CR-020): the Dispatch Office costs about a Lighter; the
|
||||||
|
// player has the first 3 minutes of every 10-minute board; pilots turn round
|
||||||
|
// in 15 game minutes (15 real seconds) and head home after 3 empty boards.
|
||||||
|
independentPilots: {
|
||||||
|
dispatchOfficePrice: 36_000,
|
||||||
|
firstLookRealMinutes: 3,
|
||||||
|
turnaroundGameMinutes: 15,
|
||||||
|
emptyPeriodsBeforeHome: 3,
|
||||||
|
maxCatchUpRealDays: 7,
|
||||||
|
defaultSpendingFloor: 500,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,6 @@ export const gameConfigSchema = z
|
||||||
avatarShipModelId: z.string().min(1),
|
avatarShipModelId: z.string().min(1),
|
||||||
veteranShipModelId: z.string().min(1),
|
veteranShipModelId: z.string().min(1),
|
||||||
bayLocationId: z.string().min(1),
|
bayLocationId: z.string().min(1),
|
||||||
/** How full the deal's ships' tanks are (CR-015, user decision): Halvorsen's leftovers. */
|
|
||||||
}),
|
}),
|
||||||
contractBoardPeriodRealMinutes: z.number().positive(),
|
contractBoardPeriodRealMinutes: z.number().positive(),
|
||||||
/** Past board periods whose boards and accepted contracts are kept (Step 7.3). */
|
/** Past board periods whose boards and accepted contracts are kept (Step 7.3). */
|
||||||
|
|
@ -330,12 +329,35 @@ export const gameConfigSchema = z
|
||||||
pilotHireCost: nonNegativeInt,
|
pilotHireCost: nonNegativeInt,
|
||||||
/** Hired pilots' share of profit (payout − fuel) per contract delivery (decision 0.5.12, CR-010). */
|
/** Hired pilots' share of profit (payout − fuel) per contract delivery (decision 0.5.12, CR-010). */
|
||||||
pilotProfitShare: fraction,
|
pilotProfitShare: fraction,
|
||||||
|
/** Independent pilots (CR-020, decisions 0.5.119–0.5.125). */
|
||||||
|
independentPilots: z.object({
|
||||||
|
/** Credits: the one-time Dispatch Office (0.5.119). */
|
||||||
|
dispatchOfficePrice: positiveInt,
|
||||||
|
/** Real minutes into each board period that its jobs are the player's alone (0.5.122). */
|
||||||
|
firstLookRealMinutes: z.number().positive(),
|
||||||
|
/** Game minutes from a pilot becoming able to act to their next job (CR-020 §2). */
|
||||||
|
turnaroundGameMinutes: z.number().positive(),
|
||||||
|
/** Empty board periods in a row away from home before heading home (0.5.124). */
|
||||||
|
emptyPeriodsBeforeHome: positiveInt,
|
||||||
|
/** Real days of absence catch-up replays at most (0.5.125). */
|
||||||
|
maxCatchUpRealDays: z.number().positive(),
|
||||||
|
/** Credits pilots never spend the company below, by default (0.5.123). */
|
||||||
|
defaultSpendingFloor: positiveInt,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(config) =>
|
(config) =>
|
||||||
config.contractMassBands.reduce((sum, band) => sum + band.count, 0) ===
|
config.contractMassBands.reduce((sum, band) => sum + band.count, 0) ===
|
||||||
config.contractBoardSize,
|
config.contractBoardSize,
|
||||||
{ message: "Mass band counts must add up to contractBoardSize", path: ["contractMassBands"] },
|
{ message: "Mass band counts must add up to contractBoardSize", path: ["contractMassBands"] },
|
||||||
|
)
|
||||||
|
.refine(
|
||||||
|
(config) =>
|
||||||
|
config.independentPilots.firstLookRealMinutes < config.contractBoardPeriodRealMinutes,
|
||||||
|
{
|
||||||
|
message: "The first-look window must be shorter than the board period",
|
||||||
|
path: ["independentPilots", "firstLookRealMinutes"],
|
||||||
|
},
|
||||||
);
|
);
|
||||||
export type GameConfig = z.infer<typeof gameConfigSchema>;
|
export type GameConfig = z.infer<typeof gameConfigSchema>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,3 +96,37 @@ describe("tutorial settings (Step 17.1, decisions 0.5.106, 0.5.107)", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("independent pilots config (Step 20.1, CR-020)", () => {
|
||||||
|
const settings = gameConfig.independentPilots;
|
||||||
|
const withSettings = (change: Partial<typeof settings>) =>
|
||||||
|
gameConfigSchema.safeParse({ ...gameConfig, independentPilots: { ...settings, ...change } })
|
||||||
|
.success;
|
||||||
|
|
||||||
|
it("has every setting, all positive, at the CR's defaults", () => {
|
||||||
|
const lighter = shipModels.find((s) => s.id === "lighter")!;
|
||||||
|
expect(settings).toEqual({
|
||||||
|
dispatchOfficePrice: lighter.price,
|
||||||
|
firstLookRealMinutes: 3,
|
||||||
|
turnaroundGameMinutes: 15,
|
||||||
|
emptyPeriodsBeforeHome: 3,
|
||||||
|
maxCatchUpRealDays: 7,
|
||||||
|
defaultSpendingFloor: 500,
|
||||||
|
});
|
||||||
|
for (const value of Object.values(settings)) expect(value).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects zero or negative values", () => {
|
||||||
|
for (const key of Object.keys(settings) as (keyof typeof settings)[]) {
|
||||||
|
expect(withSettings({ [key]: 0 }), key).toBe(false);
|
||||||
|
expect(withSettings({ [key]: -1 }), key).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the first-look window shorter than the board period", () => {
|
||||||
|
expect(settings.firstLookRealMinutes).toBeLessThan(gameConfig.contractBoardPeriodRealMinutes);
|
||||||
|
expect(withSettings({ firstLookRealMinutes: gameConfig.contractBoardPeriodRealMinutes })).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,14 @@ const validGameConfig = {
|
||||||
skillPointsPerLevel: 1,
|
skillPointsPerLevel: 1,
|
||||||
pilotHireCost: 1000,
|
pilotHireCost: 1000,
|
||||||
pilotProfitShare: 0.25,
|
pilotProfitShare: 0.25,
|
||||||
|
independentPilots: {
|
||||||
|
dispatchOfficePrice: 36_000,
|
||||||
|
firstLookRealMinutes: 3,
|
||||||
|
turnaroundGameMinutes: 15,
|
||||||
|
emptyPeriodsBeforeHome: 3,
|
||||||
|
maxCatchUpRealDays: 7,
|
||||||
|
defaultSpendingFloor: 500,
|
||||||
|
},
|
||||||
tutorialLoanerShipModelId: "company-courier",
|
tutorialLoanerShipModelId: "company-courier",
|
||||||
tutorialRunsBeforeDeal: 5,
|
tutorialRunsBeforeDeal: 5,
|
||||||
tutorialXpMultiplier: 2,
|
tutorialXpMultiplier: 2,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue