import { expect } from "vitest"; import type { GameWorld } from "../src/game/world/world"; import { settle } from "./world-driver"; /** Acknowledge pages up to the next choice; returns the pages seen. */ export function until(world: GameWorld) { const pages: string[] = []; for (let tick = 0; tick < 500; tick++) { const dialogue = world.dialogue; if (!dialogue) { if (!world.busy) return pages; world.tick(); continue; } const page = dialogue.pages[dialogue.index].replace(/\n/g, " "); if (page && pages.at(-1) !== page) pages.push(page); const last = dialogue.index === dialogue.pages.length - 1; if (last && dialogue.choice) return pages; if (last && dialogue.holdTicks !== undefined) world.tick(); else world.advanceDialogue(); } throw new Error("No choice appeared."); } export function menuItems(world: GameWorld) { const choice = world.dialogue?.choice; return choice?.kind === "menu" ? choice.items : []; } /** Choose the menu item matching `label` (the whole label, or a regex). */ export function pick(world: GameWorld, label: string | RegExp) { const items = menuItems(world); const index = items.findIndex((item) => typeof label === "string" ? item === label : label.test(item), ); expect(index, `${label} in ${items.join(", ")}`).toBeGreaterThanOrEqual(0); world.choose(index); return until(world); } /** Choose the pack entry for `item` in its pocket. */ export function pickItem(world: GameWorld, item: number) { const choice = world.dialogue?.choice; if (choice?.kind !== "pack") throw new Error(`Expected the pack, got ${choice?.kind}`); for (const [pocket, { entries }] of choice.pockets.entries()) { const index = entries.findIndex((entry) => entry.item === item); if (index >= 0) { world.choosePackEntry(pocket, index); return until(world); } } throw new Error(`Item ${item} is not in the pack.`); } export function open(world: GameWorld) { expect(world.openStartMenu()).toBe(true); return until(world); } /** PACK, the item, then USE: the pages shown up to the next choice. */ export function useItem(world: GameWorld, item: number) { open(world); pick(world, "PACK"); pickItem(world, item); return pick(world, "USE"); } /** B until every menu has closed; returns the pages shown meanwhile. */ export function close(world: GameWorld) { const pages: string[] = []; for (let i = 0; i < 20 && world.busy; i++) { if (world.dialogue?.choice) world.advanceDialogue(); pages.push(...until(world)); } pages.push(...settle(world)); expect(world.diagnostic).toBe(""); return pages; }