Display more information on the move screen - pret/pokecrystal GitHub Wiki

Hello! This tutorial will show how to edit the pokémon move screen to add more information about the moves. The information you want to show can depend on other tutorials, like if you used the new physical/special split for moves and want to show that, but it is not particularly necessary for this tutorial, and you'll be able (hopefully) to customize the screen as you like.

Adding new types or changing move's category should not impact this tutorial.

This tutorial was made after the Move Reminder (Variant 2). Thank you all for that tutorial, it helped me a lot! And I hope this one can help you too.

Contents

  1. The move screen UI
  2. Understand the code
  3. Display move's accuracy
  4. Display move's effect chance
  5. Display OLD move's category
  6. Display NEW move's category
  7. Improving UI
  8. Results

1. The move screen UI

Basically, the move screen will show the Pokémon's moves and some information about each one of them. This screen also allows change the move's order. The move screen can be accessed by selecting the pokémon and then choosing the option "MOVE".

That will display a screen with the Pokémon's:

  • Menu icon
  • Nickname
  • Level
  • Four moves
  • Current and max PP of each move
  • Move's information
    • Type
    • Attack power
    • Description

See:

pokecrystal-0 pokecrystal-1

Just looking at the screen we can already understand that it doesn't have much room for more information, so we'll have to work on that.

Another thing to have in mind is that some information is organized by other scripts that affects how UI functions in the game. That makes not an easy task to move the PP position for instance, as the game uses the same mechanic for listing items in the bag or in pokémon marts. So let's leave that for a future tutorial.

Basically, all information can be edited in one place: engine/pokemon/mon_menu.asm.

Also, there's information about the moves in game that is not displayed to the player, so we'll be adding that later.

Before anything else, open the file above to continue the tutorial from this point on.


2. Understand the code

PlaceMoveData is where the most information we need is located.

Let's make the code easier to read adding some comments, and at the same time, understand where each part of the code displays the UI we saw before:

PlaceMoveData:
	xor a
	ldh [hBGMapMode], a
+
+; Print UI elements
	hlcoord 0, 10
	ld de, String_MoveType_Top
	call PlaceString
	hlcoord 0, 11
	ld de, String_MoveType_Bottom
	call PlaceString
	hlcoord 12, 12
	ld de, String_MoveAtk
	call PlaceString
+
+; Print move type
	ld a, [wCurSpecies]
	ld b, a
	hlcoord 2, 12
	predef PrintMoveType
+
+; Print move power
	ld a, [wCurSpecies]
	dec a
	ld hl, Moves + MOVE_POWER
	ld bc, MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
	hlcoord 16, 12
	cp 2
	jr c, .no_power
	ld [wTextDecimalByte], a
	ld de, wTextDecimalByte
	lb bc, 1, 3
	call PrintNum
	jr .description

.no_power
	ld de, String_MoveNoPower
	call PlaceString

+; Print move description
.description
	hlcoord 1, 14
	predef PrintMoveDescription
	ld a, $1
	ldh [hBGMapMode], a
	ret

+; UI elements
String_MoveType_Top:
	db "┌─────┐@"
String_MoveType_Bottom:
	db "│TYPE/└@"
String_MoveAtk:
	db "ATK/@"
String_MoveNoPower:
	db "---@"

We can display a text (UI element) on the screen by adding/editting the following:

	hlcoord 0, 0
	ld de, String
	call PlaceString

String:
	db "TEXT@"

Where:

  • hlcoord 0, 0 sets the screen position in "X, Y" coordinates (1 = 1 tile long).
  • ld de, String get the text to display, in this case comes from String.
  • call PlaceString is a function that display your text in the set position.
  • String: sets a new string.
  • db "TEXT@" sets the string text. It must end with "@". Text commands works, like <NEXT> to break a line.

Aside from commands, there are others special characters (charmaps) it's possible to print within a string, which will help in customizing the UI. Strings like: "┌─────┐@" can seem a bit confusing at first, but that makes possible to print a border instead of a common letter. In this case, it will create that tab on the top-left corner of the description.

See:

image

The available charmaps and commands within a string can be found at constants/charmap.asm.


3. Display move's accuracy

