75 lines
2.5 KiB
TypeScript
75 lines
2.5 KiB
TypeScript
import { content } from "@deadhead/content";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
createDispatchRecord,
|
|
deliveryXp,
|
|
deliveryXpFor,
|
|
generateTutorialBoard,
|
|
pilotLevel,
|
|
routeDistance,
|
|
skillPointsAvailable,
|
|
type Contract,
|
|
} from "../src/index";
|
|
|
|
const { config } = content;
|
|
const loc = (id: string) => content.location(id)!;
|
|
const now = Date.parse("2100-01-01T00:30:00Z");
|
|
const pilot = {
|
|
id: "avatar",
|
|
isAvatar: true,
|
|
assignedShipId: null,
|
|
inFlight: false,
|
|
skills: {},
|
|
restEndsAt: null,
|
|
};
|
|
const loaner = { id: "loaner", modelId: "company-courier", locationId: "earth", inFlight: false };
|
|
|
|
/** The XP a contract would pay on a tutorial run. */
|
|
function tutorialXp(contract: Contract): number {
|
|
const record = createDispatchRecord(
|
|
{
|
|
id: "t",
|
|
ship: { ...loaner, locationId: contract.originId },
|
|
pilot,
|
|
contract,
|
|
tutorial: true,
|
|
now,
|
|
worldSeed: 1,
|
|
},
|
|
content,
|
|
);
|
|
return deliveryXpFor(record, contract.tier, content);
|
|
}
|
|
|
|
describe("tutorial XP (Step 17.2, decision 0.5.107)", () => {
|
|
it("grants exactly the multiplier times a normal run's XP for the same route and cargo", () => {
|
|
const [contract] = generateTutorialBoard(
|
|
{ loanerLocationId: "earth", runsCompleted: 0, periodIndex: 0 },
|
|
content,
|
|
);
|
|
const normal = deliveryXp(routeDistance(loc("earth"), loc("moon")), contract!.tier, config);
|
|
expect(tutorialXp(contract!)).toBe(normal * config.tutorialXpMultiplier);
|
|
expect(config.tutorialXpMultiplier).toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it("brings the avatar to level 1 with a skill point after five runs of the lowest-XP jobs", () => {
|
|
// Every board period gives a different board; take the worst case of many.
|
|
for (let periodIndex = 0; periodIndex < 50; periodIndex++) {
|
|
let xp = 0;
|
|
let at: "earth" | "moon" = "earth";
|
|
for (let run = 0; run < config.tutorialRunsBeforeDeal; run++) {
|
|
const board = generateTutorialBoard(
|
|
{ loanerLocationId: at, runsCompleted: run, periodIndex: periodIndex + run },
|
|
content,
|
|
);
|
|
// Run 2 offers the scripted "Unmarked crates" job; the cheapest choice is still taken.
|
|
xp += Math.min(...board.map(tutorialXp));
|
|
at = at === "earth" ? "moon" : "earth";
|
|
}
|
|
const level = pilotLevel(xp, config);
|
|
expect(level, `period ${periodIndex}: ${xp} XP`).toBeGreaterThanOrEqual(1);
|
|
expect(skillPointsAvailable(level, 0, config)).toBeGreaterThanOrEqual(1);
|
|
}
|
|
});
|
|
});
|