Open Tin Tower and Whirl Islands: Suicune, Ho-Oh and Lugia (#32)

Tin Tower's floors and roof and the Whirl Islands are open. The Clear Bell
gets the player past the sages to Suicune in Tin Tower; after the League,
the Rainbow Wing leads up to Ho-Oh, and the Silver Wing to Lugia.

New specials: MonCheck and BeastsCheck (CheckOwnMonAnywhere, with its
five-letter OT comparison and no Day-Care search).

The playthrough driver plans ledge jumps for the later stages, waterfall
descents, warp panels and Surf between warps; the run gains two stages,
Routes 45 and 46 back to Ecruteak and Tin Tower's Suicune.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kawaandClaude Opus 5.5 committed 2026-10-13 14:47:45 +02:00
1 parent 9c3579937c
commit 3fbc6798eb
10 files changed
+369 -21

No files matched your search

+59 -9
View File
@@ -134,9 +134,22 @@ function open(
return true;
}
/** Where a step in `direction` from (x, y) ends: the next tile, or, on
* ice, the last tile of the slide before something blocks it. Undefined
* when the step is blocked. */
/** .ledge_table: the directions each COLL_HOP_* tile jumps toward. */
const LEDGES: readonly (readonly Direction[])[] = [
["right"],
["left"],
["up"],
["down"],
["down", "right"],
["down", "left"],
["up", "right"],
["up", "left"],
];
/** Where a step in `direction` from (x, y) ends: the next tile, two tiles
* on when standing on a ledge that jumps that way (.TryJump), or, on ice,
* the last tile of the slide before something blocks it. Undefined when
* the step is blocked. */
export function landing(
world: GameWorld,
x: number,
@@ -146,10 +159,47 @@ export function landing(
water = false,
target,
whirlpools = false,
}: { water?: boolean; target?: { x: number; y: number }; whirlpools?: boolean } = {},
ledges = false,
}: {
water?: boolean;
target?: { x: number; y: number };
whirlpools?: boolean;
ledges?: boolean;
} = {},
): { x: number; y: number } | undefined {
if (!open(world, x, y, direction, water, target, whirlpools)) return undefined;
const [, dx, dy] = DIRECTIONS.find(([d]) => d === direction)!;
const standing = world.assets.collision(world.map, x, y);
if (ledges && (standing & 0xf0) === 0xa0 && LEDGES[standing & 7].includes(direction)) {
const lx = x + dx * 2,
ly = y + dy * 2;
const occupied = (ox: number, oy: number) =>
world.objects().some((object) => object.x === ox && object.y === oy);
if (
lx >= 0 &&
ly >= 0 &&
lx < world.map.width &&
ly < world.map.height &&
world.assets.permission(world.assets.collision(world.map, lx, ly)) === 0 &&
!occupied(x + dx, y + dy) &&
!occupied(lx, ly) &&
!(
isWarpHazard(world.assets.collision(world.map, lx, ly)) &&
world.map.warps.some((warp) => warp.x === lx && warp.y === ly) &&
(lx !== target?.x || ly !== target?.y)
)
)
return { x: lx, y: ly };
}
// COLL_WATERFALL: a surfer entering from above is carried down it.
const ahead = world.assets.collision(world.map, x + dx, y + dy);
if (water && ahead === 0x33) {
if (direction !== "down") return undefined;
let fy = y + 1;
while (world.assets.collision(world.map, x, fy) === 0x33) fy++;
if (!open(world, x, fy - 1, "down", water, target, whirlpools)) return undefined;
return { x, y: fy };
}
if (!open(world, x, y, direction, water, target, whirlpools)) return undefined;
x += dx;
y += dy;
while (
@@ -164,13 +214,13 @@ export function landing(
/** Shortest walkable path on the current map, avoiding other warps.
* `water` also crosses open water, for a player who can Surf, and
* `whirlpools` the whirlpools a player with Whirlpool can clear; a step
* onto ice slides on until blocked. */
* `whirlpools` the whirlpools a player with Whirlpool can clear; `ledges`
* takes ledge jumps; a step onto ice slides on until blocked. */
export function findPath(
world: GameWorld,
targetX: number,
targetY: number,
{ water = false, whirlpools = false } = {},
{ water = false, whirlpools = false, ledges = false } = {},
): Direction[] {
const target = { x: targetX, y: targetY };
const queue = [{ x: world.x, y: world.y, path: [] as Direction[] }];
@@ -182,7 +232,7 @@ export function findPath(
visited.add(key);
if (node.x === targetX && node.y === targetY) return node.path;
for (const [direction] of DIRECTIONS) {
const end = landing(world, node.x, node.y, direction, { water, target, whirlpools });
const end = landing(world, node.x, node.y, direction, { water, target, whirlpools, ledges });
if (end) queue.push({ ...end, path: [...node.path, direction] });
}
}