We can display to the player the accuracy of each move on the screen, as this information is already available in the game. But first, we need to find a suitable location on the screen for this feature. For now, I'll place it below the attack power of the move.

Here is the plan:

pokecrystal-1_acc

Next step is to create the string which will print "ACC/" before the accuracy value:

PlaceMoveData:
...

; UI elements
String_MoveType_Top:
	db "┌─────┐@"
String_MoveType_Bottom:
	db "│TYPE/└@"
String_MoveAtk:
	db "ATK/@"
+String_MoveAcc:
+	db "ACC/@"
String_MoveNoPower:
	db "---@"

Next, we'll need to set the coordinates to position it in the intended place. In this case it will be 1 tile bellow the "ATK" string.

PlaceMoveData:
...

; Print UI elements
	hlcoord 0, 10
	ld de, String_MoveType_Top
	call PlaceString
	hlcoord 0, 11
	ld de, String_MoveType_Bottom
	call PlaceString
	hlcoord 12, 12
	ld de, String_MoveAtk
	call PlaceString
+	hlcoord 12, 13
+	ld de, String_MoveAcc
+	call PlaceString

And the code that will actually print the accuracy value, also at 1 tile below the attack power value.

PlaceMoveData:
...

+
+; Print move accuracy
+	ld a, [wCurSpecies]
+	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, 13
+	call PrintNum

; Print move type

The following three lines in the code calls a custom function to convert the accuracy value.

	; Call ConvertPercentages
	; ld [wBuffer1], a
	; ld de, wBuffer1

The coordinates in hlcoord 16, 13 set the position.

Printing accuracy is tricky, as it's value comes in 256 format. We need to convert it to show in percentage. For that, we'll use the help of a code from the Move Reminder (Variant 2) tutorial to convert the value.

If Move Reminder (variant 2) is already implemented, there's no need to add the conversion code again, as it can run from the other script. However, if the code was not already implemented, here is how to do it:

  1. Copy the code:
Conversion code from Move Reminder (variant 2) tutorial:
+; 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
  1. Paste it in a good place (before the UI Elements is a good spot)
+ConvertPercentages:
+...

; UI elements
String_MoveType_Top:
	db "┌─────┐@"
  1. Make space in wram

In any case, make sure to set a space in wram, if already not, as mentioned in the Move Reminder (variant 2) tutorial, edit ram/wram.asm.

wBattleMenuCursorPosition:: db

-	ds 1
+wBuffer1:: db

wCurBattleMon::

It should already be working as intended.

pokecrystal-2


4. Display move's effect chance

Another information present in moves that we can display is the chance of performing an additional effect. For instance, EMBER has 10% chance to burn opponents. So let's shows that in the move screen. Again, we'll need to find a place for that.

Here is the plan:

pokecrystal-2-eff

NOTE: It is important to note that displaying the accuracy is not necessary for displaying the effect chance. They are independent of each other.

Create a string which will print "EFF/" on the screen.

PlaceMoveData:
...

; UI elements
String_MoveType_Top:
	db "┌─────┐@"
String_MoveType_Bottom:
	db "│TYPE/└@"
String_MoveAtk:
	db "ATK/@"
String_MoveAcc:
	db "ACC/@"
+String_MoveEff:
+	db "EFF/@"
String_MoveNoPower:
	db "---@"

To position at the right place, we'll need to set the coordinates. In this case it will have the same height of the "ACC" string but with 8 tiles to the left.

PlaceMoveData:
...

; Print UI elements
	hlcoord 0, 10
	ld de, String_MoveType_Top
	call PlaceString
	hlcoord 0, 11
	ld de, String_MoveType_Bottom
	call PlaceString
	hlcoord 12, 12
	ld de, String_MoveAtk
	call PlaceString
	hlcoord 12, 13
	ld de, String_MoveAcc
	call PlaceString
+	hlcoord 4, 13
+	ld de, String_MoveEff
+	call PlaceString

And the code that will actually print the effect chance value.

PlaceMoveData:
...
	hlcoord 4, 13
	ld de, String_MoveEff
	call PlaceString

