Quantity Menus: Add or Subtract 10 with Left and Right Buttons - pret/pokered GitHub Wiki

Introduction

Greetings, this is a very simple tutorial to allow you to use the left and right buttons when selecting a quantity in game to increment by 10 or decrement by 10, as has been possible since Generation 2.

This will work for buying/selling items at the PokeMart, tossing items from your item bag, and withdrawing/depositing items at the PC.

1. Make the Change

Modify home/list_menu.asm:

...
DisplayChooseQuantityMenu::
...
.waitForKeyPressLoop
	call JoypadLowSensitivity
	ldh a, [hJoyPressed] ; newly pressed buttons
	bit BIT_A_BUTTON, a
	jp nz, .buttonAPressed
	bit BIT_B_BUTTON, a
	jp nz, .buttonBPressed
	bit BIT_D_UP, a
	jr nz, .incrementQuantity
	bit BIT_D_DOWN, a
	jr nz, .decrementQuantity
+	bit BIT_D_RIGHT, a
+	jr nz, .incrementQuantityBy10
+	bit BIT_D_LEFT, a
+	jr nz, .decrementQuantityBy10
	jr .waitForKeyPressLoop
.incrementQuantity
	ld a, [wMaxItemQuantity]
	inc a
	ld b, a
	ld hl, wItemQuantity ; current quantity
	inc [hl]
	ld a, [hl]
	cp b
	jr nz, .handleNewQuantity
; wrap to 1 if the player goes above the max quantity
	ld a, 1
	ld [hl], a
	jr .handleNewQuantity
+.incrementQuantityBy10
+	ld a, [wMaxItemQuantity]
+	inc a
+	ld b, a
+	ld hl, wItemQuantity ; current quantity
+	ld a, [hl]
+	add 10
+	ld [hl], a
+	cp b
+	jr c, .handleNewQuantity
+; Set to Max if the player goes above the max quantity
+	ld a, [wMaxItemQuantity]
+	ld [hl], a
+	jr .handleNewQuantity
+.decrementQuantityBy10
+	ld a, [wMaxItemQuantity]
+	inc a
+	ld b, a
+	ld hl, wItemQuantity ; current quantity
+	ld a, [hl]
+	sub 10
+	ld [hl], a
+	cp 0
+	jr z, .tooLow
+	cp b
+	jr c, .handleNewQuantity
+; Fallthrough if above the Max. Explanation: if we went below 0, then it is now a big number, and we can probably assume bigger than the max
+; Set to 1 if the player goes below 1
+.tooLow
+	ld a, 1
+	ld [hl], a
+	jr .handleNewQuantity
.decrementQuantity
...

2. Instant Regret

Oh shucks. You've just added 53 bytes to your Home bank, and even if your game compiles, you know it's always better to think twice before cramming more stuff into the extremely full Home bank! Fear not, it's going to be okay. In fact, it's going to be better than okay.

3. Get This Thing Out of Here

We're now going to move the whole thing out of the Home bank, freeing up a whopping 262 bytes from your Home bank! Huzzah!

