Move items with Select and browse the party's moves

In the start menu's pack, Select marks an item and asks where it should
go; A or Select on another row moves it there, merging stacks of the
same item unless either holds 99, and B ends the move. The packs that
GIVE and the PC open ignore Select, as in the original.

Left and right on the MOVE screen show the previous or next party
member's moves, skipping Eggs, unless a move is being moved.

Part of #27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sébastien QUEROLandClaude Opus 5.5 committed 2026-10-03 00:56:51 +02:00
1 parent 397dcef44c
commit e2dc5ba520
9 files changed
+362 -34

No files matched your search

+137
View File
@@ -17,6 +17,7 @@ const NEW_BARK_TOWN = "24:4",
PIKACHU = 25,
RAICHU = 26;
const BICYCLE = 7,
COIN_CASE = 0x36,
ANTIDOTE = 9,
FULL_RESTORE = 0x0e,
POTION = 0x12,
@@ -313,6 +314,142 @@ describe.skipIf(!existsSync(romPath))("the start menu", () => {
expect(world.state.party[0].pp).toEqual([20, 30, 30, 0]);
});
it("shows the other party members' moves with left and right, skipping Eggs", () => {
const world = fixture();
const party = world.state.party;
party.push({ ...structuredClone(party[1]), species: 0xfd }); // EGG
open(world);
pick(world, "POKéMON");
pick(world, "CYNDAQUIL");
expect(pick(world, "MOVE")).toEqual(["CYNDAQUIL :L8"]);
const heading = (direction: "left" | "right") => {
world.moveCursor(direction);
return until(world)[0];
};
expect(heading("right")).toBe("PIDGEY :L4");
expect(menuItems(world)).toEqual(["TACKLE"]);
// Past the last Pokémon that is not an Egg, .cycle_right comes back.
expect(heading("right")).toBe("PIDGEY :L4");
expect(heading("left")).toBe("CYNDAQUIL :L8");
expect(heading("left")).toBe("CYNDAQUIL :L8");
// Not while a move is being moved.
pick(world, "LEER");
world.moveCursor("right");
expect(world.dialogue?.pages[0]).toBe("Where?");
world.advanceDialogue();
until(world);
expect(heading("right")).toBe("PIDGEY :L4");
// B returns to the party menu, on the Pokémon shown last.
world.advanceDialogue();
until(world);
const menu = world.dialogue?.choice;
expect(menu?.kind === "menu" && menu.items[menu.selected]).toBe("PIDGEY");
close(world);
});
it("moves items within a pocket with Select, merging stacks of the same item", () => {
const world = fixture();
world.state.bag.items = [
{ item: POTION, quantity: 5 },
{ item: ANTIDOTE, quantity: 1 },
{ item: ETHER, quantity: 1 },
{ item: POTION, quantity: 98 },
];
world.state.bag.keyItems = [BICYCLE, COIN_CASE];
open(world);
pick(world, "PACK");
const pack = () => {
const choice = world.dialogue?.choice;
if (choice?.kind !== "pack") throw new Error(`Expected the pack, got ${choice?.kind}`);
return choice;
};
const items = () => world.state.bag.items.map((slot) => [slot.item, slot.quantity]);
// Select marks the Potions and asks where; the pocket stays put.
world.useSelect();
until(world);
expect([pack().switching, pack().prompt]).toEqual([
0,
"Where should this\nbe moved to?",
]);
world.moveCursor("right");
expect(pack().pocket).toBe(0);
// A (or Select) on another row moves them there.
world.choosePackEntry(0, 2);
until(world);
expect(pack().switching).toBeUndefined();
expect(pack().selected).toBe(2);
// Nothing commits until the menu closes.
expect(world.save().bag.items[0].item).toBe(POTION);
world.useSelect();
until(world);
world.choosePackEntry(0, 3, true);
until(world);
// 5 + 98: the destination fills to 99 and 4 stay behind.
world.useSelect();
until(world);
world.advanceDialogue(); // B ends the move, still in the pack
until(world);
expect([pack().kind, pack().switching]).toEqual(["pack", undefined]);
world.choosePackEntry(0, 1, true);
until(world);
world.choosePackEntry(0, 1, true); // onto itself: nothing moves
until(world);
world.moveCursor("right");
world.moveCursor("right");
world.choosePackEntry(2, 1, true);
until(world);
world.choosePackEntry(2, 0, true);
until(world);
close(world);
expect(items()).toEqual([
[ANTIDOTE, 1],
[ETHER, 1],
[POTION, 4],
[POTION, 99],
]);
expect(world.state.bag.keyItems).toEqual([COIN_CASE, BICYCLE]);
// Two stacks that fit merge into one; a full stack never merges.
world.state.bag.items = [
{ item: POTION, quantity: 2 },
{ item: POTION, quantity: 3 },
{ item: ETHER, quantity: 99 },
{ item: ETHER, quantity: 1 },
];
open(world);
pick(world, "PACK");
world.choosePackEntry(0, 0, true);
until(world);
world.choosePackEntry(0, 1, true);
until(world);
world.choosePackEntry(0, 2, true);
until(world);
world.choosePackEntry(0, 1, true);
until(world);
close(world);
expect(items()).toEqual([
[POTION, 5],
[ETHER, 1],
[ETHER, 99],
]);
});
it("does not move items in the pack that GIVE opens", () => {
const world = fixture();
open(world);
pick(world, "POKéMON");
pick(world, "CYNDAQUIL");
pick(world, "ITEM");
pick(world, "GIVE");
world.useSelect();
until(world);
const choice = world.dialogue?.choice;
expect(choice?.kind).toBe("pack");
expect(choice?.kind === "pack" && choice.switching).toBeUndefined();
close(world);
expect(world.state.bag.items.map((slot) => slot.item)).toEqual([POTION, ANTIDOTE]);
});
it("gives, swaps and takes held items", () => {
const world = fixture();
world.state.bag.items.push({ item: LEFTOVERS, quantity: 1 });