Add a Move Reminder - pret/pokecrystal GitHub Wiki

This tutorial details the process of adding a Move Reminder NPC to Pokémon Crystal.

This provides a way to teach Pokémon moves that were either skipped over during level up or deleted to make room for another.

The code is extensively documented with comments that describe what each block of code is executing. Therefore, it is important to review the comments before making any changes to ensure functionality.

Note for pokecrystal16 Users:

The reader may see differences in the code that make it appear that this tutorial will not work with pokecrystal16. That is not the case. This tutorial has been tested using pokecrystal16 and there is no functional reason why it cannot be implemented. Any code differences is the result of the delay between changes to the pokecrystal repository being implemented into to the pokecrystal16 repository. For example, currently the only difference is that FadeOutPalettes has not been changed to FadeOutToWhite in pokecrystal16 at the time this note was written. The reader is urged to check for recent changes made to the pokecrystal repository if carefully implementing this tutorial in pokecrystal16 results in errors while building.

Note for Tutorial Editors:

This tutorial provides compatibility code for all of the optional customisations, allowing them to be easily implemented in any order. It is designed to minimise confusion because the required modifications can vary significantly depending on the implementation order.

As such, when making changes to code blocks (including comments), it is important to update the compatibility code after making changes to the default Move Reminder's code. Failure to do so can lead to confusion for readers attempting to implement any of the optional customisations.

Note that care was taken to avoid excessive compatibility code. This was done by omitting some portions of the code above or below the relevant code block in some of the code block diffs. For example, in step 7 of the Pay for Each Move Learned section, the code below the relevant code block is included as it remains consistent, but the code above is omitted because it may vary.

Contents


1. Choose a name for the Move Reminder NPC

There are a number of names (both official and unofficial) for this NPC such as:

  • Move Relearner
  • Move Tutor
  • Move Maniac
  • Move Teacher
  • Pokémon Move Maniac
  • Reminder Girl
  • Madam Reminder
  • Madam Memorial

For the purposes of this tutorial, Move Reminder will be used as the name of the NPC. However, the reader is free to to decide on what name to give to this NPC.


2. Define a New File to Be Imported

In this first section we will be adding a file (which we will soon create) to the list of files that will be imported into the game.

We can add this to any section we like, but it is important to ensure that there is enough space. If there isn't enough space, we would encounter a Sections would extend past the end of ROMX error while compiling. Dealing with this error is beyond the scope of this tutorial.

For this tutorial, we will be using bankB.

Edit main.asm:

SECTION "bankB", ROMX

INCLUDE "engine/battle/trainer_huds.asm"
INCLUDE "data/trainers/class_names.asm"
INCLUDE "engine/battle/ai/redundant.asm"
INCLUDE "engine/events/move_deleter.asm"
+INCLUDE "engine/events/move_reminder.asm"
INCLUDE "engine/link/mystery_gift_2.asm"
INCLUDE "engine/items/tmhm.asm"
INCLUDE "engine/pokemon/print_move_description.asm"
INCLUDE "engine/events/pokerus/pokerus.asm"
INCLUDE "engine/battle/start_battle.asm"
INCLUDE "engine/gfx/place_graphic.asm"

3. Create a Special Pointer

In order to call the move reminder routine from a script, a special pointer will have to be created. This will allow the special MoveReminder command to work.

Edit data/events/special_pointers.asm:

	add_special InitialSetDSTFlag
	add_special InitialClearDSTFlag
	add_special UnusedDummySpecial ; unused
+	
+	add_special MoveReminder

4. Create the Move Reminder NPC

Now we need to create the Move Reminder NPC object, make it perform the MoveReminder special routine when interacted with and place it in the game world.

Choose a map that the Move Reminder NPC will appear in and open the .asm file for that map. For the purposes of this tutorial, we will use the Move Deleter's House.


4.1. Create an Object Constant

First we will add a new object constant for the Move Reminder NPC (this is not necessary for this tutorial, but it is essential for controlling them outside of this script).

Edit maps/MoveDeletersHouse.asm:

	object_const_def
	const MOVEDELETERSHOUSE_SUPER_NERD
+	const MOVEDELETERSHOUSE_GRAMPS

Keep this file open for the next step.


4.2. Create an Object Script

Now we will add an object script that will execute when the player interacts with an object (the Move Reminder NPC in this case).

Note: Scripts can also be executed via coordinate events, background events and other scripts but that is beyond the scope of this tutorial.

Within that script we will have it perform the following commands:

  1. Make the Move Reminder NPC face the player.
  2. Show a text box.
  3. Call the special routine that we created previously.
  4. Wait until a button is pressed to proceed.
  5. Close the text box.
  6. Call for an end to the script's routine.

This script can be placed anywhere in this file as long as it is not interrupting other code. For consistency, it should be placed where other NPC scripts are located.

We are going to place it below all the other NPC scripts.

Here is what it will look like:

MoveDeleter:
	faceplayer
	opentext
	special MoveDeletion
	waitbutton
	closetext
	end
+
+MoveReminder:
+	faceplayer
+	opentext
+	special MoveReminder
+	waitbutton
+	closetext
+	end

MoveDeletersHouseBookshelf:
	jumpstd DifficultBookshelfScript

Keep this file open for the next step.


4.3. Create an Object Event

The last thing we have to do in respect to the NPC object, is to add it to the map. We will define its initial location, select its sprite, give it movement patterns (in this tutorial it is static and does not move), its colour palette and more.

Please view macros/scripts/maps.asm and search for the comments under MACRO object_event for information regarding the options available for defining object events.

For the purposes of this tutorial, we will duplicate Move Deleter's sprite move data (SPRITEMOVEDATA_STANDING_DOWN), use SPRITE_GRAMPS for the NPC's appearance and use MoveReminder as the object script and place it on the opposite end of the table.

	def_object_events
	object_event  2,  3, SPRITE_SUPER_NERD, SPRITEMOVEDATA_STANDING_DOWN, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, MoveDeleter, -1
+	object_event  5,  3, SPRITE_GRAMPS, SPRITEMOVEDATA_STANDING_DOWN, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, MoveReminder, -1

4.4. Update Signage

As we are placing the Move Reminder NPC in the Move Deleter's house, the sign outside the house in Blackthorn City should be updated to mention the Move Reminder NPC as well.

Edit maps/BlackthornCity.asm:

MoveDeletersHouseSignText:
-	text "MOVE DELETER'S"
-	line "HOUSE"
+	text "MOVE DELETER"
+	line "and REMINDER'S"
+	cont "HOUSE"
	done

5. Define a New WRAM Label

The move reminder routine utilizes various WRAM labels for their designated purposes. However, we also need a WRAM label to store the power points (PP) for each move and another to store the moves in the menu. However, there are no existing WRAM labels solely allocated for these tasks. Therefore, we must either repurpose an existing WRAM label while ensuring no conflicts arise or create a new one.

For the purposes of this tutorial, we will be both using an existing WRAM label (wd002) and creating a new one (wBuffer1).

Note: The existing wd002 WRAM label has been proven to not cause any conflicts by others who have used it while implementing their own Move Reminder NPC. If the reader would like to use another existing WRAM label instead of creating one, then ensure that WRAM label is not being used elsewhere during the execution of the move reminder routine.

Find an unused byte, delete it (or comment it out) and add the new WRAM label.

Tip: Look for unused bytes that have a line break both above and below it. These are safe to use.

Also, if the number is greater than one, then ensure that the leftover unused bytes are defined underneath. For example, if taking a byte from ds 7, delete (or comment it out), add the new WRAM label and define ds 6 underneath it (preferably with a line break between them). This will keep all unused bytes visible for later use.

We will be inserting a new WRAM label in between the wBattleMenuCursorPosition and wCurBattleMon WRAM labels.

Edit ram/wram.asm:

wBattleMenuCursorPosition:: db

-	ds 1
+wBuffer1:: db

wCurBattleMon::

6. Move Reminder Routine

This file contains the entire move reminder routine and all the text. It will need to be created and placed in the engine/events/ directory. Name it move_reminder.asm.

Create engine/events/move_reminder.asm:

Click here to expand the code
MoveReminder:
	; Loads and prints the "MoveReminderIntroText" text.
	; Then prompts the player to select "YES" or "NO".
	; Relative jump to the ".cancel" local jump
	; if the player selected "NO" and continue
	; if the player selected "YES".
	ld hl, MoveReminderIntroText
	call PrintText
	call YesNoBox
	jr c, .cancel

	; Loads and prints the "MoveReminderWhichMonText" text.
	ld hl, MoveReminderWhichMonText
	call PrintText

	; Loads the party menu to select a Pokémon. Relative jump
	; to the ".cancel" local jump if the player leaves
	; the party menu without selecting anything.
	farcall SelectMonFromParty
	jr c, .cancel

	; Checks if the current selection is an egg. Relative jump to
	; the ".is_an_egg" local jump if so and continue if not.
	ld a, [wCurPartySpecies]
	cp EGG
	jr z, .is_an_egg

	; Checks if the current selection is not a Pokémon. Relative jump to
	; the ".not_a_pokemon" local jump if so and continue if not.
	; Prevents continuing if glitched Pokémon are selected.
	call IsAPokemon
	jr c, .not_a_pokemon

	; Checks for any moves that can be learned. Relative
	; jump to the ".no_moves_to_learn" local jump if
	; there are none and continue if there are.
	call GetRemindableMoves
	jr z, .no_moves_to_learn

	; Loads and prints the "MoveReminderWhichMoveText" text.
	ld hl, MoveReminderWhichMoveText
	call PrintText

	; Generates the Move Reminder's menu. Relative jump to the
	; ".exit_menu" local jump if the player leaves
	; the menu and continue if they do not.
	call ChooseMoveToLearn
	jr c, .exit_menu

	; If the player selects a move, load the move's name and copy
	; it for later. This is used for displaying the move's
	; name in some of the text boxes while learning a move.
	ld a, [wMenuSelection]
	ld [wNamedObjectIndex], a
	call GetMoveName
	call CopyName1

; Begins the process of learning a move.

	; The "LearnMove" label sets the value of the "b" register to "1"
	; when a move is successfully learned and sets it to "0" if
	; cancelled at any point in the move learning process.
	predef LearnMove

	; Relative jump to the ".move_learned" local jump if
	; a move has been learned and continue if not.
	ld a, b
	dec a
	jr z, .move_learned
; This code falls through into the ".exit_menu" local jump.

; Exits the menu and goes back to the
; map with a speech text box open.
.exit_menu
	call ReturnToMapWithSpeechTextbox
; This code falls through into the ".cancel" local jump.

; Loads and prints the "MoveReminderCancelText" text.
; This ends the dialogue.
.cancel
	ld hl, MoveReminderCancelText
	jp PrintText

; Loads and prints the "MoveReminderEggText" text.
; This ends the dialogue.
.is_an_egg
	ld hl, MoveReminderEggText
	jp PrintText

; Loads and prints the "MoveReminderNotaMonText" text.
; This ends the dialogue.
.not_a_pokemon
	ld hl, MoveReminderNotaMonText
	jp PrintText

