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:
Sébastien QUEROLandClaude Opus 5.5 committed 2026-10-03 00:49:35 +02:00
1 parent 4cb4676c7d
commit 397dcef44c
20 files changed
+681 -47

No files matched your search

+25 -3
View File
@@ -14,6 +14,11 @@ import {
* Headbutt tree back: HideHeadbuttTree's WaitBGMap and the 32 shaking
* frames. */
const HEADBUTT_HIDDEN_FRAMES = 36;
/** FACING_FISH_DOWN, followed by the up, left and right fishing frames. */
const FACING_FISH_DOWN = 0x10;
/** A FACING_* piece's ABSOLUTE_TILE_ID flag: the tile ignores the
* object's first tile. */
const ABSOLUTE_TILE_ID = 0x04;
export class WorldRenderer {
width: number = BASE_VIEWPORT.width;
@@ -161,7 +166,13 @@ export class WorldRenderer {
{ transparent: true, flipX },
);
}
for (const pose of [...poses].sort((a, b) => a.y - b.y))
for (const pose of [...poses].sort((a, b) => a.y - b.y)) {
// The player's fishing frames use LoadFishingGFX's tiles.
const fishing =
pose.id === 0 &&
pose.facing !== undefined &&
pose.facing >= FACING_FISH_DOWN &&
pose.facing <= FACING_FISH_DOWN + 3;
this.actor(
pose.sprite,
pose.x - cameraX,
@@ -172,7 +183,9 @@ export class WorldRenderer {
pose.palette,
pose.facing,
pose.inGrass,
fishing ? world.avatar : undefined,
);
}
const emote =
world.emote && poses.find((pose) => pose.id === world.emote!.object);
if (emote) {
@@ -251,9 +264,13 @@ export class WorldRenderer {
paletteIndex?: number,
facing?: number,
inGrass = false,
fishing?: "chris" | "kris",
) {
const sprite = this.assets.sprite(id);
if (!sprite) return;
const graphics = fishing
? this.assets.fishingSprite(sprite.graphics, fishing === "kris")
: sprite.graphics;
const palette = this.assets.objectPalette(
paletteIndex ?? sprite.palette,
time,
@@ -263,12 +280,17 @@ export class WorldRenderer {
? this.assets.facingTiles(facing)
: this.assets.facing(direction, sprite.walking ? frame : 0);
for (const piece of pieces) {
const tile = piece.tile >= 0x80 ? piece.tile - 0x80 + 12 : piece.tile;
const tile =
fishing && piece.attributes & ABSOLUTE_TILE_ID
? 24 + piece.tile - 0xfc
: piece.tile >= 0x80
? piece.tile - 0x80 + 12
: piece.tile;
drawTile(
this.image.data,
this.width,
this.height,
sprite.graphics,
graphics,
tile,
x + piece.x,
y + piece.y,