Port the fishing rods and wild Magikarp
The Old, Good and Super Rods run FishFunction facing water: the map's fish group and Fish pick no fish, no bite, or a Pokémon from FishGroups and the time of day's TimeFishGroups. The player casts and jiggles with the fishing frames drawn from LoadFishingGFX's tiles, shows "!" on a bite, and the hooked Pokémon attacks as BATTLETYPE_FISH. Facing pieces now use signed offsets, which the rod above and left of the player needs. Wild Magikarp no longer stop a battle: LoadEnemyMon redraws their DVs through CalcMagikarpLength and its filters, as the original does. The length routine matches the original in the reference emulator. Part of #27 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4cb4676c7d
commit
397dcef44c
20 files changed
+681
-47
No files matched your search
@@ -6,6 +6,7 @@ import {
|
||||
Battle,
|
||||
applyTypeMatchups,
|
||||
calculateDamage,
|
||||
magikarpLength,
|
||||
restoreHealth,
|
||||
restoredPP,
|
||||
sendOutText,
|
||||
@@ -638,3 +639,75 @@ test("native switch texts, healing, PP, happiness and CalcPlayerStats match the
|
||||
).toEqual(results.stats[index]);
|
||||
});
|
||||
});
|
||||
|
||||
test("native Magikarp lengths match CalcMagikarpLength", async ({ page }) => {
|
||||
test.skip(
|
||||
!existsSync(romPath) || !existsSync(symbolsPath),
|
||||
"Requires the local user ROM and the private PRET rebuild.",
|
||||
);
|
||||
const { at, routine } = loadSymbols();
|
||||
const reader = new CrystalAssets(new Uint8Array(readFileSync(romPath))).reader;
|
||||
let seed = 0x4a39;
|
||||
const next = (limit: number) => {
|
||||
seed = (seed * 1103515245 + 12345) >>> 0;
|
||||
return (seed >>> 8) % limit;
|
||||
};
|
||||
// Random DVs and IDs, plus indexes below 10 and at the table's edges.
|
||||
const cases = [
|
||||
...Array.from({ length: 1500 }, () => ({ dvs: next(0x10000), id: next(0x10000) })),
|
||||
...[0x0000, 0x0009, 0x000a, 0x00ff, 0x0100, 0xfdff, 0xfe00, 0xff00, 0xffff].map((dvs) => ({
|
||||
dvs,
|
||||
id: 0,
|
||||
})),
|
||||
];
|
||||
await openReference(page);
|
||||
const addresses = {
|
||||
calc: routine("CalcMagikarpLength"),
|
||||
length: at("wMagikarpLength"),
|
||||
};
|
||||
const results = await page.evaluate(
|
||||
async ({ addresses, cases }) => {
|
||||
const { BinjgbEmulator } = await import("/src/emulator/binjgb.ts");
|
||||
const file = (document.querySelector("#reference-fixture") as HTMLInputElement).files![0];
|
||||
const reference = new BinjgbEmulator();
|
||||
await reference.load(new Uint8Array(await file.arrayBuffer()), document.createElement("canvas"));
|
||||
const write = (address: number, value: number) => reference.writeMemory(address, value & 0xff);
|
||||
// Trampoline: DI; SP=$c7fe; switch to the routine's bank; set BC (the
|
||||
// ID) and DE (the DVs); CALL CalcMagikarpLength; write a sentinel; loop.
|
||||
const dvs = 0xc100,
|
||||
id = 0xc102;
|
||||
const program = [
|
||||
0xf3, 0x31, 0xfe, 0xc7, 0x3e, addresses.calc.bank, 0xe0, 0x9d, 0xea, 0x00, 0x20,
|
||||
0x01, id & 0xff, id >> 8, 0x11, dvs & 0xff, dvs >> 8,
|
||||
0xcd, addresses.calc.address & 0xff, addresses.calc.address >> 8,
|
||||
0x3e, 0x42, 0xea, 0xff, 0xc6, 0x18, 0xfe,
|
||||
];
|
||||
write(0xff70, 1);
|
||||
const lengths = cases.map((input) => {
|
||||
write(dvs, input.dvs >> 8);
|
||||
write(dvs + 1, input.dvs);
|
||||
write(id, input.id >> 8);
|
||||
write(id + 1, input.id);
|
||||
write(0xc6ff, 0);
|
||||
program.forEach((value, index) => write(0xc000 + index, value));
|
||||
reference.setProgramCounter(0xc000);
|
||||
reference.advanceFrames(2);
|
||||
return {
|
||||
returned: reference.readMemory(0xc6ff) === 0x42,
|
||||
feet: reference.readMemory(addresses.length),
|
||||
inches: reference.readMemory(addresses.length + 1),
|
||||
};
|
||||
});
|
||||
reference.dispose();
|
||||
return lengths;
|
||||
},
|
||||
{ addresses, cases },
|
||||
);
|
||||
cases.forEach((input, index) => {
|
||||
const { returned, ...original } = results[index];
|
||||
expect(returned, `case ${index} returned`).toBe(true);
|
||||
expect(magikarpLength(reader, input.dvs, input.id), JSON.stringify(input)).toEqual(original);
|
||||
});
|
||||
const lengths = new Set(results.map(({ feet, inches }) => feet * 12 + inches));
|
||||
expect(lengths.size).toBeGreaterThan(30);
|
||||
});
|
||||
Reference in new issue
Block a user