Port forced movement (turn delays, bumps, ice, currents, pits), dynamic return warps, elevators, Surf, Cut, Strength and cycling rules, working map blocks with changeblock/changemapblocks, and the remaining NPC movement types and movement commands. Add tileset animation from the ROM's command tables, grass sprite priority and special map palettes; room decorations with ownership, the bedroom PC and save migration; and Key Items access to the Bicycle. Fourteen original-engine traces compare positions, facing, actors, event flags and scripted poses. Tile animation matches original VRAM for four tilesets, and LoadMapPals matches 192 palette cases. skyfall_top now keeps the $60 sprite offset its step function leaves behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9ac111f262
commit
451464f7c6
38 files changed
+5600
-490
No files matched your search
+19
-3
@@ -106,7 +106,10 @@ describe("native ROM formats", () => {
|
||||
it("decodes movement data and refuses unported movement commands", () => {
|
||||
const reader = new RomReader(
|
||||
Uint8Array.from([
|
||||
0x02, 0x0f, 0x08, 0x31, 0x3b, 0x40, 0x46, 12, 0x47, 0x4c, 0x47,
|
||||
0x02, 0x0f, 0x08, 0x31, 0x3b, 0x40, 0x46, 12, 0x47, 0x5a, 0x47,
|
||||
// turn_step, turn_in, set_sliding, step_dig 16, skyfall,
|
||||
// step_shake $54, fish_got_bite, step_bump, step_wait_end 5
|
||||
0x05, 0x26, 0x39, 0x4f, 16, 0x4e, 0x55, 0x54, 0x51, 0x50, 0x48, 5,
|
||||
]),
|
||||
);
|
||||
expect(readMovement(reader, 0)).toEqual([
|
||||
@@ -119,6 +122,17 @@ describe("native ROM formats", () => {
|
||||
{ kind: "sleep", ticks: 12 },
|
||||
]);
|
||||
expect(() => readMovement(reader, 9)).toThrow("not ported");
|
||||
expect(readMovement(reader, 11)).toEqual([
|
||||
{ kind: "turn-step", direction: "up", ticks: 3 },
|
||||
{ kind: "step", direction: "left", ticks: 8, animate: true, spin: true },
|
||||
{ kind: "sliding", sliding: true },
|
||||
{ kind: "pose", pose: "spin", ticks: 16 },
|
||||
{ kind: "pose", pose: "skyfall", ticks: 31 },
|
||||
{ kind: "shake", displacement: 0x54 },
|
||||
{ kind: "pose", pose: "fish-bite", ticks: 8 },
|
||||
{ kind: "bump", ticks: 1 },
|
||||
{ kind: "wait-end", ticks: 5 },
|
||||
]);
|
||||
});
|
||||
it("reads RAM-backed names and string buffers only from native values", () => {
|
||||
// text_ram wStringBuffer4, " ", text_buffer 0, done
|
||||
@@ -192,7 +206,7 @@ describe.skipIf(!existsSync(romPath))(
|
||||
load(),
|
||||
() => new Date("2026-09-22T12:00:00"),
|
||||
);
|
||||
world.restore({ ...world.save(), x: 3, y: 2, direction: "down" });
|
||||
world.restore({ ...world.save(), x: 3, y: 2, direction: "right" });
|
||||
const ticks: number[] = [];
|
||||
for (let tick = 1; tick <= 40 && world.x < 7; tick++) {
|
||||
const x = world.x;
|
||||
@@ -415,11 +429,13 @@ describe.skipIf(!existsSync(romPath))(
|
||||
objects: [],
|
||||
});
|
||||
world.tick(direction);
|
||||
expect([world.x, world.y]).toEqual([x + dx * 2, y + dy * 2]);
|
||||
// The first half of the jump occupies the tile jumped over.
|
||||
expect([world.x, world.y]).toEqual([x + dx, y + dy]);
|
||||
expect(
|
||||
world.poses().find((pose) => pose.id === 0)?.lift,
|
||||
).toBeLessThan(0);
|
||||
settle(world);
|
||||
expect([world.x, world.y]).toEqual([x + dx * 2, y + dy * 2]);
|
||||
expect(world.notice).toBe("");
|
||||
checked.add(collision);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user