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.