A new game now runs NewGame's InitGender and OakSpeech natively before the bedroom: Boy or Girl, InitClock's hour and minute steppers with their YES/NO confirmations, Oak's speech with Wooper, and NamePlayer's preset names or naming field (a blank name takes CHRIS or KRIS). Texts, menus and time strings come from a new INTRO profile table verified by check:reference; Oak's and the player's pictures decode from TrainerPicPointers, ChrisPic/KrisPic and TrainerPalettes. The chosen time is kept as a minute offset from the device clock, which time of day, the weekday, NPC hours and the daily reset now read. The intro runs only when nothing is saved; a save made during it starts it again. Saves gain intro and clock.minuteOffset, migrated by absence. The shell's name and character fields are gone. Intentional change: the steppers start at the device time rather than 10:00. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5a0fe1e313
commit
b13fd20be4
28 files changed
+975
-101
No files matched your search
@@ -0,0 +1,131 @@
|
||||
import { test, expect, type Page } from "@playwright/test";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const romPath = resolve(
|
||||
process.env.CRYSTAL_ROM_PATH ??
|
||||
"Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc",
|
||||
);
|
||||
test.beforeEach(() =>
|
||||
test.skip(!existsSync(romPath), "Requires the local user ROM."),
|
||||
);
|
||||
|
||||
async function start(page: Page) {
|
||||
await page.clock.install({ time: new Date("2026-09-25T14:37:00") });
|
||||
await page.goto("/");
|
||||
await page.locator("#rom-file").setInputFiles(romPath);
|
||||
await expect(page.locator("#status")).toContainText("verified");
|
||||
await page.clock.pauseAt(new Date("2026-09-25T14:37:05"));
|
||||
await page.locator("#play").click();
|
||||
}
|
||||
/** Press A until the dialogue shows `text`. */
|
||||
async function advanceTo(page: Page, text: string) {
|
||||
const dialogue = page.locator("#dialogue-text");
|
||||
for (let guard = 0; guard < 40; guard++) {
|
||||
if ((await dialogue.textContent())?.includes(text)) return;
|
||||
await page.keyboard.press("KeyX");
|
||||
}
|
||||
await expect(dialogue).toContainText(text);
|
||||
}
|
||||
|
||||
test("a new game asks for gender, the clock and a name before the bedroom", async ({
|
||||
page,
|
||||
}) => {
|
||||
const errors: string[] = [];
|
||||
page.on("pageerror", (error) => errors.push(error.message));
|
||||
await start(page);
|
||||
const screen = page.locator("#game-screen"),
|
||||
choice = page.locator("#dialogue-choice"),
|
||||
text = page.locator("#dialogue-text"),
|
||||
picture = page.locator("#pokepic-canvas");
|
||||
await expect(screen).toHaveAttribute("data-intro", "true");
|
||||
await expect(page.locator("#intro-screen")).toBeVisible();
|
||||
await expect(text).toHaveText("Are you a boy?\nOr are you a girl?");
|
||||
// Down, then A chooses "Girl"; B is ignored.
|
||||
await page.keyboard.press("KeyZ");
|
||||
await page.keyboard.press("ArrowDown");
|
||||
await expect(choice.getByRole("option", { name: "Girl" })).toHaveAttribute(
|
||||
"aria-selected",
|
||||
"true",
|
||||
);
|
||||
await page.keyboard.press("KeyX");
|
||||
await advanceTo(page, "What time is it?");
|
||||
// The hour starts at the device time; Up three times is 5 PM.
|
||||
const value = choice.locator("output");
|
||||
await expect(value).toHaveText("DAY 2 o'clock");
|
||||
for (let i = 0; i < 3; i++) await page.keyboard.press("ArrowUp");
|
||||
await expect(value).toHaveText("DAY 5 o'clock");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(text).toHaveText("What?\nDAY 5 o'clock?");
|
||||
await choice.getByRole("button", { name: "YES" }).click();
|
||||
await expect(text).toHaveText("How many minutes?");
|
||||
await expect(value).toHaveText("37 min.");
|
||||
await choice.getByRole("button", { name: "Earlier" }).click();
|
||||
await expect(value).toHaveText("36 min.");
|
||||
await choice.getByRole("button", { name: "OK" }).click();
|
||||
await expect(text).toHaveText("Whoa! 36 min.?");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(text).toHaveText("DAY 5:36!\nYikes! I over-\nslept!");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(picture).toHaveAttribute("aria-label", "Professor Oak");
|
||||
await advanceTo(page, "This world is in-");
|
||||
await expect(picture).toHaveAttribute("aria-label", "WOOPER");
|
||||
await advanceTo(page, "Now, what did you");
|
||||
await expect(picture).toHaveAttribute("aria-label", "Kris");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(choice.getByRole("option")).toHaveText([
|
||||
"NEW NAME",
|
||||
"KRIS",
|
||||
"AMANDA",
|
||||
"JUANA",
|
||||
"JODI",
|
||||
]);
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(text).toHaveText("YOUR NAME?");
|
||||
await choice.locator("input").fill("LYRA");
|
||||
await choice.locator("input").press("Enter");
|
||||
await expect(text).toHaveText("LYRA, are you\nready?");
|
||||
await advanceTo(page, "I'll be seeing you");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(screen).toHaveAttribute("data-intro", "false");
|
||||
await expect(page.locator("#intro-screen")).toBeHidden();
|
||||
await expect(page.locator("#pokepic")).toBeHidden();
|
||||
await expect(screen).toHaveAttribute("data-map", "24:7");
|
||||
|
||||
await page.locator("#tab-saves").click();
|
||||
const download = page.waitForEvent("download");
|
||||
await page.locator("#backup").click();
|
||||
const record = JSON.parse(
|
||||
readFileSync((await (await download).path())!, "utf8"),
|
||||
);
|
||||
const save = JSON.parse(Buffer.from(record.state, "base64").toString("utf8"));
|
||||
expect(save).toMatchObject({
|
||||
intro: false,
|
||||
name: "LYRA",
|
||||
avatar: "kris",
|
||||
clock: { minuteOffset: 17 * 60 + 36 - (14 * 60 + 37) },
|
||||
});
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test("a session saved during the introduction starts it again", async ({
|
||||
page,
|
||||
}) => {
|
||||
await start(page);
|
||||
await page.keyboard.press("KeyX");
|
||||
await advanceTo(page, "What time is it?");
|
||||
// Leaving the game tab pauses and saves.
|
||||
await page.locator("#tab-saves").click();
|
||||
await expect(page.locator("#save-status")).toContainText("Session saved");
|
||||
await page.reload();
|
||||
await page.locator("#rom-file").setInputFiles(romPath);
|
||||
await expect(page.locator("#status")).toContainText("saved session");
|
||||
await page.locator("#play").click();
|
||||
await expect(page.locator("#game-screen")).toHaveAttribute(
|
||||
"data-intro",
|
||||
"true",
|
||||
);
|
||||
await expect(page.locator("#dialogue-text")).toHaveText(
|
||||
"Are you a boy?\nOr are you a girl?",
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user