Port the start menu, the party menu and the pack outside battle
Checks / web (push) Canceled after 0s

Start opens the original start menu. POKéMON shows the party with the
Pokémon submenu (stats, switching, move order, held items); PACK shows the
four pockets with the field item effects for healing, PP and the Bicycle,
plus giving and tossing. The menu runs on a script draft, and its cursors
persist until a battle clears them. Select keeps the Key Items shortcut.

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 16:10:22 +02:00
1 parent 60e06ae678
commit 1af2beacdc
18 files changed
+1918 -49

No files matched your search

+53 -7
View File
@@ -230,12 +230,13 @@ function nameField(
}
type MartChoice = Extract<
Choice,
{ kind: "martmenu" | "martlist" | "martquantity" }
{ kind: "martmenu" | "martlist" | "martquantity" | "pack" }
>;
function martChoice(choice: Choice): MartChoice | undefined {
return choice.kind === "martmenu" ||
choice.kind === "martlist" ||
choice.kind === "martquantity"
choice.kind === "martquantity" ||
choice.kind === "pack"
? choice
: undefined;
}
@@ -248,13 +249,18 @@ function showMart(world: GameWorld, mart: MartChoice | undefined) {
martMenuKey = "";
return;
}
// The PC's item storage reuses these lists without prices.
// The PC's item storage and the pack reuse these lists without prices.
const shop =
mart.kind === "martmenu" || mart.mode === "buy" || mart.mode === "sell";
get("mart").setAttribute("aria-label", shop ? "Poké Mart" : "Item storage");
mart.kind === "martmenu" ||
(mart.kind !== "pack" && (mart.mode === "buy" || mart.mode === "sell"));
get("mart").setAttribute(
"aria-label",
shop ? "Poké Mart" : mart.kind === "pack" ? "Pack" : "Item storage",
);
get("mart-money").hidden = !shop;
get("mart-money").textContent = `MONEY ¥${world.liveState.money}`;
get("mart-prompt").textContent = mart.kind === "martlist" ? "" : mart.prompt;
get("mart-prompt").textContent =
mart.kind === "martlist" || mart.kind === "pack" ? "" : mart.prompt;
const key = JSON.stringify(mart);
if (key === martMenuKey) return;
martMenuKey = key;
@@ -291,6 +297,40 @@ function showMart(world: GameWorld, mart: MartChoice | undefined) {
);
return;
}
if (mart.kind === "pack") {
// Pocket tabs (left and right on the keyboard), then the pocket's items.
const tabs = mart.pockets.map((pocket, index) => {
const tab = button(pocket.name, () => {
const world = runtime.world;
const choice = world?.dialogue?.choice;
if (choice?.kind !== "pack" || !world) return;
choice.pocket = index;
choice.selected = 0;
showWorld(world);
});
tab.setAttribute("aria-pressed", String(mart.pocket === index));
return tab;
});
const entries = mart.pockets[mart.pocket].entries;
const buttons = entries.map((entry, index) =>
button(
entry.name,
() => runtime.choosePackEntry(mart.pocket, index),
{
detail: entry.owned === undefined ? undefined : `×${entry.owned}`,
selected: mart.selected === index,
},
),
);
if (!entries.length) {
const empty = button("Empty", () => {}, { wide: true });
empty.disabled = true;
buttons.push(empty);
}
buttons.push(button("BACK", () => runtime.choose(-1), { wide: true }));
menu.replaceChildren(...tabs, ...buttons);
return;
}
if (mart.kind === "martlist") {
const buttons = mart.entries.map((entry, index) =>
button(entry.name, () => runtime.choose(index), {
@@ -433,7 +473,13 @@ function showFieldMenu(world: GameWorld) {
function handleFieldInput(button: Button): boolean {
const world = runtime.world;
if (!world || world.battle) return false;
if ((button === "start" || button === "select") && !world.busy) {
// Start opens the start menu; Select, standing in for a registered
// item, opens the Key Items shortcut.
if (button === "start" && !runtime.fieldMenuOpen && !world.busy) {
runtime.openStartMenu();
return true;
}
if (button === "select" && !world.busy) {
runtime.fieldMenuOpen = !runtime.fieldMenuOpen;
fieldCursor = 0;
return true;