Open Mr. Pokémon's house, the first rival battle and the return to Elm
Checks / web (push) Canceled after 0s
Checks / web (push) Canceled after 0s
Mr. Pokémon's ROM scene gives the Mystery Egg and Pokédex, heals the party, sets the Cherrygrove spawn with blackoutmod and queues Elm's call. Cherrygrove's rival scene runs both triggers and all three RIVAL1_1 parties as a CANLOSE battle; reloadmap returns to the script. Smart AI is accepted for effects with no AI_Smart_EffectHandlers entry (Normal Hit, Attack Down, Defense Down) and still refuses others before any random draw, including for reserve Pokémon. Special calls read SpecialPhoneCallList: the entry's condition decides, and the ROM's CheckSpecialPhoneCall.script and Script_ReceivePhoneCall run its caller script through memcall, ending with HangUp's timed "Click!". Ringing and the caller box are not presented yet. In Elm's lab, NameRival shows the ROM's "RIVAL'S NAME?" title (the prompt previously had no page and soft-locked) and applies InitName's default to blank names. Handing over the Egg and the aide's Poké Balls run from the ROM. Saves gain rivalName and specialPhoneCall. The quest fixtures use a seeded RNG (a constant byte never ends the damage-variation loop) and no longer carry the house's moved objects into Cherrygrove. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7c1c0cc45b
commit
dc10343520
18 files changed
+812
-34
No files matched your search
@@ -255,15 +255,37 @@ describe.skipIf(!existsSync(romPath))("trainer battles", () => {
|
||||
run.next({ action: "fight", move: 0 });
|
||||
expect(internals.playerUsedMoves).toEqual([options.party[0].moves[0], 0, 0, 0]);
|
||||
});
|
||||
it("refuses the untranslated Smart AI and empty parties before mutating the party or Pokédex", () => {
|
||||
for (const kind of ["empty", "ai"] as const) {
|
||||
it.each([1, 2, 3])(
|
||||
"completes first rival starter variant %i with its ROM AI flags",
|
||||
(id) => {
|
||||
const { options } = setup(15, [9, id]);
|
||||
expect(options.trainer!.aiFlags).toBe(0x3d3);
|
||||
const result = finish(new Battle(options));
|
||||
expect(result.result).toBe(0);
|
||||
expect(result.prizeMoney).toBe(300);
|
||||
expect(result.texts).toContain("was defeated!");
|
||||
},
|
||||
);
|
||||
it("refuses untranslated Smart effects, including reserves, and empty parties before mutation", () => {
|
||||
for (const kind of ["empty", "ai", "reserve"] as const) {
|
||||
const { options } = setup();
|
||||
if (kind === "empty") options.trainer!.party = [];
|
||||
else options.trainer!.aiFlags |= 1 << 4;
|
||||
else {
|
||||
options.trainer!.aiFlags |= 1 << 4;
|
||||
const unsupported = structuredClone(options.trainer!.party[0]);
|
||||
unsupported.moves = [115, 0, 0, 0]; // Reflect has an untranslated Smart handler.
|
||||
if (kind === "reserve") options.trainer!.party.push(unsupported);
|
||||
else options.trainer!.party[0] = unsupported;
|
||||
}
|
||||
const before = structuredClone(options.party);
|
||||
const enemyBefore = structuredClone(options.trainer!.party);
|
||||
let draws = 0;
|
||||
options.random = () => { draws++; return 0; };
|
||||
expect(() => new Battle(options).run().next()).toThrow(UnsupportedBattle);
|
||||
expect(options.party).toEqual(before);
|
||||
expect(options.trainer!.party).toEqual(enemyBefore);
|
||||
expect(options.pokedex.seen.size).toBe(0);
|
||||
expect(draws).toBe(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user