import { content } from "@deadhead/content"; import { describe, expect, it } from "vitest"; import { encodeSave } from "../src/index"; import { addPilot, edit, getState, readSave, setCredits, signUp, signUpOwner, useTestApp, } from "./support"; const testApp = useTestApp(); const PRICE = content.config.independentPilots.dispatchOfficePrice; /** An owner with a hired pilot (unassigned, at Earth). */ async function ownerWithPilot() { const { app } = testApp(); const owner = await signUpOwner(app); await addPilot(app, { id: "p1", name: "Ada", locationId: "earth" }); return { app, owner }; } describe("Dispatch Office, pilot modes and orders (Step 21.3, decisions 0.5.122–0.5.124)", () => { it("refuses Independent before the Dispatch Office, and allows it after", async () => { const { app } = await ownerWithPilot(); await expect( app.call("setPilotMode", { pilotId: "p1", mode: "independent" }), ).rejects.toMatchObject({ code: "dispatch-office-required" }); await setCredits(app, PRICE + 10); const bought = await app.call("buyDispatchOffice", {}); expect(bought.dispatchOffice.owned).toBe(true); expect(bought.company.credits).toBe(10); const { pilot } = await app.call("setPilotMode", { pilotId: "p1", mode: "independent" }); expect(pilot.mode).toBe("independent"); expect((await getState(app)).pilots.find((p) => p.id === "p1")!.mode).toBe("independent"); // And back to Manual. expect((await app.call("setPilotMode", { pilotId: "p1", mode: "manual" })).pilot.mode).toBe( "manual", ); }); it("sells the Dispatch Office once, for its price, and not during the tutorial", async () => { const { app } = testApp(); await signUp(app); await setCredits(app, PRICE); await expect(app.call("buyDispatchOffice", {})).rejects.toMatchObject({ code: "tutorial-active", }); const owner = await ownerWithPilot(); await setCredits(owner.app, PRICE - 1); await expect(owner.app.call("buyDispatchOffice", {})).rejects.toMatchObject({ code: "insufficient-funds", }); await setCredits(owner.app, PRICE * 2); await owner.app.call("buyDispatchOffice", {}); await expect(owner.app.call("buyDispatchOffice", {})).rejects.toMatchObject({ code: "dispatch-office-owned", }); expect((await getState(owner.app)).company.credits).toBe(PRICE); }); it("never makes the avatar Independent", async () => { const { app, owner } = await ownerWithPilot(); await setCredits(app, PRICE); await app.call("buyDispatchOffice", {}); await expect( app.call("setPilotMode", { pilotId: owner.avatarId, mode: "independent" }), ).rejects.toMatchObject({ code: "avatar-not-independent" }); }); it("saves and loads a pilot's orders, checking them against content and bays", async () => { const { app } = await ownerWithPilot(); const orders = { destinationIds: ["moon", "mars"], cargoClassIds: ["standard", "heavy"], takePriority: false, minProfit: 250, homeLocationId: "earth", }; expect((await app.call("setPilotOrders", { pilotId: "p1", orders })).pilot.orders).toEqual( orders, ); expect((await getState(app)).pilots.find((p) => p.id === "p1")!.orders).toEqual(orders); expect((await readSave(app)).pilots.find((p) => p.id === "p1")!.orders).toEqual(orders); await expect( app.call("setPilotOrders", { pilotId: "p1", orders: { ...orders, destinationIds: ["pluto"] }, }), ).rejects.toMatchObject({ code: "unknown-location" }); await expect( app.call("setPilotOrders", { pilotId: "p1", orders: { ...orders, cargoClassIds: ["gas"] } }), ).rejects.toMatchObject({ code: "unknown-cargo-class" }); await expect( app.call("setPilotOrders", { pilotId: "p1", orders: { ...orders, homeLocationId: "mars" } }), ).rejects.toMatchObject({ code: "no-bay-at-location" }); }); it("starts pilots on Manual with default orders, home where they were hired", async () => { const { app } = testApp(); const owner = await signUpOwner(app); await edit(app, (save) => save.bays.push({ id: "b2", locationId: "moon", createdAt: 0 })); await setCredits(app, content.config.pilotHireCost); const { pilot } = await app.call("hirePilot", { locationId: "moon" }); expect(pilot.mode).toBe("manual"); expect(pilot.orders).toEqual({ destinationIds: null, cargoClassIds: content.cargoTypes.map((c) => c.id), takePriority: true, minProfit: 0, homeLocationId: "moon", }); const avatar = (await getState(app)).pilots.find((p) => p.id === owner.avatarId)!; expect(avatar.orders.homeLocationId).toBe("earth"); }); it("sets the spending floor", async () => { const { app } = await ownerWithPilot(); expect((await getState(app)).dispatchOffice).toEqual({ owned: false, spendingFloor: content.config.independentPilots.defaultSpendingFloor, }); const result = await app.call("setSpendingFloor", { credits: 2_000 }); expect(result.dispatchOffice.spendingFloor).toBe(2_000); expect((await getState(app)).dispatchOffice.spendingFloor).toBe(2_000); }); it("opens a save from before CR-021 with every pilot Manual and no Dispatch Office", async () => { const { app } = await ownerWithPilot(); const save = await readSave(app); const old = { ...save, saveVersion: 4, company: { id: save.company.id, name: save.company.name, credits: 5, loanBalance: 0 }, pilots: save.pilots.map(({ mode: _mode, orders: _orders, ...rest }) => rest), }; const device = testApp().app; const { state } = await device.call("importSave", { code: await encodeSave(old as never) }); expect(state.dispatchOffice).toEqual({ owned: false, spendingFloor: content.config.independentPilots.defaultSpendingFloor, }); for (const pilot of state.pilots) { expect(pilot.mode).toBe("manual"); expect(pilot.orders.homeLocationId).toBe("earth"); } }); });