+; Print move effect chance
+	ld a, [wCurSpecies]
+	ld bc, MOVE_LENGTH
+	ld hl, (Moves + MOVE_CHANCE) - MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
+	cp 1
+	jr c, .if_null_chance
+	Call ConvertPercentages
+	ld [wBuffer1], a
+	ld de, wBuffer1
+	lb bc, 1, 3
+	hlcoord 8, 13
+	call PrintNum
+	jr .skip_null_chance
+
+.if_null_chance
+	ld de, String_MoveNoPower
+	ld bc, 3
+	hlcoord 8, 13
+	call PlaceString
+
+.skip_null_chance

The same value problem that happenned with the accuracy also happens with the effect chance, they both need its value converted in order do show as percentage in the screen. The following three lines in the code is what calls the custom function to convert the effect chance value.

	; Call ConvertPercentages
	; ld [wBuffer1], a
	; ld de, wBuffer1

Go to the accuracy part of the tutorial to implement that if you already didn't.

The coordinates in hlcoord 8, 13 set the position.

For the effect chance, we'll print String_MoveNoPower if the move has zero chances of adding an effect in the .if_null_chance. But if there's a chance to add an effect, it should skip that, print the value, and continue the code as we see in .skip_null_chance.

It should already be working as intended:

pokecrystal-3


5. Display OLD move's category

NOTE: The categories of a move refered here is PHYSICAL and SPECIAL categories from the Pokémon Crystal, which are already present in the game. If the new system from the Physical/Special split tutorial was implemented, just skip to the next session.

Each move can be one of the two categories based of the move's type. We can see that in the file constants/type_constants.asm.

The moves of the following types are all physical:

DEF PHYSICAL EQU const_value
	const NORMAL
	const FIGHTING
	const FLYING
	const POISON
	const GROUND
	const ROCK
	const BIRD
	const BUG
	const GHOST
	const STEEL

The moves of the following types are all special:

DEF SPECIAL EQU const_value
	const FIRE
	const WATER
	const GRASS
	const ELECTRIC
	const PSYCHIC_TYPE
	const ICE
	const DRAGON
	const DARK

