-
Notifications
You must be signed in to change notification settings - Fork 1k
Add Item Sorting In Bag
voloved edited this page Feb 1, 2025
·
11 revisions
By devolov
Goal: Allow sorting items in the bag by pressing Start button.
------------------------------ home/list_menu.asm ------------------------------
index 77e75012..db84931d 100644
@@ -49,9 +49,9 @@ DisplayListMenuID::
ld a, 4
ld [wTopMenuItemY], a
ld a, 5
ld [wTopMenuItemX], a
- ld a, A_BUTTON | B_BUTTON | SELECT
+ ld a, A_BUTTON | B_BUTTON | SELECT | START
ld [wMenuWatchedKeys], a
ld c, 10
call DelayFrames
@@ -171,8 +171,10 @@ DisplayListMenuIDLoop::
bit BIT_B_BUTTON, a
jp nz, ExitListMenu ; if so, exit the menu
bit BIT_SELECT, a
jp nz, HandleItemListSwapping ; if so, allow the player to swap menu entries
+ bit BIT_START, a
+ jp nz, SortItems
ld b, a
bit BIT_D_DOWN, b
ld hl, wListScrollOffset
jr z, .upPressed
------------------------- engine/menus/swap_items.asm -------------------------
index 2d506ce2..4707ed20 100644
@@ -146,4 +146,308 @@ HandleItemListSwapping::
ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped
pop de
pop hl
jp DisplayListMenuIDLoop
+
+SortItems::
+ ld a, [wListMenuID]
+ cp ITEMLISTMENU
+ jp nz, DisplayListMenuIDLoop ; only rearrange item list menus
+ push hl
+ push bc
+ ld hl, SortItemsText ; Display the text to ask to sort
+ call PrintText
+ hlcoord 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; Used for Yes/Nop without affecting Buffer1 or 2
+ ld a, [wCurrentMenuItem]
+ and a
+ jp z, .beginSorting ; If yes
+ jr .cleanWindow
+.finishedSwapping
+ ld a, [hSwapTemp] ; If not 0, then a swap of items did occur
+ and a
+ jr z, .nothingSorted
+ ld hl, SortComplete
+ jr .printResultText
+.nothingSorted
+ ld hl, NothingToSort
+.printResultText
+ call PrintText
+.cleanWindow
+ ld a, LIST_MENU_BOX
+ ld [wTextBoxID], a
+ ld hl, EmptyString
+ ld a, [wIsInBattle]
+ and a
+ jr nz, .done ; In battle
+ ld hl, WhatDoYouWantText
+ ; This is the hackiest part. It will print the text "What do you want to do?",
+ ; even when the ideal scenerio is to show no textbox in the overwold, and a mildly different
+ ; message in the PC. but to clean up the overworld, LoadScreenTilesFromBuffer2 would need to be used,
+ ; but that would not work for the PC, so then PC tracking is needed.
+ ; To keep things simple, we will just always display a textbox after sorting items in the overworld.
+.done
+ call PrintText
+ ld a, ITEMLISTMENU
+ ld [wListMenuID], a
+ ld a, 1 ; max menu item ID is 1 if the list has less than 2 entries
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld a, [wListCount]
+ cp 2 ; does the list have less than 2 entries?
+ jr c, .setMenuVariables
+ ld a, 2 ; max menu item ID is 2 if the list has at least 2 entries
+.setMenuVariables
+ ld [wMaxMenuItem], a
+ ld a, 4
+ ld [wTopMenuItemY], a
+ ld a, 5
+ ld [wTopMenuItemX], a
+ ld a, [wMiscFlags]
+ ld b, a
+ ld a, A_BUTTON | B_BUTTON | SELECT | START
+ ld [wMenuWatchedKeys], a
+ xor a ; Zeroes a
+ pop bc
+ pop hl
+ jp DisplayListMenuIDLoop
+.beginSorting
+ xor a
+ ld [hSwapTemp], a ; 1 if something in the bag got sorted
+ ld de, 0
+ ld hl, ItemSortList
+ ld b, [hl] ; This is the first item to check for
+ ld hl, wListPointer
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ inc hl
+ ld c, 0 ; Relative to wBagItems, this is where we'd like to begin swapping
+.loopCurrItemInBag
+ ld a, [hl] ; Load the value of hl to a (which is an item number) and Increments to the quantity
+ cp -1 ; See if the item number is $ff, which is 'cancel'
+ jr z, .findNextItem ; If it is cancel, then move onto the next item
+ cp b
+ jr z, .hasItem ; If it's not b, then go to the next item in the bag
+ inc hl ; increments past the quantity to the next item to check
+ inc hl
+ jr .loopCurrItemInBag
+.findNextItem
+ ld d, 0
+ inc e
+ ld hl, ItemSortList
+ add hl, de
+ ld b, [hl]
+ ld hl, wListPointer
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ inc hl ; Resets hl to start at the beginning of the bag
+ ld a, b
+ cp -1 ; Check if we got through all of the items, to the last one
+ jp z, .finishedSwapping
+ jr .loopCurrItemInBag
+.hasItem ; c contains where to swap to relative to the start of wBagItems
+ ; hl contains where the item to swap is absolute.
+ ; b contains the item ID
+ push de
+ ld d, h
+ ld e, l
+ ld hl, wListPointer
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ inc hl
+ ld a, b
+ ld b, 0
+ add hl, bc ; hl now holds where we'd like to swap to
+ ld b, a
+ ld a, [de]
+ cp [hl]
+ jr z, .cont ; If they're the same item
+ ld a, 1
+ ld [hSwapTemp], a
+ ld a, [hl]
+ ld [hSwapItemID],a ; [hSwapItemID] = second item ID
+ inc hl
+ ld a,[hld]
+ ld [hSwapItemQuantity],a ; [hSwapItemQuantity] = second item quantity
+ ld a,[de]
+ ld [hli],a ; put first item ID in second item slot
+ inc de
+ ld a,[de]
+ ld [hl],a ; put first item quantity in second item slot
+ ld a,[hSwapItemQuantity]
+ ld [de],a ; put second item quantity in first item slot
+ dec de
+ ld a,[hSwapItemID]
+ ld [de],a ; put second item ID in first item slot
+.cont
+ inc c
+ inc c
+ ld h, d
+ ld l, e
+ pop de
+ jr .findNextItem
+
+SortItemsText::
+ text_far _SortItemsText
+ db "@"
+
+SortComplete::
+ text_far _SortComplete
+ db "@"
+
+NothingToSort::
+ text_far _NothingToSort
+EmptyString::
+ db "@"
+
+ItemSortList::
+ ; Used Key Items
+ db BICYCLE
+ db ITEMFINDER
+ db EXP_ALL
+ db TOWN_MAP
+ ; Rods
+ db OLD_ROD
+ db GOOD_ROD
+ db SUPER_ROD
+ ; Balls
+ db POKE_BALL
+ db GREAT_BALL
+ db ULTRA_BALL
+ db SAFARI_BALL
+ db MASTER_BALL
+ ; Common Items
+ db REPEL
+ db SUPER_REPEL
+ db MAX_REPEL
+ db ESCAPE_ROPE
+ db POKE_DOLL
+ ; Health
+ db POTION
+ db SUPER_POTION
+ db HYPER_POTION
+ db MAX_POTION
+ db FULL_RESTORE
+ db FRESH_WATER
+ db SODA_POP
+ db LEMONADE
+ ; Revival
+ db REVIVE
+ db MAX_REVIVE
+ ; Status
+ db ANTIDOTE
+ db BURN_HEAL
+ db ICE_HEAL
+ db AWAKENING
+ db PARLYZ_HEAL
+ db FULL_HEAL
+ db POKE_FLUTE
+ ; PP
+ db ETHER
+ db MAX_ETHER
+ db ELIXER
+ db MAX_ELIXER
+ ; Battle Raises
+ db X_ACCURACY
+ db X_ATTACK
+ db X_DEFEND
+ db X_SPEED
+ db X_SPECIAL
+ db GUARD_SPEC
+ db DIRE_HIT
+ ; Permanent Raises
+ db RARE_CANDY
+ db HP_UP
+ db PROTEIN
+ db IRON
+ db CARBOS
+ db CALCIUM
+ db PP_UP
+ ; Stones
+ db LEAF_STONE
+ db FIRE_STONE
+ db THUNDER_STONE
+ db WATER_STONE
+ db MOON_STONE
+ ; Money
+ db COIN_CASE
+ db COIN
+ db NUGGET
+ ; Fossils
+ db DOME_FOSSIL
+ db HELIX_FOSSIL
+ db OLD_AMBER
+ ; Maps and Items with No Use
+ db SAFARI_BAIT
+ db SAFARI_ROCK
+ db S_S_TICKET
+ ; Key Items With No Use
+ db SECRET_KEY
+ db BIKE_VOUCHER
+ db CARD_KEY
+ db GOLD_TEETH
+ db OAKS_PARCEL
+ db LIFT_KEY
+ db SILPH_SCOPE
+ ; TMs
+ db TM01
+ db TM01 + 1
+ db TM01 + 2
+ db TM01 + 3
+ db TM01 + 4
+ db TM01 + 5
+ db TM01 + 6
+ db TM01 + 7
+ db TM01 + 8
+ db TM01 + 9
+ db TM01 + 10
+ db TM01 + 11
+ db TM01 + 12
+ db TM01 + 13
+ db TM01 + 14
+ db TM01 + 15
+ db TM01 + 16
+ db TM01 + 17
+ db TM01 + 18
+ db TM01 + 19
+ db TM01 + 20
+ db TM01 + 21
+ db TM01 + 22
+ db TM01 + 23
+ db TM01 + 24
+ db TM01 + 25
+ db TM01 + 26
+ db TM01 + 27
+ db TM01 + 28
+ db TM01 + 29
+ db TM01 + 30
+ db TM01 + 31
+ db TM01 + 32
+ db TM01 + 33
+ db TM01 + 34
+ db TM01 + 35
+ db TM01 + 36
+ db TM01 + 37
+ db TM01 + 38
+ db TM01 + 39
+ db TM01 + 40
+ db TM01 + 41
+ db TM01 + 42
+ db TM01 + 43
+ db TM01 + 44
+ db TM01 + 45
+ db TM01 + 46
+ db TM01 + 47
+ db TM01 + 48
+ db TM01 + 49
+ ; HMs
+ db HM01
+ db HM01 + 1
+ db HM01 + 2
+ db HM01 + 3
+ db HM01 + 4
+ db -1
You may notice the following lines in the sorting code:
; This is the hackiest part. It will print the text "What do you want to do?",
; even when the ideal scenerio is to show no textbox in the overwold, and a mildly different
; message in the PC. but to clean up the overworld, LoadScreenTilesFromBuffer2 would need to be used,
; but that would not work for the PC, so then PC tracking is needed.
; To keep things simple, we will just always display a textbox after sorting items in the overworld.
This is due to there being four places where the item menu can be called, and the way to handle leaving the message differs between the locations. The locations are:
- From the Start Menu.
- From the PC.
- In the Battle Menu.
- In the Mart when selling an item.
It's less one-size-fits-all, but if you'd like each of those to appear the same way as before sorting started, you can make the following changes to the above code.
------------------------- constants/ram_constants.asm -------------------------
index aa7d829f..c7a4f6e9 100644
@@ -6,9 +6,9 @@
; wMiscFlags
const_def
const BIT_SEEN_BY_TRAINER ; 0
const BIT_BOULDER_DUST ; 1
- const BIT_TURNING ; 2
+ const BIT_TURNING ; 2 Also if we're in a Mart menu Item Sorting's purpose
const BIT_USING_GENERIC_PC ; 3
- const BIT_NO_SPRITE_UPDATES ; 4
+ const BIT_NO_SPRITE_UPDATES ; 4 Also if we're in the PC for Item Sorting's purpose
const BIT_NO_MENU_BUTTON_SOUND ; 5
const BIT_TRIED_PUSH_BOULDER ; 6
-------------------------- engine/events/pokemart.asm --------------------------
index b280c711..21f1e20a 100644
@@ -1,5 +1,7 @@
DisplayPokemartDialogue_::
+ ld hl, wMiscFlags
+ set BIT_TURNING, [hl]
ld a, [wListScrollOffset]
ld [wSavedListScrollOffset], a
call UpdateSprites
xor a
@@ -217,8 +219,10 @@ DisplayPokemartDialogue_::
ld hl, PokemartItemBagFullText
call PrintText
jr .returnToMainPokemartMenu
.done
+ ld hl, wMiscFlags
+ res BIT_TURNING, [hl]
ld hl, PokemartThankYouText
call PrintText
ld a, 1
ld [wUpdateSpritesEnabled], a
------------------------- engine/menus/players_pc.asm -------------------------
index 402275b5..ad9e5d28 100644
@@ -6,10 +6,11 @@ PlayerPC::
call SaveScreenTilesToBuffer1
xor a
ld [wBagSavedMenuItem], a
ld [wParentMenuItem], a
- ld a, [wMiscFlags]
- bit BIT_USING_GENERIC_PC, a
+ ld hl, wMiscFlags
+ bit BIT_USING_GENERIC_PC, [hl]
+ set BIT_NO_SPRITE_UPDATES, [hl]
jr nz, PlayerPCMenu
; accessing it directly
ld a, SFX_TURN_ON_PC
call PlaySound
@@ -62,10 +63,11 @@ PlayerPCMenu:
dec a
jp z, PlayerPCToss
ExitPlayerPC:
- ld a, [wMiscFlags]
- bit BIT_USING_GENERIC_PC, a
+ ld hl, [wMiscFlags]
+ bit BIT_USING_GENERIC_PC, [hl]
+ res BIT_NO_SPRITE_UPDATES, [hl]
jr nz, .next
; accessing it directly
ld a, SFX_TURN_OFF_PC
call PlaySound
------------------------- engine/menus/swap_items.asm -------------------------
index 6eaba3c4..180c1f6c 100644
@@ -181,16 +181,35 @@ SortItems::
ld hl, EmptyString
ld a, [wIsInBattle]
and a
jr nz, .done ; In battle
- ld hl, WhatDoYouWantText
- ; This is the hackiest part. It will print the text "What do you want to do?",
- ; even when the ideal scenerio is to show no textbox in the overwold, and a mildly different
- ; message in the PC. but to clean up the overworld, LoadScreenTilesFromBuffer2 would need to be used,
- ; but that would not work for the PC, so then PC tracking is needed.
- ; To keep things simple, we will just always display a textbox after sorting items in the overworld.
+ ld hl, PokemonSellingGreetingText
+ ld a, [wMiscFlags]
+ bit BIT_TURNING, a ; Is in Mart?
+ jr nz, .done
+ bit BIT_NO_SPRITE_UPDATES, a ; Is in PC?
+ jr z, .overworld
+ ld a, [wParentMenuItem]
+ and a
+ ld hl, WhatToWithdrawText
+ jr z, .done
+ ld hl, WhatToDepositText
+ dec a
+ jr z, .done
+ ld hl, WhatToTossText
+ dec a
+ jr z, .done
+ ld hl, EmptyString
+ jr .done
+.overworld
+ call LoadScreenTilesFromBuffer2 ; Reloads the overworld
+ call DisplayTextBoxID ; draw the menu text box
+ call UpdateSprites ; disable sprites behind the text box
+ jr .displayListMenuIDPostSwap
.done
call PrintText
+.displayListMenuIDPostSwap ; Very similar to the latter-half of displayListMenuID
+ ; but without the frame delay at the end.
ld a, ITEMLISTMENU
ld [wListMenuID], a
ld a, 1 ; max menu item ID is 1 if the list has less than 2 entries
ld [wMenuWatchMovingOutOfBounds], a
And that's it!
This has been tested on all menus that call DisplayListMenuID
with ITEMLISTMENU
. Tests of each are shown below.
If you run into any issues, please message me on Discord.