Step 10F.2: board preview and open slot config
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
bd2d2df14d
commit
dc24bdeb7a
|
|
@ -194,3 +194,4 @@ Spec: `memory-bank/change-requests/CR-005-ui-overhaul.md`. New decisions recorde
|
||||||
Spec: `memory-bank/change-requests/CR-006-boards-that-fit.md`. New decisions recorded as plan §0.5.52–0.5.53.
|
Spec: `memory-bank/change-requests/CR-006-boards-that-fit.md`. New decisions recorded as plan §0.5.52–0.5.53.
|
||||||
|
|
||||||
- [x] **Step 10F.1 — Client: weight warning.** `carryLimit(state, locationId)` (`LocationBoards.tsx`): the largest capacity among the player's ships at the location, else the whole fleet, else null. Contract cards show the mass in rose and a rose "Too heavy" pill when it is over that limit. Test: `weight-warning.test.tsx`.
|
- [x] **Step 10F.1 — Client: weight warning.** `carryLimit(state, locationId)` (`LocationBoards.tsx`): the largest capacity among the player's ships at the location, else the whole fleet, else null. Contract cards show the mass in rose and a rose "Too heavy" pill when it is over that limit. Test: `weight-warning.test.tsx`.
|
||||||
|
- [x] **Step 10F.2 — Content: board config.** GameConfig gains `boardPreviewSlots` (1) and `boardOpenSlotsPerBand` (1), non-negative integers. Test: `schemas.test.ts` (missing-field coverage plus a negative/fractional rejection test).
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@ export const gameConfig: GameConfig = {
|
||||||
{ min: 21, max: 100, count: 3 },
|
{ min: 21, max: 100, count: 3 },
|
||||||
{ min: 101, max: 400, count: 2 },
|
{ min: 101, max: 400, count: 2 },
|
||||||
],
|
],
|
||||||
|
// Boards that fit the fleet (CR-006): a band no ship can carry keeps one
|
||||||
|
// preview job; flyable bands keep one open job, the rest fit a ship and pilot.
|
||||||
|
boardPreviewSlots: 1,
|
||||||
|
boardOpenSlotsPerBand: 1,
|
||||||
// Mars at its closest needs no Long Haul; Jupiter at its farthest needs level 5.
|
// Mars at its closest needs no Long Haul; Jupiter at its farthest needs level 5.
|
||||||
longHaulCutoffsMillionKm: [100, 250, 400, 600, 800],
|
longHaulCutoffsMillionKm: [100, 250, 400, 600, 800],
|
||||||
xpPerDelivery: { base: 10, perMillionKm: 0.2, perCargoTier: 5 },
|
xpPerDelivery: { base: 10, perMillionKm: 0.2, perCargoTier: 5 },
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,13 @@ export const gameConfigSchema = z
|
||||||
.refine((band) => band.min <= band.max, { message: "min must not exceed max" }),
|
.refine((band) => band.min <= band.max, { message: "min must not exceed max" }),
|
||||||
)
|
)
|
||||||
.min(1),
|
.min(1),
|
||||||
|
/**
|
||||||
|
* Contracts kept in a mass band the player's fleet can't carry: a preview
|
||||||
|
* of bigger ships' work (decision 0.5.52). The rest move to flyable bands.
|
||||||
|
*/
|
||||||
|
boardPreviewSlots: nonNegativeInt,
|
||||||
|
/** Contracts per flyable band left open (skill-gated, first contact) rather than fit (0.5.52). */
|
||||||
|
boardOpenSlotsPerBand: nonNegativeInt,
|
||||||
/** One cut-off per Long Haul level (decision 0.5.7). */
|
/** One cut-off per Long Haul level (decision 0.5.7). */
|
||||||
longHaulCutoffsMillionKm: increasingList,
|
longHaulCutoffsMillionKm: increasingList,
|
||||||
xpPerDelivery: z.object({
|
xpPerDelivery: z.object({
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ const validGameConfig = {
|
||||||
{ min: 1, max: 20, count: 4 },
|
{ min: 1, max: 20, count: 4 },
|
||||||
{ min: 21, max: 100, count: 4 },
|
{ min: 21, max: 100, count: 4 },
|
||||||
],
|
],
|
||||||
|
boardPreviewSlots: 1,
|
||||||
|
boardOpenSlotsPerBand: 1,
|
||||||
longHaulCutoffsMillionKm: [100, 200, 300, 400, 500],
|
longHaulCutoffsMillionKm: [100, 200, 300, 400, 500],
|
||||||
xpPerDelivery: { base: 10, perMillionKm: 0.1, perCargoTier: 5 },
|
xpPerDelivery: { base: 10, perMillionKm: 0.1, perCargoTier: 5 },
|
||||||
pilotLevelXpThresholds: [100, 250, 500],
|
pilotLevelXpThresholds: [100, 250, 500],
|
||||||
|
|
@ -151,6 +153,20 @@ describe("GameConfig refinements", () => {
|
||||||
expect(gameConfigSchema.safeParse(config).success).toBe(false);
|
expect(gameConfigSchema.safeParse(config).success).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("rejects negative or fractional board slot counts (CR-006)", () => {
|
||||||
|
for (const bad of [-1, 0.5]) {
|
||||||
|
expect(
|
||||||
|
gameConfigSchema.safeParse({ ...validGameConfig, boardPreviewSlots: bad }).success,
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
gameConfigSchema.safeParse({ ...validGameConfig, boardOpenSlotsPerBand: bad }).success,
|
||||||
|
).toBe(false);
|
||||||
|
}
|
||||||
|
expect(
|
||||||
|
gameConfigSchema.safeParse({ ...validGameConfig, boardOpenSlotsPerBand: 0 }).success,
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("rejects Long Haul cut-offs that are not increasing", () => {
|
it("rejects Long Haul cut-offs that are not increasing", () => {
|
||||||
const config = { ...validGameConfig, longHaulCutoffsMillionKm: [100, 100, 300] };
|
const config = { ...validGameConfig, longHaulCutoffsMillionKm: [100, 100, 300] };
|
||||||
expect(gameConfigSchema.safeParse(config).success).toBe(false);
|
expect(gameConfigSchema.safeParse(config).success).toBe(false);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue