Hyper Training - pret/pokered GitHub Wiki

Introduction

Hyper Training is a mechanic introduced in Generation VII where you can exchange Bottle Caps with a "Mr. Hyper" NPC to change your Pokemon's IVs to perfect-31s. This tutorial ports that mechanic to RBY.

Hyper Training can benefit RBY hugely, as in addition to helping with sentimental value towards the player's Pokemon, it has unique gameplay differences that make it arguably essential without additional modifications. There isn't any breeding, TMs are single-use and have to be saved for a perfect catch, and DVs can never be perfect for Pokemon found in tall grass. Hyper Training can effectively nullify all of these issues. Well, except for TMs unless you're accounting for that in your own hack.

As a note, I am not entirely sure about how to change individual DVs, because they're stored as a single number. There's definitely a way, likely involving changing just a specific part of it, but it was just easier for me personally to implement a Gold Bottle Cap. Vanilla RBY generally doesn't have much use for single-DV modifications anyway: You hardly see it in competitive play as-is.

The implementation I'm sharing is taken from my commit for my own ROM Hack, so there's a little bit of theming that you may want to change yourself!

Let's implement Hyper Training!

When implementing my Hyper Training NPC, I used James, as he collects Bottle Caps in the anime, making it a little more appropriate for RBY hacks with Yellow content. I also framed it as post-game content, acting as a sort of closure for their appearance in Yellow, as they otherwise just sort of disappear. Ergo, if you use my implementation, you'll want his sprite: This commit should help you, but note that it has a bunch of other stuff, so you may fare better using this tutorial.

If you don't want James, you can change the NPC to Mr. Hyper - simply do a Find/Replace job, changing "James" to "MrHyper", and then change his object in maps/objects/locationnamehere.asm to like, BALDING_GUY or something.

NPC Script

Firstly, pick a location where you want your Mr. Hyper to be. Then, in its respective scripts\locationnamehere.asm file, add the following code. It's not the best - you'll notice some dubious associations with the Bottle Cap item - but it all works. I've included a bunch of comments to help you understand it. It's useful to note that this can also teach you how to work with 16-bit registers!

Location_TextPointers:
	...
	dw JamesText

...

JamesText:
	text_asm
	call SaveScreenTilesToBuffer2 ; It really doesn't need to be done this early, it just helps.
	
	; This is taken from the Saffron Guards, Cinnabar Fossils, and Celadon Dept Store Roof.
	ld b, BOTTLE_CAP ; Check bag for Bottle Caps. We only need one for this.
	predef GetQuantityOfItemInBag
	ld a, b
	and a
	jr z, .NoBottleCap ; If zero, James says something else.
	
	ld hl, JamesSeesBottleCap ; Otherwise, he perks up.
	call PrintText
	
	call YesNoChoice ; Yes/No Prompt
	ld a, [wCurrentMenuItem]
	and a
	jr nz, .refused
	; Proceed from here if Yes is stated. 
	
	; Here, the party menu pops up and the player picks a Pokemon to juice.
	xor a
	ld [wUpdateSpritesEnabled], a
	ld [wPartyMenuTypeOrMessageID], a
	ld [wMenuItemToSwap], a
	call DisplayPartyMenu
	push af
	call GBPalWhiteOutWithDelay3
	call RestoreScreenTilesAndReloadTilePatterns
	call LoadGBPal
	pop af
	ld hl, JamesDone
	call PrintText
	
	; DV increasing process.
	; Thanks to Vimescarrot for giving me pointers on this!
	ld a, [wWhichPokemon] ; Find the Pokemon's position in party.
	ld hl, wPartyMon1DVs ; Load DVs into hl
	ld bc, wPartyMon2 - wPartyMon1 ; This gets to the right slot for DVs
	call AddNTimes ; Gets us there
	ld a, %11111111 ; Load FFFF FFFF, perfect 15s
	ld [hli], a ; Load 1111 to Attack + Defence
	ld [hl], a ; Now load 1111 to Speed + Special
	; And we're done!
	
	; Currently, this doesn't automatically change the stats. Vitamins don't either, so you could say it's consistent, but it's also inconvenient.
	
	; Bottle Cap removal service
	ld hl, BottleCapList ; Load a list of Bottle Cap items. This is the same code as the Saffron Guard.
