Open Route 42 to Mahogany, the Lake of Rage, the Rocket Hideout and Pryce (#32)
Route 42, Mt. Mortar, Mahogany Town, Route 43 and its gates, the Lake of Rage and its houses, the Rocket Hideout and Route 44 are open. The story plays through the toll, the red Gyarados, Lance, the hideout and Pryce's GLACIERBADGE, which starts the Radio Tower takeover. The fishing guru measures Magikarp (CheckMagikarpLength, MagikarpHouseSign, FindPartyMonThatSpecies); the record is saved. takemoney now leaves a short account at zero as TakeMoney does, and setmapscene/setscene on a map without scenes consume the byte instead of refusing. The playthrough's path search slides on ice and the run gains five stages to Pryce. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
62fe4bf664
commit
061ca4d59d
11 files changed
+578
-43
No files matched your search
+65
-23
@@ -92,14 +92,75 @@ const isWarpHazard = (tile: number) =>
|
||||
* freely, unlike currents, waterfalls and whirlpools. */
|
||||
const isOpenWater = (tile: number) => tile === 0x29 || tile === 0x21;
|
||||
|
||||
/** COLL_ICE and COLL_ICE_2B: a step onto them keeps sliding (.CheckForced). */
|
||||
const isIce = (tile: number) => tile === 0x23 || tile === 0x2b;
|
||||
|
||||
/** Whether a step from (fromX, fromY) onto the next tile in `direction`
|
||||
* is open: walkable (or open water for a surfer), no side wall, no object,
|
||||
* and no warp other than the target. */
|
||||
function open(
|
||||
world: GameWorld,
|
||||
fromX: number,
|
||||
fromY: number,
|
||||
direction: Direction,
|
||||
water: boolean,
|
||||
target?: { x: number; y: number },
|
||||
) {
|
||||
const [, dx, dy] = DIRECTIONS.find(([d]) => d === direction)!;
|
||||
const x = fromX + dx,
|
||||
y = fromY + dy;
|
||||
if (x < 0 || y < 0 || x >= world.map.width || y >= world.map.height) return false;
|
||||
const tile = world.assets.collision(world.map, x, y);
|
||||
if (world.assets.permission(tile) !== 0 && !(water && isOpenWater(tile))) return false;
|
||||
if (
|
||||
edges(world.assets.collision(world.map, fromX, fromY)).includes(direction) ||
|
||||
edges(tile).includes(OPPOSITE[direction])
|
||||
)
|
||||
return false;
|
||||
if (world.objects().some((object) => object.x === x && object.y === y)) return false;
|
||||
if (
|
||||
isWarpHazard(tile) &&
|
||||
world.map.warps.some((warp) => warp.x === x && warp.y === y) &&
|
||||
(x !== target?.x || y !== target?.y)
|
||||
)
|
||||
return false;
|
||||
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. */
|
||||
export function landing(
|
||||
world: GameWorld,
|
||||
x: number,
|
||||
y: number,
|
||||
direction: Direction,
|
||||
{ water = false, target }: { water?: boolean; target?: { x: number; y: number } } = {},
|
||||
): { x: number; y: number } | undefined {
|
||||
if (!open(world, x, y, direction, water, target)) return undefined;
|
||||
const [, dx, dy] = DIRECTIONS.find(([d]) => d === direction)!;
|
||||
x += dx;
|
||||
y += dy;
|
||||
while (
|
||||
isIce(world.assets.collision(world.map, x, y)) &&
|
||||
open(world, x, y, direction, water, target)
|
||||
) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
/** Shortest walkable path on the current map, avoiding other warps.
|
||||
* `water` also crosses open water, for a player who can Surf. */
|
||||
* `water` also crosses open water, for a player who can Surf; a step onto
|
||||
* ice slides on until blocked. */
|
||||
export function findPath(
|
||||
world: GameWorld,
|
||||
targetX: number,
|
||||
targetY: number,
|
||||
{ water = false } = {},
|
||||
): Direction[] {
|
||||
const target = { x: targetX, y: targetY };
|
||||
const queue = [{ x: world.x, y: world.y, path: [] as Direction[] }];
|
||||
const visited = new Set<string>();
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
@@ -108,28 +169,9 @@ export function findPath(
|
||||
if (visited.has(key)) continue;
|
||||
visited.add(key);
|
||||
if (node.x === targetX && node.y === targetY) return node.path;
|
||||
for (const [direction, dx, dy] of DIRECTIONS) {
|
||||
const x = node.x + dx,
|
||||
y = node.y + dy;
|
||||
if (x < 0 || y < 0 || x >= world.map.width || y >= world.map.height)
|
||||
continue;
|
||||
const tile = world.assets.collision(world.map, x, y);
|
||||
if (world.assets.permission(tile) !== 0 && !(water && isOpenWater(tile)))
|
||||
continue;
|
||||
if (
|
||||
edges(world.assets.collision(world.map, node.x, node.y)).includes(direction) ||
|
||||
edges(tile).includes(OPPOSITE[direction])
|
||||
)
|
||||
continue;
|
||||
if (world.objects().some((object) => object.x === x && object.y === y))
|
||||
continue;
|
||||
if (
|
||||
isWarpHazard(tile) &&
|
||||
world.map.warps.some((warp) => warp.x === x && warp.y === y) &&
|
||||
(x !== targetX || y !== targetY)
|
||||
)
|
||||
continue;
|
||||
queue.push({ x, y, path: [...node.path, direction] });
|
||||
for (const [direction] of DIRECTIONS) {
|
||||
const end = landing(world, node.x, node.y, direction, { water, target });
|
||||
if (end) queue.push({ ...end, path: [...node.path, direction] });
|
||||
}
|
||||
}
|
||||
throw new Error(`No route to ${targetX},${targetY}`);
|
||||
|
||||
Reference in new issue
Block a user