Port field moves from the menu, SEL with Select, and moving Pokémon in Bill's PC
Checks / web (push) Canceled after 0s
Checks / web (push) Canceled after 0s
Field moves chosen in the party menu run their original checks and the ROM's from-menu scripts (Cut, Surf, Strength, Flash, Whirlpool, Waterfall, Sweet Scent), and Softboiled and Milk Drink share HP. SEL registers a key item that Select then uses, replacing the Key Items shortcut, and Bill's PC moves Pokémon between the party and every box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
45275ba83b
commit
3456aa4e11
25 files changed
+1129
-186
No files matched your search
@@ -46,25 +46,34 @@ async function backup(page: Page) {
|
||||
state: JSON.parse(Buffer.from(record.state, "base64").toString("utf8")),
|
||||
};
|
||||
}
|
||||
test("Select opens Key Items, freezes movement, and uses the Bicycle with keyboard and touch", async ({
|
||||
test("SEL registers the Bicycle, and Select rides it with keyboard and touch", async ({
|
||||
page,
|
||||
}) => {
|
||||
await start(page);
|
||||
await page.keyboard.press("ShiftLeft");
|
||||
await expect(page.locator("#field-menu")).toBeVisible();
|
||||
await page.keyboard.down("ArrowDown");
|
||||
await page.clock.runFor(1000);
|
||||
await page.keyboard.up("ArrowDown");
|
||||
await expect(page.locator("#game-screen")).toHaveAttribute("data-y", "6");
|
||||
await page
|
||||
.locator("#field-items")
|
||||
.getByRole("button", { name: "BICYCLE", exact: true })
|
||||
.click();
|
||||
const text = page.locator("#dialogue-text"),
|
||||
choices = page.locator("#dialogue-choice"),
|
||||
pack = page.locator("#mart");
|
||||
await page.keyboard.press("Enter");
|
||||
await choices.getByRole("option", { name: /^PACK/ }).click();
|
||||
// Right twice: the Balls pocket, then Key Items.
|
||||
await page.keyboard.press("ArrowRight");
|
||||
await page.keyboard.press("ArrowRight");
|
||||
await pack.getByRole("button", { name: "BICYCLE", exact: true }).click();
|
||||
await choices.getByRole("option", { name: "SEL", exact: true }).click();
|
||||
await expect(text).toContainText("Registered the");
|
||||
await page.keyboard.press("KeyX");
|
||||
await page.keyboard.press("KeyZ");
|
||||
await page.keyboard.press("KeyZ");
|
||||
await expect(choices).toBeHidden();
|
||||
await page.keyboard.press("ShiftLeft");
|
||||
await page.clock.runFor(500);
|
||||
await expect(page.locator("#field-menu")).toBeHidden();
|
||||
await expect(page.locator("#game-notice")).toHaveText("");
|
||||
expect((await backup(page)).state.playerState).toBe(1);
|
||||
await page.locator("#tab-game").click();
|
||||
await page.locator("#play").click();
|
||||
await page.getByRole("button", { name: "select", exact: true }).click();
|
||||
await page.clock.runFor(500);
|
||||
expect((await backup(page)).state.playerState).toBe(0);
|
||||
});
|
||||
test("the bedroom PC puts away a decoration and saves the result", async ({
|
||||
page,
|
||||
@@ -91,19 +100,29 @@ test("the bedroom PC puts away a decoration and saves the result", async ({
|
||||
await expect(page.locator("#game-notice")).toHaveText("");
|
||||
expect((await backup(page)).state.decorations.bed).toBe(0);
|
||||
});
|
||||
test("B cancels the pack and an indoor Bicycle attempt leaves the player walking", async ({
|
||||
test("Select without a registered item explains SEL, and the Bicycle stays packed indoors", async ({
|
||||
page,
|
||||
}) => {
|
||||
await start(page, true);
|
||||
const text = page.locator("#dialogue-text"),
|
||||
choices = page.locator("#dialogue-choice"),
|
||||
pack = page.locator("#mart");
|
||||
await page.keyboard.press("ShiftLeft");
|
||||
await expect(text).toContainText("An item in your");
|
||||
await page.keyboard.press("KeyX");
|
||||
await page.keyboard.press("KeyX");
|
||||
await page.keyboard.press("Enter");
|
||||
await choices.getByRole("option", { name: /^PACK/ }).click();
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
await pack.getByRole("button", { name: "BICYCLE", exact: true }).click();
|
||||
await choices.getByRole("option", { name: "USE", exact: true }).click();
|
||||
await expect(text).toContainText("This isn't the");
|
||||
await page.keyboard.press("KeyX");
|
||||
await pack.getByRole("button", { name: "BACK", exact: true }).click();
|
||||
await page.keyboard.press("KeyZ");
|
||||
await expect(page.locator("#field-menu")).toBeHidden();
|
||||
await page.keyboard.press("ShiftLeft");
|
||||
await page
|
||||
.locator("#field-items")
|
||||
.getByRole("button", { name: "BICYCLE", exact: true })
|
||||
.click();
|
||||
await expect(page.locator("#game-notice")).toContainText("can’t use");
|
||||
await page.clock.runFor(500);
|
||||
await expect(page.locator("#game-notice")).toHaveText("");
|
||||
expect((await backup(page)).state.playerState).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
@@ -419,6 +419,169 @@ describe.skipIf(!existsSync(romPath))("field moves with the local ROM", () => {
|
||||
expect(used).toContain("used\nWHIRLPOOL!");
|
||||
expect(world.map.blocks).not.toEqual(before);
|
||||
});
|
||||
|
||||
/** Start → POKéMON → the first Pokémon → `move`; returns the pages
|
||||
* shown until the menu closed or a choice (the party menu) returned. */
|
||||
const useFromMenu = (world: GameWorld, move: string) => {
|
||||
expect(world.openStartMenu()).toBe(true);
|
||||
const choose = (label: string) => {
|
||||
const choice = world.dialogue?.choice;
|
||||
if (choice?.kind !== "menu") throw new Error(`No menu for ${label}`);
|
||||
const index = choice.items.findIndex((item) => item.startsWith(label));
|
||||
expect(index, `${label} in ${choice.items}`).toBeGreaterThanOrEqual(0);
|
||||
world.choose(index);
|
||||
};
|
||||
choose("POKéMON");
|
||||
choose("SWIMMY");
|
||||
choose(move);
|
||||
const pages: string[] = [];
|
||||
for (let i = 0; i < 50 && world.dialogue && !world.dialogue.choice; i++) {
|
||||
pages.push(world.dialogue.pages[world.dialogue.index]);
|
||||
world.advanceDialogue();
|
||||
}
|
||||
return pages;
|
||||
};
|
||||
/** B out of every menu, then let any queued script finish. */
|
||||
const closeMenus = (world: GameWorld) => {
|
||||
for (let i = 0; i < 10 && world.dialogue?.choice; i++) world.advanceDialogue();
|
||||
return settle(world).join("\n");
|
||||
};
|
||||
|
||||
it("surfs from the party menu, after the badge and facing checks", () => {
|
||||
const { assets, world } = fixture([]);
|
||||
world.state.party = partyWith(assets, [0x0f, 0x39]); // CUT, SURF
|
||||
world.state.scenes["24:4"] = 1;
|
||||
world.restore({ ...world.save(), map: "24:4", x: 17, y: 7, direction: "right", objects: [] });
|
||||
expect(world.dialogue).toBeUndefined();
|
||||
// CUT and SURF come first in the submenu.
|
||||
expect(useFromMenu(world, "SURF")).toEqual(["Sorry! A new BADGE\nis required."]);
|
||||
closeMenus(world);
|
||||
world.state.flags.add(30); // ENGINE_FOGBADGE
|
||||
world.restore({ ...world.save(), direction: "up" });
|
||||
expect(useFromMenu(world, "SURF")).toEqual(["You can't SURF\nhere."]);
|
||||
closeMenus(world);
|
||||
world.restore({ ...world.save(), direction: "right" });
|
||||
const shown = [...useFromMenu(world, "SURF"), closeMenus(world)].join("\n");
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
// No "Want to SURF?" question from the menu.
|
||||
expect(shown).not.toContain("?");
|
||||
expect(shown).toContain("SWIMMY used\nSURF!");
|
||||
expect([world.x, world.y, world.state.playerState]).toEqual([18, 7, 4]);
|
||||
expect(useFromMenu(world, "SURF")).toEqual(["You're already\nSURFING."]);
|
||||
closeMenus(world);
|
||||
});
|
||||
|
||||
it("cuts from the party menu, or finds nothing to cut", () => {
|
||||
const { assets, world } = fixture([]);
|
||||
const spot = find(assets, (map, x, y) =>
|
||||
[1, 2, 3, 12, 25].includes(map.tileset) &&
|
||||
assets.collision(map, x, y) === 0x12 &&
|
||||
assets.permission(assets.collision(map, x, y + 1)) === 0 &&
|
||||
!map.objects.some((o) => o.x === x && o.y === y + 1) &&
|
||||
map.callbacks.length === 0,
|
||||
);
|
||||
const ported = assets.isPorted.bind(assets);
|
||||
assets.isPorted = (id) => ported(id) || id === spot.map;
|
||||
world.state.party = partyWith(assets, [0x0f]);
|
||||
world.state.flags.add(28); // ENGINE_HIVEBADGE
|
||||
world.warpTo(spot.map, spot.x, spot.y + 1);
|
||||
world.restore({ ...world.save(), direction: "down" });
|
||||
expect(useFromMenu(world, "CUT")).toEqual(["There's nothing to\nCUT here."]);
|
||||
closeMenus(world);
|
||||
world.restore({ ...world.save(), direction: "up" });
|
||||
const before = world.map.blocks.slice();
|
||||
const shown = [...useFromMenu(world, "CUT"), closeMenus(world)].join("\n");
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(shown).toContain("SWIMMY used\nCUT!");
|
||||
expect(world.map.blocks).not.toEqual(before);
|
||||
});
|
||||
|
||||
it("uses Strength from the party menu", () => {
|
||||
const { assets, world } = fixture([]);
|
||||
world.state.party = partyWith(assets, [0x46]);
|
||||
world.state.flags.add(29); // ENGINE_PLAINBADGE
|
||||
world.state.scenes["24:4"] = 1;
|
||||
world.restore({ ...world.save(), map: "24:4", x: 13, y: 7, direction: "down", objects: [] });
|
||||
const shown = [...useFromMenu(world, "STRENGTH"), closeMenus(world)].join("\n");
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(shown).toContain("SWIMMY used\nSTRENGTH!");
|
||||
expect(world.state.flags.has(24)).toBe(true); // ENGINE_STRENGTH_ACTIVE
|
||||
});
|
||||
|
||||
it("lights a dark cave with Flash, which fails elsewhere", () => {
|
||||
// Dark Cave's Violet entrance (PALETTE_DARK).
|
||||
const DARK_CAVE = "3:78";
|
||||
const { assets, world } = fixture([DARK_CAVE]);
|
||||
const map = assets.map(DARK_CAVE);
|
||||
expect(map.paletteMode).toBe(4);
|
||||
let spot = { map: DARK_CAVE, x: 0, y: 0 };
|
||||
search: for (let y = 1; y < map.height - 1; y++)
|
||||
for (let x = 1; x < map.width - 1; x++)
|
||||
if (
|
||||
assets.permission(assets.collision(map, x, y)) === 0 &&
|
||||
!map.objects.some((o) => o.x === x && o.y === y)
|
||||
) {
|
||||
spot = { map: DARK_CAVE, x, y };
|
||||
break search;
|
||||
}
|
||||
world.state.party = partyWith(assets, [0x94]); // FLASH
|
||||
world.state.flags.add(27); // ENGINE_ZEPHYRBADGE
|
||||
world.warpTo(spot.map, spot.x, spot.y);
|
||||
expect(world.timeOfDay).toBe(3);
|
||||
const shown = [...useFromMenu(world, "FLASH"), closeMenus(world)].join("\n");
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(shown).toContain("A blinding FLASH\nlights the area!");
|
||||
expect(world.state.flash).toBe(true);
|
||||
expect(world.timeOfDay).toBe(2);
|
||||
expect(useFromMenu(world, "FLASH")).toEqual(["Can't use that\nhere."]);
|
||||
closeMenus(world);
|
||||
});
|
||||
|
||||
it("recognises a smashable rock for Rock Smash, which is not ported further", () => {
|
||||
// A rock (SPRITEMOVEDATA_SMASHABLE_ROCK) in Dark Cave at 27, 6.
|
||||
const spot = { map: "3:78", x: 27, y: 6 };
|
||||
const { assets, world } = fixture([spot.map]);
|
||||
expect(
|
||||
assets.map(spot.map).objects.some(
|
||||
(o) => o.x === spot.x && o.y === spot.y && o.movement === 0x18,
|
||||
),
|
||||
).toBe(true);
|
||||
world.state.party = partyWith(assets, [0xf9]); // ROCK_SMASH
|
||||
world.warpTo(spot.map, spot.x, spot.y + 1);
|
||||
world.restore({ ...world.save(), direction: "down" });
|
||||
expect(useFromMenu(world, "ROCK SMASH")).toEqual(["Can't use that\nhere."]);
|
||||
closeMenus(world);
|
||||
world.restore({ ...world.save(), direction: "up" });
|
||||
useFromMenu(world, "ROCK SMASH");
|
||||
expect(world.notice).toBe("Smashing rocks is not ported yet.");
|
||||
closeMenus(world);
|
||||
expect(world.diagnostic).toBe("");
|
||||
});
|
||||
|
||||
it("draws a wild Pokémon with Sweet Scent in grass, and nothing elsewhere", () => {
|
||||
const { assets, world } = fixture([]);
|
||||
world.state.party = partyWith(assets, [0xe6]); // SWEET_SCENT
|
||||
world.state.scenes["24:4"] = 1;
|
||||
world.restore({ ...world.save(), map: "24:4", x: 13, y: 7, direction: "down", objects: [] });
|
||||
const nothing = [...useFromMenu(world, "SWEET SCENT"), closeMenus(world)].join("\n");
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(nothing).toContain("SWIMMY used\nSWEET SCENT!");
|
||||
expect(nothing).toContain("nothing");
|
||||
const grass = find(assets, (map, x, y) =>
|
||||
map.id === "24:3" &&
|
||||
assets.grassCollisions().includes(assets.collision(map, x, y)) &&
|
||||
!map.objects.some((o) => o.x === x && o.y === y),
|
||||
);
|
||||
world.warpTo(grass.map, grass.x, grass.y);
|
||||
useFromMenu(world, "SWEET SCENT");
|
||||
for (let i = 0; i < 10 && world.dialogue?.choice; i++) world.advanceDialogue();
|
||||
for (let tick = 0; tick < 200 && !world.battle; tick++) {
|
||||
if (world.dialogue) world.advanceDialogue();
|
||||
world.tick();
|
||||
}
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(world.battle).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe.skipIf(!existsSync(romPath))("Strength boulders and stone tables with the local ROM", () => {
|
||||
|
||||
@@ -226,6 +226,115 @@ describe.skipIf(!existsSync(romPath))("the Pokémon Center PC", () => {
|
||||
expect(world.save()).toEqual(saved);
|
||||
});
|
||||
|
||||
it("moves Pokémon between the party and boxes without mail", () => {
|
||||
const world = fixture();
|
||||
const data = world.assets.pokemon;
|
||||
const [cyndaquil, pidgey] = world.state.party;
|
||||
pidgey.hp = 1;
|
||||
pidgey.pp = [1, 0, 0, 0];
|
||||
const boxed = toBoxMon(
|
||||
generatePartyMon(data, {
|
||||
species: 19, // RATTATA
|
||||
level: 6,
|
||||
item: 0,
|
||||
otId: world.playerId,
|
||||
ot: "CHRIS",
|
||||
dvs: 0x1234,
|
||||
caughtData: 0,
|
||||
}),
|
||||
);
|
||||
world.state.storage.boxes[0] = [boxed];
|
||||
open(world);
|
||||
pick(world, "BILL's PC");
|
||||
expect(pick(world, "MOVE POKéMON W/O MAIL")).toEqual([
|
||||
"Each time you move a POKéMON, data will be saved. OK?",
|
||||
]);
|
||||
world.choose(1);
|
||||
until(world);
|
||||
// The party, then the fourteen boxes, with the current box highlighted.
|
||||
const boxes = world.dialogue?.choice;
|
||||
expect(boxes?.kind === "menu" && [boxes.items.length, boxes.selected]).toEqual([15, 1]);
|
||||
expect(menuItems(world)[0]).toBe("PARTY POKéMON");
|
||||
// PIDGEY from the party to the end of BOX1: its PP are restored.
|
||||
pick(world, "PARTY POKéMON");
|
||||
pick(world, /^PIDGEY/);
|
||||
expect(menuItems(world)).toEqual(["MOVE", "STATS", "CANCEL"]);
|
||||
pick(world, "MOVE");
|
||||
pick(world, "BOX1");
|
||||
expect(menuItems(world)).toEqual(["RATTATA", "▶"]);
|
||||
pick(world, "▶");
|
||||
// RATTATA from BOX1 to the front of the party, with full HP.
|
||||
pick(world, "BOX1");
|
||||
pick(world, /^RATTATA/);
|
||||
pick(world, "MOVE");
|
||||
pick(world, "PARTY POKéMON");
|
||||
pick(world, /^CYNDAQUIL/);
|
||||
expect(world.liveState.party.map((mon) => mon.species)).toEqual([19, 155]);
|
||||
// Within the party: RATTATA to the end.
|
||||
pick(world, "PARTY POKéMON");
|
||||
pick(world, /^RATTATA/);
|
||||
pick(world, "MOVE");
|
||||
pick(world, "PARTY POKéMON");
|
||||
pick(world, "▶");
|
||||
close(world);
|
||||
expect(world.state.party.map((mon) => mon.species)).toEqual([155, 19]);
|
||||
const rattata = world.state.party[1];
|
||||
expect(rattata.hp).toBe(rattata.stats[0]);
|
||||
expect(rattata.stats).toEqual(calcStats(data, 19, 0x1234, 6, rattata.statExp));
|
||||
expect(world.state.storage.boxes[0].map((mon) => mon.species)).toEqual([16]);
|
||||
expect(world.state.storage.boxes[0][0].pp[0]).toBe(data.move(pidgey.moves[0]).pp);
|
||||
expect(cyndaquil.species).toBe(155);
|
||||
});
|
||||
|
||||
it("keeps the last Pokémon and refuses a full destination when moving", () => {
|
||||
const world = fixture();
|
||||
const data = world.assets.pokemon;
|
||||
world.state.party = [world.state.party[0]];
|
||||
const full = Array.from({ length: 20 }, () =>
|
||||
toBoxMon(
|
||||
generatePartyMon(data, {
|
||||
species: 16,
|
||||
level: 3,
|
||||
item: 0,
|
||||
otId: world.playerId,
|
||||
ot: "CHRIS",
|
||||
dvs: 0,
|
||||
caughtData: 0,
|
||||
}),
|
||||
),
|
||||
);
|
||||
world.state.storage.boxes[1] = full;
|
||||
world.state.storage.boxes[2] = [structuredClone(full[0])];
|
||||
world.state.storage.boxes[2][0].nickname = "SPARE";
|
||||
open(world);
|
||||
pick(world, "BILL's PC");
|
||||
pick(world, "MOVE POKéMON W/O MAIL");
|
||||
world.choose(1);
|
||||
until(world);
|
||||
pick(world, "PARTY POKéMON");
|
||||
expect(pick(world, /^CYNDAQUIL/)).toEqual(["What's up?"]);
|
||||
expect(pick(world, "MOVE")).toContain("It's your last POKéMON!");
|
||||
// BOX3 into the full BOX2: no room, and the position is chosen again.
|
||||
pick(world, "BOX3");
|
||||
pick(world, /^SPARE/);
|
||||
pick(world, "MOVE");
|
||||
pick(world, "BOX2");
|
||||
expect(pick(world, "▶")).toContain("There's no room!");
|
||||
// B leaves the destination list for the list of boxes to move from.
|
||||
world.advanceDialogue();
|
||||
until(world);
|
||||
// Within the full BOX2 a Pokémon can still move.
|
||||
pick(world, "BOX2");
|
||||
pick(world, /^PIDGEY/);
|
||||
pick(world, "MOVE");
|
||||
pick(world, "BOX2");
|
||||
expect(pick(world, "▶")).toContain("Saving… Leave ON!");
|
||||
close(world);
|
||||
expect(world.state.storage.boxes[1]).toHaveLength(20);
|
||||
expect(world.state.storage.boxes[2].map((mon) => mon.nickname)).toEqual(["SPARE"]);
|
||||
expect(world.state.party).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("opens the stats screen from Bill's PC for party and boxed Pokémon", () => {
|
||||
const world = fixture();
|
||||
const data = world.assets.pokemon;
|
||||
|
||||
@@ -33,6 +33,7 @@ const BICYCLE = 7,
|
||||
LEFTOVERS = 0x92,
|
||||
FLOWER_MAIL = 0x9e,
|
||||
BERRY = 0xad;
|
||||
const SOFTBOILED = 0x87;
|
||||
const CUT = 0x0f,
|
||||
GUST = 0x10,
|
||||
SAND_ATTACK = 0x1c,
|
||||
@@ -197,8 +198,11 @@ describe.skipIf(!existsSync(romPath))("the start menu", () => {
|
||||
pick(world, "CANCEL");
|
||||
pick(world, "PIDGEY");
|
||||
expect(menuItems(world)).toEqual(["CUT", "STATS", "SWITCH", "MOVE", "ITEM", "CANCEL"]);
|
||||
pick(world, "CUT");
|
||||
expect(world.notice).toBe("Using CUT from the menu is not ported yet.");
|
||||
// CutFunction's CheckBadge, then the party menu again.
|
||||
expect(pick(world, "CUT")).toEqual([
|
||||
"Sorry! A new BADGE is required.",
|
||||
"Choose a POKéMON.",
|
||||
]);
|
||||
// STATS shows the stats screen, and B returns to the party.
|
||||
pick(world, "PIDGEY");
|
||||
world.choose(menuItems(world).indexOf("STATS"));
|
||||
@@ -209,6 +213,56 @@ describe.skipIf(!existsSync(romPath))("the start menu", () => {
|
||||
close(world);
|
||||
});
|
||||
|
||||
it("shares HP with Softboiled, which needs more than a fifth of it", () => {
|
||||
const world = fixture();
|
||||
const data = world.assets.pokemon;
|
||||
const chansey = generatePartyMon(data, {
|
||||
species: 113,
|
||||
level: 20,
|
||||
item: 0,
|
||||
otId: world.playerId,
|
||||
ot: "CHRIS",
|
||||
dvs: 0x9abc,
|
||||
caughtData: 0,
|
||||
});
|
||||
chansey.moves = [SOFTBOILED, 0, 0, 0];
|
||||
world.state.party.push(chansey);
|
||||
world.state.party[0].hp = 3;
|
||||
const fifth = Math.floor(chansey.stats[0] / 5);
|
||||
open(world);
|
||||
pick(world, "POKéMON");
|
||||
pick(world, "CHANSEY");
|
||||
expect(pick(world, "SOFTBOILED")).toEqual(["Use on which POKéMON?"]);
|
||||
// Not on itself, and not on a Pokémon at full HP.
|
||||
expect(pick(world, "CHANSEY")).toEqual([
|
||||
"That can't be used on this POKéMON.",
|
||||
"Use on which POKéMON?",
|
||||
]);
|
||||
expect(pick(world, "PIDGEY")[0]).toBe("That can't be used on this POKéMON.");
|
||||
expect(pick(world, "CYNDAQUIL")).toEqual([
|
||||
`CYNDAQUIL recovered ${Math.min(fifth, world.liveState.party[0].stats[0] - 3)}HP!`,
|
||||
"Choose a POKéMON.",
|
||||
]);
|
||||
// The cursor returns to the user.
|
||||
const party = world.dialogue?.choice;
|
||||
expect(party?.kind === "menu" && party.selected).toBe(2);
|
||||
close(world);
|
||||
expect(world.state.party[2].hp).toBe(chansey.stats[0] - fifth);
|
||||
expect(world.state.party[0].hp).toBe(
|
||||
Math.min(world.state.party[0].stats[0], 3 + fifth),
|
||||
);
|
||||
|
||||
world.state.party[2].hp = fifth;
|
||||
open(world);
|
||||
pick(world, "POKéMON");
|
||||
pick(world, "CHANSEY");
|
||||
expect(pick(world, "SOFTBOILED")).toEqual([
|
||||
"Not enough HP!",
|
||||
"Choose a POKéMON.",
|
||||
]);
|
||||
close(world);
|
||||
});
|
||||
|
||||
it("switches two Pokémon, and B cancels the move", () => {
|
||||
const world = fixture();
|
||||
open(world);
|
||||
@@ -525,6 +579,49 @@ describe.skipIf(!existsSync(romPath))("the start menu", () => {
|
||||
expect(indoors.state.playerState).toBe(0);
|
||||
});
|
||||
|
||||
it("registers the Bicycle with SEL, and Select rides it without a message", () => {
|
||||
const world = fixture();
|
||||
// Nothing registered yet.
|
||||
expect(world.useSelect()).toBe(true);
|
||||
expect(until(world)).toEqual([
|
||||
"An item in your PACK may be",
|
||||
"registered for use on SELECT Button.",
|
||||
]);
|
||||
close(world);
|
||||
open(world);
|
||||
pick(world, "PACK");
|
||||
pickItem(world, BICYCLE);
|
||||
expect(pick(world, "SEL")).toEqual(["Registered the BICYCLE."]);
|
||||
close(world);
|
||||
expect(world.state.registeredItem).toEqual({ which: 0x81, item: BICYCLE });
|
||||
// The registration is saved.
|
||||
world.restore(world.save());
|
||||
expect(world.state.registeredItem).toEqual({ which: 0x81, item: BICYCLE });
|
||||
expect(world.useSelect()).toBe(true);
|
||||
expect(settle(world)).toEqual([]);
|
||||
expect(world.diagnostic).toBe("");
|
||||
expect(world.state.playerState).toBe(1);
|
||||
expect(world.useSelect()).toBe(true);
|
||||
expect(settle(world)).toEqual([]);
|
||||
expect(world.state.playerState).toBe(0);
|
||||
|
||||
const indoors = fixture(PLAYERS_HOUSE, 7, 4);
|
||||
indoors.state.registeredItem = { which: 0x81, item: BICYCLE };
|
||||
indoors.useSelect();
|
||||
expect(until(indoors)).toEqual(["OAK: CHRIS! This isn't the time to use that!"]);
|
||||
close(indoors);
|
||||
expect(indoors.state.playerState).toBe(0);
|
||||
});
|
||||
|
||||
it("forgets a registered item that left its slot", () => {
|
||||
const world = fixture();
|
||||
world.state.registeredItem = { which: 0x02, item: POTION };
|
||||
world.useSelect();
|
||||
expect(until(world)[0]).toBe("An item in your PACK may be");
|
||||
close(world);
|
||||
expect(world.state.registeredItem).toEqual({ which: 0, item: 0 });
|
||||
});
|
||||
|
||||
it("offers no USE without a field menu, and a notice for unported effects", () => {
|
||||
const world = fixture();
|
||||
world.state.bag.items.push(
|
||||
|
||||
Reference in new issue
Block a user