deadhead-space-freight-tycoon/packages/sim/test/resolution.test.ts

124 lines
3.7 KiB
TypeScript

import { content } from "@deadhead/content";
import { describe, expect, it } from "vitest";
import {
createDispatchRecord,
createRepositionRecord,
resolveDispatch,
type Contract,
type PilotState,
type ResolveInput,
type ShipState,
} from "../src/index";
const now = Date.parse("2100-01-01T00:30:00Z");
const contract: Contract = {
id: "c1",
originId: "earth",
destinationId: "moon",
cargoTypeId: "standard",
tier: 0,
massTonnes: 10,
payout: 2000,
requiredSkill: null,
requiredLongHaulLevel: 0,
offeredAt: Date.parse("2100-01-01T00:00:00Z"),
expiresAt: Date.parse("2100-01-01T01:00:00Z"),
firstContact: false,
};
const ship: ShipState = { id: "s1", modelId: "courier", locationId: "earth", inFlight: false };
const pilot: PilotState = {
id: "p1",
isAvatar: false,
assignedShipId: "s1",
inFlight: false,
skills: {},
restEndsAt: null,
};
const record = createDispatchRecord(
{ id: "d1", ship, pilot, contract, tutorial: false, now, worldSeed: 7 },
content,
);
const arrived: ResolveInput = {
record,
pilotIsAvatar: false,
discoveredIds: new Set(["earth", "moon"]),
loanBalance: 0,
pilotDutyHours: 0,
pilotSkills: {},
now: record.arrivalAt,
};
const resolve = (input: ResolveInput) => {
const result = resolveDispatch(input, content);
if (result.status !== "resolved") throw new Error("expected a resolution");
return result.resolution;
};
describe("dispatch resolution", () => {
it("reports in flight before arrival", () => {
expect(resolveDispatch({ ...arrived, now: record.arrivalAt - 1 }, content)).toEqual({
status: "in-flight",
});
});
it("resolves at and after arrival, moving the ship to the destination", () => {
expect(resolve(arrived).shipLocationId).toBe("moon");
expect(resolveDispatch({ ...arrived, now: record.arrivalAt + 60_000 }, content).status).toBe(
"resolved",
);
});
it("deducts pilot pay for a hired pilot and grants XP", () => {
const resolution = resolve(arrived);
expect(resolution.credits).toBe(2000 - content.config.pilotPayPerDelivery);
expect(resolution.pilotXp).toBeGreaterThan(0);
});
it("deducts no pilot pay when the avatar flies", () => {
expect(resolve({ ...arrived, pilotIsAvatar: true }).credits).toBe(2000);
});
it("pays exactly the stored tutorial share on a tutorial run", () => {
const tutorialRecord = createDispatchRecord(
{
id: "t1",
ship: { ...ship, id: "loaner" },
pilot: { ...pilot, id: "avatar", isAvatar: true, assignedShipId: null },
contract: { ...contract, payout: 1000 },
tutorial: true,
now,
worldSeed: 7,
},
content,
);
const resolution = resolve({ ...arrived, record: tutorialRecord, pilotIsAvatar: true });
expect(resolution.credits).toBe(1000);
expect(resolution.shipLocationId).toBe("moon");
});
it("marks an undiscovered destination as discovered", () => {
expect(resolve(arrived).discoversDestination).toBe(false);
expect(resolve({ ...arrived, discoveredIds: new Set(["earth"]) }).discoversDestination).toBe(
true,
);
});
it("gives no credits or XP for a reposition", () => {
const reposition = createRepositionRecord(
{ id: "r1", ship, pilot, destinationId: "moon", now, worldSeed: 7 },
content,
);
expect(resolve({ ...arrived, record: reposition, now: reposition.arrivalAt })).toMatchObject({
credits: 0,
loanRepayment: 0,
pilotXp: 0,
shipLocationId: "moon",
discoversDestination: false,
});
});
it("returns identical results when resolving the same dispatch twice", () => {
expect(resolveDispatch(arrived, content)).toEqual(resolveDispatch(arrived, content));
});
});