.loop
	ld a, [hli]
	ldh [hItemToRemoveID], a
	and a
	ret z
	push hl
	ld b, a
	call IsItemInBag
	pop hl
	jr z, .loop
	farcall RemoveItemByID
	jr .done
.NoBottleCap
	ld hl, JamesNoCap
	call PrintText
	jr .done
.refused
	ld hl, JamesNo
	call PrintText
	jr .done
.done
	jp TextScriptEnd

; This list is loaded for the Bottle Cap removal script, it otherwise didn't work properly.
BottleCapList:
	db BOTTLE_CAP
	;db GOLD_BOTTLE_CAP if you ever want to add Gold Bottle caps, you can chuck that in here.
	db 0 ; end

...
; These are text pointers for the script to load.
JamesNoCap:
	text_far _JamesText
	text_end

JamesSeesBottleCap:
	text_far _JamesSeesBottleCap
	text_end

JamesYes:
	text_far _JamesYes
	text_end

JamesNo:
	text_far _JamesNo
	text_end

JamesDone:
	text_far _JamesDone
	text_end

Adding the Bottle Cap

Look through this tutorial, with BOTTLE CAP being the name. It covers it better than I would! In this case, the Bottle Cap should be an unusable item, making it nice and easy!

Adding text

In the text/locationnamehere.asm equivalent for the script you chose, add the following text, changing it as it interests you. This text will be farcalled from the scripts file we edited earlier.

_JamesText::
	text "I'm tired..."
	
	para "JESSIE won't stop"
	line "nagging us..."
	done

_JamesSeesBottleCap::
	text "Hey, is that a"
	line "BOTTLE CAP? I"
	cont "collect these!"
	
	para "Tell you what..."
	
	para "You give me that"
	line "BOTTLE CAP, and"
	cont "I'll make your"
	cont "#MON a little"
	cont "stronger!"
	done

_JamesYes::
	text "Which #MON is"
	line "is getting"
	cont "juiced?"
	done

_JamesNo::
	text "But it's so"
	line "rare..."
	done

_JamesDone::
	text "There! Come back"
	line "with more if"
	cont "you find any!"
	done

Placing the NPC

In data/maps/objects/locationnamehere.asm, you'll need to add an NPC with the script. Where you see the number next to ; James, it should match the position JamesText has in Location_TextPointers: in scripts/locationnamehere.asm. Put simply, if it's the 6th entry down, it will be 6, and so on.

LocationNameHere_Object:
	...

	def_object_events
	...
	object_event  9,  5, SPRITE_JAMES, STAY, DOWN, 6 ; James

	def_warps_to LOCATION_NAME_HERE

Conclusion

And that's it! You now have your own Mr. Hyper (or James!). All you need to do now is distribute the Bottle Cap across Kanto!

There are some shortcomings of my implementation that you may want to try and modify yourself. If you do, feel free to edit this page so everyone else can do a better implementation!

  • There's no SFX, which may make it feel less impactful than it should.
  • The Bottle Cap system here is solely for Gold Bottle Cap-like functionality. In vanilla RBY, there are situations where you may want lower Attack, such as for confusion damage, but on the other hand, higher Attack means winning Struggle wars. Otherwise, though, there is little reason to implement single Bottle Cap functionality. In fact, it may be better to have a "Rusty Bottle Cap" that tanks the Attack stat instead.
  • Stats aren't recalculated immediately - this doesn't happen for vitamins either. Ergo, players need to use the Box Trick or level up to see the results of the training. In trying to do this, I experienced some hardship (even when calling CalcStats), so I'm not entirely sure how to do it.