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

+18
View File
@@ -111,6 +111,8 @@ export type MenuFieldMove =
wild?: { species: number; level: number };
/** hLastTalked, when the move acts on a facing object. */
lastTalked?: number;
/** BATTLETYPE_FISH: the wild Pokémon is on a fishing rod's hook. */
fish?: boolean;
}
| { texts: number[] }
| { notice: string }
@@ -144,6 +146,8 @@ export interface StartMenuHost {
gameTime(): GameTime;
/** CheckForHiddenItems: an unfound hidden item on the screen. */
hiddenItemOnScreen(state: ScriptState): boolean;
/** FishFunction with rod 0-2: a script, or a silent failure. */
fish(state: ScriptState, rod: number): MenuFieldMove;
}
/** The overworld start menu (engine/menus/start_menu.asm) with its
@@ -989,6 +993,20 @@ export class StartMenuRun {
this.deferred = MENU.SacredAshScript;
this.deferredItem = item;
return true;
case MENU.OldRodEffect:
case MENU.GoodRodEffect:
case MENU.SuperRodEffect: {
// UseRod: FishFunction queues a script unless it fails.
const result = this.host.fish(
this.state,
effect === MENU.OldRodEffect ? 0 : effect === MENU.GoodRodEffect ? 1 : 2,
);
if (!("script" in result)) return false;
this.deferred = result.script;
this.deferredItem = item;
this.deferredMove = result;
return true;
}
case MENU.ItemfinderEffect:
// ItemFinder: one script or the other, and always a success.
this.deferred = this.host.hiddenItemOnScreen(this.state)