(In case you're interested, that's the original 217 byte function, the extra 53 bytes we just carelessly added, minus 8 bytes that we're going to add to make this work)

Once again, modify home/list_menu.asm:

...
DisplayChooseQuantityMenu::
+	jpfar _DisplayChooseQuantityMenu
- ; text box dimensions/coordinates for just quantity
-	hlcoord 15, 9
-	ld b, 1 ; height
-	ld c, 3 ; width
-	ld a, [wListMenuID]
-	cp PRICEDITEMLISTMENU
-	jr nz, .drawTextBox
-; text box dimensions/coordinates for quantity and price
-	hlcoord 7, 9
-	ld b, 1  ; height
-	ld c, 11 ; width
-.drawTextBox
-	call TextBoxBorder
-	hlcoord 16, 10
-	ld a, [wListMenuID]
-	cp PRICEDITEMLISTMENU
-	jr nz, .printInitialQuantity
-	hlcoord 8, 10
-.printInitialQuantity
-	ld de, InitialQuantityText
-	call PlaceString
-	xor a
-	ld [wItemQuantity], a ; initialize current quantity to 0
-	jp .incrementQuantity
-.waitForKeyPressLoop
-	call JoypadLowSensitivity
-	ldh a, [hJoyPressed] ; newly pressed buttons
-	bit BIT_A_BUTTON, a
-	jp nz, .buttonAPressed
-	bit BIT_B_BUTTON, a
-	jp nz, .buttonBPressed
-	bit BIT_D_UP, a
-	jr nz, .incrementQuantity
-	bit BIT_D_DOWN, a
-	jr nz, .decrementQuantity
-	bit BIT_D_RIGHT, a
-	jr nz, .incrementQuantityBy10
-	bit BIT_D_LEFT, a
-	jr nz, .decrementQuantityBy10
-	jr .waitForKeyPressLoop
-.incrementQuantity
-	ld a, [wMaxItemQuantity]
-	inc a
-	ld b, a
-	ld hl, wItemQuantity ; current quantity
-	inc [hl]
-	ld a, [hl]
-	cp b
-	jr nz, .handleNewQuantity
-; wrap to 1 if the player goes above the max quantity
-	ld a, 1
-	ld [hl], a
-	jr .handleNewQuantity
-.incrementQuantityBy10
-	ld a, [wMaxItemQuantity]
-	inc a
-	ld b, a
-	ld hl, wItemQuantity ; current quantity
-	ld a, [hl]
-	add 10
-	ld [hl], a
-	cp b
-	jr c, .handleNewQuantity
-; Set to Max if the player goes above the max quantity
-	ld a, [wMaxItemQuantity]
-	ld [hl], a
-	jr .handleNewQuantity
-.decrementQuantityBy10
-	ld a, [wMaxItemQuantity]
-	inc a
-	ld b, a
-	ld hl, wItemQuantity ; current quantity
-	ld a, [hl]
-	sub 10
-	ld [hl], a
-	cp 0
-	jr z, .tooLow
-	cp b
-	jr c, .handleNewQuantity
-; Fallthrough if above the Max. Explanation: if we went below 0, then it is now a big number, and we can probably assume bigger than the max
-; Set to 1 if the player goes below 1
-.tooLow
-	ld a, 1
-	ld [hl], a
-	jr .handleNewQuantity
-.decrementQuantity
-	ld hl, wItemQuantity ; current quantity
-	dec [hl]
-	jr nz, .handleNewQuantity
-; wrap to the max quantity if the player goes below 1
-	ld a, [wMaxItemQuantity]
-	ld [hl], a
-.handleNewQuantity
-	hlcoord 17, 10
-	ld a, [wListMenuID]
-	cp PRICEDITEMLISTMENU
-	jr nz, .printQuantity
-.printPrice
-	ld c, $03
-	ld a, [wItemQuantity]
-	ld b, a
-	ld hl, hMoney ; total price
-; initialize total price to 0
-	xor a
-	ld [hli], a
-	ld [hli], a
-	ld [hl], a
-.addLoop ; loop to multiply the individual price by the quantity to get the total price
-	ld de, hMoney + 2
-	ld hl, hItemPrice + 2
-	push bc
-	predef AddBCDPredef ; add the individual price to the current sum
-	pop bc
-	dec b
-	jr nz, .addLoop
-	ldh a, [hHalveItemPrices]
-	and a ; should the price be halved (for selling items)?
-	jr z, .skipHalvingPrice
-	xor a
-	ldh [hDivideBCDDivisor], a
-	ldh [hDivideBCDDivisor + 1], a
-	ld a, $02
-	ldh [hDivideBCDDivisor + 2], a
-	predef DivideBCDPredef3 ; halves the price
-; store the halved price
-	ldh a, [hDivideBCDQuotient]
-	ldh [hMoney], a
-	ldh a, [hDivideBCDQuotient + 1]
-	ldh [hMoney + 1], a
-	ldh a, [hDivideBCDQuotient + 2]
-	ldh [hMoney + 2], a
-.skipHalvingPrice
-	hlcoord 12, 10
-	ld de, SpacesBetweenQuantityAndPriceText
-	call PlaceString
-	ld de, hMoney ; total price
-	ld c, $a3
-	call PrintBCDNumber
-	hlcoord 9, 10
-.printQuantity
-	ld de, wItemQuantity ; current quantity
-	lb bc, LEADING_ZEROES | 1, 2 ; 1 byte, 2 digits
-	call PrintNumber
-	jp .waitForKeyPressLoop
-.buttonAPressed ; the player chose to make the transaction
-	xor a
-	ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped
-	ret
-.buttonBPressed ; the player chose to cancel the transaction
-	xor a
-	ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped
-	ld a, $ff
-	ret
...

Now make a new file in engine/menus, name it quantity_menu.asm.

The contents will be pretty much what we just removed from Home. For convenience, just copy the below into your new file:

_DisplayChooseQuantityMenu::
; text box dimensions/coordinates for just quantity
	hlcoord 15, 9
	ld b, 1 ; height
	ld c, 3 ; width
	ld a, [wListMenuID]
	cp PRICEDITEMLISTMENU
	jr nz, .drawTextBox
; text box dimensions/coordinates for quantity and price
	hlcoord 7, 9
	ld b, 1  ; height
	ld c, 11 ; width
.drawTextBox
	call TextBoxBorder
	hlcoord 16, 10
	ld a, [wListMenuID]
	cp PRICEDITEMLISTMENU
	jr nz, .printInitialQuantity
	hlcoord 8, 10
.printInitialQuantity
	ld de, InitialQuantityText
	call PlaceString
	xor a
	ld [wItemQuantity], a ; initialize current quantity to 0
	jp .incrementQuantity
.waitForKeyPressLoop
	call JoypadLowSensitivity
	ldh a, [hJoyPressed] ; newly pressed buttons
	bit BIT_A_BUTTON, a
	jp nz, .buttonAPressed
	bit BIT_B_BUTTON, a
	jp nz, .buttonBPressed
	bit BIT_D_UP, a
	jr nz, .incrementQuantity
	bit BIT_D_DOWN, a
	jr nz, .decrementQuantity
	bit BIT_D_RIGHT, a
	jr nz, .incrementQuantityBy10
	bit BIT_D_LEFT, a
	jr nz, .decrementQuantityBy10
	jr .waitForKeyPressLoop
.incrementQuantity
	ld a, [wMaxItemQuantity]
	inc a
	ld b, a
	ld hl, wItemQuantity ; current quantity
	inc [hl]
	ld a, [hl]
	cp b
	jr nz, .handleNewQuantity
; wrap to 1 if the player goes above the max quantity
	ld a, 1
	ld [hl], a
	jr .handleNewQuantity
.incrementQuantityBy10
	ld a, [wMaxItemQuantity]
	inc a
	ld b, a
	ld hl, wItemQuantity ; current quantity
	ld a, [hl]
	add 10
	ld [hl], a
	cp b
	jr c, .handleNewQuantity
; Set to Max if the player goes above the max quantity
	ld a, [wMaxItemQuantity]
	ld [hl], a
	jr .handleNewQuantity
.decrementQuantityBy10
	ld a, [wMaxItemQuantity]
	inc a
	ld b, a
	ld hl, wItemQuantity ; current quantity
	ld a, [hl]
	sub 10
	ld [hl], a
	cp 0
	jr z, .tooLow
	cp b
	jr c, .handleNewQuantity
; Fallthrough if above the Max. Explanation: if we went below 0, then it is now a big number, and we can probably assume bigger than the max
; Set to 1 if the player goes below 1
.tooLow
	ld a, 1
	ld [hl], a
	jr .handleNewQuantity
.decrementQuantity
	ld hl, wItemQuantity ; current quantity
	dec [hl]
	jr nz, .handleNewQuantity
; wrap to the max quantity if the player goes below 1
	ld a, [wMaxItemQuantity]
	ld [hl], a
.handleNewQuantity
	hlcoord 17, 10
	ld a, [wListMenuID]
	cp PRICEDITEMLISTMENU
	jr nz, .printQuantity
.printPrice
	ld c, $03
	ld a, [wItemQuantity]
	ld b, a
	ld hl, hMoney ; total price
; initialize total price to 0
	xor a
	ld [hli], a
	ld [hli], a
	ld [hl], a
.addLoop ; loop to multiply the individual price by the quantity to get the total price
	ld de, hMoney + 2
	ld hl, hItemPrice + 2
	push bc
	predef AddBCDPredef ; add the individual price to the current sum
	pop bc
	dec b
	jr nz, .addLoop
	ldh a, [hHalveItemPrices]
	and a ; should the price be halved (for selling items)?
	jr z, .skipHalvingPrice
	xor a
	ldh [hDivideBCDDivisor], a
	ldh [hDivideBCDDivisor + 1], a
	ld a, $02
	ldh [hDivideBCDDivisor + 2], a
	predef DivideBCDPredef3 ; halves the price
; store the halved price
	ldh a, [hDivideBCDQuotient]
	ldh [hMoney], a
	ldh a, [hDivideBCDQuotient + 1]
	ldh [hMoney + 1], a
	ldh a, [hDivideBCDQuotient + 2]
	ldh [hMoney + 2], a
.skipHalvingPrice
	hlcoord 12, 10
	ld de, SpacesBetweenQuantityAndPriceText
	call PlaceString
	ld de, hMoney ; total price
	ld c, $a3
	call PrintBCDNumber
	hlcoord 9, 10
.printQuantity
	ld de, wItemQuantity ; current quantity
	lb bc, LEADING_ZEROES | 1, 2 ; 1 byte, 2 digits
	call PrintNumber
	jp .waitForKeyPressLoop
.buttonAPressed ; the player chose to make the transaction
	xor a
	ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped
	ret
.buttonBPressed ; the player chose to cancel the transaction
	xor a
	ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped
	ld a, $ff
	ret

Remember to leave an empty line at the end of your file, lest you incite the fury of RGBASM.

Now modify main.asm

You just need to add your new file anywhere that it will fit.

This is an example, but it doesn't matter where you put it:

SECTION "bank1E", ROMX

INCLUDE "engine/battle/animations.asm"
INCLUDE "engine/overworld/cut2.asm"
INCLUDE "engine/overworld/dust_smoke.asm"
INCLUDE "gfx/fishing.asm"
INCLUDE "data/moves/animations.asm"
INCLUDE "data/battle_anims/subanimations.asm"
INCLUDE "data/battle_anims/frame_blocks.asm"
INCLUDE "engine/movie/evolution.asm"
INCLUDE "engine/overworld/elevator.asm"
INCLUDE "engine/items/tm_prices.asm"

+SECTION "bankPoryman", ROMX
+INCLUDE "engine/menus/quantity_menu.asm" ; This can go anywhere

5. Wish it Was Better

Yes, I know, I can hear you saying "But in Gen 2, you can hold down Up, Down, Left or Right and it will keep incrementing or decrementing until you let go. Why do we have to keep pushing buttons like it's 1996?".

I feel your pain. I did look into it. It made my head spin a little. If I figure it out, I'll update the tutorial. If you figure it out, please feel free to update it yourself and please ping me (Porygondolier) on the pret discord because I'd love to have that as well.

Outroduction

Thank you for reading this tutorial, I have been your host, Mr. Poryman of Porygondolier, and I'd like to wish you a wonderful day.