; Loads and prints the "MoveReminderNoMovesText" text.
; This ends the dialogue.
.no_moves_to_learn
	ld hl, MoveReminderNoMovesText
	jp PrintText

; Exits the menu and goes back to the map with a
; speech text box open and then loads and prints
; the "MoveReminderMoveLearnedText" text.
; This ends the dialogue.
.move_learned
	call ReturnToMapWithSpeechTextbox
	ld hl, MoveReminderMoveLearnedText
	jp PrintText

; Checks for moves that can be learned and returns
; a zero flag if there are none.
GetRemindableMoves:

	; The "wd002" wram label is being used to store
	; the moves for placement in the move list.
	ld hl, wd002
	xor a
	ld [hli], a
	ld [hl], $ff

	; Retrieves and stores the species of
	; the currently selected Pokémon.
	ld a, MON_SPECIES
	call GetPartyParamLocation
	ld a, [hl]
	ld [wCurPartySpecies], a

	; Retrieves and stores the level of the
	; currently selected Pokémon.
	push af
	ld a, MON_LEVEL
	call GetPartyParamLocation
	ld a, [hl]
	ld [wCurPartyLevel], a

	; The "b" register will contain the number of
	; moves in the move list and "wd002 + 1"
	; is where the move list begins.
	ld b, 0
	ld de, wd002 + 1

	; Retrieves the currently selected Pokémon's evolution
	; and attack address from the "EvosAttacksPointers"
	; table that is located in another bank. This is the
	; list of evolutions and learnset of every Pokémon.
	ld a, [wCurPartySpecies]
	dec a
	push bc
	ld c, a
	ld hl, EvosAttacksPointers
	add hl, bc
	add hl, bc
	ld a, BANK(EvosAttacksPointers)
	call GetFarWord

; Skips the evolution data to start at the learnset for the
; currently selected Pokémon in the "EvosAttacksPointers"
; table. This is "db 0 ; no more evolutions".
.skip_evos
	ld a, BANK("Evolutions and Attacks")
	call GetFarByte
	inc hl
	and a
	jr nz, .skip_evos

; Loops through the move list until it reaches
; the end of the "EvosAttacksPointers" table
; for the currently selected Pokémon. This is
; "db 0 ; no more level-up moves".

; It then compares the currently selected Pokémon's
; level with the level the move is learned at and
; if the Pokémon's level is higher, it will
; attempt to add the move to the move list.
.loop_moves
	ld a, BANK("Evolutions and Attacks")
	call GetFarByte
	inc hl
	and a
	jr z, .done
	ld c, a
	ld a, [wCurPartyLevel]
	cp c
	ld a, BANK("Evolutions and Attacks")
	call GetFarByte
	inc hl
	jr c, .loop_moves

	; Checks if the move is already in the
	; move list or already learned by the
	; Pokémon. If both are false, then the
	; move will be added to the move list.
	ld c, a
	call CheckAlreadyInList
	jr c, .loop_moves
	call CheckPokemonAlreadyKnowsMove
	jr c, .loop_moves
	ld a, c
	ld [de], a
	inc de
	ld a, $ff
	ld [de], a
	pop bc
	inc b
	push bc
	jr .loop_moves

; Adds all the possible moves the currently
; selected Pokémon can learn into "wd002".
; Which is the move list.
.done
	pop bc
	pop af
	ld [wCurPartySpecies], a
	ld a, b
	ld [wd002], a
	and a
	ret

; Checks if there is a move already placed
; in the move list. This prevents
; duplicate entries in the move list.
CheckAlreadyInList:
	push hl
	ld hl, wd002 + 1
.loop
	ld a, [hli]
	inc a
	jr z, .nope
	dec a
	cp c
	jr nz, .loop
	pop hl
	scf
	ret
.nope
	pop hl
	and a
	ret

; Checks if a Pokémon already knows a move. This
; prevents the player teaching the currently
; selected Pokémon a move it already knows.
CheckPokemonAlreadyKnowsMove:
	push hl
	push bc
	ld a, MON_MOVES
	call GetPartyParamLocation
	ld b, 4
.loop
	ld a, [hli]
	cp c
	jr z, .yes
	dec b
	jr nz, .loop
	pop bc
	pop hl
	and a
	ret
.yes
	pop bc
	pop hl
	scf
	ret

; Creates a scrolling menu and sets up the menu gui.
; The number of items is stored in "wd002"
; The list of items is stored in "wd002 + 1"
ChooseMoveToLearn:
	farcall FadeOutToWhite
	farcall BlankScreen
	ld hl, .MenuHeader
	call CopyMenuHeader
	xor a
	ld [wMenuCursorPosition], a
	ld [wMenuScrollPosition], a

	; This creates a border around the move list.
	; "hlcoord" configures the position.
	; "lb bc" configures the size.
	hlcoord 0,  1
	lb bc, 9, 18
	call TextboxBorder

	; This replaces the tile using the identifier
	; of "$6e" with the fourteenth tile of the
	; "FontBattleExtra gfx" font. Also, only 1
	; tile will be loaded as loading the entire
	; "FontBattleExtra gfx" font will overwrite
	; the "UP" arrow in the menu.
	ld de, FontBattleExtra + 14 tiles
	ld hl, vTiles2 tile $6e
	lb bc, BANK(FontBattleExtra), 1
	call Get2bppViaHDMA

	; This is used for displaying the symbol that
	; appears before the Pokémon's level. Without
	; it, an incorrect symbol will appear.
	farcall LoadStatsScreenPageTilesGFX

	; This displays the Pokémon's species
	; name (not nickname) at the
	; coordinates defined at "hlcoord".
	; In this case that is the
	; top left of the screen.
	xor a
	ld [wMonType], a
	ld a, [wCurPartySpecies]
	ld [wNamedObjectIndex], a
	call GetPokemonName
	hlcoord  5, 1
	call PlaceString

	; This displays the Pokémon's level
	; right after the Pokémon's name.
	push bc
	farcall CopyMonToTempMon
	pop hl
	call PrintLevel

	; Creates the menu, sets the "B_BUTTON"
	; to cancel and sets up each entry
	; to behave like a tm/hm.
	call ScrollingMenu
	ld a, [wMenuJoypad]
	cp B_BUTTON
	jr z, .carry
	ld a, [wMenuSelection]
	ld [wPutativeTMHMMove], a
	and a
	ret

; Sets the carry flag and returns from
; this subroutine. Setting the carry
; flag being set will cause the
; menu to exit after returning.
.carry
	scf
	ret

; The menu header defines the menu's position and
; what will be included. The last two values
; of "menu_coords" will determine where the
; vertical scroll arrows will be located.
.MenuHeader:
	db MENU_BACKUP_TILES
	menu_coords 1, 2, SCREEN_WIDTH - 2, 10
	dw .MenuData
	db 1

; This sets up the menu's contents, including the amount
; of entries displayed before scrolling is required.

; Vertical scroll arrows and the move's
; details will be displayed.

; This menu is populated with an item and three functions.
; The item is "wd002".
; Function 1 is the ".print_move_name" local jump.
; Function 2 is the ".print_pp" local jump.
; Function 3 is the ".print_move_details" local jump.
.MenuData:
	db SCROLLINGMENU_DISPLAY_ARROWS | SCROLLINGMENU_ENABLE_FUNCTION3
	db 4, SCREEN_WIDTH + 2
	db SCROLLINGMENU_ITEMS_NORMAL
	dba  wd002
	dba .print_move_name
	dba .print_pp
	dba .print_move_details

; This prints the move's name in the menu.
; This is purely visual as the actual
; entry is stored in "wd002".
.print_move_name
	push de
	ld a, [wMenuSelection]
	ld [wNamedObjectIndex], a
	call GetMoveName
	pop hl
	jp PlaceString

; This prints the move's pp offset by one
; line with some spacing from the left.
.print_pp
	ld hl, wStringBuffer1
	ld bc, wStringBuffer2 - wStringBuffer1
	ld a, " "
	call ByteFill
	ld a, [wMenuSelection]
	inc a
	ret z
	dec a
	push de

	ld a, [wMenuSelection]
	ld bc, MOVE_LENGTH
	ld hl, (Moves + MOVE_PP) - MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
	ld [wBuffer1], a
	ld hl, wStringBuffer1 + 9
	ld de, wBuffer1
	lb bc, 1, 2
	call PrintNum
	ld hl, wStringBuffer1 + 11
	ld [hl], "/"
	ld hl, wStringBuffer1 + 12
	call PrintNum
	
	ld hl, wStringBuffer1 + 14
	ld [hl], "@"

	pop hl
	ld de, wStringBuffer1
	call PlaceString

	; This prints the pp gfx before the move's pp.
	ld bc, 6
	add hl, bc
	ld a, $3e
	ld [hli], a
	ld [hl], a
	ret

; This adds a text box border line to the description
; box that replaces a leftover piece of the notch
; that remains when the cancel option is highlighted.
.cancel_border_fix
	hlcoord 0, 10
	ld [hl], "│"
	inc hl
	ret

; This begins the printing of all of the move's details,
; including the border around the description.
.print_move_details

	; This creates a border around the description.
	hlcoord 0, 11
	lb bc, 5, 18
	call TextboxBorder

	; This code will relative jump to the
	; ".cancel_border_fix" local jump if
	; the cancel entry is highlighted.
	ld a, [wMenuSelection]
	cp -1
	jr z, .cancel_border_fix
; This code falls through into the ".print_move_desc" local jump.

; This prints the moves description.
.print_move_desc
	push de
	ld a, [wMenuSelection]
	inc a
	pop de
	ret z
	dec a
	ld [wCurSpecies], a
	hlcoord 1, 14
	predef PrintMoveDescription
; This code falls through into the ".print_move_type" local jump.

; This prints the move's type.
.print_move_type
	ld a, [wCurSpecies]
	ld b, a
	hlcoord 2, 12
	predef PrintMoveType
; This code falls through into the ".print_move_stat_strings" local jump.

; This prints the notch in the description text box border
; and the "TYPE/" and "ATK/" strings.
.print_move_stat_strings
	hlcoord 0, 10
	ld de, MoveTypeTopString
	call PlaceString
	hlcoord 0, 11
	ld de, MoveTypeString
	call PlaceString
	hlcoord 12, 12
	ld de, MoveAttackString
	call PlaceString
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack
	ld a, [wMenuSelection]
	ld bc, MOVE_LENGTH
	ld hl, (Moves + MOVE_POWER) - MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
	cp 2
	jr c, .print_move_null_attack
	ld [wBuffer1], a
	ld de, wBuffer1
	lb bc, 1, 3
	hlcoord 16, 12
	jp PrintNum

; This prints "---" if the move has an attack of "0".
; This means that the move does not initially cause
; damage or is a one hit knockout move.
.print_move_null_attack
	hlcoord 16, 12
	ld de, MoveNullValueString
	ld bc, 3
	jp PlaceString

; This is a notch that will be placed on
; the top left of the description box.
MoveTypeTopString:
	db "┌─────┐@"