To display that, we'll do some magic, as it doesn't have a fast way to print the category, as well as no current way to tell if a move is a status moves (there's no such category). Found a way to make that work, but I'm sure there's a simpler way to do that. For now, let's stick with how I did it.

But first, the plan:

pokecrystal-3-cat

Let's replace the text TYPE/ with the category name. To do that we must consider that PHYSYCAL have 8 characters, so we'll need to expand the tab. Also, adding a slash before the category just feels right.

Change the String_MoveType_Top and String_MoveType_Bottom strings to make space for printing the category. Create the strings which will print "PHYSICAL", "SPECIAL", and "STATUS" on the screen.

PlaceMoveData:
...

; UI elements
String_MoveType_Top:
-	db "┌─────┐@"
+	db "┌────────┐@"
String_MoveType_Bottom:
-	db "│TYPE/└@"
+	db "│        └@"
String_MoveAtk:
	db "ATK/@"
String_MoveAcc:
	db "ACC/@"
String_MoveEff:
	db "EFF/@"
String_MoveNoPower:
	db "---@"
+String_MovePhy:
+	db "PHYSICAL@"
+String_MoveSpe:
+	db "SPECIAL @"
+String_MoveSta:
+	db "STATUS  @"

Note that we adjusted the strings to fit the bigger one (PHYSICAL). The minor ones are filled with blank spaces just to guarantee it won't render any other character in that place.

We can now go straight to the code that will print the category.

PlaceMoveData:
...

; Print UI elements
...
	call PlaceString
+
+; Print move category
+
+; Verify if it has power
+	ld a, [wCurSpecies]
+	dec a
+	ld hl, Moves + MOVE_POWER
+	ld bc, MOVE_LENGTH
+	call AddNTimes
+	ld a, BANK(Moves)
+	call GetFarByte
+	hlcoord 16, 12
+	cp 2
+	jr c, .status_move
+	
+; Verifify if physical or special
+	ld a, [wCurSpecies]
+	dec a
+	ld bc, MOVE_LENGTH
+	ld hl, Moves
+	call AddNTimes
+	ld de, wStringBuffer1
+	ld a, BANK(Moves)
+	call FarCopyBytes
+	ld a, [wStringBuffer1 + MOVE_TYPE]
+	cp SPECIAL
+	jr nc, .special_category
+
+; IF PHYSICAL
+	hlcoord 1, 11
+	ld de, String_MovePhy
+	call PlaceString
+	jr .printed_category
+
+; IF SPECIAL
+.special_category
+	hlcoord 1, 11
+	ld de, String_MoveSpe
+	call PlaceString
+	jr .printed_category
+
+; IF STATUS
+.status_move
+	hlcoord 1, 11
+	ld de, String_MoveSta
+	call PlaceString
+
+.printed_category
+	hlcoord 1, 12
+	ld [hl], "/"
+	call PlaceString

The code is very simple:

  • Verify if the move has Attack Power
  • If YES, verify if it's SPECIAL
    • IF SPECIAL, print String_MoveSpe
    • IF NOT SPECIAL, print String_MovePhy
  • IF NOT, print String_MoveSta

At the end, it prints a slash before the name of the type.

It should be working. Note that in the images below RAZOR LEAF is listed as special because in this game all GRASS-TYPE moves are in special category. REFLECT should also be listed as special because it is PSYCHIC-TYPE, however, our code identifies that REFLECT has no damage value, so instead of printing "special" it prints "status".

pokecrystal-9 pokecrystal-10 pokecrystal-11


6. Display NEW move's category

The categories of a move referred here is the system from the newer games. Any move, independently of its type, can be in PHYSICAL, SPECIAL, or STATUS category. That is NOT implemented in the base game, as these categories are from future generations, but they can be implemented following this tutorial: Physical/Special split.

If it is implemented, here is how to display that on the screen.

The plan:

pokecrystal-3-cat

Let's replace the text TYPE/ with the category name. To do that we must consider that PHYSYCAL have 8 characters, so we'll need to expand the tab. Also, adding a slash before the category just feels right.

Change the String_MoveType_Top and String_MoveType_Bottom strings to make space for printing the category.

PlaceMoveData:
...

; UI elements
String_MoveType_Top:
-	db "┌─────┐@"
+	db "┌────────┐@"
String_MoveType_Bottom:
-	db "│TYPE/└@"
+	db "│        └@"
String_MoveAtk:
	db "ATK/@"
...

Now we can go straight to the code that will print the category.

PlaceMoveData:
...

; Print UI elements
...
	call PlaceString
+
+; 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

The hlcoord 1, 11 coordinates set the position for the category text.

The hlcoord 1, 12 coordinates set the position for the slash before the name of the move's type.

That's it! It should be working.

pokecrystal-12 pokecrystal-13 pokecrystal-14


7. Improving UI

Ok... We implement all of that. But now there's too much going on...

Let's make a new plan aiming a better experience for the player.

pokecrystal-13-plan

The thoughts here are:

  • Remove the side lines to make it feel less heavy
  • As we'll won't move PPs, we should work around it
  • The left side of the description can fit 3 small data, good positions for ATK, ACC, and EFF
  • The right side of the description can fit 2 bigger ones, good positions for category and type
  • We'll make changes in the tab to better fit these information
  • We'll make changes to not break a line in the move description

So, let's begin!

PlaceMoveData:
...


; Print UI elements
	hlcoord 0, 10
	ld de, String_MoveType_Top
	call PlaceString
	hlcoord 0, 11
	ld de, String_MoveType_Bottom
	call PlaceString
-	hlcoord 12, 12
+	hlcoord 1, 11
	ld de, String_MoveAtk
	call PlaceString
-	hlcoord 12, 13
+	hlcoord 1, 12
	ld de, String_MoveAcc
	call PlaceString
-	hlcoord 4, 13
+	hlcoord 1, 13
	ld de, String_MoveEff
	call PlaceString

; Print move category
	ld a, [wCurSpecies]
	ld b, a
	farcall GetMoveCategoryName
-	hlcoord 1, 11
+	hlcoord 11, 13
	ld de, wStringBuffer1
	call PlaceString
-	hlcoord 1, 12
+	hlcoord 10, 13
	ld [hl], "/"
	inc hl

; Print move effect chance
...
	lb bc, 1, 3
-	hlcoord 8, 13
+	hlcoord 5, 13
	call PrintNum
	jr .skip_null_chance

.if_null_chance
	ld de, String_MoveNoPower
	ld bc, 3
-	hlcoord 8, 13
+	hlcoord 5, 13
	call PlaceString

.skip_null_chance

; Print move accuracy
...
	lb bc, 1, 3
-	hlcoord 16, 13
+	hlcoord 5, 12
	call PrintNum

; Print move type
	ld a, [wCurSpecies]
	ld b, a
-	hlcoord 2, 12
+	hlcoord 10, 12
	predef PrintMoveType

; Print move attack power
	ld a, [wCurSpecies]
	dec a
	ld hl, Moves + MOVE_POWER
	ld bc, MOVE_LENGTH
	call AddNTimes
	ld a, BANK(Moves)
	call GetFarByte
-	hlcoord 16, 12
+	hlcoord 5, 11
	cp 2
	jr c, .no_power
...

; Print move description
.description
-	hlcoord 1, 14
+	hlcoord 1, 15
	predef PrintMoveDescription
	ld a, $1
	ldh [hBGMapMode], a
	ret

; UI elements
String_MoveType_Top:
-	db "┌────────┐@"
+	db "┌───────┐@"
String_MoveType_Bottom:
-	db "│        └@"
+	db "│       └@"
String_MoveAtk:
	db "ATK/@"
String_MoveAcc:
	db "ACC/@"
String_MoveEff:
	db "EFF/@"
String_MoveNoPower:
	db "---@"

All elements are now in the right position. But there are more changes to make...

Edit the following function to change the side bar's position. Let's just move them upwards to keep visible at the top of the screen.

SetUpMoveScreenBG:
	call ClearBGPalettes
	call ClearTilemap
	call ClearSprites
	xor a
	ldh [hBGMapMode], a
	farcall LoadStatsScreenPageTilesGFX
	farcall ClearSpriteAnims2
	ld a, [wCurPartyMon]
	ld e, a
	ld d, 0
	ld hl, wPartySpecies
	add hl, de
	ld a, [hl]
	ld [wTempIconSpecies], a
	ld e, MONICON_MOVES
	farcall LoadMenuMonIcon
-	hlcoord 0, 1
+	hlcoord 0, -1
-	ld b, 9
+	ld b, 1
	ld c, 18
	call Textbox

To remove the break line in the move's description we'll need to edit each move description in another file. Open data/moves/descriptions.asm.

For each move description in the file, replace next for line. Example:

PoundDescription:
	db   "Pounds with fore-"
-	next "legs or tail.@"
+	line "legs or tail.@"

And the last thing... When trying to swap the move order, the screen seems to be buggy. That we can fix with few changes inside the engine/pokemon/mon_menu.asm script:

.moving_move
	ld a, " "
	hlcoord 1, 11
	ld bc, 5
	call ByteFill
+	hlcoord 1, 11
+	lb bc, 5, 7
+	call ClearBox
	hlcoord 1, 12
	lb bc, 5, SCREEN_WIDTH - 2
	call ClearBox
-	hlcoord 1, 12
+	hlcoord 2, 13
	ld de, String_MoveWhere
	call PlaceString
	jp .joy_loop
...

String_MoveWhere:
-	db "Where?@"
+	db "Select a move<NEXT>to swap places.@"

The change in "Where" text is just to add some flavor.

And for bonus point: sound effects!

It will play when switching between Pokémon.

.d_right
	ld a, [wSwappingMove]
	and a
	jp nz, .joy_loop

	ld a, [wCurPartyMon]
	ld b, a
	push bc
	call .cycle_right
	pop bc
	ld a, [wCurPartyMon]
	cp b
	jp z, .joy_loop
+	ld de, SFX_SWITCH_POCKETS
+	call PlaySFX
	jp MoveScreenLoop

.d_left
	ld a, [wSwappingMove]
	and a
	jp nz, .joy_loop
	ld a, [wCurPartyMon]
	ld b, a
	push bc
	call .cycle_left
	pop bc
	ld a, [wCurPartyMon]
	cp b
	jp z, .joy_loop
+	ld de, SFX_SWITCH_POCKETS
+	call PlaySFX
	jp MoveScreenLoop

8. Results

That' all!

pokecrystal-18 pokecrystal-17

⚠️ **GitHub.com Fallback** ⚠️