Open the League: Victory Road, the Elite Four, the Hall of Fame and the credits (#33)
Routes 26 and 27 with their houses, Tohjo Falls, the Victory Road gate, Victory Road, Route 23, Indigo Plateau, the Elite Four's rooms, Lance's room and the Hall of Fame are open. - halloffame (game/scripts/league.ts): the music fades as SAVING RECORD shows, the win and the team (without Eggs) are recorded, each Pokémon's back picture passes and its front picture slides in for its "New Hall of Famer!" page and cry, then the player's card with the play time and Professor Oak's rating; the play time stops meanwhile. The world then continues at New Bark as Continue does after the cartridge's reset. - credits and the credits after the Hall of Fame (CreditsMachine): the ROM's script, strings, banner Pokémon and THE END, frame by frame as VBlank_Credits presents them, with the frames each command group runs over measured on the cartridge. B shortens credits seen before. - wait, appear/disappear of the player, and rival names in battle texts (PlaceEnemysName prints wRivalName alone: "???" before he is named). - The tile-screen renderer wraps hSCX/hSCY at 256 pixels and gives rows their own hSCX. - Reference: the credits match the cartridge on every frame (BG map, banner tiles, CRAM palettes, LY overrides, parser), with and without B; the ceremony's pages match for every species and the player's card for Chris and Kris. - Playthrough: five stages from Tin Tower to the Hall of Fame (HM07 from Ice Path, Tohjo Falls' waterfall, Victory Road's ladders, the Elite Four and Lance); the driver follows warps within a map and fights carefully from here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b6fdc23508
commit
f05fd8ebee
23 files changed
+1943
-107
No files matched your search
+168
-1
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, onTestFailed } from "vitest";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { CrystalAssets } from "../src/game/rom/assets";
|
||||
import { C } from "../src/game/battle/constants";
|
||||
import { tmhmItem } from "../src/game/rom/pokemon";
|
||||
import { Playthrough } from "./playthrough-driver";
|
||||
import { findPath } from "./world-driver";
|
||||
|
||||
@@ -214,6 +215,29 @@ const ROUTE_46 = "5:9",
|
||||
TIN_TOWER_1F = "3:4",
|
||||
EVENT_RANG_CLEAR_BELL_2 = 0x767,
|
||||
EVENT_FOUGHT_SUICUNE = 0x335;
|
||||
const WISE_TRIOS_ROOM_EXIT = "4:2",
|
||||
MAHOGANY_ROUTE_44 = "2:6",
|
||||
ROUTE_27 = "24:2",
|
||||
TOHJO_FALLS = "3:83",
|
||||
ROUTE_26 = "24:1",
|
||||
VICTORY_ROAD_GATE = "23:13",
|
||||
VICTORY_ROAD = "3:91",
|
||||
ROUTE_23 = "16:1",
|
||||
INDIGO_PLATEAU = "16:2",
|
||||
ELITE_FOUR = ["16:3", "16:4", "16:5", "16:6"],
|
||||
LANCES_ROOM = "16:7";
|
||||
const HM07 = 0xf9,
|
||||
WATERFALL = 127,
|
||||
IRON_TAIL = 231,
|
||||
TYPHLOSION = 157,
|
||||
EVENT_GOT_HM07_WATERFALL = 0x688,
|
||||
EVENT_RIVAL_VICTORY_ROAD = 0x6c2,
|
||||
EVENT_BEAT_ELITE_4_WILL = 0x5b8,
|
||||
EVENT_BEAT_ELITE_4_KOGA = 0x5b9,
|
||||
EVENT_BEAT_ELITE_4_BRUNO = 0x5ba,
|
||||
EVENT_BEAT_ELITE_4_KAREN = 0x5bb,
|
||||
EVENT_BEAT_CHAMPION_LANCE = 0x5bc,
|
||||
EVENT_BEAT_ELITE_FOUR = 0x44;
|
||||
|
||||
/** The acceptance run for PLAN section 2: a new game played from the
|
||||
* introduction to the catching tutorial through the world's own inputs,
|
||||
@@ -343,7 +367,8 @@ describe.skipIf(!existsSync(romPath))(
|
||||
// The rival's scene blocks the way east to Route 29.
|
||||
cross("right", ROUTE_29);
|
||||
const rival = p.log.join("\n");
|
||||
expect(rival).toContain("RIVAL ?");
|
||||
// PlaceEnemysName: a rival is wRivalName alone, "???" until named.
|
||||
expect(rival).toContain("???\nwants to battle!");
|
||||
expect(rival).toContain("My name's ???.");
|
||||
expect(p.world.state.scenes[CHERRYGROVE]).toBe(0);
|
||||
reload();
|
||||
@@ -1486,5 +1511,147 @@ describe.skipIf(!existsSync(romPath))(
|
||||
reload();
|
||||
writeTestSave("tin-tower");
|
||||
}, 240_000);
|
||||
|
||||
it("goes back to Ice Path for HM07 and walks home to New Bark Town", () => {
|
||||
traceFailures();
|
||||
// From here on the run fights as a player facing the League would.
|
||||
p.careful = true;
|
||||
// Tin Tower's grounds lead back through the Wise Trio's room and the
|
||||
// entrance building, whose far side comes down by its stairs.
|
||||
stairs(ECRUTEAK);
|
||||
stairs(WISE_TRIOS_ROOM_EXIT);
|
||||
stairs(TIN_TOWER_ENTRANCE);
|
||||
walk(17, 14);
|
||||
p.world.move("down");
|
||||
play();
|
||||
stairs(ECRUTEAK);
|
||||
stairs(ROUTE_42_GATE);
|
||||
stairs(ROUTE_42);
|
||||
cross("right", MAHOGANY, { water: true });
|
||||
heal(MAHOGANY_CENTER, MAHOGANY);
|
||||
cross("right", MAHOGANY_ROUTE_44);
|
||||
stairs(ICE_PATH[0]);
|
||||
// The HM07 ball at (31, 7), reached from its left over the ice.
|
||||
walk(30, 7);
|
||||
if (p.world.save().direction !== "right") p.world.move("right");
|
||||
p.world.interact();
|
||||
play();
|
||||
expect(p.world.state.events.has(EVENT_GOT_HM07_WATERFALL)).toBe(true);
|
||||
expect(p.world.state.bag.tmsHms[56]).toBe(1);
|
||||
stairs(MAHOGANY_ROUTE_44);
|
||||
cross("left", MAHOGANY);
|
||||
cross("left", ROUTE_42, { water: true });
|
||||
sail(2, 8);
|
||||
stairs(ROUTE_42_GATE);
|
||||
stairs(ECRUTEAK);
|
||||
cross("down", ROUTE_37);
|
||||
cross("down", ROUTE_36);
|
||||
cross("right", VIOLET);
|
||||
stairs(ROUTE_31_GATE);
|
||||
stairs(ROUTE_31);
|
||||
cross("down", ROUTE_30);
|
||||
cross("down", CHERRYGROVE);
|
||||
cross("right", ROUTE_29);
|
||||
cross("right", NEW_BARK);
|
||||
reload();
|
||||
}, 240_000);
|
||||
|
||||
it("surfs Route 27, climbs Tohjo Falls and takes Victory Road to the Plateau", () => {
|
||||
traceFailures();
|
||||
// Dratini learns Waterfall; Typhlosion takes Iron Tail for Leer.
|
||||
p.teach(HM07, 5, 0);
|
||||
p.teach(tmhmItem(23), 0, 1);
|
||||
expect(p.world.state.party[5].moves[0]).toBe(WATERFALL);
|
||||
expect(p.world.state.party[0].moves[1]).toBe(IRON_TAIL);
|
||||
cross("right", ROUTE_27, { water: true });
|
||||
travel(TOHJO_FALLS, [], { water: true });
|
||||
// Up the western waterfall, along the top pool and down the eastern
|
||||
// one to the far shore and Route 27's other half.
|
||||
sail(9, 12);
|
||||
if (p.world.save().direction !== "up") p.world.move("up");
|
||||
p.world.interact();
|
||||
play([1]);
|
||||
expect({ x: p.world.x, y: p.world.y }).toEqual({ x: 9, y: 7 });
|
||||
sail(22, 14);
|
||||
walk(25, 15);
|
||||
if (p.world.mapId === TOHJO_FALLS) {
|
||||
p.world.move("down");
|
||||
play();
|
||||
}
|
||||
expect(p.world.mapId).toBe(ROUTE_27);
|
||||
cross("right", ROUTE_26, { water: true });
|
||||
travel(VICTORY_ROAD_GATE, [], { water: true });
|
||||
// VictoryRoadGateBadgeCheckScript on the way north.
|
||||
stairs(VICTORY_ROAD);
|
||||
expect(p.log.join(" ")).toContain("BADGES of JOHTO!");
|
||||
// Victory Road's floors are joined by its own ladders; the rival
|
||||
// waits by the exit.
|
||||
travel(ROUTE_23, [VICTORY_ROAD]);
|
||||
// The rival leaves (EVENT_RIVAL_VICTORY_ROAD hides him) after the battle.
|
||||
expect(p.world.state.events.has(EVENT_RIVAL_VICTORY_ROAD)).toBe(true);
|
||||
expect(p.log.join(" ")).toContain("SILVER\nwants to battle!");
|
||||
reload();
|
||||
}, 240_000);
|
||||
|
||||
it("stocks up at Indigo Plateau and beats Will", () => {
|
||||
traceFailures();
|
||||
stairs(INDIGO_PLATEAU);
|
||||
talkTo(2); // the nurse, across the counter
|
||||
play();
|
||||
expect(p.world.state.party.every((mon) => mon.hp === mon.stats[0])).toBe(true);
|
||||
writeTestSave("indigo-plateau");
|
||||
buy(3, C.FULL_HEAL, 15);
|
||||
buy(3, C.MAX_POTION, Math.floor((p.world.state.money - 2000) / 2500));
|
||||
stairs(ELITE_FOUR[0]);
|
||||
play(); // the door locks behind the player
|
||||
talkTo(p.world.map.objects[0].id);
|
||||
play();
|
||||
expect(p.world.state.events.has(EVENT_BEAT_ELITE_4_WILL)).toBe(true);
|
||||
reload();
|
||||
}, 240_000);
|
||||
|
||||
it("beats Koga, Bruno and Karen", () => {
|
||||
traceFailures();
|
||||
const events = [EVENT_BEAT_ELITE_4_KOGA, EVENT_BEAT_ELITE_4_BRUNO, EVENT_BEAT_ELITE_4_KAREN];
|
||||
ELITE_FOUR.slice(1).forEach((room, index) => {
|
||||
stairs(room);
|
||||
play();
|
||||
talkTo(p.world.map.objects[0].id);
|
||||
play();
|
||||
expect(p.world.mapId).toBe(room);
|
||||
expect(p.world.state.events.has(events[index])).toBe(true);
|
||||
});
|
||||
reload();
|
||||
}, 240_000);
|
||||
|
||||
it("beats Lance and enters the Hall of Fame, then wakes in New Bark Town", () => {
|
||||
traceFailures();
|
||||
stairs(LANCES_ROOM);
|
||||
play();
|
||||
// Script_ApproachLanceFromRight: the step towards Lance starts it;
|
||||
// after the battle Mary and Oak rush in and Lance leads the way to
|
||||
// the Hall of Fame, where the ceremony and the credits play.
|
||||
walk(5, 6);
|
||||
p.world.move("up");
|
||||
play();
|
||||
const log = p.log.join(" ");
|
||||
expect(log).toContain("CHAMPION LANCE\nwas defeated!");
|
||||
expect(log).toContain("MARY: Let's inter-");
|
||||
expect(log).toContain("and your partners\nas CHAMPIONS!");
|
||||
expect(p.world.state.events.has(EVENT_BEAT_CHAMPION_LANCE)).toBe(true);
|
||||
expect(p.world.state.events.has(EVENT_BEAT_ELITE_FOUR)).toBe(true);
|
||||
expect(p.world.state.hallOfFame).toBe(true);
|
||||
expect(p.world.state.hallOfFameCount).toBe(1);
|
||||
expect(p.world.state.hallOfFameTeams[0].mons.map((mon) => mon.species)).toEqual(
|
||||
p.world.state.party.map((mon) => mon.species),
|
||||
);
|
||||
expect(p.world.state.hallOfFameTeams[0].mons[0].species).toBe(TYPHLOSION);
|
||||
// Continue after the credits: SPAWN_NEW_BARK, outside the lab.
|
||||
expect(p.world.mapId).toBe(NEW_BARK);
|
||||
expect({ x: p.world.x, y: p.world.y }).toEqual({ x: 13, y: 6 });
|
||||
expect(p.world.busy).toBe(false);
|
||||
reload();
|
||||
writeTestSave("hall-of-fame-new-bark");
|
||||
}, 300_000);
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user