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

  1. Modifying the overworld functionality
  2. Adding the fish item code
  3. Adding support for more rods
  4. 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?)!

bgb00105 bgb00106 bgb00107