Use Strength by "Talking" to Boulders - pret/pokered GitHub Wiki

Introduction

In Generation 2 and onward you can activate Strength simply by interacting with the overworld boulders. This tutorial will show how to implement the same thing in Generation 1. You'll be able to push boulders with no menu-ing required!

Editing the Boulder Text

We'll start by opening home\overworld_text.asm and finding the entry for BoulderText::. We're going to do the following here:

  • Adding some logic to display different boulder text if Strength is already active
  • Add a Badge check to verify we're allowed to use Strength
  • Finally, call a routine (which we'll be creating shortly) to check your team for a Strength user

For now, we'll be overhauling BoulderText::

BoulderText::
-	text_far _BoulderText
-	text_end
+	text_asm
+	ld a, [wStatusFlags1]
+	bit BIT_STRENGTH_ACTIVE, a		; check if Strength is already active
+	jr nz, .BouldersCanBeMovedText	; if so, display alternate boulder text
+	ld hl, RequiresStrengthText
+	call PrintText
+	ld a, [wObtainedBadges]
+	bit BIT_RAINBOWBADGE, a			; check for Erika's badge
+	jr z, .BoulderTextDone			; if no RainbowBadge, text ends here
+	farcall CheckPartyForStrength	; calls routine to check party movesets for Strength
+	jr .BoulderTextDone
+.BouldersCanBeMovedText
+	ld hl, BouldersCanBeMovedText
+	call PrintText
+.BoulderTextDone
+	jr TextScriptEnd
+
+RequiresStrengthText:
+	text_far _BoulderText			; recycling the existing BoulderText
+	text_end
+
+BouldersCanBeMovedText:
+	text_far _BouldersCanBeMovedText
+	text_end

MartSignText::
...

Now we need to create CheckPartyForStrength. We're going to place this somewhere else entirely and use a farcall.

Make a Way to Check Your Party's Moves

Go open engine\overworld\field_move_messages.asm and Copy/Paste the following right at the beginning of the file (annotations aplenty!)

+CheckPartyForStrength::
+	push de							; preserves 'de' for later, to prevent game crash
+	ld a, 1							; constant we'll use to correspond (currently) with PartyMon1
+	push af							; preserves 'a' for incrementing purposes later
+	ld hl, wPartySpecies			; loads PartyMon1 species data into 'hl'
+	ld a, [hli]						; copies Mon1 data into 'a', increments 'hl' to Mon2
+	ld [wCurPartySpecies], a		; sets Mon1's cry to play during Strength text
+	push hl							; preserves species data to be incremented later
+.LoadMon1Data
+	ld hl, wPartyMon1Moves			; loads Mon1 moveset into 'hl'
+	push hl							; preserves Mon1 moveset, to be used shortly
+	ld hl, wPartyMon1Nick			; loads Mon1 name into 'hl'
+	jr .PrepStrengthText			; jump all the way to .PrepStrengthText
+.LoadMon2Data
+	ld hl, wPartyMon2Moves			; see corresponding steps above for explanations
+	push hl
+	ld hl, wPartyMon2Nick
+	jr .PrepStrengthText
+.LoadMon3Data
+	ld hl, wPartyMon3Moves
+	push hl
+	ld hl, wPartyMon3Nick
+	jr .PrepStrengthText
+.LoadMon4Data
+	ld hl, wPartyMon4Moves
+	push hl
+	ld hl, wPartyMon4Nick
+	jr .PrepStrengthText
+.LoadMon5Data
+	ld hl, wPartyMon5Moves
+	push hl
+	ld hl, wPartyMon5Nick
+	jr .PrepStrengthText
+.LoadMon6Data
+	ld hl, wPartyMon6Moves
+	push hl
+	ld hl, wPartyMon6Nick
+.PrepStrengthText					; this section causes current Mon's name
+	ld de, wStringBuffer			; to appear in UsedStrengthAltText
+	ld bc, 11
+	call CopyData
+.PrepCheckMoves:
+	pop hl							; pulls current Mon's moveset from the stack into 'hl'
+	ld b, STRENGTH					; loads 'STRENGTH' into 'b' for comparison
+	ld c, NUM_MOVES					; loads # of moves in Mon's moveset
+.CheckMovesLoop
+	ld a, [hli]						; loads move data into 'a' and increments 'hl' to next move
+	cp b							; compares move data with 'STRENGTH'
+	jr z, .FoundMonWithStrength		; jump if there's a match
+	dec c							; reduce # of moves to check by one
+	jr nz, .CheckMovesLoop			; if # of moves remaining !=0, loop back to check next move
+.PrepNextMonForCheck
+	pop hl							; pulls next Mon's species data from the stack into 'hl'
+	ld a, [hli]						; loads this Mon's species data into 'a' and increments 'hl' to next Mon
+	cp $ff							; check if species data in 'a' = $ff, which is the end of party data
+	jr z, .NoStrengthMonFound		; if we've reached end of party, jump to exit routine 
+	ld [wCurPartySpecies], a		; otherwise, load new Mon's data to be used for cry during Strength text
+	pop af							; pulls constant (corresponds to current PartyMon) from stack into 'a'
+	inc a							; increase 'a' by 1
+	push af							; immediately move value in 'a' back onto the stack
+	push hl							; also move species data in 'hl' back onto the stack
+	cp 2							; checks if value in 'a' = 2
+	jr z, .LoadMon2Data				; if a = 2, jump back to load Mon2's data
+	cp 3							; same if a = 3, and so on...
+	jr z, .LoadMon3Data
+	cp 4
+	jr z, .LoadMon4Data
+	cp 5
+	jr z, .LoadMon5Data
+	cp 6
+	jr z, .LoadMon6Data
+.NoStrengthMonFound				; we arrive here if we ever load in species = $ff (end of party)
+	pop de							; clears data off the stack so that we can...
+	pop de							; load original data back into 'de' to prevent game crash
+	ret								; exit routine
+.FoundMonWithStrength
+	pop de								; as above, we need to restore 'de' to its
+	pop de								; original value... game crashes are bad, m'kay?
+	call WaitForTextScrollButtonPress
+	ld hl, LikeToUseStrengthText		; display text offering to use Strength
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	jr z, .MonUsingStrength				; jump if player selected YES
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	jp CloseTextDisplay
+	ret
+.MonUsingStrength
+	ld hl, wStatusFlags1
+	set BIT_STRENGTH_ACTIVE, [hl]		; boulders can now be moved
+	ld hl, UsedStrengthAltText			; displays Strength text, complete with user's name and cry
+	call PrintText
+	ret
+
+LikeToUseStrengthText:
+	text_far _LikeToUseStrengthText
+	text_end
+
PrintStrengthText:
	ld hl, wStatusFlags1
	set BIT_STRENGTH_ACTIVE, [hl]
	ld hl, UsedStrengthText
	call PrintText
	ld hl, CanMoveBouldersText
	jp PrintText

UsedStrengthText:
	text_far _UsedStrengthText
	text_asm
	ld a, [wCurPartySpecies]
	call PlayCry
	call Delay3
	jp TextScriptEnd

CanMoveBouldersText:
	text_far _CanMoveBouldersText
	text_end

+UsedStrengthAltText:					; displayed when Strength is used from the overworld
+	text_far _UsedStrengthAltText
+	text_asm
+	ld a, [wCurPartySpecies]
+	call PlayCry
+	call Delay3
+	jp TextScriptEnd
+
IsSurfingAllowed:
...

Creating the Text to be Displayed

Finally, open data\text\text_5.asm and Copy\Paste the following:

_CableClubNPCMakingPreparationsText::
	text "We're making"
	line "preparations."
	cont "Please wait."
	done

+_LikeToUseStrengthText::
+	text "Would you like to"
+	line "use STRENGTH?"
+	done
+
_UsedStrengthText::
	text_ram wNameBuffer
	text " used"
	line "STRENGTH.@"
	text_end

_CanMoveBouldersText::
	text_ram wNameBuffer
	text " can"
	line "move boulders."
	prompt

+_UsedStrengthAltText::
+	text_ram wStringBuffer
+	text " used"
+	line "STRENGTH.@"
+	text_end
+
+_BouldersCanBeMovedText::
+	text "Boulders can now"
+	line "be moved."
+	done
+
_CurrentTooFastText::
...

That should do it!

Conclusion

Thanks for following along. Two things I'd like to point out as we wrap up:

  • I noticed that for most of the instances a Pokemon's name is mentioned in a text display, the game references the wNameBuffer address. For the life of me I COULD NOT get that address to cooperate... so wStringBuffer it was! I've tested this pretty extensively, though, and it all seems to work fine, so...
  • I wanted to try and implement a similar QoL improvement for Cutting trees and Surfing without using the menu. The snag is that, while you can interact with boulders in the vanilla game, you cannot do the same with Cut trees and water tiles. If someone out there figures a way around this, I hope this tutorial about the boulders might provide a stepping stone to get those figured out as well!