deadhead-space-freight-tycoon/packages/engine/test/live-play.test.ts

91 lines
3.0 KiB
TypeScript

import { content } from "@deadhead/content";
import { MS_PER_MINUTE } from "@deadhead/sim";
import { describe, expect, it, vi } from "vitest";
import { turnaroundMs } from "../src/game/independent";
import { ResolutionLoop, type LoopHost } from "../src/index";
import {
addPilot,
addShip,
newTestApp,
putBoard,
readSave,
setCredits,
signUpOwner,
testContract,
TEST_NOW,
type TestApp,
} from "./support";
const FIRST_LOOK = content.config.independentPilots.firstLookRealMinutes * MS_PER_MINUTE;
/** An owner with the Dispatch Office and two Couriers at Earth with hired (Manual) pilots. */
async function setup(): Promise<TestApp> {
const test = newTestApp();
test.clock.now = TEST_NOW + FIRST_LOOK;
const { app } = test;
await signUpOwner(app);
await setCredits(app, content.config.independentPilots.dispatchOfficePrice + 50_000);
await app.call("buyDispatchOffice", {});
for (const id of ["pa", "pb"]) {
await addShip(app, { id: `ship-${id}`, modelId: "courier", name: `Courier ${id}` });
await addPilot(app, { id, name: `Pilot ${id}`, assignedShipId: `ship-${id}` });
}
await putBoard(
app,
[
testContract({ id: "earth:best", payout: 5_000 }),
testContract({ id: "earth:next", payout: 3_000 }),
],
test.clock.now,
);
return test;
}
const takenBy = async (test: TestApp, pilotId: string) =>
(await readSave(test.app)).dispatches.find((d) => d.pilotId === pilotId && d.independent)?.record
.contract?.id;
describe("Independent pilots in live play (Step 21.5, decision 0.5.126)", () => {
it("leaves the contract the player has open, and takes it once it's closed", async () => {
const test = await setup();
const { app, clock } = test;
app.setViewedContract("earth:best");
await app.call("setPilotMode", { pilotId: "pa", mode: "independent" });
clock.now += turnaroundMs();
await app.call("getPlayerState", {});
expect(await takenBy(test, "pa")).toBe("earth:next");
// The player closes the sheet without taking it: the next pilot may.
app.setViewedContract(null);
await app.call("setPilotMode", { pilotId: "pb", mode: "independent" });
clock.now += turnaroundMs();
await app.call("getPlayerState", {});
expect(await takenBy(test, "pb")).toBe("earth:best");
});
it("runs pilots' decisions from the resolution loop while the app is open", async () => {
const test = await setup();
await test.app.call("setPilotMode", { pilotId: "pa", mode: "independent" });
let tick: () => void = () => {};
const host: LoopHost = {
setInterval: (fn) => {
tick = fn;
return 1;
},
clearInterval: () => {},
onResume: () => () => {},
};
const onChange = vi.fn();
const loop = new ResolutionLoop(test.app, host, onChange);
await loop.start();
expect(await takenBy(test, "pa")).toBeUndefined();
test.clock.now += turnaroundMs();
tick();
await loop.tick();
expect(await takenBy(test, "pa")).toBe("earth:best");
expect(onChange).toHaveBeenCalled();
loop.stop();
});
});