; This is the string that displays
; above the move's type.
MoveTypeString:
	db "│TYPE/└@"

; This is the string that precedes
; the move's attack number.
MoveAttackString:
	db "ATK/@"

; This displays when a move has
; a metric with a null value.
MoveNullValueString:
	db "---@"

; This is the text that displays when the player
; first talks to the move reminder.
MoveReminderIntroText:
	text "Hi, I'm the Move"
	line "Reminder!"

	para "I can make #MON"
	line "remember moves."

	para "Are you"
	line "interested?"
	done

; This is the text that displays just
; before the party menu opens.
MoveReminderWhichMonText:
	text "Which #MON?"
	prompt

; This is the text that displays after
; a Pokémon has been selected.
MoveReminderWhichMoveText:
	text "Which move should"
	line "it remember, then?"
	prompt

; This is the text that displays just before
; the player ends the dialogue
; with the move reminder.
MoveReminderCancelText:
	text "Come visit me"
	line "again."
	done

; This is the text that displays if the player
; selects an egg in the party menu.
MoveReminderEggText:
	text "An EGG can't learn"
	line "any moves!"
	done

; This is the text that displays if the player
; selects an entry in the party menu that
; is neither a Pokémon or an egg.
MoveReminderNotaMonText:
	text "What is that!?"

	para "I'm sorry, but I"
	line "can only teach"
	cont "moves to #MON!"
	done

; This is the text that displays if the player
; selects a Pokémon in the party menu that
; has no moves that can be learned.
MoveReminderNoMovesText:
	text "There are no moves"
	line "for this #MON"
	cont "to learn."
	done

; This is the text that displays after a
; Pokémon successfully learns a move.
MoveReminderMoveLearnedText:
	text "Done! Your #MON"
	line "remembered the"
	cont "move."
	done

Now the Move Reminder has been fully implemented.

Move Reminder Implemented


7. Customisations

There are a few optional customisations/modifications/upgrades that can be implemented if the reader chooses to.

Compatibility code is provided for all of the customisations, allowing them to be easily implemented in any order.

Note: Adding more Pokémon/move types, such as FAIRY, will not require any additional modifications and will display as expected.


7.1. Physical Special Split

If the reader has implemented the Physical Special Split tutorial, the Move Reminder's menu can be modified to match the changes made to the move screen for consistency and to provide more information about each move.

Note: Implementing this section is optional as even if the reader has implemented the Physical Special Split tutorial, there will not be any functional issues with the Move Reminder's menu.

All edits will be made in engine/events/move_reminder.asm for this entire section.


  1. Create the .print_move_category local jump to retrieve the move's category name and print it in the correct location.

This step will vary depending on whether the reader has already implemented the Show Move Accuracy and Status Effect Chance section. Click to expand the applicable code.

Show Move Accuracy and Status Effect Chance - Not Implemented
; This prints the notch in the description text box border
; and the "TYPE/" and "ATK/" strings.
.print_move_stat_strings
	hlcoord 0, 10
	ld de, MoveTypeTopString
	call PlaceString
	hlcoord 0, 11
	ld de, MoveTypeString
	call PlaceString
	hlcoord 12, 12
	ld de, MoveAttackString
	call PlaceString
+; This code falls through into the ".print_move_category" local jump.
+
+; This prints the move's category ("PHYSICAL",
+; "SPECIAL" or "STATUS").
+.print_move_category
+	ld a, [wCurSpecies]
+	ld b, a
+	farcall GetMoveCategoryName
+	hlcoord 1, 11
+	ld de, wStringBuffer1
+	call PlaceString
+	hlcoord 1, 12
+	ld [hl], "/"
+	inc hl
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack
Show Move Accuracy and Status Effect Chance - Implemented

Note: If the reader has made only one metric visible (either accuracy or status effect chance) then the following code will be slightly different.

; This prints the notch in the description text box border
; and the "TYPE/", "ATK/", "EFF/" and "ACC/" strings.
.print_move_stat_strings
	hlcoord 0,  9
	ld de, MoveTypeTopString
	call PlaceString
	hlcoord 0, 10
	ld de, MoveTypeString
	call PlaceString
	hlcoord 12, 11
	ld de, MoveAttackString
	call PlaceString
	hlcoord  4, 12
	ld de, MoveChanceString
	call PlaceString
	hlcoord 12, 12
	ld de, MoveAccuracyString
	call PlaceString
+; This code falls through into the ".print_move_category" local jump.
+
+; This prints the move's category ("PHYSICAL",
+; "SPECIAL" or "STATUS").
+.print_move_category
+	ld a, [wCurSpecies]
+	ld b, a
+	farcall GetMoveCategoryName
+	hlcoord 1, 10
+	ld de, wStringBuffer1
+	call PlaceString
+	hlcoord 1, 11
+	ld [hl], "/"
+	inc hl
; This code falls through into the ".print_move_chance" local jump.

; This prints the move's status effect chance number.
.print_move_chance

  1. Expand the notch above the description box to accommodate the move's category as it will require more space. We will also remove the TYPE/ string inside the MoveTypeString string label as that will be replaced with the move's category.
; This is a notch that will be placed on
; the top left of the description box.
MoveTypeTopString:
-	db "┌─────┐@"
+	db "┌────────┐@"

; This is the string that displays
; above the move's type.
MoveTypeString:
-	db "│TYPE/└@"
+	db "│        └@"

Now the move's category will show when browsing through the Move Reminder's menu.

Physical-Special Split Customisation Implemented


7.2. Show Move Accuracy and Status Effect Chance

Accuracy and status effect chance are important metrics to consider when choosing moves. Unfortunately, they are not visible in game and require the player to look them up elsewhere. Fortunately, we can display these metrics on the Move Reminder's menu itself to make it more convenient for the player.

Note: Both metrics can be made visible independently of each other. As such, notes have been left in every step that could be different if the reader decides to only make one metric visible.


7.2.1. Make Room for the Move Accuracy and Status Effect Chance Metrics

Currently, there is not enough room to place these metrics in the description box. So we must perform several modifications to the menu first.

All edits will be made in engine/events/move_reminder.asm for this entire subsection.


  1. Raise the text box border around the move list by one tile.
	; This creates a border around the move list.
	; "hlcoord" configures the position.
	; "lb bc" configures the size.
-	hlcoord 0,  1
+	hlcoord 0,  0
	lb bc, 9, 18
	call TextboxBorder

  1. Raise the Pokémon's name and level by one tile.

This step will vary depending on whether the reader has already implemented the Space Out the Pokémon's Name and Level section. Click to expand the applicable code.

Space Out the Pokémon's Name and Level - Not Implemented

Note: The level is displayed directly after the Pokémon's name so it does not have to be handled separately.

	; This displays the Pokémon's species
	; name (not nickname) at the
	; coordinates defined at "hlcoord".
	; In this case that is the
	; top left of the screen.
	xor a
	ld [wMonType], a
	ld a, [wCurPartySpecies]
	ld [wNamedObjectIndex], a
	call GetPokemonName
-	hlcoord  5, 1
+	hlcoord  5, 0
	call PlaceString
Space Out the Pokémon's Name and Level - Implemented
	; This displays the Pokémon's species
	; name (not nickname) at the
	; coordinates defined at "hlcoord".
	; In this case that is the
	; top left of the screen.
	xor a
	ld [wMonType], a
	ld a, [wCurPartySpecies]
	ld [wNamedObjectIndex], a
	call GetPokemonName
-	hlcoord  3, 1
+	hlcoord  3, 0
	call PlaceString

	; This displays the Pokémon's level
	; at the coordinates defined at
	; "hlcoord". In this case that is
	; the top right of the screen.
	farcall CopyMonToTempMon
-	hlcoord 14, 1
+	hlcoord 14, 0
	call PrintLevel

  1. Raise the move list and top arrow by one tile.
; The menu header defines the menu's position and
; what will be included. The last two values
; of "menu_coords" will determine where the
; vertical scroll arrows will be located.
.MenuHeader:
	db MENU_BACKUP_TILES
-	menu_coords 1, 2, SCREEN_WIDTH - 2, 10
+	menu_coords 1, 1, SCREEN_WIDTH - 2,  9
	dw .MenuData
	db 1

  1. Raise the text box border fix by one tile.
; This adds a text box border line to the description
; box that replaces a leftover piece of the notch
; that remains when the cancel option is highlighted.
.cancel_border_fix
-	hlcoord 0, 10
+	hlcoord 0,  9
	ld [hl], "│"
	inc hl
	ret

  1. Raise the description text box border up by one tile and also increase its height by one tile.
; This begins the printing of all of the move's details,
; including the border around the description.
.print_move_details

	; This creates a border around the description.
-	hlcoord 0, 11
+	hlcoord 0, 10
-	lb bc, 5, 18
+	lb bc, 6, 18
	call TextboxBorder

  1. Raise the existing metrics in the description box by one tile.

This step will vary depending on whether the reader has already implemented the Physical Special Split section. Click to expand the applicable code.

Physical Special Split - Not Implemented
; This prints the move's type.
.print_move_type
	ld a, [wCurSpecies]
	ld b, a
-	hlcoord 2, 12
+	hlcoord 2, 11
	predef PrintMoveType
; This code falls through into the ".print_move_stat_strings" local jump.

; This prints the notch in the description text box border
; and the "TYPE/" and "ATK/" strings.
.print_move_stat_strings
-	hlcoord 0, 10
+	hlcoord 0,  9
	ld de, MoveTypeTopString
	call PlaceString
-	hlcoord 0, 11
+	hlcoord 0, 10
	ld de, MoveTypeString
	call PlaceString
-	hlcoord 12, 12
+	hlcoord 12, 11
	ld de, MoveAttackString
	call PlaceString
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack
	ld a, [wMenuSelection]
	ld bc, MOVE_LENGTH
	ld hl, (Moves + MOVE_POWER) - MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
	cp 2
	jr c, .print_move_null_attack
	ld [wBuffer1], a
	ld de, wBuffer1
	lb bc, 1, 3
-	hlcoord 16, 12
+	hlcoord 16, 11
	jp PrintNum

; This prints "---" if the move has an attack of "0".
; This means that the move does not initially cause
; damage or is a one hit knockout move.
.print_move_null_attack
-	hlcoord 16, 12
+	hlcoord 16, 11
	ld de, MoveNullValueString
	ld bc, 3
	jp PlaceString
Physical Special Split - Implemented
; This prints the move's type.
.print_move_type
	ld a, [wCurSpecies]
	ld b, a
-	hlcoord 2, 12
+	hlcoord 2, 11
	predef PrintMoveType
; This code falls through into the ".print_move_stat_strings" local jump.

; This prints the notch in the description text box border
; and the "TYPE/" and "ATK/" strings.
.print_move_stat_strings
-	hlcoord 0, 10
+	hlcoord 0,  9
	ld de, MoveTypeTopString
	call PlaceString
-	hlcoord 0, 11
+	hlcoord 0, 10
	ld de, MoveTypeString
	call PlaceString
