The Spirit fish gate - fishing's rarest catch, decompiled
The Spirit fish is Legend of Legaia's rarest catch, and the observed catch rate has never matched the published tables: the accepted model - Normal Lure at Buma with a “Good!” bait, 1 in 32 - predicts a Spirit well inside a long session, while players routinely report hundreds of catches without one. This page documents the species-selection mechanism from the disassembly of the fishing overlay: the spawn-table lookup, the band roll and its cutoffs, the reel-cadence recogniser behind the “Good!” splash, and the strike-time gate that is the only path to the rare column. The published table has every species in the right cell; the gap is in what the rare column's probability actually is.
The spawn tables
The hooked species is assigned in the pre-hook half of the hooked-fish handler FUN_801d26cc: species = spawn_table[lure*8 + band]. The equipped lure (_DAT_80084450) picks the row; the cast band (DAT_801d90e8, 0–4) picks the column. Decoded from the disc (rodata 0x801D8334), the species agree cell-for-cell with the published community tables:
| Buma / lure | band 3 (~75.0%) | band 2 (~15.2%) | band 1 (~4.9%) | band 0 (~4.9%) | band 4 (gated) |
|---|---|---|---|---|---|
| Light | High Charger | High Charger | Jack | Jack | Spikefish† |
| Normal | High Charger | Jack | High Charger | High Charger | Spirit |
| Heavy | Charger | Fly Pie | Lippian | Devourer | Spikefish† |
| Vidna / lure | band 3 | band 2 | band 1 | band 0 | band 4 (gated) |
|---|---|---|---|---|---|
| Light | Jack | Jack | Fly Pie | Jack | Spikefish† |
| Normal | High Charger | Fly Pie | Spikefish | Fly Pie | Spikefish† |
| Heavy | Fly Pie | Fly Pie | Charger | Octoban | Baraba |
† Dead cells: each venue's band-4 gate hardwires the lure it fires on (Normal at Buma, Heavy at Vidna), so band 4 is only ever read on the row carrying the rare fish. The Spikefish entries in the other rows are unreachable in retail play - the only code path that could touch them is a developer shortcut (debug print flag + R1 held at the strike forces band 4 unconditionally), which retail can't enable.
The selection pipeline
Band assignment happens in two phases. While the lure is in the water, the band is re-derived every frame - a cadence match pins it for a ~64-frame hold window (and boosts the bite roll for the same window); otherwise the frame's random roll replaces it. At the instant a fish strikes, a venue-specific gate may upgrade band 0 to band 4; the species lookup then reads whatever band is current. Everything downstream of the strike is deterministic.
FUN_801d26cc, immediately before the species lookup.The band roll
While the lure sits deep enough (line record over 500 units) and no fish is hooked, the band re-rolls on every frame no cadence holds it: rand() & 0xfff against three fixed cutoffs. The published 184/48/16/8-out-of-256 estimate has the right shape; the actual cutoffs sit slightly lower at the rare end. No outcome maps to band 4 - there is no fifth cutoff.
The reel-cadence recogniser
Before each frame's band roll, the game consults a reel-cadence recogniser (FUN_801d3db4): a 16-slot ring buffer of {button, held-frames} reel history, walked backwards against four rodata templates (0x801D87D4), each hold matched with ±10 frames of tolerance. A match does three things: it fires the splash rendered on a successful bait (“Good!”), it stores the matched template's id as the band, and it arms the check countdown to 0x40 - for the next ~64 frames the band holds instead of re-rolling, and because that same countdown feeds the strike credit (credit = countdown + 2), the bite roll runs at roughly twenty times its idle rate for the duration. A strike can only land on a frame where a reel button is held, so the final press of the cadence doubles as the bite window.
Performing the band-0 cadence
Setup: cast deep - the cadence check and the band itself only run once the line record exceeds 500, i.e. a hard cast that sinks well - then press either reel button briefly and release, so the first rest below starts from a measurable point. (When repeating the loop, the previous hold already serves as that marker.) Then, matching the numbered steps in the figure:
- Hands off - no buttons - for about two-thirds of a second (0.52–0.82 s accepted).
- Hold ✕ for about 0.4 s (0.27–0.57 s), then release.
- Hands off again for about two-thirds of a second.
- Press and hold □. The “Good!” splash fires about a tenth of a second into the hold: band 0 is set. Keep holding - the boosted bite lands only while a reel button is down, and it is most likely in the first second after the splash. If nothing bites, release and repeat from step 1.
The hold window is the load-bearing part. An unmatched band is re-rolled every frame, so a rolled band 0 exists only on the frames the dice happen to produce it; a cadence-set band is pinned for the full window, and the boosted strike usually lands inside it. The four templates compared:
The band-4 gate at the strike
When a strike lands (reel held, per-frame strike roll passed, nothing hooked yet), one branch in FUN_801d26cc - executed immediately before the species lookup - decides whether an active band 0 upgrades to band 4. Every condition must hold simultaneously:
if (venue == Buma) { // DAT_801d90d0 == 0
if (0x32 < casts // > 50 lifetime casts (_DAT_80084460)
&& (casts & 1) == 0 // cast counter EVEN at this instant
&& lure == 1 // Normal Lure (_DAT_80084450)
&& rod == 2 // third rod (_DAT_80084454)
&& band == 0) // rare band active (DAT_801d90e8)
if ((rand() & 0xf) == 0) // ... then 1 in 16
band = 4; // -> Spirit
} else { // Vidna
if ((casts & 1) == 0 && lure == 2 && rod == 2 && band == 0)
if ((rand() & 3) == 0) // 1 in 4, no cast-count threshold
band = 4; // -> Baraba
}
Three of these conditions are not externally observable, which is why no published table carries them. The cast counter (_DAT_80084460) lives in the save block: it increments once per cast at the moment the flown lure lands, persists across sessions, and is saved with the game - band 4 at Buma is unreachable inside the first 51 lifetime casts. The counter's parity makes alternating casts ineligible regardless of play. The rod condition means that with either other rod equipped, the gate short-circuits and band 4 cannot occur. The parity has exactly one visible correlate: it selects the lure's drift direction on wall contact.
Effective probabilities
With the gate's structure known, the per-catch Spirit probability at Buma follows directly. All rows assume the venue's other preconditions (deep cast, strike landed):
| Setup | Per-catch probability | Derivation |
|---|---|---|
| Third rod, Normal Lure, 50+ lifetime casts, template-0 cadence held at the strike | ≈ 1/32 | 1/16 gate roll × ½ counter parity; the cadence pins band 0 and boosts the bite roll ~20× for its ~1 s hold window, so the strike normally lands inside it |
| Third rod, Normal Lure, 50+ lifetime casts, no cadence | ≈ 1/660 | 199/4096 band-0 roll × ½ parity × 1/16 gate roll |
| Either other rod, or under 51 lifetime casts | 0 | The gate's conjunction fails before the random roll |
These figures reconcile the published model with the observed rates. Under a flat 1/32 per catch, 250 catches without a Spirit has probability ~0.04%; under the gated model without cadence input, the same drought has probability ~68% - long empty sessions are the expected outcome, not an anomaly. The catches that did occur correspond to sessions in which every condition held, including a reel rhythm ending in a Square tap. Baraba at Vidna carries no cast-count threshold and a 1-in-4 gate roll, consistent with its far higher observed rate.
Passing the gate selects the species; landing it is a separate problem. The Spirit record carries the species table's largest pull factor - the strongest tension fight in the minigame - so a gated strike can still be lost in the reel.
Player reports, mapped to mechanisms
Long-standing player observations about Spirit fishing each correspond to a specific mechanism above:
| Reported behaviour | Mechanism |
|---|---|
| Square presses associated with Spirit catches | Template 0 - the only cadence that sets band 0 - is the only template ending in a Square tap |
| Pond position affects results | Cells flagged 0x4000 in the floor grid raise the strike rate, increasing how often the gate is evaluated (the species table is unaffected) |
| Cast distance matters | Below 200 line units no strike occurs; below 500 the band never re-rolls and the cadence check never runs |
| “Good!” improves the odds | Any match boosts the bite rate ~20× for ~1 s, but only template 0 sets band 0; the other three set bands 1–3 and fire the same splash |
| 1/32 with a “Good!” on Normal Lure | Matches the effective rate when the rod, cast-counter, and cadence conditions all hold; the additional conditions are not externally observable |
| Long droughts despite correct lure | Expected under the gated model - and certain with either other rod or under 51 lifetime casts |
Provenance
Every mechanic above is read from the MIPS disassembly of the fishing overlay (PROT entry 0972, load base 0x801CE818) and verified against a retail USA disc image: the strike branch and band-4 gate in FUN_801d26cc, the cadence recogniser FUN_801d3db4, the reel-button decode FUN_801d7450, the species table at 0x801D81A4, the venue spawn tables at 0x801D8334 / 0x801D8434, and the gesture templates at 0x801D87D4. The full subsystem reference - state machine, tension fight, scoring, prize exchange - is minigame-fishing.md; the spawn-table and species parsers live in legaia_asset::fishing_species. No Sony-owned bytes appear here - only addresses, offsets, and decoded parameters.