Port the introduction's naming and clock screens
Checks / web (push) Canceled after 0s

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:
Sébastien QUEROLandClaude Opus 5.5 committed 2026-09-30 09:53:52 +02:00
1 parent 5a0fe1e313
commit b13fd20be4
28 files changed
+975 -101

No files matched your search

+9 -3
View File
@@ -8,6 +8,7 @@ import { GameWorld } from "../src/game/world/world";
import type { Direction } from "../src/game/world/types";
import { VECTORS } from "../src/game/world/types";
import { settle } from "./world-driver";
import { completeIntro } from "./intro-driver";
const romPath = resolve(
process.env.CRYSTAL_ROM_PATH ??
@@ -59,10 +60,13 @@ test("local maps follow a stairs transition and importing a previous position",
await page.goto("/");
await page.locator("#rom-file").setInputFiles(romPath);
await expect(page.locator("#status")).toContainText("verified");
await page.locator("#play").click();
await completeIntro(page);
await page.locator("#tab-saves").click();
const downloaded = page.waitForEvent("download");
await page.locator("#backup").click();
const backup = await downloaded;
await page.locator("#tab-game").click();
await page.locator("#play").click();
for (const direction of path!) {
const [dx, dy] = VECTORS[direction];
@@ -128,6 +132,7 @@ test("Mom's opening scene runs with native keyboard and pointer choices", async
await page.locator("#rom-file").setInputFiles(romPath);
await expect(page.locator("#status")).toContainText("verified");
await page.locator("#play").click();
await completeIntro(page);
const screen = page.locator("#game-screen");
const step = async (key: string, map: string, x: number, y: number) => {
await page.keyboard.down(key);
@@ -437,17 +442,18 @@ test("native player loads the actual ROM without loading an emulator and persist
page.on("request", (request) => requests.push(request.url()));
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/");
await page.locator("#player-name").fill("KAWA");
await page.locator("#player-avatar").selectOption("kris");
await page.locator("#rom-file").setInputFiles(romPath);
await expect(page.locator("#status")).toContainText("verified");
await expect(page.locator("#game-screen")).toHaveAttribute(
"data-map",
"24:7",
);
await expect(page.locator("#minimap")).toBeVisible();
await expect(page.locator("#minimap")).toHaveAttribute("data-map", "24:7");
await page.locator("#play").click();
// The introduction covers the world and its minimap until it ends.
await expect(page.locator("#minimap")).toBeHidden();
await completeIntro(page, { girl: true, name: "KAWA" });
await expect(page.locator("#minimap")).toBeVisible();
await expect(page.locator("#game-screen")).toHaveAttribute("data-x", "3");
await page.keyboard.down("ArrowUp");
await expect(page.locator("#game-screen")).toHaveAttribute("data-y", "2");