156 lines
5.9 KiB
TypeScript
156 lines
5.9 KiB
TypeScript
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");
|
||
}
|
||
});
|
||
});
|