This commit is contained in:
1 parent
6a74f59e46
commit
3e92936cb9
20 files changed
+1417
-32
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { CrystalAssets } from "../src/game/rom/assets";
|
||||
import { C } from "../src/game/battle/constants";
|
||||
import { trainerFixtureWorld } from "./trainer-fixture";
|
||||
import type { GameWorld } from "../src/game/world/world";
|
||||
|
||||
const romPath = process.env.CRYSTAL_ROM_PATH ?? "Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc";
|
||||
|
||||
const fixture = (tail?: number[], lose = false, winText = false) =>
|
||||
trainerFixtureWorld(new CrystalAssets(new Uint8Array(readFileSync(romPath))), tail, lose, winText);
|
||||
|
||||
function finish(world: GameWorld) {
|
||||
const messages: string[] = [];
|
||||
for (let tick = 0; tick < 10000; tick++) {
|
||||
const view = world.battle;
|
||||
if (view) {
|
||||
if (view.message) messages.push(view.message.text);
|
||||
if (view.prompt?.kind === "action") world.battleAction({ action: "fight", move: 0 });
|
||||
else if (view.prompt?.kind === "yesno") world.battleChoose(1);
|
||||
else if (view.prompt?.kind === "forget") world.battleChoose(3);
|
||||
else if (view.prompt?.kind === "stats" || view.message?.prompt) world.interact();
|
||||
else world.tick();
|
||||
} else if (world.dialogue) world.advanceDialogue();
|
||||
else if (world.busy) world.tick();
|
||||
else return messages.join("\n");
|
||||
}
|
||||
throw new Error(`Trainer battle did not settle: ${world.diagnostic}`);
|
||||
}
|
||||
|
||||
describe.skipIf(!existsSync(romPath))("scripted trainer world transactions", () => {
|
||||
it("commits Joey's ROM reward, party experience, sighting and postbattle event together", () => {
|
||||
const world = fixture();
|
||||
const before = world.save();
|
||||
world.interact();
|
||||
expect(world.battle).toBeDefined();
|
||||
expect(world.save()).toEqual(before);
|
||||
const messages = finish(world);
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(messages).toContain("JOEY");
|
||||
expect(world.state.money).toBe(before.money + 64);
|
||||
expect(world.state.party[0].exp).toBeGreaterThan(before.party[0].exp);
|
||||
expect(world.state.pokedex.seen.has(19)).toBe(true);
|
||||
expect(world.state.events.has(256)).toBe(true);
|
||||
const saved = world.save();
|
||||
world.restore(saved);
|
||||
expect(world.save()).toEqual(saved);
|
||||
});
|
||||
|
||||
it("reads scripted win text from the script bank's ROM pointer", () => {
|
||||
const world = fixture(undefined, false, true);
|
||||
world.interact();
|
||||
const messages = finish(world);
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(messages).toContain("Ack! I lost again!");
|
||||
expect(messages.indexOf("Ack! I lost again!")).toBeLessThan(messages.lastIndexOf("64"));
|
||||
});
|
||||
|
||||
it("caps trainer rewards at the money limit", () => {
|
||||
const world = fixture();
|
||||
world.state.money = C.MAX_MONEY - 1;
|
||||
world.interact();
|
||||
finish(world);
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(world.state.money).toBe(C.MAX_MONEY);
|
||||
});
|
||||
|
||||
it("rolls back reward, experience and sightings if the following script is unsupported", () => {
|
||||
const world = fixture([0xff]);
|
||||
const before = world.save();
|
||||
world.interact();
|
||||
finish(world);
|
||||
expect(world.notice).toContain("not been ported");
|
||||
expect(world.save()).toEqual(before);
|
||||
});
|
||||
|
||||
it("restores a midbattle save without partially paying the reward", () => {
|
||||
const world = fixture();
|
||||
const before = world.save();
|
||||
world.interact();
|
||||
for (let tick = 0; tick < 1000 && !world.battle?.prompt; tick++) {
|
||||
if (world.battle?.message?.prompt) world.interact();
|
||||
else world.tick();
|
||||
}
|
||||
expect(world.save()).toEqual(before);
|
||||
world.restore(before);
|
||||
expect(world.battle).toBeUndefined();
|
||||
expect(world.save()).toEqual(before);
|
||||
});
|
||||
|
||||
it("CANLOSE scripts receive LOSE in wScriptVar without forcing a whiteout", () => {
|
||||
// ifequal LOSE, .lost; invalid; .lost: setevent 256; end
|
||||
const world = fixture([0x91], true);
|
||||
const clerk = world.assets.map("26:4").objects[0];
|
||||
const start = clerk.script + 12; // after winlosstext/loadtrainer/loadvar/startbattle
|
||||
const target = ((start + 5) % 0x4000) + 0x4000;
|
||||
world.assets.reader.bytes.set([0x06, 1, target & 255, target >> 8, 0xff, 0x33, 0, 1, 0x91], start);
|
||||
const before = world.save();
|
||||
world.interact();
|
||||
const messages = finish(world);
|
||||
expect(world.notice, world.diagnostic).toBe("");
|
||||
expect(world.state.events.has(256)).toBe(true);
|
||||
expect(world.state.money).toBe(before.money);
|
||||
expect(world.save().map).toBe(before.map);
|
||||
expect(messages).not.toMatch(/whited\s+out/i);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user