Port Fly, Dig, Teleport and the Escape Rope
FLY opens _FlyMap in the party menu: the region's Town Map with "Where?", the visited flypoints in turn and the Fly user's icon, then FlyScript flies the icon off along SpriteAnimFunc_FlyFrom's path, enters the spawn point and lands it with SpriteAnimFunc_FlyTo. Teleport returns to the last Pokémon Center's town through IsSpawnPoint. Dig and the Escape Rope lead out of a cave to the warp EnterMapWarp's SaveDigWarp recorded, which saves now keep. newloadmap stops its script as StopScript does and the world resumes it on the new map, which the landing and return movements need. A destination that is not ported yet gives a notice. Closes #25 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c4cc0e756c
commit
a7aceda089
21 files changed
+1166
-97
No files matched your search
@@ -177,6 +177,29 @@ export class WorldRenderer {
|
||||
{ transparent: true },
|
||||
);
|
||||
}
|
||||
const flying = world.flyingIcon;
|
||||
if (flying) {
|
||||
// SPRITE_ANIM_OBJ_RED_WALK with the icon's tiles: each frame shows
|
||||
// for nine video frames, the second one mirrored on every other pass.
|
||||
const graphics = this.assets.monIcon(flying.species),
|
||||
phase = Math.floor(flying.frames / 9) % 4,
|
||||
flip = phase === 3,
|
||||
palette = this.assets.objectPalette(0, time);
|
||||
for (let tile = 0; tile < 4; tile++) {
|
||||
const column = tile & 1;
|
||||
drawTile(
|
||||
this.image.data,
|
||||
this.width,
|
||||
this.height,
|
||||
graphics,
|
||||
(phase & 1) * 4 + tile,
|
||||
player.x - cameraX + flying.x + (flip ? 8 - column * 8 : column * 8),
|
||||
player.y - cameraY - 4 + flying.y + (tile >> 1) * 8,
|
||||
palette,
|
||||
{ transparent: true, flipX: flip },
|
||||
);
|
||||
}
|
||||
}
|
||||
this.context.putImageData(this.image, 0, 0);
|
||||
}
|
||||
private actor(
|
||||
|
||||
@@ -3,18 +3,25 @@ import { OVERWORLD, SYMBOLS } from "../rom/profile";
|
||||
import { decompress } from "../rom/lz";
|
||||
import { drawTile } from "./pixels";
|
||||
|
||||
/** The ROM's Town Map, used by wall maps, the bedroom poster and the
|
||||
/** The ROM's Town Map, used by wall maps, the bedroom poster, the
|
||||
* Pokégear's map card, which chooses the region and marks the landmark
|
||||
* under its cursor. */
|
||||
* under its cursor, and the Fly map, which shows the Fly user's party icon
|
||||
* on the selected flypoint (`species`) and may leave out the player. */
|
||||
export function drawTownMap(
|
||||
canvas: HTMLCanvasElement,
|
||||
world: GameWorld,
|
||||
card?: { kanto: boolean; player: number; cursor: number; name: string },
|
||||
card?: {
|
||||
kanto: boolean;
|
||||
player?: number;
|
||||
cursor: number;
|
||||
name: string;
|
||||
species?: number;
|
||||
},
|
||||
) {
|
||||
const { assets } = world,
|
||||
r = assets.reader;
|
||||
const landmark = card?.player ?? world.map.landmark;
|
||||
const kanto = card?.kanto ?? landmark >= 47;
|
||||
const landmark = card ? card.player : world.map.landmark;
|
||||
const kanto = card?.kanto ?? world.map.landmark >= 47;
|
||||
const map = kanto ? OVERWORLD.KantoMap : OVERWORLD.JohtoMap;
|
||||
const graphics = decompress(r, OVERWORLD.TownMapGFX, 0x1000);
|
||||
const palette =
|
||||
@@ -48,10 +55,30 @@ export function drawTownMap(
|
||||
const p = SYMBOLS.landmarks + index * 4;
|
||||
return { x: r.byte(p) - 8, y: r.byte(p + 1) - 16 };
|
||||
};
|
||||
const player = at(landmark);
|
||||
context.fillStyle = "#b32628";
|
||||
context.fillRect(player.x - 2, player.y - 2, 4, 4);
|
||||
if (card) {
|
||||
if (landmark !== undefined) {
|
||||
const player = at(landmark);
|
||||
context.fillStyle = "#b32628";
|
||||
context.fillRect(player.x - 2, player.y - 2, 4, 4);
|
||||
}
|
||||
if (card?.species !== undefined) {
|
||||
// TownMapMon: the icon's first frame, centred on the landmark.
|
||||
const cursor = at(card.cursor),
|
||||
icon = assets.monIcon(card.species),
|
||||
iconImage = context.getImageData(cursor.x - 8, cursor.y - 8, 16, 16);
|
||||
for (let tile = 0; tile < 4; tile++)
|
||||
drawTile(
|
||||
iconImage.data,
|
||||
16,
|
||||
16,
|
||||
icon,
|
||||
tile,
|
||||
(tile & 1) * 8,
|
||||
(tile >> 1) * 8,
|
||||
assets.objectPalette(0, world.timeOfDay),
|
||||
{ transparent: true },
|
||||
);
|
||||
context.putImageData(iconImage, cursor.x - 8, cursor.y - 8);
|
||||
} else if (card) {
|
||||
// The Pokégear's arrow cursor, drawn as a frame around the landmark.
|
||||
const cursor = at(card.cursor);
|
||||
context.strokeStyle = "#2366a5";
|
||||
@@ -61,7 +88,7 @@ export function drawTownMap(
|
||||
canvas.setAttribute(
|
||||
"aria-label",
|
||||
card
|
||||
? `Town Map: ${card.name}${card.cursor === card.player ? ", you are here" : ""}`
|
||||
? `${card.species !== undefined ? "Fly map" : "Town Map"}: ${card.name}${card.cursor === card.player ? ", you are here" : ""}`
|
||||
: `Town Map: ${world.map.location}`,
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user