import type { RomReader } from "../rom/reader"; import { OVERWORLD } from "../rom/profile"; /** The fishing rods' encounters (engine/events/fish.asm and * data/wild/fish.asm). */ export interface FishMon { species: number; level: number; } /** FISHGROUP_DATA_LENGTH: the bite chance, then a table per rod. */ const FISHGROUP_DATA_LENGTH = 7; /** NITE_F: TimeFishGroups' second species from the night on. */ const NITE_F = 2; /** Fish: rod 0 (Old), 1 (Good) or 2 (Super) in FishGroups entry `group` * (FISHGROUP_*, never FISHGROUP_NONE). A bite when Random falls below the * group's chance, then the first entry whose chance a second Random does * not exceed; species 0 picks the time of day's TimeFishGroups entry. * Undefined when nothing bites. GetFishGroupIndex's swarm groups need * DAILYFLAGS1_FISH_SWARM_F and wFishingSwarmFlag, which only trainer calls * set; they are not ported, so the map's own group is used. */ export function fish( r: RomReader, group: number, rod: number, timeOfDay: number, random: () => number, ): FishMon | undefined { const entry = OVERWORLD.FishGroups + (group - 1) * FISHGROUP_DATA_LENGTH; if (random() >= r.byte(entry)) return undefined; let p = r.near(entry + 1 + rod * 2, r.bank(entry)); const roll = random(); while (roll > r.byte(p)) p += 3; let species = r.byte(p + 1), level = r.byte(p + 2); if (!species) { // .TimeEncounter: the level byte indexes TimeFishGroups. const time = OVERWORLD.TimeFishGroups + level * 4 + (timeOfDay >= NITE_F ? 2 : 0); species = r.byte(time); level = r.byte(time + 1); } return { species, level }; }