Fishing has a chance to catch items - pret/pokecrystal GitHub Wiki
Implementation based on the tutorial Smashing rocks has a chance to contain items.
Adds a chance to catch items when you fail to catch a fish.
Contents
- Modifying the overworld functionality
- Adding the fish item code
- Adding support for more rods
- Adding a fourth rod that only catches items
1. Modifying the overworld functionality
First, we must edit the Script_NotEvenANibble
and Script_NotEvenANibble2
within engine/events/overworld.asm:
+ callasm FishItemEncounter
+ iffalse .no_item
+ scall Script_FishCastRod
+ callasm Fishing_CheckFacingUp
+ iffalse .NotFacingUp
+ applymovement PLAYER, .Movement_FacingUp
+.NotFacingUp:
+ applymovement PLAYER, .Movement_NotFacingUp
+ pause 40
+ applymovement PLAYER, .Movement_RestoreRod
+ writetext RodBiteText
+ callasm PutTheRodAway
+ callasm FishItemEncounter
+ iffalse .line_snapped
+ writetext FishedAnItemText
+ waitbutton
+ verbosegiveitem ITEM_FROM_MEM
+ sjump Script_NotEvenANibble_FallThrough
+.no_item
scall Script_FishCastRod
writetext RodNothingText
sjump Script_NotEvenANibble_FallThrough
+.line_snapped
+ writetext LineSnappedText
+ playsound SFX_RAZOR_WIND
+ waitbutton
+ sjump Script_NotEvenANibble_FallThrough
+
+.Movement_NotFacingUp:
+ fish_got_bite
+ fish_got_bite
+ fish_got_bite
+ fish_got_bite
+ show_emote
+ step_end
+
+.Movement_FacingUp:
+ fish_got_bite
+ fish_got_bite
+ fish_got_bite
+ fish_got_bite
+ step_sleep 1
+ show_emote
+ step_end
+
+.Movement_RestoreRod:
+ hide_emote
+ fish_cast_rod
+ step_end
Script_NotEvenANibble2:
scall Script_FishCastRod
writetext RodNothingText
+LineSnappedText:
+ text "The line snapped…"
+ done
+
+FishedAnItemText:
+ text "Snagged an item!"
+ done
These changes stop the Script_NotEvenANibble
from ending if it doesn't find a Pokémon, jumping to .no_battle
in that case, and then calling FishItemEncounter
to determine whether or not an item will be found. If no item is selected, the script will jump to .no_item
, ending the script and giving the player nothing. Due to how some data is carried, we have to call FishItemEncounter twice so the player will not receive a bugged item, while not efficient it does make the success rate a bit more reasonable. To account for a situation where the first call is a "Yes" but the second is a "No", the line will snap.
But FishItemEncounter
has yet to be defined, so these changes on their own will not work.
2. Adding the fish item code
We have to define FishItemEncounter
, and a suitable place to put it would be in engine/events/checkforhiddenitems.asm at the bottom of the script:
+FishItemEncounter:
+ ld hl, .FishItems
+ call Random
+.loop
+ sub [hl]
+ jr c, .ok
+ inc hl
+ inc hl
+ jr .loop
+
+.ok
+ ld a, [hli]
+ inc a
+ jr z, .done
+ ld a, [hli]
+.done
+ ld [wScriptVar], a
+ ret
+
+.FishItems:
+ db 1, RARE_CANDY
+ db 2, NUGGET
+ db 4, ULTRA_BALL
+ db 6, STAR_PIECE
+ db 12, BIG_PEARL
+ db 18, ULTRA_BALL
+ db 24, DRAGON_SCALE
+ db 24, MYSTIC_WATER
+ db 48, PEARL
+ db 64, POKE_BALL
+ db -1
This bit is taken from the rock smash tutorial, and applies basically word for word here:
For starters, FishItemEncounter
will load the .FishItems
table into hl
, and then call Random
, which will load a random byte (i.e., a value between 0 and 255) in a
. With this random value generated, it performs comparisons against the probabilities encoded in the .FishItems
table, picking an item with the listed odds (out of 256: in the example, there's a 1 in 256 chance of getting a Max Revive, and a 64 in 256 (or 1 in 4) chance of getting a Pokeball).
Since the probabilities in the table add up to less than 256, the db -1
line at the end indicates the end of the table. If this line is reached, the function will load a 0 (which represents NO_ITEM
), indicating that the cast didn't contain an item. If this is the case, the iffalse .no_item
line in the script will skip giving an item to the player. Otherwise, the selected item, which will have already been loaded into the script variable (wScriptVar
) by the function, will be awarded to the player by the script.
And there you have it, the player can now find items while fishing! You can add your own items and chances by editing the .FishItems
table in checkhiddenitems.asm
.
Note that the probabilities can only add up to 256 or less. (If they add up to 256 exactly, the final db -1
is not needed, but it can be left in place just in case.) If they add up to more than 256, the last entries in the table will be inaccessible.
3. Adding support for more rods
So the above code works, but all rods will pull from the same table. Maybe you want the Old Rod to only fish up junk and leave the good stuff for the Super Rod...
First of all, let's go back to engine/events/checkforhiddenitems.asm
at FishItemEncounter
:
-ld hl, .FishItems
+ ld a, [wFishingRodUsed]
+ cp $0
+ jr z, .OldRodItems
+ cp $1
+ jr z, .GoodRodItems
+ cp $2
+ jr z, .SuperRodItems
+.OldRodItems
+ ld hl, .OldRodItemTable
+ jr .continue
+.GoodRodItems
+ ld hl, .GoodRodItemTable
+ jr .continue
+.SuperRodItems
+ ld hl, .SuperRodItemTable
+.continue
call Random
By default, using a rod sets value to wram based on which rod you use. $0
is Old Rod, $1
is the Good Rod, etc. This is how crystal pulls encounter tables! We can jack this knowledge to apply to items too.
And then edit the item tables, in this example, I just relegated the original table to SuperRodItemTable
:
-.FishItems:
+.OldRodItemTable:
+ db 1, PEARL
+ db 1, POTION
+ db 2, POKE_BALL
+ db 4, POTION
+ db 6, STARDUST
+ db 8, POKE_BALL
+ db 12, EVERSTONE
+ db 12, POTION
+ db 24, PEARL
+ db 48, POKE_BALL
+ db -1
+
+.GoodRodItemTable:
+ db 1, STAR_PIECE
+ db 1, SUPER_POTION
+ db 2, GREAT_BALL
+ db 4, SUPER_POTION
+ db 6, STARDUST
+ db 8, GREAT_BALL
+ db 12, SUPER_POTION
+ db 12, STARDUST
+ db 24, PEARL
+ db 48, POKE_BALL
+ db -1
+
+.SuperRodItemTable:
And that's it! You should now get different items based on what Rod you use.
4. Adding a fourth rod that only catches items
This goes slightly out of scope for this tutorial, but some find the Itemfinder to be a useless item. I gave it dual functionality as a fishing rod that only catches items. Here's how to add it:
First, back to FishItemEncounter
:
jr z, .SuperRodItems
+ cp $3
+ jr z, .ItemfinderRodItems
.OldRodItems
ld hl, .OldRodItemTable
jr .continue
.GoodRodItems
ld hl, .GoodRodItemTable
jr .continue
.SuperRodItems
ld hl, .SuperRodItemTable
+ jr .continue
+.ItemfinderRodItems
+ ld hl, .ItemfinderRodItemTable
.continue
And add this under the SuperRodItemTable
:
+.ItemfinderRodItemTable:
+ db 1, NUGGET
+ db 1, STAR_PIECE
+ db 2, PP_UP
+ db 4, KINGS_ROCK
+ db 6, BIG_PEARL
+ db 8, ULTRA_BALL
+ db 12, HYPER_POTION
+ db 12, STARDUST
+ db 24, PEARL
+ db 48, BRICK_PIECE
+ db -1
Now, go to engine/events/itemfinder.asm
. We need to make it so when you are facing a water tile, the player will fish instead of search for hidden items:
ItemFinder:
+ call GetFacingTileCoord
+ call GetTileCollision
+ cp WATER_TILE
+ jr nz, .RegularItemfinderFunction
+ ld e, $3
+ farcall FishFunction
+ jr .end
+.RegularItemfinderFunction
farcall CheckForHiddenItems
jr c, .found_something
ld hl, .Script_FoundNothing
jr .resume
.found_something
ld hl, .Script_FoundSomething
.resume
call QueueScript
ld a, $1
ld [wItemEffectSucceeded], a
+.end
ret
We are utilizing that wram space that checks for the rod used, with that it should be possible to add up to 8 different rods if you wanted to.
Now back to our old friend engine/events/overworld.asm
and go to FishFunction
:
.goodtofish
ld d, a
ld a, [wFishingRodUsed]
+ cp $3
+ jr z, .FishNoBite
ld e, a
farcall Fish
ld a, d
This skips the battle function, so this rod can't actually catch wild Pokemon. The only results will be "Not even a bite!", "The line snapped...", or `"Snagged an item!".
That's all I've got! With this and the Rock Smash tutorial, think of other ways to apply the knowledge (Headbutt items?)!