-	hlcoord 12, 12
+	hlcoord 12, 11
	ld de, MoveAttackString
	call PlaceString
; This code falls through into the ".print_move_category" local jump.

; This prints the move's category ("PHYSICAL",
; "SPECIAL" or "STATUS").
.print_move_category
	ld a, [wCurSpecies]
	ld b, a
	farcall GetMoveCategoryName
-	hlcoord 1, 11
+	hlcoord 1, 10
	ld de, wStringBuffer1
	call PlaceString
-	hlcoord 1, 12
+	hlcoord 1, 11
	ld [hl], "/"
	inc hl
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack
	ld a, [wMenuSelection]
	ld bc, MOVE_LENGTH
	ld hl, (Moves + MOVE_POWER) - MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
	cp 2
	jr c, .print_move_null_attack
	ld [wBuffer1], a
	ld de, wBuffer1
	lb bc, 1, 3
-	hlcoord 16, 12
+	hlcoord 16, 11
	jp PrintNum

; This prints "---" if the move has an attack of "0".
; This means that the move does not initially cause
; damage or is a one hit knockout move.
.print_move_null_attack
-	hlcoord 16, 12
+	hlcoord 16, 11
	ld de, MoveNullValueString
	ld bc, 3
	jp PlaceString

  1. Raise the gap in the text box border that surrounds the move list by one tile (Compatibility with the Space Out the Pokémon's Name and Level section).

Note: This step is only necessary if the reader has implemented the Space Out the Pokémon's Name and Level section of this tutorial. Click to expand the code.

Space Out the Pokémon's Name and Level - Implemented
	; Adds a gap in the move list's text box border
	; that prevents clipping with some names.
-	hlcoord 2, 1
+	hlcoord 2, 0
	lb bc, 1, 16
	call ClearBox

Now there is enough room to add both the accuracy and status effect chance metrics.


7.2.2. Add the Move Accuracy and Status Effect Chance Metrics

Now that there is enough room, we can proceed to add the accuracy and status effect chance metrics.

All edits will be made in engine/events/move_reminder.asm for this entire subsection.


  1. Insert the string labels (we will create them later) that precede the accuracy and status effect chance numbers.

Note: If the reader wishes to only make one of the metrics visible, then only the code pertaining to that metric needs to be introduced and its positioning can be altered using its corresponding hlcoord (the comment should also be altered to reflect the code but that is optional).

; This prints the notch in the description text box border
-; and the "TYPE/" and "ATK/" strings.
+; and the "TYPE/", "ATK/", "EFF/" and "ACC/" strings.
.print_move_stat_strings
	hlcoord 0,  9
	ld de, MoveTypeTopString
	call PlaceString
	hlcoord 0, 10
	ld de, MoveTypeString
	call PlaceString
	hlcoord 12, 11
	ld de, MoveAttackString
	call PlaceString
+	hlcoord  4, 12
+	ld de, MoveChanceString
+	call PlaceString
+	hlcoord 12, 12
+	ld de, MoveAccuracyString
+	call PlaceString

  1. Add the code that processes and prints the move accuracy and status effect chance.

This step will vary depending on whether the reader has already implemented the Physical Special Split section. Click to expand the applicable code.

Note: If the reader wishes to only make one of the metrics visible, then only the local jumps pertaining to that metric need to be introduced. If making only the status effect chance visible, ensure that the relative jump at the end of the .print_move_chance local jump points to the .print_move_attack local jump (the comments should also be altered to reflect the code but that is optional).

Physical Special Split - Not Implemented
	call PlaceString
+; This code falls through into the ".print_move_chance" local jump.
+
+; This prints the move's status effect chance number.
+.print_move_chance
+	ld a, [wMenuSelection]
+	ld bc, MOVE_LENGTH
+	ld hl, (Moves + MOVE_CHANCE) - MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
+	cp 1
+	jr c, .print_move_null_chance
+	Call ConvertPercentages
+	ld [wBuffer1], a
+	ld de, wBuffer1
+	lb bc, 1, 3
+	hlcoord  8, 12
+	call PrintNum
+	jr .print_move_accuracy
+
+; This prints "---" if the move has a status effect chance of "0".
+; This means one of three things:
+; It does not inflict a status effect.
+; It is always successful in inflicting a status
+; effect unless something blocks it.
+; Causes a weather effect.
+.print_move_null_chance
+	ld de, MoveNullValueString
+	ld bc, 3
+	hlcoord  8, 12
+	call PlaceString
+; This code falls through into the ".print_move_accuracy" local jump.
+
+; This prints the move's accuracy number.
+.print_move_accuracy
+	ld a, [wMenuSelection]
+	ld bc, MOVE_LENGTH
+	ld hl, (Moves + MOVE_ACC) - MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
+	Call ConvertPercentages
+	ld [wBuffer1], a
+	ld de, wBuffer1
+	lb bc, 1, 3
+	hlcoord 16, 12
+	call PrintNum
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack
Physical Special Split - Implemented
	ld [hl], "/"
	inc hl
+; This code falls through into the ".print_move_chance" local jump.
+
+; This prints the move's status effect chance number.
+.print_move_chance
+	ld a, [wMenuSelection]
+	ld bc, MOVE_LENGTH
+	ld hl, (Moves + MOVE_CHANCE) - MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
+	cp 1
+	jr c, .print_move_null_chance
+	Call ConvertPercentages
+	ld [wBuffer1], a
+	ld de, wBuffer1
+	lb bc, 1, 3
+	hlcoord  8, 12
+	call PrintNum
+	jr .print_move_accuracy
+
+; This prints "---" if the move has a status effect chance of "0".
+; This means one of three things:
+; It does not inflict a status effect.
+; It is always successful in inflicting a status
+; effect unless something blocks it.
+; Causes a weather effect.
+.print_move_null_chance
+	ld de, MoveNullValueString
+	ld bc, 3
+	hlcoord  8, 12
+	call PlaceString
+; This code falls through into the ".print_move_accuracy" local jump.
+
+; This prints the move's accuracy number.
+.print_move_accuracy
+	ld a, [wMenuSelection]
+	ld bc, MOVE_LENGTH
+	ld hl, (Moves + MOVE_ACC) - MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
+	Call ConvertPercentages
+	ld [wBuffer1], a
+	ld de, wBuffer1
+	lb bc, 1, 3
+	hlcoord 16, 12
+	call PrintNum
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack

  1. Convert the accuracy and status effect chance values from a number out of 256 to a number out of 100.

Note: This is necessary as percentage numbers produce a value out of 256. So we need to convert this value to a number out of 100 in order to print the correct value.

; This prints "---" if the move has an attack of "0".
; This means that the move does not initially cause
; damage or is a one hit knockout move.
.print_move_null_attack
	hlcoord 16, 11
	ld de, MoveNullValueString
	ld bc, 3
	jp PlaceString
+
+; This converts values out of 256 into a value
+; out of 100. It achieves this by multiplying
+; the value by 100 and dividing it by 256.
+ConvertPercentages:
+
+	; Overwrite the "hl" register.
+	ld l, a
+	ld h, 0
+	push af
+
+	; Multiplies the value of the "hl" register by 3.
+	add hl, hl
+	add a, l
+	ld l, a
+	adc h
+	sub l
+	ld h, a
+
+	; Multiplies the value of the "hl" register
+	; by 8. The value of the "hl" register
+	; is now 24 times its original value.
+	add hl, hl
+	add hl, hl
+	add hl, hl
+
+	; Add the original value of the "hl" value to itself,
+	; making it 25 times its original value.
+	pop af
+	add a, l
+	ld l, a
+	adc h
+	sbc l
+	ld h, a
+
+	; Multiply the value of the "hl" register by
+	; 4, making it 100 times its original value.
+	add hl, hl
+	add hl, hl
+
+	; Set the "l" register to 0.5, otherwise the rounded
+	; value may be lower than expected. Round the
+	; high byte to nearest and drop the low byte.
+	ld l, 0.5
+	sla l
+	sbc a
+	and 1
+	add a, h
+	ret

; This is a notch that will be placed on
; the top left of the description box.
MoveTypeTopString:

  1. Create the string labels that precede the accuracy and status effect chance numbers.

Note: If the reader wishes to only make one of the metrics visible, then only the string label pertaining to that metric needs to be introduced.

; This displays when a move has
; a metric with a null value.
MoveNullValueString:
	db "---@"
+
+; This is the string that precedes
+; the move's accuracy number.
+MoveAccuracyString:
+	db "ACC/@"
+
+; This is the string that precedes the
+; move's status effect chance number.
+MoveChanceString:
+	db "EFF/@"

; This is the text that displays when the player
; first talks to the move reminder.
MoveReminderIntroText:

Now the accuracy and status effect chance metrics will appear when browsing through the Move Reminder's menu.

Accuracy and Status Effect Chance Customisation Implemented

However, there is an additional modification that can be made to provide more accurate information to the player.


7.2.3. Don't Show a Number for Accuracy for Moves That Never Miss

Note: This subsection is only necessary if the reader has made the accuracy metric visible, it is futile with just making the status effect chance metric visible alone.

Currently all moves will display a number for their accuracy, even those that bypass accuracy checks (perfect accuracy moves). Some of these perfect accuracy moves even display a number less than 100%. Perfect accuracy moves are:

  • Moves that target the opponent and never miss (unless the target is in the semi-invulnerable turn of a move such as Dig or Fly) - Swift, Faint Attack and Vital Throw.
  • Moves that target the user - All moves that target the user will never miss.
  • Weather moves - Some of these moves have an accuracy of less than 100% despite never missing.

We will make modifications so that perfect accuracy moves will display --- instead of a number.

We will achieve this by modifying the order of the move effects in move_effect_constants.asm and effects_pointers.asm so that all effects that have perfect accuracy will be listed after all that do not.

Then we will modify the Move Reminder's code to first check if a move's effect is listed at or below the first effect that has perfect accuracy. If so, --- will be displayed instead of the number. If not, a number for the move's accuracy will display as normal.

Note: The entries in move_effect_constants.asm have a corresponding relationship with the entries in effects_pointers.asm. As such, any modification that is made in one file, must be made in the other. For example, if EFFECT_ALWAYS_HIT is moved to just above EFFECT_CONVERSION in move_effect_constants.asm, then dw NormalHit must be moved to just above dw Conversion in effects_pointers.asm.

This may become confusing as while the entries in move_effect_constants.asm are unique, some entries in effects_pointers.asm are used multiple times. For example, everything in the following list in move_effect_constants.asm use NormalHit in effects_pointers.asm.

  • EFFECT_NORMAL_HIT
  • EFFECT_ALWAYS_HIT
  • EFFECT_JUMP_KICK
  • EFFECT_PRIORITY_HIT
  • All EFFECT_UNUSED_*
  1. Modify move_effect_constants.asm to list all effects that do not have perfect accuracy first, followed by those that do.

Note: If the reader has added/removed/modified any moves or move effects, then this file may have different contents. Please ensure that is taken into consideration before modifying this file.

Edit constants/move_effect_constants.asm:

Click here to expand the code
; MoveEffectsPointers indexes (see data/moves/effects_pointers.asm)
	const_def
	const EFFECT_NORMAL_HIT
	const EFFECT_SLEEP
	const EFFECT_POISON_HIT
	const EFFECT_LEECH_HIT
	const EFFECT_BURN_HIT
	const EFFECT_FREEZE_HIT
	const EFFECT_PARALYZE_HIT
	const EFFECT_SELFDESTRUCT
	const EFFECT_DREAM_EATER
-	const EFFECT_MIRROR_MOVE
-	const EFFECT_ATTACK_UP
-	const EFFECT_DEFENSE_UP
-	const EFFECT_SPEED_UP
-	const EFFECT_SP_ATK_UP
-	const EFFECT_SP_DEF_UP
-	const EFFECT_ACCURACY_UP
-	const EFFECT_EVASION_UP
-	const EFFECT_ALWAYS_HIT
	const EFFECT_ATTACK_DOWN
	const EFFECT_DEFENSE_DOWN
	const EFFECT_SPEED_DOWN
	const EFFECT_SP_ATK_DOWN
	const EFFECT_SP_DEF_DOWN
	const EFFECT_ACCURACY_DOWN
	const EFFECT_EVASION_DOWN
-	const EFFECT_RESET_STATS
	const EFFECT_BIDE
	const EFFECT_RAMPAGE
	const EFFECT_FORCE_SWITCH
	const EFFECT_MULTI_HIT
-	const EFFECT_CONVERSION
	const EFFECT_FLINCH_HIT
-	const EFFECT_HEAL
	const EFFECT_TOXIC
	const EFFECT_PAY_DAY
-	const EFFECT_LIGHT_SCREEN
	const EFFECT_TRI_ATTACK
	const EFFECT_UNUSED_25
	const EFFECT_OHKO
	const EFFECT_RAZOR_WIND
	const EFFECT_SUPER_FANG
	const EFFECT_STATIC_DAMAGE
	const EFFECT_TRAP_TARGET
	const EFFECT_UNUSED_2B
	const EFFECT_DOUBLE_HIT
	const EFFECT_JUMP_KICK
-	const EFFECT_MIST
-	const EFFECT_FOCUS_ENERGY
	const EFFECT_RECOIL_HIT
	const EFFECT_CONFUSE
-	const EFFECT_ATTACK_UP_2
-	const EFFECT_DEFENSE_UP_2
-	const EFFECT_SPEED_UP_2
-	const EFFECT_SP_ATK_UP_2
-	const EFFECT_SP_DEF_UP_2
-	const EFFECT_ACCURACY_UP_2
-	const EFFECT_EVASION_UP_2
-	const EFFECT_TRANSFORM
	const EFFECT_ATTACK_DOWN_2
	const EFFECT_DEFENSE_DOWN_2
	const EFFECT_SPEED_DOWN_2
	const EFFECT_SP_ATK_DOWN_2
	const EFFECT_SP_DEF_DOWN_2
	const EFFECT_ACCURACY_DOWN_2
	const EFFECT_EVASION_DOWN_2
-	const EFFECT_REFLECT
	const EFFECT_POISON
	const EFFECT_PARALYZE
	const EFFECT_ATTACK_DOWN_HIT
	const EFFECT_DEFENSE_DOWN_HIT
	const EFFECT_SPEED_DOWN_HIT
	const EFFECT_SP_ATK_DOWN_HIT
	const EFFECT_SP_DEF_DOWN_HIT
	const EFFECT_ACCURACY_DOWN_HIT
	const EFFECT_EVASION_DOWN_HIT
	const EFFECT_SKY_ATTACK
	const EFFECT_CONFUSE_HIT
	const EFFECT_POISON_MULTI_HIT
	const EFFECT_UNUSED_4E
-	const EFFECT_SUBSTITUTE
	const EFFECT_HYPER_BEAM
	const EFFECT_RAGE
	const EFFECT_MIMIC
-	const EFFECT_METRONOME
	const EFFECT_LEECH_SEED
-	const EFFECT_SPLASH
	const EFFECT_DISABLE
	const EFFECT_LEVEL_DAMAGE
	const EFFECT_PSYWAVE
	const EFFECT_COUNTER
	const EFFECT_ENCORE
	const EFFECT_PAIN_SPLIT
	const EFFECT_SNORE
-	const EFFECT_CONVERSION2
	const EFFECT_LOCK_ON
-	const EFFECT_SKETCH
	const EFFECT_DEFROST_OPPONENT
-	const EFFECT_SLEEP_TALK
-	const EFFECT_DESTINY_BOND
	const EFFECT_REVERSAL
	const EFFECT_SPITE
	const EFFECT_FALSE_SWIPE
-	const EFFECT_HEAL_BELL
	const EFFECT_PRIORITY_HIT
	const EFFECT_TRIPLE_KICK
	const EFFECT_THIEF
-	const EFFECT_MEAN_LOOK
-	const EFFECT_NIGHTMARE
	const EFFECT_FLAME_WHEEL
-	const EFFECT_CURSE
	const EFFECT_UNUSED_6E
-	const EFFECT_PROTECT
-	const EFFECT_SPIKES
	const EFFECT_FORESIGHT
-	const EFFECT_PERISH_SONG
-	const EFFECT_SANDSTORM
-	const EFFECT_ENDURE
	const EFFECT_ROLLOUT
	const EFFECT_SWAGGER
	const EFFECT_FURY_CUTTER
	const EFFECT_ATTRACT
	const EFFECT_RETURN
	const EFFECT_PRESENT
	const EFFECT_FRUSTRATION
-	const EFFECT_SAFEGUARD
	const EFFECT_SACRED_FIRE
	const EFFECT_MAGNITUDE
-	const EFFECT_BATON_PASS
	const EFFECT_PURSUIT
	const EFFECT_RAPID_SPIN
	const EFFECT_UNUSED_82
	const EFFECT_UNUSED_83
-	const EFFECT_MORNING_SUN
-	const EFFECT_SYNTHESIS
-	const EFFECT_MOONLIGHT
	const EFFECT_HIDDEN_POWER
-	const EFFECT_RAIN_DANCE
-	const EFFECT_SUNNY_DAY
	const EFFECT_DEFENSE_UP_HIT
	const EFFECT_ATTACK_UP_HIT
	const EFFECT_ALL_UP_HIT
	const EFFECT_FAKE_OUT
-	const EFFECT_BELLY_DRUM
-	const EFFECT_PSYCH_UP
	const EFFECT_MIRROR_COAT
	const EFFECT_SKULL_BASH
	const EFFECT_TWISTER
	const EFFECT_EARTHQUAKE
	const EFFECT_FUTURE_SIGHT
	const EFFECT_GUST
	const EFFECT_STOMP
	const EFFECT_SOLARBEAM
	const EFFECT_THUNDER
-	const EFFECT_TELEPORT
	const EFFECT_BEAT_UP
	const EFFECT_FLY
+	const EFFECT_MIRROR_MOVE
+	const EFFECT_ATTACK_UP
+	const EFFECT_DEFENSE_UP
+	const EFFECT_SPEED_UP
+	const EFFECT_SP_ATK_UP
+	const EFFECT_SP_DEF_UP
+	const EFFECT_ACCURACY_UP
+	const EFFECT_EVASION_UP
+	const EFFECT_ALWAYS_HIT
+	const EFFECT_RESET_STATS
+	const EFFECT_CONVERSION
+	const EFFECT_HEAL
+	const EFFECT_LIGHT_SCREEN
+	const EFFECT_MIST
+	const EFFECT_FOCUS_ENERGY
+	const EFFECT_ATTACK_UP_2
+	const EFFECT_DEFENSE_UP_2
+	const EFFECT_SPEED_UP_2
+	const EFFECT_SP_ATK_UP_2
+	const EFFECT_SP_DEF_UP_2
+	const EFFECT_ACCURACY_UP_2
+	const EFFECT_EVASION_UP_2
+	const EFFECT_TRANSFORM
+	const EFFECT_REFLECT
+	const EFFECT_SUBSTITUTE
+	const EFFECT_METRONOME
+	const EFFECT_SPLASH
+	const EFFECT_CONVERSION2
+	const EFFECT_SKETCH
+	const EFFECT_SLEEP_TALK
+	const EFFECT_DESTINY_BOND
+	const EFFECT_HEAL_BELL
+	const EFFECT_MEAN_LOOK
+	const EFFECT_NIGHTMARE
+	const EFFECT_CURSE
+	const EFFECT_PROTECT
+	const EFFECT_SPIKES
+	const EFFECT_PERISH_SONG
+	const EFFECT_SANDSTORM
+	const EFFECT_ENDURE
+	const EFFECT_SAFEGUARD
+	const EFFECT_BATON_PASS
+	const EFFECT_MORNING_SUN
+	const EFFECT_SYNTHESIS
+	const EFFECT_MOONLIGHT
+	const EFFECT_RAIN_DANCE
+	const EFFECT_SUNNY_DAY
+	const EFFECT_BELLY_DRUM
+	const EFFECT_PSYCH_UP
+	const EFFECT_TELEPORT
	const EFFECT_DEFENSE_CURL
DEF NUM_MOVE_EFFECTS EQU const_value

  1. Modify effects_pointers.asm to list all effects that do not have perfect accuracy first, followed by those that do.

Note: If the reader has added/removed/modified any moves or move effects, then this file may have different contents. Please ensure that is taken into consideration before modifying this file.

Edit data/moves/effects_pointers.asm:

Click here to expand the code
MoveEffectsPointers:
; entries correspond to EFFECT_* constants
	table_width 2, MoveEffectsPointers
	dw NormalHit
	dw DoSleep
	dw PoisonHit
	dw LeechHit
	dw BurnHit
	dw FreezeHit
	dw ParalyzeHit
	dw Selfdestruct
	dw DreamEater
-	dw MirrorMove
-	dw AttackUp
-	dw DefenseUp
-	dw SpeedUp
-	dw SpecialAttackUp
-	dw SpecialDefenseUp
-	dw AccuracyUp
-	dw EvasionUp
-	dw NormalHit
	dw AttackDown
	dw DefenseDown
	dw SpeedDown
	dw SpecialAttackDown
	dw SpecialDefenseDown
	dw AccuracyDown
	dw EvasionDown
-	dw ResetStats
	dw Bide
	dw Rampage
	dw ForceSwitch
	dw MultiHit
-	dw Conversion
	dw FlinchHit
-	dw Heal
	dw Toxic
	dw PayDay
-	dw LightScreen
	dw TriAttack
	dw NormalHit
	dw OHKOHit
	dw RazorWind
	dw SuperFang
	dw StaticDamage
	dw TrapTarget
	dw NormalHit
	dw MultiHit
	dw NormalHit
-	dw Mist
-	dw FocusEnergy
	dw RecoilHit
	dw DoConfuse
-	dw AttackUp2
-	dw DefenseUp2
-	dw SpeedUp2
-	dw SpecialAttackUp2
-	dw SpecialDefenseUp2
-	dw AccuracyUp2
-	dw EvasionUp2
-	dw Transform
	dw AttackDown2
	dw DefenseDown2
	dw SpeedDown2
	dw SpecialAttackDown2
	dw SpecialDefenseDown2
	dw AccuracyDown2
	dw EvasionDown2
-	dw Reflect
	dw DoPoison
	dw DoParalyze
	dw AttackDownHit
	dw DefenseDownHit
	dw SpeedDownHit
	dw SpecialAttackDownHit
	dw SpecialDefenseDownHit
	dw AccuracyDownHit
	dw EvasionDownHit
	dw SkyAttack
	dw ConfuseHit
	dw PoisonMultiHit
	dw NormalHit
-	dw Substitute
	dw HyperBeam
	dw Rage
	dw Mimic
-	dw Metronome
	dw LeechSeed
-	dw Splash
	dw Disable
	dw StaticDamage
	dw Psywave
	dw Counter
	dw Encore
	dw PainSplit
	dw Snore
-	dw Conversion2
	dw LockOn
-	dw Sketch
	dw DefrostOpponent
-	dw SleepTalk
-	dw DestinyBond
	dw Reversal
	dw Spite
	dw FalseSwipe
-	dw HealBell
	dw NormalHit
	dw TripleKick
	dw Thief
-	dw MeanLook
-	dw Nightmare
	dw FlameWheel
-	dw Curse
	dw NormalHit
-	dw Protect
-	dw Spikes
	dw Foresight
-	dw PerishSong
-	dw Sandstorm
-	dw Endure
	dw Rollout
	dw Swagger
	dw FuryCutter
	dw Attract
	dw Return
	dw Present
	dw Frustration
-	dw Safeguard
	dw SacredFire
	dw Magnitude
-	dw BatonPass
	dw Pursuit
	dw RapidSpin
	dw NormalHit
	dw NormalHit
-	dw MorningSun
-	dw Synthesis
-	dw Moonlight
	dw HiddenPower
-	dw RainDance
-	dw SunnyDay
	dw DefenseUpHit
	dw AttackUpHit
	dw AllUpHit
	dw FakeOut
-	dw BellyDrum
-	dw PsychUp
	dw MirrorCoat
	dw SkullBash
	dw Twister
	dw Earthquake
	dw FutureSight
	dw Gust
	dw Stomp
	dw Solarbeam
	dw Thunder
-	dw Teleport
	dw BeatUp
	dw Fly
+	dw MirrorMove
+	dw AttackUp
+	dw DefenseUp
+	dw SpeedUp
+	dw SpecialAttackUp
+	dw SpecialDefenseUp
+	dw AccuracyUp
+	dw EvasionUp
+	dw NormalHit
+	dw ResetStats
+	dw Conversion
+	dw Heal
+	dw LightScreen
+	dw Mist
+	dw FocusEnergy
+	dw AttackUp2
+	dw DefenseUp2
+	dw SpeedUp2
+	dw SpecialAttackUp2
+	dw SpecialDefenseUp2
+	dw AccuracyUp2
+	dw EvasionUp2
+	dw Transform
+	dw Reflect
+	dw Substitute
+	dw Metronome
+	dw Splash
+	dw Conversion2
+	dw Sketch
+	dw SleepTalk
+	dw DestinyBond
+	dw HealBell
+	dw MeanLook
+	dw Nightmare
+	dw Curse
+	dw Protect
+	dw Spikes
+	dw PerishSong
+	dw Sandstorm
+	dw Endure
+	dw Safeguard
+	dw BatonPass
+	dw MorningSun
+	dw Synthesis
+	dw Moonlight
+	dw RainDance
+	dw SunnyDay
+	dw BellyDrum
+	dw PsychUp
+	dw Teleport
	dw DefenseCurl
	assert_table_length NUM_MOVE_EFFECTS

  1. Modify the .print_move_accuracy and add the .perfect_accuracy local jumps in the Move Reminder's code so that --- is printed instead of a number if the move effect has perfect accuracy.

Edit engine/events/move_reminder.asm:

; This prints the move's accuracy number.
.print_move_accuracy
	ld a, [wMenuSelection]
	ld bc, MOVE_LENGTH
-	ld hl, (Moves + MOVE_ACC) - MOVE_LENGTH
+	ld hl, (Moves + MOVE_EFFECT) - MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
+	cp EFFECT_MIRROR_MOVE
+	jr nc, .perfect_accuracy
+	ld a, [wMenuSelection]
+	ld bc, MOVE_LENGTH
+	ld hl, (Moves + MOVE_ACC) - MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
	Call ConvertPercentages
	ld [wBuffer1], a
	ld de, wBuffer1
	lb bc, 1, 3
	hlcoord 16, 12
	call PrintNum
+	jr .print_move_attack
+
+; This prints "---" if the move
+; has perfect accuracy.
+.perfect_accuracy
+	ld de, MoveNullValueString
+	ld bc, 3
+	hlcoord 16, 12
+	call PlaceString
; This code falls through into the ".print_move_attack" local jump.

; This prints the move's attack number.
.print_move_attack

Now every move that has perfect accuracy will display --- instead of a number.

Accuracy and Status Effect Chance - Show Perfect Accuracy Moves as --- Customisation Implemented


7.3. Pay for Each Move Learned

Considering how powerful the ability to have Pokémon relearn moves is, the reader may choose to make it a paid service.

We will make modifications so that each move learned will cost ¥500.

All edits will be made in engine/events/move_reminder.asm for this entire section.


  1. Place the player's current money total at the top right of the screen to indicate that the Move Reminder is a paid service and compare the player's current money against the cost of learning a move (the code that executes if the player does not have enough money will be created at a later point).
MoveReminder:
-	; Loads and prints the "MoveReminderIntroText" text.
-	; Then prompts the player to select "YES" or "NO".
+	; Loads and prints the "MoveReminderIntroText" text and places
+	; the player's current money at the top right corner of the
+	; screen. Then prompts the player to select "YES" or "NO".
	; Relative jump to the ".cancel" local jump
	; if the player selected "NO" and continue
	; if the player selected "YES".
	ld hl, MoveReminderIntroText
	call PrintText
+	farcall PlaceMoneyTopRight
	call YesNoBox
	jr c, .cancel
+
+	; Calls the "CheckCostAgainstPlayerMoney" label. Relative jump
+	; to the ".not_enough_money" local jump if the player does
+	; not have enough money and continue if they do.
+	call CheckCostAgainstPlayerMoney
+	jr c, .not_enough_money

	; Loads and prints the "MoveReminderWhichMonText" text.
	ld hl, MoveReminderWhichMonText
	call PrintText

  1. Create the .not_enough_money local jump for when the player does not have enough money.

This step will vary depending on whether the reader has already implemented the Add Party Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Party Menu Loop - Not Implemented

Note: This will print text that will be created at a later point.

; Loads and prints the "MoveReminderNoMovesText" text.
; This ends the dialogue.
.no_moves_to_learn
	ld hl, MoveReminderNoMovesText
	jp PrintText
+
+; Loads and prints the "MoveReminderNotEnoughMoneyText" text.
+; This will end the dialogue.
+.not_enough_money
+	ld hl, MoveReminderNotEnoughMoneyText
+	jp PrintText
Add Party Menu Loop - Implemented

Note: This will print text that will be created at a later point.

; Loads and prints the "MoveReminderNoMovesText" text and then waits
; for the player to press a button for the text to progress. Then
; relative jump to the ".loop_party_menu" local jump.
.no_moves_to_learn
	ld hl, MoveReminderNoMovesText
	call PrintText
	jr .loop_party_menu
+
+; Loads and prints the "MoveReminderNotEnoughMoneyText" text.
+; This will end the dialogue.
+.not_enough_money
+	ld hl, MoveReminderNotEnoughMoneyText
+	jp PrintText

  1. Create the .pay_for_move local jump to processes the payment after a move is learned.

This step will vary depending on whether the reader has already implemented the Add Move Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Move Menu Loop - Not Implemented

The .move_learned local jump will also need to be modified so that it falls through into the .pay_for_move local jump.

Note: The .pay_for_move local jump will not be jumped to directly. It only needs to execute via fall through.

; Exits the menu and goes back to the map with a
; speech text box open and then loads and prints
; the "MoveReminderMoveLearnedText" text.
-; This ends the dialogue.
.move_learned
	call ReturnToMapWithSpeechTextbox
	ld hl, MoveReminderMoveLearnedText
-	jp PrintText
+	call PrintText
+; This code falls through into the ".pay_for_move" local jump.
+
+; Places the player's current money at the top right corner of
+; the screen, retrieves the amount of money defined in the
+; "MoveCost" label, removes the defined amount of money from
+; the player, plays the "SFX_TRANSACTION" sound effect,
+; prints the "MoveReminderPaymentReceivedText"text and
+; finally relative jump to the ".cancel" local jump.
+.pay_for_move
+	farcall PlaceMoneyTopRight
+	ld hl, MoveCost
+	ld de, hMoneyTemp
+	ld bc, 3
+	call CopyBytes
+	call ApplyTilemap
+	call PromptButton
+	call WaitSFX
+	ld bc, hMoneyTemp
+	ld de, wMoney
+	farcall TakeMoney
+	farcall PlaceMoneyTopRight
+	ld de, SFX_TRANSACTION
+	call PlaySFX
+	call WaitSFX
+	ld hl, MoveReminderPaymentReceivedText
+	call PrintText
+	jr .cancel
Add Move Menu Loop - Implemented

Note: This local jump will never be directly jumped to. It will only execute when the .move_learned local jump falls through into it.

; Exits the menu and goes back to the map with a
-; speech text box open, loads and prints
-; the "MoveReminderMoveLearnedText"
-; text and relative jump to the
-; ".recheck_for_moves" local jump.
+; speech text box open and then loads and prints
+; the "MoveReminderMoveLearnedText" text.
.move_learned
	call ReturnToMapWithSpeechTextbox
	ld hl, MoveReminderMoveLearnedText
	call PrintText
+; This code falls through into the ".pay_for_move" local jump.
+
+; Places the player's current money at the top right corner of
+; the screen, retrieves the amount of money defined in the
+; "MoveCost" label, removes the defined amount of money from
+; the player, plays the "SFX_TRANSACTION" sound effect and
+; finally prints the "MoveReminderPaymentReceivedText" text.
+.pay_for_move
+	farcall PlaceMoneyTopRight
+	ld hl, MoveCost
+	ld de, hMoneyTemp
+	ld bc, 3
+	call CopyBytes
+	call ApplyTilemap
+	call PromptButton
+	call WaitSFX
+	ld bc, hMoneyTemp
+	ld de, wMoney
+	farcall TakeMoney
+	farcall PlaceMoneyTopRight
+	ld de, SFX_TRANSACTION
+	call PlaySFX
+	call WaitSFX
+	ld hl, MoveReminderPaymentReceivedText
+	call PrintText
+
+	; Calls the "CheckCostAgainstPlayerMoney" label. Relative
+	; jump to the ".not_enough_money" local jump if the
+	; player does not have enough money and relative jump
+	; to the ".recheck_for_moves" local jump if they do.
+	call CheckCostAgainstPlayerMoney
+	jr c, .not_enough_money
	jr .recheck_for_moves

  1. Create the CheckCostAgainstPlayerMoney label that will compare the player's money against the cost of learning a move.

This step will vary depending on whether the reader has already implemented the Add Move Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Move Menu Loop - Not Implemented
.pay_for_move
	farcall PlaceMoneyTopRight
	ld hl, MoveCost
	ld de, hMoneyTemp
	ld bc, 3
	call CopyBytes
	call ApplyTilemap
	call PromptButton
	call WaitSFX
	ld bc, hMoneyTemp
	ld de, wMoney
	farcall TakeMoney
	farcall PlaceMoneyTopRight
	ld de, SFX_TRANSACTION
	call PlaySFX
	call WaitSFX
	ld hl, MoveReminderPaymentReceivedText
	call PrintText
	jr .cancel
+
+; Compares the value of "MoveCost" to
+; the amount of money the player has.
+CheckCostAgainstPlayerMoney:
+	ld hl, MoveCost
+	ld de, hMoneyTemp
+	ld bc, 3
+	call CopyBytes
+	ld bc, hMoneyTemp
+	ld de, wMoney
+	farcall CompareMoney
+	ret
Add Move Menu Loop - Implemented
	; Calls the "CheckCostAgainstPlayerMoney" label. Relative
	; jump to the ".not_enough_money" local jump if the
	; player does not have enough money and relative jump
	; to the ".recheck_for_moves" local jump if they do.
	call CheckCostAgainstPlayerMoney
	jr c, .not_enough_money
	jr .recheck_for_moves
+
+; Compares the value of "MoveCost" to
+; the amount of money the player has.
+CheckCostAgainstPlayerMoney:
+	ld hl, MoveCost
+	ld de, hMoneyTemp
+	ld bc, 3
+	call CopyBytes
+	ld bc, hMoneyTemp
+	ld de, wMoney
+	farcall CompareMoney
+	ret

  1. Define the cost of learning a move.

Note: The reader is free to define their own cost of learning a move.

; Compares the value of "MoveCost" to
; the amount of money the player has.
CheckCostAgainstPlayerMoney:
	ld hl, MoveCost
	ld de, hMoneyTemp
	ld bc, 3
	call CopyBytes
	ld bc, hMoneyTemp
	ld de, wMoney
	farcall CompareMoney
	ret
+
+; The cost for learning a move.
+MoveCost:
+	dt 500

  1. Modify the Move Reminder's intro text to mention the cost to learn a move.

Note: If the reader has defined their own cost for learning a move, then remember to modify this text entry to reflect that.

; This is the text that displays when the player
; first talks to the move reminder.
MoveReminderIntroText:
	text "Hi, I'm the Move"
	line "Reminder!"

-	para "I can make #MON"
-	line "remember moves."
+	para "For ¥500, I can"
+	line "make #MON"
+	cont "remember a move."

	para "Are you"
	line "interested?"
	done

  1. Create a label that contains the text that will display when the player does not have enough money.
+
+; This is the text that displays if the player
+; does not have enough money to learn a move.
+MoveReminderNotEnoughMoneyText:
+	text "Hm… You don't have"
+	line "enough money."
+
+	para "Please come back"
+	line "when you do."
+	done

; This is the text that displays after a
; Pokémon successfully learns a move.
MoveReminderMoveLearnedText:

  1. Add a label that contains the text that will be printed after a payment has been accepted by the Move Reminder.

This step will vary depending on whether the reader has already implemented the Add Move Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Move Menu Loop - Not Implemented
; This is the text that displays after a
; Pokémon successfully learns a move.
MoveReminderMoveLearnedText:
	text "Done! Your #MON"
	line "remembered the"
	cont "move."
	done
+
+; This is the text that displays after the
+; Move Reminder accepts payment.
+MoveReminderPaymentReceivedText:
+	text "Pleasure doing"
+	line "business with"
+	cont "you!"
+	prompt
Add Move Menu Loop - Implemented

Note: The prompt in the MoveReminderMoveLearnedText label will also be removed to prevent a double prompt after the text is printed.

; This is the text that displays after a
; Pokémon successfully learns a move.
MoveReminderMoveLearnedText:
	text "Done! Your #MON"
	line "remembered the"
	cont "move."
-	prompt
+	done
+
+; This is the text that displays after the
+; Move Reminder accepts payment.
+MoveReminderPaymentReceivedText:
+	text "Pleasure doing"
+	line "business with"
+	cont "you!"
+	prompt

Now each move learned via the Move Reminder will cost the player ¥500 (unless the reader has opted to change this value).

Pay for Move Customisation Implemented


7.4. Space Out the Pokémon's Name and Level

Currently, the Pokémon's name and level are attached together, longer names appear less centred on the screen and the move list's text box border clips with the Pokémon's name.

We will separate the Pokémon's name and level, make them more centred on the screen and create an opening in the move list's text box border.

All edits will be made in engine/events/move_reminder.asm for this entire section.


  1. Create an opening in the move list's text box border.

This step will vary depending on whether the reader has already implemented the Show Move Accuracy and Status Effect Chance section. Click to expand the applicable code.

Show Move Accuracy and Status Effect Chance - Not Implemented
	lb bc, 9, 18
	call TextboxBorder
+
+	; Adds a gap in the move list's text box border
+	; that prevents clipping with some names.
+	hlcoord 2, 1
+	lb bc, 1, 16
+	call ClearBox

	; This replaces the tile using the identifier
	; of "$6e" with the fourteenth tile of the
	; "FontBattleExtra gfx" font. Also, only 1
	; tile will be loaded as loading the entire
	; "FontBattleExtra gfx" font will overwrite
	; the "UP" arrow in the menu.
Show Move Accuracy and Status Effect Chance - Implemented
	lb bc, 9, 18
	call TextboxBorder
+
+	; Adds a gap in the move list's text box border
+	; that prevents clipping with some names.
+	hlcoord 2, 0
+	lb bc, 1, 16
+	call ClearBox

	; This replaces the tile using the identifier
	; of "$6e" with the fourteenth tile of the
	; "FontBattleExtra gfx" font. Also, only 1
	; tile will be loaded as loading the entire
	; "FontBattleExtra gfx" font will overwrite
	; the "UP" arrow in the menu.

  1. Move the Pokémon's name slightly to the left and separate the Pokémon's level from its name.

This step will vary depending on whether the reader has already implemented the Show Move Accuracy and Status Effect Chance section. Click to expand the applicable code.

Show Move Accuracy and Status Effect Chance - Not Implemented
	; This displays the Pokémon's species
	; name (not nickname) at the
	; coordinates defined at "hlcoord".
	; In this case that is the
	; top left of the screen.
	xor a
	ld [wMonType], a
	ld a, [wCurPartySpecies]
	ld [wNamedObjectIndex], a
	call GetPokemonName
-	hlcoord  5, 1
+	hlcoord  3, 1
	call PlaceString

	; This displays the Pokémon's level
-	; right after the Pokémon's name.
-	push bc
+	; at the coordinates defined at
+	; "hlcoord". In this case that is
+	; the top right of the screen.
	farcall CopyMonToTempMon
-	pop hl
+	hlcoord 14, 1
	call PrintLevel
Show Move Accuracy and Status Effect Chance - Implemented
	; This displays the Pokémon's species
	; name (not nickname) at the
	; coordinates defined at "hlcoord".
	; In this case that is the
	; top left of the screen.
	xor a
	ld [wMonType], a
	ld a, [wCurPartySpecies]
	ld [wNamedObjectIndex], a
	call GetPokemonName
-	hlcoord  5, 0
+	hlcoord  3, 0
	call PlaceString

	; This displays the Pokémon's level
-	; right after the Pokémon's name.
-	push bc
+	; at the coordinates defined at
+	; "hlcoord". In this case that is
+	; the top right of the screen.
	farcall CopyMonToTempMon
-	pop hl
+	hlcoord 14, 0
	call PrintLevel

Now the Pokémon's name and level will be centred on the screen and the text box border will not clip with the Pokémon's name.

Space Out Name and Level Customisation Implemented


7.5. Loop menus for Convenience

Currently, the Move Reminder will end dialogue with the player in the following situations:

  • When they select a Pokémon without any moves to learn.
  • When they select an egg or a glitched Pokémon.
  • When a Pokémon learns a move.
  • When a player selects a move to teach to a Pokémon but then decides against it.

This is quite inconvenient if the player would like to select another Pokémon or move.

We will introduce two loops in order to make the Move Reminder's service more convenient for the player. The two loops are:

  • Party Menu Loop - Loops the party menu when the player selects a Pokémon with no moves to learn, an egg, a glitched Pokémon or leaves the Move Reminder's move menu.
  • Move Menu Loop - Loops the move menu when a Pokémon learns a move or the player selects a move to teach to a Pokémon but then decides against it.

Note: The reader does not have to implement both of the loops. They can be implemented independently.

All edits will be made in engine/events/move_reminder.asm for this entire section.


7.5.1. Add Party Menu Loop

  1. Create the .loop_party_menu local jump after the MoveReminderWhichMonText text as this is where the party menu loop will begin.
	; Loads and prints the "MoveReminderWhichMonText" text.
	ld hl, MoveReminderWhichMonText
	call PrintText
+; This code falls through into the ".loop_party_menu" local jump.
+
+; This is where the party menu loop begins.

-	; Loads the party menu to select a Pokémon. Relative jump
-	; to the ".cancel" local jump if the player leaves
-	; the party menu without selecting anything.
+; Loads the party menu to select a Pokémon. Relative jump
+; to the ".cancel" local jump if the player leaves
+; the party menu without selecting anything.
+.loop_party_menu
	farcall SelectMonFromParty
	jr c, .cancel

  1. Replace the relative jump that exits the menu when there are no moves to learn with a relative jump that loops back to the party menu.

This step will vary depending on whether the reader has already implemented the Add Move Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Move Menu Loop - Not Implemented
	; Generates the Move Reminder's menu. Relative jump to the
-	; ".exit_menu" local jump if the player leaves
+	; ".loop_party_menu" local jump if the player leaves
	; the menu and continue if they do not.
	call ChooseMoveToLearn
-	jr c, .exit_menu
+	jr c, .loop_party_menu
Add Move Menu Loop - Implemented
; Generates the Move Reminder's menu. Relative jump to the
-; ".exit_menu" local jump if the player leaves
+; ".loop_party_menu" local jump if the player leaves
; the menu and continue if they do not.
.loop_move_menu
	call ChooseMoveToLearn
-	jr c, .exit_menu
+	jr c, .loop_party_menu

  1. Remove the now redundant .exit_menu local jump.

This step will vary depending on whether the reader has already implemented the Add Move Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Move Menu Loop - Not Implemented

Note: The reason for removing only the .exit_menu local jump and not its code block is that it is still needed to return to the map with the speech text box open, ensuring that the subsequent cancel text is displayed correctly.

	; Relative jump to the ".move_learned" local jump if
	; a move has been learned and continue if not.
	ld a, b
	dec a
	jr z, .move_learned
-; This code falls through into the ".exit_menu" local jump.

-; Exits the menu and goes back to the
-; map with a speech text box open.
-.exit_menu
+	; Exits the menu and goes back to the
+	; map with a speech text box open.
	call ReturnToMapWithSpeechTextbox
; This code falls through into the ".cancel" local jump.

; Loads and prints the "MoveReminderCancelText" text.
; This ends the dialogue.
.cancel
Add Move Menu Loop - Implemented

Note: The reason that the entire .exit_menu local jump is being removed, is that it will no longer be executed by anything (neither via jumping or fall through).

; Rechecks for any moves that can be learned. Relative
; jump to the ".no_moves_to_learn" local jump if
; there are none and relative jump to the
; ".loop_move_menu" local jump if there are.
.recheck_for_moves
	call GetRemindableMoves
	jr z, .no_moves_to_learn
	jr .loop_move_menu
-
-; Exits the menu and goes back to the
-; map with a speech text box open.
-.exit_menu
-	call ReturnToMapWithSpeechTextbox
-; This code falls through into the ".cancel" local jump.

; Loads and prints the "MoveReminderCancelText" text.
; This ends the dialogue.
.cancel

  1. Make the party menu loop if the currently selected Pokémon has no moves to learn or if the player selects an egg or a glitched Pokémon.
-; Loads and prints the "MoveReminderEggText" text.
-; This ends the dialogue.
+; Loads and prints the "MoveReminderEggText" text and then waits for
+; the player to press a button for the text to progress. Then
+; relative jump to the ".loop_party_menu" local jump.
.is_an_egg
	ld hl, MoveReminderEggText
-	jp PrintText
+	call PrintText
+	jr .loop_party_menu

-; Loads and prints the "MoveReminderNotaMonText" text.
-; This ends the dialogue.
+; Loads and prints the "MoveReminderNotaMonText" text and then waits
+; for the player to press a button for the text to progress. Then
+; relative jump to the ".loop_party_menu" local jump.
.not_a_pokemon
	ld hl, MoveReminderNotaMonText
-	jp PrintText
+	call PrintText
+	jr .loop_party_menu

-; Loads and prints the "MoveReminderNoMovesText" text.
-; This ends the dialogue.
+; Loads and prints the "MoveReminderNoMovesText" text and then waits
+; for the player to press a button for the text to progress. Then
+; relative jump to the ".loop_party_menu" local jump.
.no_moves_to_learn
	ld hl, MoveReminderNoMovesText
-	jp PrintText
+	call PrintText
+	jr .loop_party_menu

  1. Prompt the player to press a button after the text that appears when the currently selected Pokémon has no moves to learn or if the player selects an egg or a glitched Pokémon.

Note: This prevents the text from disappearing as soon as it is printed.

; This is the text that displays if the player
; selects an egg in the party menu.
MoveReminderEggText:
	text "An EGG can't learn"
	line "any moves!"
-	done
+	prompt

; This is the text that displays if the player
; selects an entry in the party menu that
; is neither a Pokémon or an egg.
MoveReminderNotaMonText:
	text "What is that!?"

	para "I'm sorry, but I"
	line "can only teach"
	cont "moves to #MON!"
-	done
+	prompt

; This is the text that displays if the player
; selects a Pokémon in the party menu that
; has no moves that can be learned.
MoveReminderNoMovesText:
	text "There are no moves"
	line "for this #MON"
	cont "to learn."
-	done
+	prompt

The party menu loop has now been implemented.


7.5.2. Add Move Menu Loop

  1. Create the .loop_move_menu local jump after MoveReminderWhichMoveText text as this is where the move menu loop will begin.

This step will vary depending on whether the reader has already implemented the Add Party Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Party Menu Loop - Not Implemented
	; Loads and prints the "MoveReminderWhichMoveText" text.
	ld hl, MoveReminderWhichMoveText
	call PrintText
+; This code falls through into the ".loop_move_menu" local jump.
+
+; This is where the move menu loop begins.

-	; Generates the Move Reminder's menu. Relative jump to the
-	; ".exit_menu" local jump if the player leaves
-	; the menu and continue if they do not.
+; Generates the Move Reminder's menu. Relative jump to the
+; ".exit_menu" local jump if the player leaves
+; the menu and continue if they do not.
+.loop_move_menu
	call ChooseMoveToLearn
	jr c, .exit_menu
Add Party Menu Loop - Implemented
	; Loads and prints the "MoveReminderWhichMoveText" text.
	ld hl, MoveReminderWhichMoveText
	call PrintText
+; This code falls through into the ".loop_move_menu" local jump.
+
+; This is where the move menu loop begins.

-	; Generates the Move Reminder's menu. Relative jump to the
-	; ".loop_party_menu" local jump if the player leaves
-	; the menu and continue if they do not.
+; Generates the Move Reminder's menu. Relative jump to the
+; ".loop_party_menu" local jump if the player leaves
+; the menu and continue if they do not.
+.loop_move_menu
	call ChooseMoveToLearn
	jr c, .loop_party_menu

  1. Create the .recheck_for_moves local jump that will execute if a move has not been learned.

This is what allows the move menu to loop if there are any further moves to learn.

This step will vary depending on whether the reader has already implemented the Add Party Menu Loop subsection of the Loop menus for Convenience. Click to expand the applicable code.

Add Party Menu Loop - Not Implemented

Note: Here the .recheck_for_moves local jump is executed via fall through when a move is not learned. However, it is also jumped to when a move is learned in code that will be introduced at a later point.

	; Relative jump to the ".move_learned" local jump if
	; a move has been learned and continue if not.
	ld a, b
	dec a
	jr z, .move_learned
-; This code falls through into the ".exit_menu" local jump.
+; This code falls through into the ".recheck_for_moves" local jump.
+
+; Rechecks for any moves that can be learned. Relative
+; jump to the ".no_moves_to_learn" local jump if
+; there are none and relative jump to the
+; ".loop_move_menu" local jump if there are.
+.recheck_for_moves
+	call GetRemindableMoves
+	jr z, .no_moves_to_learn
+	jr .loop_move_menu
Add Party Menu Loop - Implemented

Note: Here the .recheck_for_moves local jump is executed via fall through when a move is not learned. However, it is also jumped to when a move is learned in code that will be introduced at a later point.

	; Relative jump to the ".move_learned" local jump if
	; a move has been learned and continue if not.
	ld a, b
	dec a
	jr z, .move_learned
+; This code falls through into the ".recheck_for_moves" local jump.
+
+; Rechecks for any moves that can be learned. Relative
+; jump to the ".no_moves_to_learn" local jump if
+; there are none and relative jump to the
+; ".loop_move_menu" local jump if there are.
+.recheck_for_moves
+	call GetRemindableMoves
+	jr z, .no_moves_to_learn
+	jr .loop_move_menu

  1. Remove redundant code (Compatibility with the Add Party Menu Loop section).

Note: This step is only relevant if the reader has implemented the Add Party Menu Loop subsection of the Loop menus for Convenience of this tutorial. Click to expand the code.

Add Party Menu Loop - Implemented

Note: This code is being removed as it will no longer be executed via fall through. The two relative jumps above it prevent that.

; Rechecks for any moves that can be learned. Relative
; jump to the ".no_moves_to_learn" local jump if
; there are none and relative jump to the
; ".loop_move_menu" local jump if there are.
.recheck_for_moves
	call GetRemindableMoves
	jr z, .no_moves_to_learn
	jr .loop_move_menu
-
-	; Exits the menu and goes back to the
-	; map with a speech text box open.
-	call ReturnToMapWithSpeechTextbox
-; This code falls through into the ".cancel" local jump.

; Loads and prints the "MoveReminderCancelText" text.
; This ends the dialogue.
.cancel

  1. Recheck for moves once a move has been learned.

This step will vary depending on whether the reader has already implemented the Pay for Each Move Learned section. Click to expand the applicable code.

Pay for Each Move Learned - Not Implemented
; Exits the menu and goes back to the map with a
-; speech text box open and then loads and prints
-; the "MoveReminderMoveLearnedText" text.
-; This ends the dialogue.
+; speech text box open, loads and prints
+; the "MoveReminderMoveLearnedText"
+; text and relative jump to the
+; ".recheck_for_moves" local jump.
.move_learned
	call ReturnToMapWithSpeechTextbox
	ld hl, MoveReminderMoveLearnedText
-	jp PrintText
+	call PrintText
+	jr .recheck_for_moves
Pay for Each Move Learned - Implemented
; Places the player's current money at the top right corner of
; the screen, retrieves the amount of money defined in the
; "MoveCost" label, removes the defined amount of money from
-; the player, plays the "SFX_TRANSACTION" sound effect,
-; prints the "MoveReminderPaymentReceivedText"text and
-; finally relative jump to the ".cancel" local jump.
+; the player, plays the "SFX_TRANSACTION" sound effect and
+; finally prints the "MoveReminderPaymentReceivedText" text.
.pay_for_move
	farcall PlaceMoneyTopRight
	ld hl, MoveCost
	ld de, hMoneyTemp
	ld bc, 3
	call CopyBytes
	call ApplyTilemap
	call PromptButton
	call WaitSFX
	ld bc, hMoneyTemp
	ld de, wMoney
	farcall TakeMoney
	farcall PlaceMoneyTopRight
	ld de, SFX_TRANSACTION
	call PlaySFX
	call WaitSFX
	ld hl, MoveReminderPaymentReceivedText
	call PrintText
-	jr .cancel
+
+	; Calls the "CheckCostAgainstPlayerMoney" label. Relative
+	; jump to the ".not_enough_money" local jump if the
+	; player does not have enough money and relative jump
+	; to the ".recheck_for_moves" local jump if they do.
+	call CheckCostAgainstPlayerMoney
+	jr c, .not_enough_money
+	jr .recheck_for_moves

  1. Prompt the player to press a button after the text that appears when a move has been learned (Compatibility with the Pay for Each Move Learned section).

This prevents the text from disappearing as soon as it is printed.

Note: This step is only necessary if the reader has not implemented the Pay for Each Move Learned section of this tutorial. Click to expand the code.

Pay for Each Move Learned - Not Implemented
; This is the text that displays after a
; Pokémon successfully learns a move.
MoveReminderMoveLearnedText:
	text "Done! Your #MON"
	line "remembered the"
	cont "move."
-	done
+	prompt

The move menu loop has now been implemented.


8. Credits

TwitchPlaysPokemon

Polished Crystal

Idain

Damien

  • Payment code.

  • Show perfect accuracy moves as --- code.

Nayru62

  • Original percentage conversion code.

  • Explainations of updated percentage conversion code and ASM.

Ax6

  • Updated percentage conversion code.

SourApple/SoupPotato

  • Bugging Motivating the writer of this tutorial to complete it.

  • Implemented this Move Reminder into Sour Crystal which allowed for more extensive testing.

  • Testing

Vulcandth

  • Explanation of why some missing code was necessary.
⚠️ **GitHub.com Fallback** ⚠️