91 lines
3.0 KiB
TypeScript
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();
|
|
});
|
|
});
|