diff options
Diffstat (limited to 'engine/menus')
-rw-r--r-- | engine/menus/display_text_id_init.asm | 78 | ||||
-rw-r--r-- | engine/menus/draw_badges.asm | 120 | ||||
-rw-r--r-- | engine/menus/draw_start_menu.asm | 89 | ||||
-rwxr-xr-x | engine/menus/league_pc.asm | 120 | ||||
-rwxr-xr-x | engine/menus/main_menu.asm | 712 | ||||
-rwxr-xr-x | engine/menus/naming_screen.asm | 494 | ||||
-rwxr-xr-x | engine/menus/oaks_pc.asm | 28 | ||||
-rwxr-xr-x | engine/menus/party_menu.asm | 325 | ||||
-rwxr-xr-x | engine/menus/pc.asm | 141 | ||||
-rwxr-xr-x | engine/menus/players_pc.asm | 303 | ||||
-rwxr-xr-x | engine/menus/pokedex.asm | 665 | ||||
-rwxr-xr-x | engine/menus/save.asm | 708 | ||||
-rwxr-xr-x | engine/menus/start_sub_menus.asm | 808 | ||||
-rw-r--r-- | engine/menus/swap_items.asm | 149 | ||||
-rw-r--r-- | engine/menus/text_box.asm | 767 |
15 files changed, 5507 insertions, 0 deletions
diff --git a/engine/menus/display_text_id_init.asm b/engine/menus/display_text_id_init.asm new file mode 100644 index 00000000..5043ad22 --- /dev/null +++ b/engine/menus/display_text_id_init.asm @@ -0,0 +1,78 @@ +; function that performs initialization for DisplayTextID +DisplayTextIDInit:: + xor a + ld [wListMenuID], a + ld a, [wAutoTextBoxDrawingControl] + bit 0, a + jr nz, .skipDrawingTextBoxBorder + ld a, [hSpriteIndexOrTextID] ; text ID (or sprite ID) + and a + jr nz, .notStartMenu +; if text ID is 0 (i.e. the start menu) +; Note that the start menu text border is also drawn in the function directly +; below this, so this seems unnecessary. + CheckEvent EVENT_GOT_POKEDEX +; start menu with pokedex + coord hl, 10, 0 + ld b, $0e + ld c, $08 + jr nz, .drawTextBoxBorder +; start menu without pokedex + coord hl, 10, 0 + ld b, $0c + ld c, $08 + jr .drawTextBoxBorder +; if text ID is not 0 (i.e. not the start menu) then do a standard dialogue text box +.notStartMenu + coord hl, 0, 12 + ld b, $04 + ld c, $12 +.drawTextBoxBorder + call TextBoxBorder +.skipDrawingTextBoxBorder + ld hl, wFontLoaded + set 0, [hl] + ld hl, wFlags_0xcd60 + bit 4, [hl] + res 4, [hl] + jr nz, .skipMovingSprites + call UpdateSprites +.skipMovingSprites +; loop to copy C1X9 (direction the sprite is facing) to C2X9 for each sprite +; this is done because when you talk to an NPC, they turn to look your way +; the original direction they were facing must be restored after the dialogue is over + ld hl, wSpriteStateData1 + $19 + ld c, $0f + ld de, $0010 +.spriteFacingDirectionCopyLoop + ld a, [hl] + inc h + ld [hl], a + dec h + add hl, de + dec c + jr nz, .spriteFacingDirectionCopyLoop +; loop to force all the sprites in the middle of animation to stand still +; (so that they don't like they're frozen mid-step during the dialogue) + ld hl, wSpriteStateData1 + 2 + ld de, $0010 + ld c, e +.spriteStandStillLoop + ld a, [hl] + cp $ff ; is the sprite visible? + jr z, .nextSprite +; if it is visible + and $fc + ld [hl], a +.nextSprite + add hl, de + dec c + jr nz, .spriteStandStillLoop + ld b, $9c ; window background address + call CopyScreenTileBufferToVRAM ; transfer background in WRAM to VRAM + xor a + ld [hWY], a ; put the window on the screen + call LoadFontTilePatterns + ld a, $01 + ld [H_AUTOBGTRANSFERENABLED], a ; enable continuous WRAM to VRAM transfer each V-blank + ret diff --git a/engine/menus/draw_badges.asm b/engine/menus/draw_badges.asm new file mode 100644 index 00000000..1888e32f --- /dev/null +++ b/engine/menus/draw_badges.asm @@ -0,0 +1,120 @@ +DrawBadges: +; Draw 4x2 gym leader faces, with the faces replaced by +; badges if they are owned. Used in the player status screen. + +; In Japanese versions, names are displayed above faces. +; Instead of removing relevant code, the name graphics were erased. + +; Tile ids for face/badge graphics. + ld de, wBadgeOrFaceTiles + ld hl, .FaceBadgeTiles + ld bc, 8 + call CopyData + +; Booleans for each badge. + ld hl, wTempObtainedBadgesBooleans + ld bc, 8 + xor a + call FillMemory + +; Alter these based on owned badges. + ld de, wTempObtainedBadgesBooleans + ld hl, wBadgeOrFaceTiles + ld a, [wObtainedBadges] + ld b, a + ld c, 8 +.CheckBadge + srl b + jr nc, .NextBadge + ld a, [hl] + add 4 ; Badge graphics are after each face + ld [hl], a + ld a, 1 + ld [de], a +.NextBadge + inc hl + inc de + dec c + jr nz, .CheckBadge + +; Draw two rows of badges. + ld hl, wBadgeNumberTile + ld a, $d8 ; [1] + ld [hli], a + ld [hl], $60 ; First name + + coord hl, 2, 11 + ld de, wTempObtainedBadgesBooleans + call .DrawBadgeRow + + coord hl, 2, 14 + ld de, wTempObtainedBadgesBooleans + 4 +; call .DrawBadgeRow +; ret + +.DrawBadgeRow +; Draw 4 badges. + + ld c, 4 +.DrawBadge + push de + push hl + +; Badge no. + ld a, [wBadgeNumberTile] + ld [hli], a + inc a + ld [wBadgeNumberTile], a + +; Names aren't printed if the badge is owned. + ld a, [de] + and a + ld a, [wBadgeNameTile] + jr nz, .SkipName + call .PlaceTiles + jr .PlaceBadge + +.SkipName + inc a + inc a + inc hl + +.PlaceBadge + ld [wBadgeNameTile], a + ld de, SCREEN_WIDTH - 1 + add hl, de + ld a, [wBadgeOrFaceTiles] + call .PlaceTiles + add hl, de + call .PlaceTiles + +; Shift badge array back one byte. + push bc + ld hl, wBadgeOrFaceTiles + 1 + ld de, wBadgeOrFaceTiles + ld bc, 8 + call CopyData + pop bc + + pop hl + ld de, 4 + add hl, de + + pop de + inc de + dec c + jr nz, .DrawBadge + ret + +.PlaceTiles + ld [hli], a + inc a + ld [hl], a + inc a + ret + +.FaceBadgeTiles + db $20, $28, $30, $38, $40, $48, $50, $58 + +GymLeaderFaceAndBadgeTileGraphics: + INCBIN "gfx/trainer_card/badges.2bpp" diff --git a/engine/menus/draw_start_menu.asm b/engine/menus/draw_start_menu.asm new file mode 100644 index 00000000..21e444e9 --- /dev/null +++ b/engine/menus/draw_start_menu.asm @@ -0,0 +1,89 @@ +; function that displays the start menu +DrawStartMenu:: + CheckEvent EVENT_GOT_POKEDEX +; menu with pokedex + coord hl, 10, 0 + ld b, $0e + ld c, $08 + jr nz, .drawTextBoxBorder +; shorter menu if the player doesn't have the pokedex + coord hl, 10, 0 + ld b, $0c + ld c, $08 +.drawTextBoxBorder + call TextBoxBorder + ld a, D_DOWN | D_UP | START | B_BUTTON | A_BUTTON + ld [wMenuWatchedKeys], a + ld a, $02 + ld [wTopMenuItemY], a ; Y position of first menu choice + ld a, $0b + ld [wTopMenuItemX], a ; X position of first menu choice + ld a, [wBattleAndStartSavedMenuItem] ; remembered menu selection from last time + ld [wCurrentMenuItem], a + ld [wLastMenuItem], a + xor a + ld [wMenuWatchMovingOutOfBounds], a + ld hl, wd730 + set 6, [hl] ; no pauses between printing each letter + coord hl, 12, 2 + CheckEvent EVENT_GOT_POKEDEX +; case for not having pokedex + ld a, $06 + jr z, .storeMenuItemCount +; case for having pokedex + ld de, StartMenuPokedexText + call PrintStartMenuItem + ld a, $07 +.storeMenuItemCount + ld [wMaxMenuItem], a ; number of menu items + ld de, StartMenuPokemonText + call PrintStartMenuItem + ld de, StartMenuItemText + call PrintStartMenuItem + ld de, wPlayerName ; player's name + call PrintStartMenuItem + ld a, [wd72e] + bit 6, a ; is the player using the link feature? +; case for not using link feature + ld de, StartMenuSaveText + jr z, .printSaveOrResetText +; case for using link feature + ld de, StartMenuResetText +.printSaveOrResetText + call PrintStartMenuItem + ld de, StartMenuOptionText + call PrintStartMenuItem + ld de, StartMenuExitText + call PlaceString + ld hl, wd730 + res 6, [hl] ; turn pauses between printing letters back on + ret + +StartMenuPokedexText: + db "POKéDEX@" + +StartMenuPokemonText: + db "POKéMON@" + +StartMenuItemText: + db "ITEM@" + +StartMenuSaveText: + db "SAVE@" + +StartMenuResetText: + db "RESET@" + +StartMenuExitText: + db "EXIT@" + +StartMenuOptionText: + db "OPTION@" + +PrintStartMenuItem: + push hl + call PlaceString + pop hl + ld de, SCREEN_WIDTH * 2 + add hl, de + ret diff --git a/engine/menus/league_pc.asm b/engine/menus/league_pc.asm new file mode 100755 index 00000000..170c0ef3 --- /dev/null +++ b/engine/menus/league_pc.asm @@ -0,0 +1,120 @@ +PKMNLeaguePC: + ld hl, AccessedHoFPCText + call PrintText + ld hl, wd730 + set 6, [hl] + push hl + ld a, [wUpdateSpritesEnabled] + push af + ld a, [hTilesetType] + push af + xor a + ld [hTilesetType], a + ld [wSpriteFlipped], a + ld [wUpdateSpritesEnabled], a + ld [wHoFTeamIndex2], a + ld [wHoFTeamNo], a + ld a, [wNumHoFTeams] + ld b, a + cp HOF_TEAM_CAPACITY + 1 + jr c, .loop +; If the total number of hall of fame teams is greater than the storage +; capacity, then calculate the number of the first team that is still recorded. + ld b, HOF_TEAM_CAPACITY + sub b + ld [wHoFTeamNo], a +.loop + ld hl, wHoFTeamNo + inc [hl] + push bc + ld a, [wHoFTeamIndex2] + ld [wHoFTeamIndex], a + callba LoadHallOfFameTeams + call LeaguePCShowTeam + pop bc + jr c, .doneShowingTeams + ld hl, wHoFTeamIndex2 + inc [hl] + ld a, [hl] + cp b + jr nz, .loop +.doneShowingTeams + pop af + ld [hTilesetType], a + pop af + ld [wUpdateSpritesEnabled], a + pop hl + res 6, [hl] + call GBPalWhiteOutWithDelay3 + call ClearScreen + call RunDefaultPaletteCommand + jp GBPalNormal + +LeaguePCShowTeam: + ld c, PARTY_LENGTH +.loop + push bc + call LeaguePCShowMon + call WaitForTextScrollButtonPress + ld a, [hJoyHeld] + bit 1, a + jr nz, .exit + ld hl, wHallOfFame + HOF_MON + ld de, wHallOfFame + ld bc, HOF_TEAM - HOF_MON + call CopyData + pop bc + ld a, [wHallOfFame + 0] + cp $ff + jr z, .done + dec c + jr nz, .loop +.done + and a + ret +.exit + pop bc + scf + ret + +LeaguePCShowMon: + call GBPalWhiteOutWithDelay3 + call ClearScreen + ld hl, wHallOfFame + ld a, [hli] + ld [wHoFMonSpecies], a + ld [wcf91], a + ld [wd0b5], a + ld [wBattleMonSpecies2], a + ld [wWholeScreenPaletteMonSpecies], a + ld a, [hli] + ld [wHoFMonLevel], a + ld de, wcd6d + ld bc, NAME_LENGTH + call CopyData + ld b, SET_PAL_POKEMON_WHOLE_SCREEN + ld c, 0 + call RunPaletteCommand + coord hl, 12, 5 + call GetMonHeader + call LoadFrontSpriteByMonIndex + call GBPalNormal + coord hl, 0, 13 + ld b, 2 + ld c, $12 + call TextBoxBorder + coord hl, 1, 15 + ld de, HallOfFameNoText + call PlaceString + coord hl, 16, 15 + ld de, wHoFTeamNo + lb bc, 1, 3 + call PrintNumber + jpba HoFDisplayMonInfo + +HallOfFameNoText: + db "HALL OF FAME No @" + +AccessedHoFPCText: + TX_FAR _AccessedHoFPCText + db "@" diff --git a/engine/menus/main_menu.asm b/engine/menus/main_menu.asm new file mode 100755 index 00000000..8eda6744 --- /dev/null +++ b/engine/menus/main_menu.asm @@ -0,0 +1,712 @@ +MainMenu: +; Check save file + call InitOptions + xor a + ld [wOptionsInitialized], a + inc a + ld [wSaveFileStatus], a + call CheckForPlayerNameInSRAM + jr nc, .mainMenuLoop + + predef LoadSAV + +.mainMenuLoop + ld c, 20 + call DelayFrames + xor a ; LINK_STATE_NONE + ld [wLinkState], a + ld hl, wPartyAndBillsPCSavedMenuItem + ld [hli], a + ld [hli], a + ld [hli], a + ld [hl], a + ld [wDefaultMap], a + ld hl, wd72e + res 6, [hl] + call ClearScreen + call RunDefaultPaletteCommand + call LoadTextBoxTilePatterns + call LoadFontTilePatterns + ld hl, wd730 + set 6, [hl] + ld a, [wSaveFileStatus] + cp 1 + jr z, .noSaveFile +; there's a save file + coord hl, 0, 0 + ld b, 6 + ld c, 13 + call TextBoxBorder + coord hl, 2, 2 + ld de, ContinueText + call PlaceString + jr .next2 +.noSaveFile + coord hl, 0, 0 + ld b, 4 + ld c, 13 + call TextBoxBorder + coord hl, 2, 2 + ld de, NewGameText + call PlaceString +.next2 + ld hl, wd730 + res 6, [hl] + call UpdateSprites + xor a + ld [wCurrentMenuItem], a + ld [wLastMenuItem], a + ld [wMenuJoypadPollCount], a + inc a + ld [wTopMenuItemX], a + inc a + ld [wTopMenuItemY], a + ld a, A_BUTTON | B_BUTTON | START + ld [wMenuWatchedKeys], a + ld a, [wSaveFileStatus] + ld [wMaxMenuItem], a + call HandleMenuInput + bit 1, a ; pressed B? + jp nz, DisplayTitleScreen ; if so, go back to the title screen + ld c, 20 + call DelayFrames + ld a, [wCurrentMenuItem] + ld b, a + ld a, [wSaveFileStatus] + cp 2 + jp z, .skipInc +; If there's no save file, increment the current menu item so that the numbers +; are the same whether or not there's a save file. + inc b +.skipInc + ld a, b + and a + jr z, .choseContinue + cp 1 + jp z, StartNewGame + call DisplayOptionMenu + ld a, 1 + ld [wOptionsInitialized], a + jp .mainMenuLoop +.choseContinue + call DisplayContinueGameInfo + ld hl, wCurrentMapScriptFlags + set 5, [hl] +.inputLoop + xor a + ld [hJoyPressed], a + ld [hJoyReleased], a + ld [hJoyHeld], a + call Joypad + ld a, [hJoyHeld] + bit 0, a + jr nz, .pressedA + bit 1, a + jp nz, .mainMenuLoop ; pressed B + jr .inputLoop +.pressedA + call GBPalWhiteOutWithDelay3 + call ClearScreen + ld a, PLAYER_DIR_DOWN + ld [wPlayerDirection], a + ld c, 10 + call DelayFrames + ld a, [wNumHoFTeams] + and a + jp z, SpecialEnterMap + ld a, [wCurMap] ; map ID + cp HALL_OF_FAME + jp nz, SpecialEnterMap + xor a + ld [wDestinationMap], a + ld hl, wd732 + set 2, [hl] ; fly warp or dungeon warp + call SpecialWarpIn + jp SpecialEnterMap + +InitOptions: + ld a, 1 ; no delay + ld [wLetterPrintingDelayFlags], a + ld a, 3 ; medium speed + ld [wOptions], a + ret + +LinkMenu: + xor a + ld [wLetterPrintingDelayFlags], a + ld hl, wd72e + set 6, [hl] + ld hl, TextTerminator_6b20 + call PrintText + call SaveScreenTilesToBuffer1 + ld hl, WhereWouldYouLikeText + call PrintText + coord hl, 5, 5 + ld b, $6 + ld c, $d + call TextBoxBorder + call UpdateSprites + coord hl, 7, 7 + ld de, CableClubOptionsText + call PlaceString + xor a + ld [wUnusedCD37], a + ld [wd72d], a + ld hl, wTopMenuItemY + ld a, $7 + ld [hli], a + ld a, $6 + ld [hli], a + xor a + ld [hli], a + inc hl + ld a, $2 + ld [hli], a + inc a + ; ld a, A_BUTTON | B_BUTTON + ld [hli], a ; wMenuWatchedKeys + xor a + ld [hl], a +.waitForInputLoop + call HandleMenuInput + and A_BUTTON | B_BUTTON + add a + add a + ld b, a + ld a, [wCurrentMenuItem] + add b + add $d0 + ld [wLinkMenuSelectionSendBuffer], a + ld [wLinkMenuSelectionSendBuffer + 1], a +.exchangeMenuSelectionLoop + call Serial_ExchangeLinkMenuSelection + ld a, [wLinkMenuSelectionReceiveBuffer] + ld b, a + and $f0 + cp $d0 + jr z, .asm_5c7d + ld a, [wLinkMenuSelectionReceiveBuffer + 1] + ld b, a + and $f0 + cp $d0 + jr nz, .exchangeMenuSelectionLoop +.asm_5c7d + ld a, b + and $c ; did the enemy press A or B? + jr nz, .enemyPressedAOrB +; the enemy didn't press A or B + ld a, [wLinkMenuSelectionSendBuffer] + and $c ; did the player press A or B? + jr z, .waitForInputLoop ; if neither the player nor the enemy pressed A or B, try again + jr .doneChoosingMenuSelection ; if the player pressed A or B but the enemy didn't, use the player's selection +.enemyPressedAOrB + ld a, [wLinkMenuSelectionSendBuffer] + and $c ; did the player press A or B? + jr z, .useEnemyMenuSelection ; if the enemy pressed A or B but the player didn't, use the enemy's selection +; the enemy and the player both pressed A or B +; The gameboy that is clocking the connection wins. + ld a, [hSerialConnectionStatus] + cp USING_INTERNAL_CLOCK + jr z, .doneChoosingMenuSelection +.useEnemyMenuSelection + ld a, b + ld [wLinkMenuSelectionSendBuffer], a + and $3 + ld [wCurrentMenuItem], a +.doneChoosingMenuSelection + ld a, [hSerialConnectionStatus] + cp USING_INTERNAL_CLOCK + jr nz, .skipStartingTransfer + call DelayFrame + call DelayFrame + ld a, START_TRANSFER_INTERNAL_CLOCK + ld [rSC], a +.skipStartingTransfer + ld b, $7f + ld c, $7f + ld d, $ec + ld a, [wLinkMenuSelectionSendBuffer] + and (B_BUTTON << 2) ; was B button pressed? + jr nz, .updateCursorPosition +; A button was pressed + ld a, [wCurrentMenuItem] + cp $2 + jr z, .updateCursorPosition + ld c, d + ld d, b + dec a + jr z, .updateCursorPosition + ld b, c + ld c, d +.updateCursorPosition + ld a, b + Coorda 6, 7 + ld a, c + Coorda 6, 9 + ld a, d + Coorda 6, 11 + ld c, 40 + call DelayFrames + call LoadScreenTilesFromBuffer1 + ld a, [wLinkMenuSelectionSendBuffer] + and (B_BUTTON << 2) ; was B button pressed? + jr nz, .choseCancel ; cancel if B pressed + ld a, [wCurrentMenuItem] + cp $2 + jr z, .choseCancel + xor a + ld [wWalkBikeSurfState], a ; start walking + ld a, [wCurrentMenuItem] + and a + ld a, COLOSSEUM + jr nz, .next + ld a, TRADE_CENTER +.next + ld [wd72d], a + ld hl, PleaseWaitText + call PrintText + ld c, 50 + call DelayFrames + ld hl, wd732 + res 1, [hl] + ld a, [wDefaultMap] + ld [wDestinationMap], a + call SpecialWarpIn + ld c, 20 + call DelayFrames + xor a + ld [wMenuJoypadPollCount], a + ld [wSerialExchangeNybbleSendData], a + inc a ; LINK_STATE_IN_CABLE_CLUB + ld [wLinkState], a + ld [wEnteringCableClub], a + jr SpecialEnterMap +.choseCancel + xor a + ld [wMenuJoypadPollCount], a + call Delay3 + call CloseLinkConnection + ld hl, LinkCanceledText + call PrintText + ld hl, wd72e + res 6, [hl] + ret + +WhereWouldYouLikeText: + TX_FAR _WhereWouldYouLikeText + db "@" + +PleaseWaitText: + TX_FAR _PleaseWaitText + db "@" + +LinkCanceledText: + TX_FAR _LinkCanceledText + db "@" + +StartNewGame: + ld hl, wd732 + res 1, [hl] + call OakSpeech + ld c, 20 + call DelayFrames + +; enter map after using a special warp or loading the game from the main menu +SpecialEnterMap:: + xor a + ld [hJoyPressed], a + ld [hJoyHeld], a + ld [hJoy5], a + ld [wd72d], a + ld hl, wd732 + set 0, [hl] ; count play time + call ResetPlayerSpriteData + ld c, 20 + call DelayFrames + ld a, [wEnteringCableClub] + and a + ret nz + jp EnterMap + +ContinueText: + db "CONTINUE", $4e + +NewGameText: + db "NEW GAME" + next "OPTION@" + +CableClubOptionsText: + db "TRADE CENTER" + next "COLOSSEUM" + next "CANCEL@" + +DisplayContinueGameInfo: + xor a + ld [H_AUTOBGTRANSFERENABLED], a + coord hl, 4, 7 + ld b, 8 + ld c, 14 + call TextBoxBorder + coord hl, 5, 9 + ld de, SaveScreenInfoText + call PlaceString + coord hl, 12, 9 + ld de, wPlayerName + call PlaceString + coord hl, 17, 11 + call PrintNumBadges + coord hl, 16, 13 + call PrintNumOwnedMons + coord hl, 13, 15 + call PrintPlayTime + ld a, 1 + ld [H_AUTOBGTRANSFERENABLED], a + ld c, 30 + jp DelayFrames + +PrintSaveScreenText: + xor a + ld [H_AUTOBGTRANSFERENABLED], a + coord hl, 4, 0 + ld b, $8 + ld c, $e + call TextBoxBorder + call LoadTextBoxTilePatterns + call UpdateSprites + coord hl, 5, 2 + ld de, SaveScreenInfoText + call PlaceString + coord hl, 12, 2 + ld de, wPlayerName + call PlaceString + coord hl, 17, 4 + call PrintNumBadges + coord hl, 16, 6 + call PrintNumOwnedMons + coord hl, 13, 8 + call PrintPlayTime + ld a, $1 + ld [H_AUTOBGTRANSFERENABLED], a + ld c, 30 + jp DelayFrames + +PrintNumBadges: + push hl + ld hl, wObtainedBadges + ld b, $1 + call CountSetBits + pop hl + ld de, wNumSetBits + lb bc, 1, 2 + jp PrintNumber + +PrintNumOwnedMons: + push hl + ld hl, wPokedexOwned + ld b, wPokedexOwnedEnd - wPokedexOwned + call CountSetBits + pop hl + ld de, wNumSetBits + lb bc, 1, 3 + jp PrintNumber + +PrintPlayTime: + ld de, wPlayTimeHours + lb bc, 1, 3 + call PrintNumber + ld [hl], $6d + inc hl + ld de, wPlayTimeMinutes + lb bc, LEADING_ZEROES | 1, 2 + jp PrintNumber + +SaveScreenInfoText: + db "PLAYER" + next "BADGES " + next "#DEX " + next "TIME@" + +DisplayOptionMenu: + coord hl, 0, 0 + ld b, 3 + ld c, 18 + call TextBoxBorder + coord hl, 0, 5 + ld b, 3 + ld c, 18 + call TextBoxBorder + coord hl, 0, 10 + ld b, 3 + ld c, 18 + call TextBoxBorder + coord hl, 1, 1 + ld de, TextSpeedOptionText + call PlaceString + coord hl, 1, 6 + ld de, BattleAnimationOptionText + call PlaceString + coord hl, 1, 11 + ld de, BattleStyleOptionText + call PlaceString + coord hl, 2, 16 + ld de, OptionMenuCancelText + call PlaceString + xor a + ld [wCurrentMenuItem], a + ld [wLastMenuItem], a + inc a + ld [wLetterPrintingDelayFlags], a + ld [wOptionsCancelCursorX], a + ld a, 3 ; text speed cursor Y coordinate + ld [wTopMenuItemY], a + call SetCursorPositionsFromOptions + ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate + ld [wTopMenuItemX], a + ld a, $01 + ld [H_AUTOBGTRANSFERENABLED], a ; enable auto background transfer + call Delay3 +.loop + call PlaceMenuCursor + call SetOptionsFromCursorPositions +.getJoypadStateLoop + call JoypadLowSensitivity + ld a, [hJoy5] + ld b, a + and A_BUTTON | B_BUTTON | START | D_RIGHT | D_LEFT | D_UP | D_DOWN ; any key besides select pressed? + jr z, .getJoypadStateLoop + bit 1, b ; B button pressed? + jr nz, .exitMenu + bit 3, b ; Start button pressed? + jr nz, .exitMenu + bit 0, b ; A button pressed? + jr z, .checkDirectionKeys + ld a, [wTopMenuItemY] + cp 16 ; is the cursor on Cancel? + jr nz, .loop +.exitMenu + ld a, SFX_PRESS_AB + call PlaySound + ret +.eraseOldMenuCursor + ld [wTopMenuItemX], a + call EraseMenuCursor + jp .loop +.checkDirectionKeys + ld a, [wTopMenuItemY] + bit 7, b ; Down pressed? + jr nz, .downPressed + bit 6, b ; Up pressed? + jr nz, .upPressed + cp 8 ; cursor in Battle Animation section? + jr z, .cursorInBattleAnimation + cp 13 ; cursor in Battle Style section? + jr z, .cursorInBattleStyle + cp 16 ; cursor on Cancel? + jr z, .loop +.cursorInTextSpeed + bit 5, b ; Left pressed? + jp nz, .pressedLeftInTextSpeed + jp .pressedRightInTextSpeed +.downPressed + cp 16 + ld b, -13 + ld hl, wOptionsTextSpeedCursorX + jr z, .updateMenuVariables + ld b, 5 + cp 3 + inc hl + jr z, .updateMenuVariables + cp 8 + inc hl + jr z, .updateMenuVariables + ld b, 3 + inc hl + jr .updateMenuVariables +.upPressed + cp 8 + ld b, -5 + ld hl, wOptionsTextSpeedCursorX + jr z, .updateMenuVariables + cp 13 + inc hl + jr z, .updateMenuVariables + cp 16 + ld b, -3 + inc hl + jr z, .updateMenuVariables + ld b, 13 + inc hl +.updateMenuVariables + add b + ld [wTopMenuItemY], a + ld a, [hl] + ld [wTopMenuItemX], a + call PlaceUnfilledArrowMenuCursor + jp .loop +.cursorInBattleAnimation + ld a, [wOptionsBattleAnimCursorX] ; battle animation cursor X coordinate + xor $0b ; toggle between 1 and 10 + ld [wOptionsBattleAnimCursorX], a + jp .eraseOldMenuCursor +.cursorInBattleStyle + ld a, [wOptionsBattleStyleCursorX] ; battle style cursor X coordinate + xor $0b ; toggle between 1 and 10 + ld [wOptionsBattleStyleCursorX], a + jp .eraseOldMenuCursor +.pressedLeftInTextSpeed + ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate + cp 1 + jr z, .updateTextSpeedXCoord + cp 7 + jr nz, .fromSlowToMedium + sub 6 + jr .updateTextSpeedXCoord +.fromSlowToMedium + sub 7 + jr .updateTextSpeedXCoord +.pressedRightInTextSpeed + ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate + cp 14 + jr z, .updateTextSpeedXCoord + cp 7 + jr nz, .fromFastToMedium + add 7 + jr .updateTextSpeedXCoord +.fromFastToMedium + add 6 +.updateTextSpeedXCoord + ld [wOptionsTextSpeedCursorX], a ; text speed cursor X coordinate + jp .eraseOldMenuCursor + +TextSpeedOptionText: + db "TEXT SPEED" + next " FAST MEDIUM SLOW@" + +BattleAnimationOptionText: + db "BATTLE ANIMATION" + next " ON OFF@" + +BattleStyleOptionText: + db "BATTLE STYLE" + next " SHIFT SET@" + +OptionMenuCancelText: + db "CANCEL@" + +; sets the options variable according to the current placement of the menu cursors in the options menu +SetOptionsFromCursorPositions: + ld hl, TextSpeedOptionData + ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate + ld c, a +.loop + ld a, [hli] + cp c + jr z, .textSpeedMatchFound + inc hl + jr .loop +.textSpeedMatchFound + ld a, [hl] + ld d, a + ld a, [wOptionsBattleAnimCursorX] ; battle animation cursor X coordinate + dec a + jr z, .battleAnimationOn +.battleAnimationOff + set 7, d + jr .checkBattleStyle +.battleAnimationOn + res 7, d +.checkBattleStyle + ld a, [wOptionsBattleStyleCursorX] ; battle style cursor X coordinate + dec a + jr z, .battleStyleShift +.battleStyleSet + set 6, d + jr .storeOptions +.battleStyleShift + res 6, d +.storeOptions + ld a, d + ld [wOptions], a + ret + +; reads the options variable and places menu cursors in the correct positions within the options menu +SetCursorPositionsFromOptions: + ld hl, TextSpeedOptionData + 1 + ld a, [wOptions] + ld c, a + and $3f + push bc + ld de, 2 + call IsInArray + pop bc + dec hl + ld a, [hl] + ld [wOptionsTextSpeedCursorX], a ; text speed cursor X coordinate + coord hl, 0, 3 + call .placeUnfilledRightArrow + sla c + ld a, 1 ; On + jr nc, .storeBattleAnimationCursorX + ld a, 10 ; Off +.storeBattleAnimationCursorX + ld [wOptionsBattleAnimCursorX], a ; battle animation cursor X coordinate + coord hl, 0, 8 + call .placeUnfilledRightArrow + sla c + ld a, 1 + jr nc, .storeBattleStyleCursorX + ld a, 10 +.storeBattleStyleCursorX + ld [wOptionsBattleStyleCursorX], a ; battle style cursor X coordinate + coord hl, 0, 13 + call .placeUnfilledRightArrow +; cursor in front of Cancel + coord hl, 0, 16 + ld a, 1 +.placeUnfilledRightArrow + ld e, a + ld d, 0 + add hl, de + ld [hl], $ec ; unfilled right arrow menu cursor + ret + +; table that indicates how the 3 text speed options affect frame delays +; Format: +; 00: X coordinate of menu cursor +; 01: delay after printing a letter (in frames) +TextSpeedOptionData: + db 14,5 ; Slow + db 7,3 ; Medium + db 1,1 ; Fast + db 7 ; default X coordinate (Medium) + db $ff ; terminator + +CheckForPlayerNameInSRAM: +; Check if the player name data in SRAM has a string terminator character +; (indicating that a name may have been saved there) and return whether it does +; in carry. + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld b, NAME_LENGTH + ld hl, sPlayerName +.loop + ld a, [hli] + cp "@" + jr z, .found + dec b + jr nz, .loop +; not found + xor a + ld [MBC1SRamEnable], a + ld [MBC1SRamBankingMode], a + and a + ret +.found + xor a + ld [MBC1SRamEnable], a + ld [MBC1SRamBankingMode], a + scf + ret diff --git a/engine/menus/naming_screen.asm b/engine/menus/naming_screen.asm new file mode 100755 index 00000000..2b86d6f4 --- /dev/null +++ b/engine/menus/naming_screen.asm @@ -0,0 +1,494 @@ +AskName: + call SaveScreenTilesToBuffer1 + call GetPredefRegisters + push hl + ld a, [wIsInBattle] + dec a + coord hl, 0, 0 + ld b, 4 + ld c, 11 + call z, ClearScreenArea ; only if in wild battle + ld a, [wcf91] + ld [wd11e], a + call GetMonName + ld hl, DoYouWantToNicknameText + call PrintText + coord hl, 14, 7 + lb bc, 8, 15 + ld a, TWO_OPTION_MENU + ld [wTextBoxID], a + call DisplayTextBoxID + pop hl + ld a, [wCurrentMenuItem] + and a + jr nz, .declinedNickname + ld a, [wUpdateSpritesEnabled] + push af + xor a + ld [wUpdateSpritesEnabled], a + push hl + ld a, NAME_MON_SCREEN + ld [wNamingScreenType], a + call DisplayNamingScreen + ld a, [wIsInBattle] + and a + jr nz, .inBattle + call ReloadMapSpriteTilePatterns +.inBattle + call LoadScreenTilesFromBuffer1 + pop hl + pop af + ld [wUpdateSpritesEnabled], a + ld a, [wcf4b] + cp "@" + ret nz +.declinedNickname + ld d, h + ld e, l + ld hl, wcd6d + ld bc, NAME_LENGTH + jp CopyData + +DoYouWantToNicknameText: + TX_FAR _DoYouWantToNicknameText + db "@" + +DisplayNameRaterScreen:: + ld hl, wBuffer + xor a + ld [wUpdateSpritesEnabled], a + ld a, NAME_MON_SCREEN + ld [wNamingScreenType], a + call DisplayNamingScreen + call GBPalWhiteOutWithDelay3 + call RestoreScreenTilesAndReloadTilePatterns + call LoadGBPal + ld a, [wcf4b] + cp "@" + jr z, .playerCancelled + ld hl, wPartyMonNicks + ld bc, NAME_LENGTH + ld a, [wWhichPokemon] + call AddNTimes + ld e, l + ld d, h + ld hl, wBuffer + ld bc, NAME_LENGTH + call CopyData + and a + ret +.playerCancelled + scf + ret + +DisplayNamingScreen: + push hl + ld hl, wd730 + set 6, [hl] + call GBPalWhiteOutWithDelay3 + call ClearScreen + call UpdateSprites + ld b, SET_PAL_GENERIC + call RunPaletteCommand + call LoadHpBarAndStatusTilePatterns + call LoadEDTile + callba LoadMonPartySpriteGfx + coord hl, 0, 4 + ld b, 9 + ld c, 18 + call TextBoxBorder + call PrintNamingText + ld a, 3 + ld [wTopMenuItemY], a + ld a, 1 + ld [wTopMenuItemX], a + ld [wLastMenuItem], a + ld [wCurrentMenuItem], a + ld a, $ff + ld [wMenuWatchedKeys], a + ld a, 7 + ld [wMaxMenuItem], a + ld a, "@" + ld [wcf4b], a + xor a + ld hl, wNamingScreenSubmitName + ld [hli], a + ld [hli], a + ld [wAnimCounter], a +.selectReturnPoint + call PrintAlphabet + call GBPalNormal +.ABStartReturnPoint + ld a, [wNamingScreenSubmitName] + and a + jr nz, .submitNickname + call PrintNicknameAndUnderscores +.dPadReturnPoint + call PlaceMenuCursor +.inputLoop + ld a, [wCurrentMenuItem] + push af + callba AnimatePartyMon_ForceSpeed1 + pop af + ld [wCurrentMenuItem], a + call JoypadLowSensitivity + ld a, [hJoyPressed] + and a + jr z, .inputLoop + ld hl, .namingScreenButtonFunctions +.checkForPressedButton + sla a + jr c, .foundPressedButton + inc hl + inc hl + inc hl + inc hl + jr .checkForPressedButton +.foundPressedButton + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + ld a, [hli] + ld h, [hl] + ld l, a + push de + jp hl + +.submitNickname + pop de + ld hl, wcf4b + ld bc, NAME_LENGTH + call CopyData + call GBPalWhiteOutWithDelay3 + call ClearScreen + call ClearSprites + call RunDefaultPaletteCommand + call GBPalNormal + xor a + ld [wAnimCounter], a + ld hl, wd730 + res 6, [hl] + ld a, [wIsInBattle] + and a + jp z, LoadTextBoxTilePatterns + jpab LoadHudTilePatterns + +.namingScreenButtonFunctions + dw .dPadReturnPoint + dw .pressedDown + dw .dPadReturnPoint + dw .pressedUp + dw .dPadReturnPoint + dw .pressedLeft + dw .dPadReturnPoint + dw .pressedRight + dw .ABStartReturnPoint + dw .pressedStart + dw .selectReturnPoint + dw .pressedSelect + dw .ABStartReturnPoint + dw .pressedB + dw .ABStartReturnPoint + dw .pressedA + +.pressedA_changedCase + pop de + ld de, .selectReturnPoint + push de +.pressedSelect + ld a, [wAlphabetCase] + xor $1 + ld [wAlphabetCase], a + ret + +.pressedStart + ld a, 1 + ld [wNamingScreenSubmitName], a + ret + +.pressedA + ld a, [wCurrentMenuItem] + cp $5 ; "ED" row + jr nz, .didNotPressED + ld a, [wTopMenuItemX] + cp $11 ; "ED" column + jr z, .pressedStart +.didNotPressED + ld a, [wCurrentMenuItem] + cp $6 ; case switch row + jr nz, .didNotPressCaseSwtich + ld a, [wTopMenuItemX] + cp $1 ; case switch column + jr z, .pressedA_changedCase +.didNotPressCaseSwtich + ld hl, wMenuCursorLocation + ld a, [hli] + ld h, [hl] + ld l, a + inc hl + ld a, [hl] + ld [wNamingScreenLetter], a + call CalcStringLength + ld a, [wNamingScreenLetter] + cp $e5 + ld de, Dakutens + jr z, .dakutensAndHandakutens + cp $e4 + ld de, Handakutens + jr z, .dakutensAndHandakutens + ld a, [wNamingScreenType] + cp NAME_MON_SCREEN + jr nc, .checkMonNameLength + ld a, [wNamingScreenNameLength] + cp $7 ; max length of player/rival names + jr .checkNameLength +.checkMonNameLength + ld a, [wNamingScreenNameLength] + cp $a ; max length of pokemon nicknames +.checkNameLength + jr c, .addLetter + ret + +.dakutensAndHandakutens + push hl + call DakutensAndHandakutens + pop hl + ret nc + dec hl +.addLetter + ld a, [wNamingScreenLetter] + ld [hli], a + ld [hl], "@" + ld a, SFX_PRESS_AB + call PlaySound + ret +.pressedB + ld a, [wNamingScreenNameLength] + and a + ret z + call CalcStringLength + dec hl + ld [hl], "@" + ret +.pressedRight + ld a, [wCurrentMenuItem] + cp $6 + ret z ; can't scroll right on bottom row + ld a, [wTopMenuItemX] + cp $11 ; max + jp z, .wrapToFirstColumn + inc a + inc a + jr .done +.wrapToFirstColumn + ld a, $1 + jr .done +.pressedLeft + ld a, [wCurrentMenuItem] + cp $6 + ret z ; can't scroll right on bottom row + ld a, [wTopMenuItemX] + dec a + jp z, .wrapToLastColumn + dec a + jr .done +.wrapToLastColumn + ld a, $11 ; max + jr .done +.pressedUp + ld a, [wCurrentMenuItem] + dec a + ld [wCurrentMenuItem], a + and a + ret nz + ld a, $6 ; wrap to bottom row + ld [wCurrentMenuItem], a + ld a, $1 ; force left column + jr .done +.pressedDown + ld a, [wCurrentMenuItem] + inc a + ld [wCurrentMenuItem], a + cp $7 + jr nz, .wrapToTopRow + ld a, $1 + ld [wCurrentMenuItem], a + jr .done +.wrapToTopRow + cp $6 + ret nz + ld a, $1 +.done + ld [wTopMenuItemX], a + jp EraseMenuCursor + +LoadEDTile: + ld de, ED_Tile + ld hl, vFont + $700 + ld bc, (ED_TileEnd - ED_Tile) / $8 + ; to fix the graphical bug on poor emulators + ;lb bc, BANK(ED_Tile), (ED_TileEnd - ED_Tile) / $8 + jp CopyVideoDataDouble + +ED_Tile: + INCBIN "gfx/font/ED.1bpp" +ED_TileEnd: + +PrintAlphabet: + xor a + ld [H_AUTOBGTRANSFERENABLED], a + ld a, [wAlphabetCase] + and a + ld de, LowerCaseAlphabet + jr nz, .lowercase + ld de, UpperCaseAlphabet +.lowercase + coord hl, 2, 5 + lb bc, 5, 9 ; 5 rows, 9 columns +.outerLoop + push bc +.innerLoop + ld a, [de] + ld [hli], a + inc hl + inc de + dec c + jr nz, .innerLoop + ld bc, SCREEN_WIDTH + 2 + add hl, bc + pop bc + dec b + jr nz, .outerLoop + call PlaceString + ld a, $1 + ld [H_AUTOBGTRANSFERENABLED], a + jp Delay3 + +INCLUDE "text/alphabets.asm" + +PrintNicknameAndUnderscores: + call CalcStringLength + ld a, c + ld [wNamingScreenNameLength], a + coord hl, 10, 2 + lb bc, 1, 10 + call ClearScreenArea + coord hl, 10, 2 + ld de, wcf4b + call PlaceString + coord hl, 10, 3 + ld a, [wNamingScreenType] + cp NAME_MON_SCREEN + jr nc, .pokemon1 + ld b, 7 ; player or rival max name length + jr .playerOrRival1 +.pokemon1 + ld b, 10 ; pokemon max name length +.playerOrRival1 + ld a, $76 ; underscore tile id +.placeUnderscoreLoop + ld [hli], a + dec b + jr nz, .placeUnderscoreLoop + ld a, [wNamingScreenType] + cp NAME_MON_SCREEN + ld a, [wNamingScreenNameLength] + jr nc, .pokemon2 + cp 7 ; player or rival max name length + jr .playerOrRival2 +.pokemon2 + cp 10 ; pokemon max name length +.playerOrRival2 + jr nz, .emptySpacesRemaining + ; when all spaces are filled, force the cursor onto the ED tile + call EraseMenuCursor + ld a, $11 ; "ED" x coord + ld [wTopMenuItemX], a + ld a, $5 ; "ED" y coord + ld [wCurrentMenuItem], a + ld a, [wNamingScreenType] + cp NAME_MON_SCREEN + ld a, 9 ; keep the last underscore raised + jr nc, .pokemon3 + ld a, 6 ; keep the last underscore raised +.pokemon3 +.emptySpacesRemaining + ld c, a + ld b, $0 + coord hl, 10, 3 + add hl, bc + ld [hl], $77 ; raised underscore tile id + ret + +DakutensAndHandakutens: + push de + call CalcStringLength + dec hl + ld a, [hl] + pop hl + ld de, $2 + call IsInArray + ret nc + inc hl + ld a, [hl] + ld [wNamingScreenLetter], a + ret + +INCLUDE "text/dakutens.asm" + +; calculates the length of the string at wcf4b and stores it in c +CalcStringLength: + ld hl, wcf4b + ld c, $0 +.loop + ld a, [hl] + cp "@" + ret z + inc hl + inc c + jr .loop + +PrintNamingText: + coord hl, 0, 1 + ld a, [wNamingScreenType] + ld de, YourTextString + and a + jr z, .notNickname + ld de, RivalsTextString + dec a + jr z, .notNickname + ld a, [wcf91] + ld [wMonPartySpriteSpecies], a + push af + callba WriteMonPartySpriteOAMBySpecies + pop af + ld [wd11e], a + call GetMonName + coord hl, 4, 1 + call PlaceString + ld hl, $1 + add hl, bc + ld [hl], $c9 + coord hl, 1, 3 + ld de, NicknameTextString + jr .placeString +.notNickname + call PlaceString + ld l, c + ld h, b + ld de, NameTextString +.placeString + jp PlaceString + +YourTextString: + db "YOUR @" + +RivalsTextString: + db "RIVAL's @" + +NameTextString: + db "NAME?@" + +NicknameTextString: + db "NICKNAME?@" diff --git a/engine/menus/oaks_pc.asm b/engine/menus/oaks_pc.asm new file mode 100755 index 00000000..03c9b8f1 --- /dev/null +++ b/engine/menus/oaks_pc.asm @@ -0,0 +1,28 @@ +OpenOaksPC: + call SaveScreenTilesToBuffer2 + ld hl, AccessedOaksPCText + call PrintText + ld hl, GetDexRatedText + call PrintText + call YesNoChoice + ld a, [wCurrentMenuItem] + and a + jr nz, .closePC + predef DisplayDexRating +.closePC + ld hl, ClosedOaksPCText + call PrintText + jp LoadScreenTilesFromBuffer2 + +GetDexRatedText: + TX_FAR _GetDexRatedText + db "@" + +ClosedOaksPCText: + TX_FAR _ClosedOaksPCText + TX_WAIT + db "@" + +AccessedOaksPCText: + TX_FAR _AccessedOaksPCText + db "@" diff --git a/engine/menus/party_menu.asm b/engine/menus/party_menu.asm new file mode 100755 index 00000000..41b6074b --- /dev/null +++ b/engine/menus/party_menu.asm @@ -0,0 +1,325 @@ +; [wPartyMenuTypeOrMessageID] = menu type / message ID +; if less than $F0, it is a menu type +; menu types: +; 00: normal pokemon menu (e.g. Start menu) +; 01: use healing item on pokemon menu +; 02: in-battle switch pokemon menu +; 03: learn TM/HM menu +; 04: swap pokemon positions menu +; 05: use evolution stone on pokemon menu +; otherwise, it is a message ID +; f0: poison healed +; f1: burn healed +; f2: freeze healed +; f3: sleep healed +; f4: paralysis healed +; f5: HP healed +; f6: health returned +; f7: revitalized +; f8: leveled up +DrawPartyMenu_:: + xor a + ld [H_AUTOBGTRANSFERENABLED], a + call ClearScreen + call UpdateSprites + callba LoadMonPartySpriteGfxWithLCDDisabled ; load pokemon icon graphics + +RedrawPartyMenu_:: + ld a, [wPartyMenuTypeOrMessageID] + cp SWAP_MONS_PARTY_MENU + jp z, .printMessage + call ErasePartyMenuCursors + callba InitPartyMenuBlkPacket + coord hl, 3, 0 + ld de, wPartySpecies + xor a + ld c, a + ld [hPartyMonIndex], a + ld [wWhichPartyMenuHPBar], a +.loop + ld a, [de] + cp $FF ; reached the terminator? + jp z, .afterDrawingMonEntries + push bc + push de + push hl + ld a, c + push hl + ld hl, wPartyMonNicks + call GetPartyMonName + pop hl + call PlaceString ; print the pokemon's name + callba WriteMonPartySpriteOAMByPartyIndex ; place the appropriate pokemon icon + ld a, [hPartyMonIndex] + ld [wWhichPokemon], a + inc a + ld [hPartyMonIndex], a + call LoadMonData + pop hl + push hl + ld a, [wMenuItemToSwap] + and a ; is the player swapping pokemon positions? + jr z, .skipUnfilledRightArrow +; if the player is swapping pokemon positions + dec a + ld b, a + ld a, [wWhichPokemon] + cp b ; is the player swapping the current pokemon in the list? + jr nz, .skipUnfilledRightArrow +; the player is swapping the current pokemon in the list + dec hl + dec hl + dec hl + ld a, "▷" ; unfilled right arrow menu cursor + ld [hli], a ; place the cursor + inc hl + inc hl +.skipUnfilledRightArrow + ld a, [wPartyMenuTypeOrMessageID] ; menu type + cp TMHM_PARTY_MENU + jr z, .teachMoveMenu + cp EVO_STONE_PARTY_MENU + jr z, .evolutionStoneMenu + push hl + ld bc, 14 ; 14 columns to the right + add hl, bc + ld de, wLoadedMonStatus + call PrintStatusCondition + pop hl + push hl + ld bc, SCREEN_WIDTH + 1 ; down 1 row and right 1 column + ld a, [hFlags_0xFFF6] + set 0, a + ld [hFlags_0xFFF6], a + add hl, bc + predef DrawHP2 ; draw HP bar and prints current / max HP + ld a, [hFlags_0xFFF6] + res 0, a + ld [hFlags_0xFFF6], a + call SetPartyMenuHPBarColor ; color the HP bar (on SGB) + pop hl + jr .printLevel +.teachMoveMenu + push hl + predef CanLearnTM ; check if the pokemon can learn the move + pop hl + ld de, .ableToLearnMoveText + ld a, c + and a + jr nz, .placeMoveLearnabilityString + ld de, .notAbleToLearnMoveText +.placeMoveLearnabilityString + ld bc, 20 + 9 ; down 1 row and right 9 columns + push hl + add hl, bc + call PlaceString + pop hl +.printLevel + ld bc, 10 ; move 10 columns to the right + add hl, bc + call PrintLevel + pop hl + pop de + inc de + ld bc, 2 * 20 + add hl, bc + pop bc + inc c + jp .loop +.ableToLearnMoveText + db "ABLE@" +.notAbleToLearnMoveText + db "NOT ABLE@" +.evolutionStoneMenu + push hl + ld hl, EvosMovesPointerTable + ld b, 0 + ld a, [wLoadedMonSpecies] + dec a + add a + rl b + ld c, a + add hl, bc + ld de, wEvosMoves + ld a, BANK(EvosMovesPointerTable) + ld bc, 2 + call FarCopyData + ld hl, wEvosMoves + ld a, [hli] + ld h, [hl] + ld l, a + ld de, wEvosMoves + ld a, BANK(EvosMovesPointerTable) + ld bc, wEvosMoves.end - wEvosMoves + call FarCopyData + ld hl, wEvosMoves + ld de, .notAbleToEvolveText +; loop through the pokemon's evolution entries +.checkEvolutionsLoop + ld a, [hli] + and a ; reached terminator? + jr z, .placeEvolutionStoneString ; if so, place the "NOT ABLE" string + inc hl + inc hl + cp EV_ITEM + jr nz, .checkEvolutionsLoop +; if it's a stone evolution entry + dec hl + dec hl + ld b, [hl] + ld a, [wEvoStoneItemID] ; the stone the player used + inc hl + inc hl + inc hl + cp b ; does the player's stone match this evolution entry's stone? + jr nz, .checkEvolutionsLoop +; if it does match + ld de, .ableToEvolveText +.placeEvolutionStoneString + ld bc, 20 + 9 ; down 1 row and right 9 columns + pop hl + push hl + add hl, bc + call PlaceString + pop hl + jr .printLevel +.ableToEvolveText + db "ABLE@" +.notAbleToEvolveText + db "NOT ABLE@" +.afterDrawingMonEntries + ld b, SET_PAL_PARTY_MENU + call RunPaletteCommand +.printMessage + ld hl, wd730 + ld a, [hl] + push af + push hl + set 6, [hl] ; turn off letter printing delay + ld a, [wPartyMenuTypeOrMessageID] ; message ID + cp $F0 + jr nc, .printItemUseMessage + add a + ld hl, PartyMenuMessagePointers + ld b, 0 + ld c, a + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + call PrintText +.done + pop hl + pop af + ld [hl], a + ld a, 1 + ld [H_AUTOBGTRANSFERENABLED], a + call Delay3 + jp GBPalNormal +.printItemUseMessage + and $0F + ld hl, PartyMenuItemUseMessagePointers + add a + ld c, a + ld b, 0 + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + push hl + ld a, [wUsedItemOnWhichPokemon] + ld hl, wPartyMonNicks + call GetPartyMonName + pop hl + call PrintText + jr .done + +PartyMenuItemUseMessagePointers: + dw AntidoteText + dw BurnHealText + dw IceHealText + dw AwakeningText + dw ParlyzHealText + dw PotionText + dw FullHealText + dw ReviveText + dw RareCandyText + +PartyMenuMessagePointers: + dw PartyMenuNormalText + dw PartyMenuItemUseText + dw PartyMenuBattleText + dw PartyMenuUseTMText + dw PartyMenuSwapMonText + dw PartyMenuItemUseText + +PartyMenuNormalText: + TX_FAR _PartyMenuNormalText + db "@" + +PartyMenuItemUseText: + TX_FAR _PartyMenuItemUseText + db "@" + +PartyMenuBattleText: + TX_FAR _PartyMenuBattleText + db "@" + +PartyMenuUseTMText: + TX_FAR _PartyMenuUseTMText + db "@" + +PartyMenuSwapMonText: + TX_FAR _PartyMenuSwapMonText + db "@" + +PotionText: + TX_FAR _PotionText + db "@" + +AntidoteText: + TX_FAR _AntidoteText + db "@" + +ParlyzHealText: + TX_FAR _ParlyzHealText + db "@" + +BurnHealText: + TX_FAR _BurnHealText + db "@" + +IceHealText: + TX_FAR _IceHealText + db "@" + +AwakeningText: + TX_FAR _AwakeningText + db "@" + +FullHealText: + TX_FAR _FullHealText + db "@" + +ReviveText: + TX_FAR _ReviveText + db "@" + +RareCandyText: + TX_FAR _RareCandyText + TX_SFX_ITEM_1 ; probably supposed to play SFX_LEVEL_UP but the wrong music bank is loaded + TX_BLINK + db "@" + +SetPartyMenuHPBarColor: + ld hl, wPartyMenuHPBarColors + ld a, [wWhichPartyMenuHPBar] + ld c, a + ld b, 0 + add hl, bc + call GetHealthBarColor + ld b, UPDATE_PARTY_MENU_BLK_PACKET + call RunPaletteCommand + ld hl, wWhichPartyMenuHPBar + inc [hl] + ret diff --git a/engine/menus/pc.asm b/engine/menus/pc.asm new file mode 100755 index 00000000..6ec45f2e --- /dev/null +++ b/engine/menus/pc.asm @@ -0,0 +1,141 @@ +ActivatePC:: + call SaveScreenTilesToBuffer2 + ld a, SFX_TURN_ON_PC + call PlaySound + ld hl, TurnedOnPC1Text + call PrintText + call WaitForSoundToFinish + ld hl, wFlags_0xcd60 + set 3, [hl] + call LoadScreenTilesFromBuffer2 + call Delay3 +PCMainMenu: + callba DisplayPCMainMenu + ld hl, wFlags_0xcd60 + set 5, [hl] + call HandleMenuInput + bit 1, a ;if player pressed B + jp nz, LogOff + ld a, [wMaxMenuItem] + cp 2 + jr nz, .next ;if not 2 menu items (not counting log off) (2 occurs before you get the pokedex) + ld a, [wCurrentMenuItem] + and a + jp z, BillsPC ;if current menu item id is 0, it's bills pc + cp 1 + jr z, .playersPC ;if current menu item id is 1, it's players pc + jp LogOff ;otherwise, it's 2, and you're logging off +.next + cp 3 + jr nz, .next2 ;if not 3 menu items (not counting log off) (3 occurs after you get the pokedex, before you beat the pokemon league) + ld a, [wCurrentMenuItem] + and a + jp z, BillsPC ;if current menu item id is 0, it's bills pc + cp 1 + jr z, .playersPC ;if current menu item id is 1, it's players pc + cp 2 + jp z, OaksPC ;if current menu item id is 2, it's oaks pc + jp LogOff ;otherwise, it's 3, and you're logging off +.next2 + ld a, [wCurrentMenuItem] + and a + jp z, BillsPC ;if current menu item id is 0, it's bills pc + cp 1 + jr z, .playersPC ;if current menu item id is 1, it's players pc + cp 2 + jp z, OaksPC ;if current menu item id is 2, it's oaks pc + cp 3 + jp z, PKMNLeague ;if current menu item id is 3, it's pkmnleague + jp LogOff ;otherwise, it's 4, and you're logging off +.playersPC + ld hl, wFlags_0xcd60 + res 5, [hl] + set 3, [hl] + ld a, SFX_ENTER_PC + call PlaySound + call WaitForSoundToFinish + ld hl, AccessedMyPCText + call PrintText + callba PlayerPC + jr ReloadMainMenu +OaksPC: + ld a, SFX_ENTER_PC + call PlaySound + call WaitForSoundToFinish + callba OpenOaksPC + jr ReloadMainMenu +PKMNLeague: + ld a, SFX_ENTER_PC + call PlaySound + call WaitForSoundToFinish + callba PKMNLeaguePC + jr ReloadMainMenu +BillsPC: + ld a, SFX_ENTER_PC + call PlaySound + call WaitForSoundToFinish + CheckEvent EVENT_MET_BILL + jr nz, .billsPC ;if you've met bill, use that bill's instead of someone's + ld hl, AccessedSomeonesPCText + jr .printText +.billsPC + ld hl, AccessedBillsPCText +.printText + call PrintText + callba BillsPC_ +ReloadMainMenu: + xor a + ld [wDoNotWaitForButtonPressAfterDisplayingText], a + call ReloadMapData + call UpdateSprites + jp PCMainMenu +LogOff: + ld a, SFX_TURN_OFF_PC + call PlaySound + call WaitForSoundToFinish + ld hl, wFlags_0xcd60 + res 3, [hl] + res 5, [hl] + ret + +TurnedOnPC1Text: + TX_FAR _TurnedOnPC1Text + db "@" + +AccessedBillsPCText: + TX_FAR _AccessedBillsPCText + db "@" + +AccessedSomeonesPCText: + TX_FAR _AccessedSomeonesPCText + db "@" + +AccessedMyPCText: + TX_FAR _AccessedMyPCText + db "@" + +; removes one of the specified item ID [hItemToRemoveID] from bag (if existent) +RemoveItemByID:: + ld hl, wBagItems + ld a, [hItemToRemoveID] + ld b, a + xor a + ld [hItemToRemoveIndex], a +.loop + ld a, [hli] + cp -1 ; reached terminator? + ret z + cp b + jr z, .foundItem + inc hl + ld a, [hItemToRemoveIndex] + inc a + ld [hItemToRemoveIndex], a + jr .loop +.foundItem + ld a, $1 + ld [wItemQuantity], a + ld a, [hItemToRemoveIndex] + ld [wWhichPokemon], a + ld hl, wNumBagItems + jp RemoveItemFromInventory diff --git a/engine/menus/players_pc.asm b/engine/menus/players_pc.asm new file mode 100755 index 00000000..403632fa --- /dev/null +++ b/engine/menus/players_pc.asm @@ -0,0 +1,303 @@ +PlayerPC:: + ld hl, wd730 + set 6, [hl] + ld a, ITEM_NAME + ld [wNameListType], a + call SaveScreenTilesToBuffer1 + xor a + ld [wBagSavedMenuItem], a + ld [wParentMenuItem], a + ld a, [wFlags_0xcd60] + bit 3, a ; accessing player's PC through another PC? + jr nz, PlayerPCMenu +; accessing it directly + ld a, SFX_TURN_ON_PC + call PlaySound + ld hl, TurnedOnPC2Text + call PrintText + +PlayerPCMenu: + ld a, [wParentMenuItem] + ld [wCurrentMenuItem], a + ld hl, wFlags_0xcd60 + set 5, [hl] + call LoadScreenTilesFromBuffer2 + coord hl, 0, 0 + ld b, $8 + ld c, $e + call TextBoxBorder + call UpdateSprites + coord hl, 2, 2 + ld de, PlayersPCMenuEntries + call PlaceString + ld hl, wTopMenuItemY + ld a, 2 + ld [hli], a ; wTopMenuItemY + dec a + ld [hli], a ; wTopMenuItemX + inc hl + inc hl + ld a, 3 + ld [hli], a ; wMaxMenuItem + ld a, A_BUTTON | B_BUTTON + ld [hli], a ; wMenuWatchedKeys + xor a + ld [hl], a + ld hl, wListScrollOffset + ld [hli], a ; wListScrollOffset + ld [hl], a ; wMenuWatchMovingOutOfBounds + ld [wPlayerMonNumber], a + ld hl, WhatDoYouWantText + call PrintText + call HandleMenuInput + bit 1, a + jp nz, ExitPlayerPC + call PlaceUnfilledArrowMenuCursor + ld a, [wCurrentMenuItem] + ld [wParentMenuItem], a + and a + jp z, PlayerPCWithdraw + dec a + jp z, PlayerPCDeposit + dec a + jp z, PlayerPCToss + +ExitPlayerPC: + ld a, [wFlags_0xcd60] + bit 3, a ; accessing player's PC through another PC? + jr nz, .next +; accessing it directly + ld a, SFX_TURN_OFF_PC + call PlaySound + call WaitForSoundToFinish +.next + ld hl, wFlags_0xcd60 + res 5, [hl] + call LoadScreenTilesFromBuffer2 + xor a + ld [wListScrollOffset], a + ld [wBagSavedMenuItem], a + ld hl, wd730 + res 6, [hl] + xor a + ld [wDoNotWaitForButtonPressAfterDisplayingText], a + ret + +PlayerPCDeposit: + xor a + ld [wCurrentMenuItem], a + ld [wListScrollOffset], a + ld a, [wNumBagItems] + and a + jr nz, .loop + ld hl, NothingToDepositText + call PrintText + jp PlayerPCMenu +.loop + ld hl, WhatToDepositText + call PrintText + ld hl, wNumBagItems + ld a, l + ld [wListPointer], a + ld a, h + ld [wListPointer + 1], a + xor a + ld [wPrintItemPrices], a + ld a, ITEMLISTMENU + ld [wListMenuID], a + call DisplayListMenuID + jp c, PlayerPCMenu + call IsKeyItem + ld a, 1 + ld [wItemQuantity], a + ld a, [wIsKeyItem] + and a + jr nz, .next +; if it's not a key item, there can be more than one of the item + ld hl, DepositHowManyText + call PrintText + call DisplayChooseQuantityMenu + cp $ff + jp z, .loop +.next + ld hl, wNumBoxItems + call AddItemToInventory + jr c, .roomAvailable + ld hl, NoRoomToStoreText + call PrintText + jp .loop +.roomAvailable + ld hl, wNumBagItems + call RemoveItemFromInventory + call WaitForSoundToFinish + ld a, SFX_WITHDRAW_DEPOSIT + call PlaySound + call WaitForSoundToFinish + ld hl, ItemWasStoredText + call PrintText + jp .loop + +PlayerPCWithdraw: + xor a + ld [wCurrentMenuItem], a + ld [wListScrollOffset], a + ld a, [wNumBoxItems] + and a + jr nz, .loop + ld hl, NothingStoredText + call PrintText + jp PlayerPCMenu +.loop + ld hl, WhatToWithdrawText + call PrintText + ld hl, wNumBoxItems + ld a, l + ld [wListPointer], a + ld a, h + ld [wListPointer + 1], a + xor a + ld [wPrintItemPrices], a + ld a, ITEMLISTMENU + ld [wListMenuID], a + call DisplayListMenuID + jp c, PlayerPCMenu + call IsKeyItem + ld a, 1 + ld [wItemQuantity], a + ld a, [wIsKeyItem] + and a + jr nz, .next +; if it's not a key item, there can be more than one of the item + ld hl, WithdrawHowManyText + call PrintText + call DisplayChooseQuantityMenu + cp $ff + jp z, .loop +.next + ld hl, wNumBagItems + call AddItemToInventory + jr c, .roomAvailable + ld hl, CantCarryMoreText + call PrintText + jp .loop +.roomAvailable + ld hl, wNumBoxItems + call RemoveItemFromInventory + call WaitForSoundToFinish + ld a, SFX_WITHDRAW_DEPOSIT + call PlaySound + call WaitForSoundToFinish + ld hl, WithdrewItemText + call PrintText + jp .loop + +PlayerPCToss: + xor a + ld [wCurrentMenuItem], a + ld [wListScrollOffset], a + ld a, [wNumBoxItems] + and a + jr nz, .loop + ld hl, NothingStoredText + call PrintText + jp PlayerPCMenu +.loop + ld hl, WhatToTossText + call PrintText + ld hl, wNumBoxItems + ld a, l + ld [wListPointer], a + ld a, h + ld [wListPointer + 1], a + xor a + ld [wPrintItemPrices], a + ld a, ITEMLISTMENU + ld [wListMenuID], a + push hl + call DisplayListMenuID + pop hl + jp c, PlayerPCMenu + push hl + call IsKeyItem + pop hl + ld a, 1 + ld [wItemQuantity], a + ld a, [wIsKeyItem] + and a + jr nz, .next + ld a, [wcf91] + call IsItemHM + jr c, .next +; if it's not a key item, there can be more than one of the item + push hl + ld hl, TossHowManyText + call PrintText + call DisplayChooseQuantityMenu + pop hl + cp $ff + jp z, .loop +.next + call TossItem ; disallows tossing key items + jp .loop + +PlayersPCMenuEntries: + db "WITHDRAW ITEM" + next "DEPOSIT ITEM" + next "TOSS ITEM" + next "LOG OFF@" + +TurnedOnPC2Text: + TX_FAR _TurnedOnPC2Text + db "@" + +WhatDoYouWantText: + TX_FAR _WhatDoYouWantText + db "@" + +WhatToDepositText: + TX_FAR _WhatToDepositText + db "@" + +DepositHowManyText: + TX_FAR _DepositHowManyText + db "@" + +ItemWasStoredText: + TX_FAR _ItemWasStoredText + db "@" + +NothingToDepositText: + TX_FAR _NothingToDepositText + db "@" + +NoRoomToStoreText: + TX_FAR _NoRoomToStoreText + db "@" + +WhatToWithdrawText: + TX_FAR _WhatToWithdrawText + db "@" + +WithdrawHowManyText: + TX_FAR _WithdrawHowManyText + db "@" + +WithdrewItemText: + TX_FAR _WithdrewItemText + db "@" + +NothingStoredText: + TX_FAR _NothingStoredText + db "@" + +CantCarryMoreText: + TX_FAR _CantCarryMoreText + db "@" + +WhatToTossText: + TX_FAR _WhatToTossText + db "@" + +TossHowManyText: + TX_FAR _TossHowManyText + db "@" diff --git a/engine/menus/pokedex.asm b/engine/menus/pokedex.asm new file mode 100755 index 00000000..8e1fd480 --- /dev/null +++ b/engine/menus/pokedex.asm @@ -0,0 +1,665 @@ +ShowPokedexMenu: + call GBPalWhiteOut + call ClearScreen + call UpdateSprites + ld a, [wListScrollOffset] + push af + xor a + ld [wCurrentMenuItem], a + ld [wListScrollOffset], a + ld [wLastMenuItem], a + inc a + ld [wd11e], a + ld [hJoy7], a +.setUpGraphics + ld b, SET_PAL_GENERIC + call RunPaletteCommand + callab LoadPokedexTilePatterns +.doPokemonListMenu + ld hl, wTopMenuItemY + ld a, 3 + ld [hli], a ; top menu item Y + xor a + ld [hli], a ; top menu item X + inc a + ld [wMenuWatchMovingOutOfBounds], a + inc hl + inc hl + ld a, 6 + ld [hli], a ; max menu item ID + ld [hl], D_LEFT | D_RIGHT | B_BUTTON | A_BUTTON + call HandlePokedexListMenu + jr c, .goToSideMenu ; if the player chose a pokemon from the list +.exitPokedex + xor a + ld [wMenuWatchMovingOutOfBounds], a + ld [wCurrentMenuItem], a + ld [wLastMenuItem], a + ld [hJoy7], a + ld [wWastedByteCD3A], a + ld [wOverrideSimulatedJoypadStatesMask], a + pop af + ld [wListScrollOffset], a + call GBPalWhiteOutWithDelay3 + call RunDefaultPaletteCommand + jp ReloadMapData +.goToSideMenu + call HandlePokedexSideMenu + dec b + jr z, .exitPokedex ; if the player chose Quit + dec b + jr z, .doPokemonListMenu ; if pokemon not seen or player pressed B button + jp .setUpGraphics ; if pokemon data or area was shown + +; handles the menu on the lower right in the pokedex screen +; OUTPUT: +; b = reason for exiting menu +; 00: showed pokemon data or area +; 01: the player chose Quit +; 02: the pokemon has not been seen yet or the player pressed the B button +HandlePokedexSideMenu: + call PlaceUnfilledArrowMenuCursor + ld a, [wCurrentMenuItem] + push af + ld b, a + ld a, [wLastMenuItem] + push af + ld a, [wListScrollOffset] + push af + add b + inc a + ld [wd11e], a + ld a, [wd11e] + push af + ld a, [wDexMaxSeenMon] + push af ; this doesn't need to be preserved + ld hl, wPokedexSeen + call IsPokemonBitSet + ld b, 2 + jr z, .exitSideMenu + call PokedexToIndex + ld hl, wTopMenuItemY + ld a, 10 + ld [hli], a ; top menu item Y + ld a, 15 + ld [hli], a ; top menu item X + xor a + ld [hli], a ; current menu item ID + inc hl + ld a, 3 + ld [hli], a ; max menu item ID + ;ld a, A_BUTTON | B_BUTTON + ld [hli], a ; menu watched keys (A button and B button) + xor a + ld [hli], a ; old menu item ID + ld [wMenuWatchMovingOutOfBounds], a +.handleMenuInput + call HandleMenuInput + bit 1, a ; was the B button pressed? + ld b, 2 + jr nz, .buttonBPressed + ld a, [wCurrentMenuItem] + and a + jr z, .choseData + dec a + jr z, .choseCry + dec a + jr z, .choseArea +.choseQuit + ld b, 1 +.exitSideMenu + pop af + ld [wDexMaxSeenMon], a + pop af + ld [wd11e], a + pop af + ld [wListScrollOffset], a + pop af + ld [wLastMenuItem], a + pop af + ld [wCurrentMenuItem], a + push bc + coord hl, 0, 3 + ld de, 20 + lb bc, " ", 13 + call DrawTileLine ; cover up the menu cursor in the pokemon list + pop bc + ret + +.buttonBPressed + push bc + coord hl, 15, 10 + ld de, 20 + lb bc, " ", 7 + call DrawTileLine ; cover up the menu cursor in the side menu + pop bc + jr .exitSideMenu + +.choseData + call ShowPokedexDataInternal + ld b, 0 + jr .exitSideMenu + +; play pokemon cry +.choseCry + ld a, [wd11e] + call GetCryData + call PlaySound + jr .handleMenuInput + +.choseArea + predef LoadTownMap_Nest ; display pokemon areas + ld b, 0 + jr .exitSideMenu + +; handles the list of pokemon on the left of the pokedex screen +; sets carry flag if player presses A, unsets carry flag if player presses B +HandlePokedexListMenu: + xor a + ld [H_AUTOBGTRANSFERENABLED], a +; draw the horizontal line separating the seen and owned amounts from the menu + coord hl, 15, 8 + ld a, "─" + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + ld [hli], a + coord hl, 14, 0 + ld [hl], $71 ; vertical line tile + coord hl, 14, 1 + call DrawPokedexVerticalLine + coord hl, 14, 9 + call DrawPokedexVerticalLine + ld hl, wPokedexSeen + ld b, wPokedexSeenEnd - wPokedexSeen + call CountSetBits + ld de, wNumSetBits + coord hl, 16, 3 + lb bc, 1, 3 + call PrintNumber ; print number of seen pokemon + ld hl, wPokedexOwned + ld b, wPokedexOwnedEnd - wPokedexOwned + call CountSetBits + ld de, wNumSetBits + coord hl, 16, 6 + lb bc, 1, 3 + call PrintNumber ; print number of owned pokemon + coord hl, 16, 2 + ld de, PokedexSeenText + call PlaceString + coord hl, 16, 5 + ld de, PokedexOwnText + call PlaceString + coord hl, 1, 1 + ld de, PokedexContentsText + call PlaceString + coord hl, 16, 10 + ld de, PokedexMenuItemsText + call PlaceString +; find the highest pokedex number among the pokemon the player has seen + ld hl, wPokedexSeenEnd - 1 + ld b, (wPokedexSeenEnd - wPokedexSeen) * 8 + 1 +.maxSeenPokemonLoop + ld a, [hld] + ld c, 8 +.maxSeenPokemonInnerLoop + dec b + sla a + jr c, .storeMaxSeenPokemon + dec c + jr nz, .maxSeenPokemonInnerLoop + jr .maxSeenPokemonLoop + +.storeMaxSeenPokemon + ld a, b + ld [wDexMaxSeenMon], a +.loop + xor a + ld [H_AUTOBGTRANSFERENABLED], a + coord hl, 4, 2 + lb bc, 14, 10 + call ClearScreenArea + coord hl, 1, 3 + ld a, [wListScrollOffset] + ld [wd11e], a + ld d, 7 + ld a, [wDexMaxSeenMon] + cp 7 + jr nc, .printPokemonLoop + ld d, a + dec a + ld [wMaxMenuItem], a +; loop to print pokemon pokedex numbers and names +; if the player has owned the pokemon, it puts a pokeball beside the name +.printPokemonLoop + ld a, [wd11e] + inc a + ld [wd11e], a + push af + push de + push hl + ld de, -SCREEN_WIDTH + add hl, de + ld de, wd11e + lb bc, LEADING_ZEROES | 1, 3 + call PrintNumber ; print the pokedex number + ld de, SCREEN_WIDTH + add hl, de + dec hl + push hl + ld hl, wPokedexOwned + call IsPokemonBitSet + pop hl + ld a, " " + jr z, .writeTile + ld a, $72 ; pokeball tile +.writeTile + ld [hl], a ; put a pokeball next to pokemon that the player has owned + push hl + ld hl, wPokedexSeen + call IsPokemonBitSet + jr nz, .getPokemonName ; if the player has seen the pokemon + ld de, .dashedLine ; print a dashed line in place of the name if the player hasn't seen the pokemon + jr .skipGettingName +.dashedLine ; for unseen pokemon in the list + db "----------@" +.getPokemonName + call PokedexToIndex + call GetMonName +.skipGettingName + pop hl + inc hl + call PlaceString + pop hl + ld bc, 2 * SCREEN_WIDTH + add hl, bc + pop de + pop af + ld [wd11e], a + dec d + jr nz, .printPokemonLoop + ld a, 01 + ld [H_AUTOBGTRANSFERENABLED], a + call Delay3 + call GBPalNormal + call HandleMenuInput + bit 1, a ; was the B button pressed? + jp nz, .buttonBPressed +.checkIfUpPressed + bit 6, a ; was Up pressed? + jr z, .checkIfDownPressed +.upPressed ; scroll up one row + ld a, [wListScrollOffset] + and a + jp z, .loop + dec a + ld [wListScrollOffset], a + jp .loop +.checkIfDownPressed + bit 7, a ; was Down pressed? + jr z, .checkIfRightPressed +.downPressed ; scroll down one row + ld a, [wDexMaxSeenMon] + cp 7 + jp c, .loop ; can't if the list is shorter than 7 + sub 7 + ld b, a + ld a, [wListScrollOffset] + cp b + jp z, .loop + inc a + ld [wListScrollOffset], a + jp .loop +.checkIfRightPressed + bit 4, a ; was Right pressed? + jr z, .checkIfLeftPressed +.rightPressed ; scroll down 7 rows + ld a, [wDexMaxSeenMon] + cp 7 + jp c, .loop ; can't if the list is shorter than 7 + sub 6 + ld b, a + ld a, [wListScrollOffset] + add 7 + ld [wListScrollOffset], a + cp b + jp c, .loop + dec b + ld a, b + ld [wListScrollOffset], a + jp .loop +.checkIfLeftPressed ; scroll up 7 rows + bit 5, a ; was Left pressed? + jr z, .buttonAPressed +.leftPressed + ld a, [wListScrollOffset] + sub 7 + ld [wListScrollOffset], a + jp nc, .loop + xor a + ld [wListScrollOffset], a + jp .loop +.buttonAPressed + scf + ret +.buttonBPressed + and a + ret + +DrawPokedexVerticalLine: + ld c, 9 ; height of line + ld de, SCREEN_WIDTH + ld a, $71 ; vertical line tile +.loop + ld [hl], a + add hl, de + xor 1 ; toggle between vertical line tile and box tile + dec c + jr nz, .loop + ret + +PokedexSeenText: + db "SEEN@" + +PokedexOwnText: + db "OWN@" + +PokedexContentsText: + db "CONTENTS@" + +PokedexMenuItemsText: + db "DATA" + next "CRY" + next "AREA" + next "QUIT@" + +; tests if a pokemon's bit is set in the seen or owned pokemon bit fields +; INPUT: +; [wd11e] = pokedex number +; hl = address of bit field +IsPokemonBitSet: + ld a, [wd11e] + dec a + ld c, a + ld b, FLAG_TEST + predef FlagActionPredef + ld a, c + and a + ret + +; function to display pokedex data from outside the pokedex +ShowPokedexData: + call GBPalWhiteOutWithDelay3 + call ClearScreen + call UpdateSprites + callab LoadPokedexTilePatterns ; load pokedex tiles + +; function to display pokedex data from inside the pokedex +ShowPokedexDataInternal: + ld hl, wd72c + set 1, [hl] + ld a, $33 ; 3/7 volume + ld [rNR50], a + call GBPalWhiteOut ; zero all palettes + call ClearScreen + ld a, [wd11e] ; pokemon ID + ld [wcf91], a + push af + ld b, SET_PAL_POKEDEX + call RunPaletteCommand + pop af + ld [wd11e], a + ld a, [hTilesetType] + push af + xor a + ld [hTilesetType], a + + coord hl, 0, 0 + ld de, 1 + lb bc, $64, SCREEN_WIDTH + call DrawTileLine ; draw top border + + coord hl, 0, 17 + ld b, $6f + call DrawTileLine ; draw bottom border + + coord hl, 0, 1 + ld de, 20 + lb bc, $66, $10 + call DrawTileLine ; draw left border + + coord hl, 19, 1 + ld b, $67 + call DrawTileLine ; draw right border + + ld a, $63 ; upper left corner tile + Coorda 0, 0 + ld a, $65 ; upper right corner tile + Coorda 19, 0 + ld a, $6c ; lower left corner tile + Coorda 0, 17 + ld a, $6e ; lower right corner tile + Coorda 19, 17 + + coord hl, 0, 9 + ld de, PokedexDataDividerLine + call PlaceString ; draw horizontal divider line + + coord hl, 9, 6 + ld de, HeightWeightText + call PlaceString + + call GetMonName + coord hl, 9, 2 + call PlaceString + + ld hl, PokedexEntryPointers + ld a, [wd11e] + dec a + ld e, a + ld d, 0 + add hl, de + add hl, de + ld a, [hli] + ld e, a + ld d, [hl] ; de = address of pokedex entry + + coord hl, 9, 4 + call PlaceString ; print species name + + ld h, b + ld l, c + push de + ld a, [wd11e] + push af + call IndexToPokedex + + coord hl, 2, 8 + ld a, "№" + ld [hli], a + ld a, "⠄" + ld [hli], a + ld de, wd11e + lb bc, LEADING_ZEROES | 1, 3 + call PrintNumber ; print pokedex number + + ld hl, wPokedexOwned + call IsPokemonBitSet + pop af + ld [wd11e], a + ld a, [wcf91] + ld [wd0b5], a + pop de + + push af + push bc + push de + push hl + + call Delay3 + call GBPalNormal + call GetMonHeader ; load pokemon picture location + coord hl, 1, 1 + call LoadFlippedFrontSpriteByMonIndex ; draw pokemon picture + ld a, [wcf91] + call PlayCry ; play pokemon cry + + pop hl + pop de + pop bc + pop af + + ld a, c + and a + jp z, .waitForButtonPress ; if the pokemon has not been owned, don't print the height, weight, or description + inc de ; de = address of feet (height) + ld a, [de] ; reads feet, but a is overwritten without being used + coord hl, 12, 6 + lb bc, 1, 2 + call PrintNumber ; print feet (height) + ld a, $60 ; feet symbol tile (one tick) + ld [hl], a + inc de + inc de ; de = address of inches (height) + coord hl, 15, 6 + lb bc, LEADING_ZEROES | 1, 2 + call PrintNumber ; print inches (height) + ld a, $61 ; inches symbol tile (two ticks) + ld [hl], a +; now print the weight (note that weight is stored in tenths of pounds internally) + inc de + inc de + inc de ; de = address of upper byte of weight + push de +; put weight in big-endian order at hDexWeight + ld hl, hDexWeight + ld a, [hl] ; save existing value of [hDexWeight] + push af + ld a, [de] ; a = upper byte of weight + ld [hli], a ; store upper byte of weight in [hDexWeight] + ld a, [hl] ; save existing value of [hDexWeight + 1] + push af + dec de + ld a, [de] ; a = lower byte of weight + ld [hl], a ; store lower byte of weight in [hDexWeight + 1] + ld de, hDexWeight + coord hl, 11, 8 + lb bc, 2, 5 ; 2 bytes, 5 digits + call PrintNumber ; print weight + coord hl, 14, 8 + ld a, [hDexWeight + 1] + sub 10 + ld a, [hDexWeight] + sbc 0 + jr nc, .next + ld [hl], "0" ; if the weight is less than 10, put a 0 before the decimal point +.next + inc hl + ld a, [hli] + ld [hld], a ; make space for the decimal point by moving the last digit forward one tile + ld [hl], "⠄" ; decimal point tile + pop af + ld [hDexWeight + 1], a ; restore original value of [hDexWeight + 1] + pop af + ld [hDexWeight], a ; restore original value of [hDexWeight] + pop hl + inc hl ; hl = address of pokedex description text + coord bc, 1, 11 + ld a, 2 + ld [$fff4], a + call TextCommandProcessor ; print pokedex description text + xor a + ld [$fff4], a +.waitForButtonPress + call JoypadLowSensitivity + ld a, [hJoy5] + and A_BUTTON | B_BUTTON + jr z, .waitForButtonPress + pop af + ld [hTilesetType], a + call GBPalWhiteOut + call ClearScreen + call RunDefaultPaletteCommand + call LoadTextBoxTilePatterns + call GBPalNormal + ld hl, wd72c + res 1, [hl] + ld a, $77 ; max volume + ld [rNR50], a + ret + +HeightWeightText: + db "HT ?",$60,"??",$61 + next "WT ???lb@" + +; XXX does anything point to this? +PokeText: + db "#@" + +; horizontal line that divides the pokedex text description from the rest of the data +PokedexDataDividerLine: + db $68,$69,$6B,$69,$6B + db $69,$6B,$69,$6B,$6B + db $6B,$6B,$69,$6B,$69 + db $6B,$69,$6B,$69,$6A + db "@" + +; draws a line of tiles +; INPUT: +; b = tile ID +; c = number of tile ID's to write +; de = amount to destination address after each tile (1 for horizontal, 20 for vertical) +; hl = destination address +DrawTileLine: + push bc + push de +.loop + ld [hl], b + add hl, de + dec c + jr nz, .loop + pop de + pop bc + ret + +INCLUDE "data/pokedex_entries.asm" + +PokedexToIndex: + ; converts the Pokédex number at wd11e to an index + push bc + push hl + ld a, [wd11e] + ld b, a + ld c, 0 + ld hl, PokedexOrder + +.loop ; go through the list until we find an entry with a matching dex number + inc c + ld a, [hli] + cp b + jr nz, .loop + + ld a, c + ld [wd11e], a + pop hl + pop bc + ret + +IndexToPokedex: + ; converts the index number at wd11e to a Pokédex number + push bc + push hl + ld a, [wd11e] + dec a + ld hl, PokedexOrder + ld b, 0 + ld c, a + add hl, bc + ld a, [hl] + ld [wd11e], a + pop hl + pop bc + ret + +INCLUDE "data/pokedex_order.asm" diff --git a/engine/menus/save.asm b/engine/menus/save.asm new file mode 100755 index 00000000..33a7ba8d --- /dev/null +++ b/engine/menus/save.asm @@ -0,0 +1,708 @@ +LoadSAV: +;(if carry -> write +;"the file data is destroyed") + call ClearScreen + call LoadFontTilePatterns + call LoadTextBoxTilePatterns + call LoadSAV0 + jr c, .badsum + call LoadSAV1 + jr c, .badsum + call LoadSAV2 + jr c, .badsum + ld a, $2 ; good checksum + jr .goodsum +.badsum + ld hl, wd730 + push hl + set 6, [hl] + ld hl, FileDataDestroyedText + call PrintText + ld c, 100 + call DelayFrames + pop hl + res 6, [hl] + ld a, $1 ; bad checksum +.goodsum + ld [wSaveFileStatus], a + ret + +FileDataDestroyedText: + TX_FAR _FileDataDestroyedText + db "@" + +LoadSAV0: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld hl, sPlayerName ; hero name located in SRAM + ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV + call SAVCheckSum + ld c, a + ld a, [sMainDataCheckSum] ; SAV's checksum + cp c + jp z, .checkSumsMatched + +; If the computed checksum didn't match the saved on, try again. + ld hl, sPlayerName + ld bc, sMainDataCheckSum - sPlayerName + call SAVCheckSum + ld c, a + ld a, [sMainDataCheckSum] ; SAV's checksum + cp c + jp nz, SAVBadCheckSum + +.checkSumsMatched + ld hl, sPlayerName + ld de, wPlayerName + ld bc, NAME_LENGTH + call CopyData + ld hl, sMainData + ld de, wMainDataStart + ld bc, wMainDataEnd - wMainDataStart + call CopyData + ld hl, wCurMapTileset + set 7, [hl] + ld hl, sSpriteData + ld de, wSpriteDataStart + ld bc, wSpriteDataEnd - wSpriteDataStart + call CopyData + ld a, [sTilesetType] + ld [hTilesetType], a + ld hl, sCurBoxData + ld de, wBoxDataStart + ld bc, wBoxDataEnd - wBoxDataStart + call CopyData + and a + jp SAVGoodChecksum + +LoadSAV1: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld hl, sPlayerName ; hero name located in SRAM + ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV + call SAVCheckSum + ld c, a + ld a, [sMainDataCheckSum] ; SAV's checksum + cp c + jr nz, SAVBadCheckSum + ld hl, sCurBoxData + ld de, wBoxDataStart + ld bc, wBoxDataEnd - wBoxDataStart + call CopyData + and a + jp SAVGoodChecksum + +LoadSAV2: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld hl, sPlayerName ; hero name located in SRAM + ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV + call SAVCheckSum + ld c, a + ld a, [sMainDataCheckSum] ; SAV's checksum + cp c + jp nz, SAVBadCheckSum + ld hl, sPartyData + ld de, wPartyDataStart + ld bc, wPartyDataEnd - wPartyDataStart + call CopyData + ld hl, sMainData + ld de, wPokedexOwned + ld bc, wPokedexSeenEnd - wPokedexOwned + call CopyData + and a + jp SAVGoodChecksum + +SAVBadCheckSum: + scf + +SAVGoodChecksum: + ld a, $0 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +LoadSAVIgnoreBadCheckSum: +; unused function that loads save data and ignores bad checksums + call LoadSAV0 + call LoadSAV1 + jp LoadSAV2 + +SaveSAV: + callba PrintSaveScreenText + ld hl, WouldYouLikeToSaveText + call SaveSAVConfirm + and a ;|0 = Yes|1 = No| + ret nz + ld a, [wSaveFileStatus] + dec a + jr z, .save + call SAVCheckRandomID + jr z, .save + ld hl, OlderFileWillBeErasedText + call SaveSAVConfirm + and a + ret nz +.save + call SaveSAVtoSRAM + coord hl, 1, 13 + lb bc, 4, 18 + call ClearScreenArea + coord hl, 1, 14 + ld de, NowSavingString + call PlaceString + ld c, 120 + call DelayFrames + ld hl, GameSavedText + call PrintText + ld a, SFX_SAVE + call PlaySoundWaitForCurrent + call WaitForSoundToFinish + ld c, 30 + jp DelayFrames + +NowSavingString: + db "Now saving...@" + +SaveSAVConfirm: + call PrintText + coord hl, 0, 7 + lb bc, 8, 1 + ld a, TWO_OPTION_MENU + ld [wTextBoxID], a + call DisplayTextBoxID ; yes/no menu + ld a, [wCurrentMenuItem] + ret + +WouldYouLikeToSaveText: + TX_FAR _WouldYouLikeToSaveText + db "@" + +GameSavedText: + TX_FAR _GameSavedText + db "@" + +OlderFileWillBeErasedText: + TX_FAR _OlderFileWillBeErasedText + db "@" + +SaveSAVtoSRAM0: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld hl, wPlayerName + ld de, sPlayerName + ld bc, NAME_LENGTH + call CopyData + ld hl, wMainDataStart + ld de, sMainData + ld bc, wMainDataEnd - wMainDataStart + call CopyData + ld hl, wSpriteDataStart + ld de, sSpriteData + ld bc, wSpriteDataEnd - wSpriteDataStart + call CopyData + ld hl, wBoxDataStart + ld de, sCurBoxData + ld bc, wBoxDataEnd - wBoxDataStart + call CopyData + ld a, [hTilesetType] + ld [sTilesetType], a + ld hl, sPlayerName + ld bc, sMainDataCheckSum - sPlayerName + call SAVCheckSum + ld [sMainDataCheckSum], a + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +SaveSAVtoSRAM1: +; stored pokémon + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld hl, wBoxDataStart + ld de, sCurBoxData + ld bc, wBoxDataEnd - wBoxDataStart + call CopyData + ld hl, sPlayerName + ld bc, sMainDataCheckSum - sPlayerName + call SAVCheckSum + ld [sMainDataCheckSum], a + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +SaveSAVtoSRAM2: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld hl, wPartyDataStart + ld de, sPartyData + ld bc, wPartyDataEnd - wPartyDataStart + call CopyData + ld hl, wPokedexOwned ; pokédex only + ld de, sMainData + ld bc, wPokedexSeenEnd - wPokedexOwned + call CopyData + ld hl, sPlayerName + ld bc, sMainDataCheckSum - sPlayerName + call SAVCheckSum + ld [sMainDataCheckSum], a + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +SaveSAVtoSRAM:: + ld a, $2 + ld [wSaveFileStatus], a + call SaveSAVtoSRAM0 + call SaveSAVtoSRAM1 + jp SaveSAVtoSRAM2 + +SAVCheckSum: +;Check Sum (result[1 byte] is complemented) + ld d, 0 +.loop + ld a, [hli] + add d + ld d, a + dec bc + ld a, b + or c + jr nz, .loop + ld a, d + cpl + ret + +CalcIndividualBoxCheckSums: + ld hl, sBox1 ; sBox7 + ld de, sBank2IndividualBoxChecksums ; sBank3IndividualBoxChecksums + ld b, NUM_BOXES / 2 +.loop + push bc + push de + ld bc, wBoxDataEnd - wBoxDataStart + call SAVCheckSum + pop de + ld [de], a + inc de + pop bc + dec b + jr nz, .loop + ret + +GetBoxSRAMLocation: +; in: a = box num +; out: b = box SRAM bank, hl = pointer to start of box + ld hl, BoxSRAMPointerTable + ld a, [wCurrentBoxNum] + and $7f + cp NUM_BOXES / 2 + ld b, 2 + jr c, .next + inc b + sub NUM_BOXES / 2 +.next + ld e, a + ld d, 0 + add hl, de + add hl, de + ld a, [hli] + ld h, [hl] + ld l, a + ret + +BoxSRAMPointerTable: + dw sBox1 ; sBox7 + dw sBox2 ; sBox8 + dw sBox3 ; sBox9 + dw sBox4 ; sBox10 + dw sBox5 ; sBox11 + dw sBox6 ; sBox12 + +ChangeBox:: + ld hl, WhenYouChangeBoxText + call PrintText + call YesNoChoice + ld a, [wCurrentMenuItem] + and a + ret nz ; return if No was chosen + ld hl, wCurrentBoxNum + bit 7, [hl] ; is it the first time player is changing the box? + call z, EmptyAllSRAMBoxes ; if so, empty all boxes in SRAM + call DisplayChangeBoxMenu + call UpdateSprites + ld hl, hFlags_0xFFF6 + set 1, [hl] + call HandleMenuInput + ld hl, hFlags_0xFFF6 + res 1, [hl] + bit 1, a ; pressed b + ret nz + call GetBoxSRAMLocation + ld e, l + ld d, h + ld hl, wBoxDataStart + call CopyBoxToOrFromSRAM ; copy old box from WRAM to SRAM + ld a, [wCurrentMenuItem] + set 7, a + ld [wCurrentBoxNum], a + call GetBoxSRAMLocation + ld de, wBoxDataStart + call CopyBoxToOrFromSRAM ; copy new box from SRAM to WRAM + ld hl, wMapTextPtr + ld de, wChangeBoxSavedMapTextPointer + ld a, [hli] + ld [de], a + inc de + ld a, [hl] + ld [de], a + call RestoreMapTextPointer + call SaveSAVtoSRAM + ld hl, wChangeBoxSavedMapTextPointer + call SetMapTextPointer + ld a, SFX_SAVE + call PlaySoundWaitForCurrent + call WaitForSoundToFinish + ret + +WhenYouChangeBoxText: + TX_FAR _WhenYouChangeBoxText + db "@" + +CopyBoxToOrFromSRAM: +; copy an entire box from hl to de with b as the SRAM bank + push hl + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld a, b + ld [MBC1SRamBank], a + ld bc, wBoxDataEnd - wBoxDataStart + call CopyData + pop hl + +; mark the memory that the box was copied from as am empty box + xor a + ld [hli], a + dec a + ld [hl], a + + ld hl, sBox1 ; sBox7 + ld bc, sBank2AllBoxesChecksum - sBox1 + call SAVCheckSum + ld [sBank2AllBoxesChecksum], a ; sBank3AllBoxesChecksum + call CalcIndividualBoxCheckSums + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +DisplayChangeBoxMenu: + xor a + ld [H_AUTOBGTRANSFERENABLED], a + ld a, A_BUTTON | B_BUTTON + ld [wMenuWatchedKeys], a + ld a, 11 + ld [wMaxMenuItem], a + ld a, 1 + ld [wTopMenuItemY], a + ld a, 12 + ld [wTopMenuItemX], a + xor a + ld [wMenuWatchMovingOutOfBounds], a + ld a, [wCurrentBoxNum] + and $7f + ld [wCurrentMenuItem], a + ld [wLastMenuItem], a + coord hl, 0, 0 + ld b, 2 + ld c, 9 + call TextBoxBorder + ld hl, ChooseABoxText + call PrintText + coord hl, 11, 0 + ld b, 12 + ld c, 7 + call TextBoxBorder + ld hl, hFlags_0xFFF6 + set 2, [hl] + ld de, BoxNames + coord hl, 13, 1 + call PlaceString + ld hl, hFlags_0xFFF6 + res 2, [hl] + ld a, [wCurrentBoxNum] + and $7f + cp 9 + jr c, .singleDigitBoxNum + sub 9 + coord hl, 8, 2 + ld [hl], "1" + add "0" + jr .next +.singleDigitBoxNum + add "1" +.next + Coorda 9, 2 + coord hl, 1, 2 + ld de, BoxNoText + call PlaceString + call GetMonCountsForAllBoxes + coord hl, 18, 1 + ld de, wBoxMonCounts + ld bc, SCREEN_WIDTH + ld a, $c +.loop + push af + ld a, [de] + and a ; is the box empty? + jr z, .skipPlacingPokeball + ld [hl], $78 ; place pokeball tile next to box name if box not empty +.skipPlacingPokeball + add hl, bc + inc de + pop af + dec a + jr nz, .loop + ld a, 1 + ld [H_AUTOBGTRANSFERENABLED], a + ret + +ChooseABoxText: + TX_FAR _ChooseABoxText + db "@" + +BoxNames: + db "BOX 1" + next "BOX 2" + next "BOX 3" + next "BOX 4" + next "BOX 5" + next "BOX 6" + next "BOX 7" + next "BOX 8" + next "BOX 9" + next "BOX10" + next "BOX11" + next "BOX12@" + +BoxNoText: + db "BOX No.@" + +EmptyAllSRAMBoxes: +; marks all boxes in SRAM as empty (initialisation for the first time the +; player changes the box) + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld a, 2 + ld [MBC1SRamBank], a + call EmptySRAMBoxesInBank + ld a, 3 + ld [MBC1SRamBank], a + call EmptySRAMBoxesInBank + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +EmptySRAMBoxesInBank: +; marks every box in the current SRAM bank as empty + ld hl, sBox1 ; sBox7 + call EmptySRAMBox + ld hl, sBox2 ; sBox8 + call EmptySRAMBox + ld hl, sBox3 ; sBox9 + call EmptySRAMBox + ld hl, sBox4 ; sBox10 + call EmptySRAMBox + ld hl, sBox5 ; sBox11 + call EmptySRAMBox + ld hl, sBox6 ; sBox12 + call EmptySRAMBox + ld hl, sBox1 ; sBox7 + ld bc, sBank2AllBoxesChecksum - sBox1 + call SAVCheckSum + ld [sBank2AllBoxesChecksum], a ; sBank3AllBoxesChecksum + call CalcIndividualBoxCheckSums + ret + +EmptySRAMBox: + xor a + ld [hli], a + dec a + ld [hl], a + ret + +GetMonCountsForAllBoxes: + ld hl, wBoxMonCounts + push hl + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + ld a, $2 + ld [MBC1SRamBank], a + call GetMonCountsForBoxesInBank + ld a, $3 + ld [MBC1SRamBank], a + call GetMonCountsForBoxesInBank + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + pop hl + +; copy the count for the current box from WRAM + ld a, [wCurrentBoxNum] + and $7f + ld c, a + ld b, 0 + add hl, bc + ld a, [wNumInBox] + ld [hl], a + + ret + +GetMonCountsForBoxesInBank: + ld a, [sBox1] ; sBox7 + ld [hli], a + ld a, [sBox2] ; sBox8 + ld [hli], a + ld a, [sBox3] ; sBox9 + ld [hli], a + ld a, [sBox4] ; sBox10 + ld [hli], a + ld a, [sBox5] ; sBox11 + ld [hli], a + ld a, [sBox6] ; sBox12 + ld [hli], a + ret + +SAVCheckRandomID: +;checks if Sav file is the same by checking player's name 1st letter ($a598) +; and the two random numbers generated at game beginning +;(which are stored at wPlayerID)s + ld a, $0a + ld [MBC1SRamEnable], a + ld a, $01 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamBank], a + ld a, [sPlayerName] + and a + jr z, .next + ld hl, sPlayerName + ld bc, sMainDataCheckSum - sPlayerName + call SAVCheckSum + ld c, a + ld a, [sMainDataCheckSum] + cp c + jr nz, .next + ld hl, sMainData + (wPlayerID - wMainDataStart) ; player ID + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [wPlayerID] + cp l + jr nz, .next + ld a, [wPlayerID + 1] + cp h +.next + ld a, $00 + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +SaveHallOfFameTeams: + ld a, [wNumHoFTeams] + dec a + cp HOF_TEAM_CAPACITY + jr nc, .shiftHOFTeams + ld hl, sHallOfFame + ld bc, HOF_TEAM + call AddNTimes + ld e, l + ld d, h + ld hl, wHallOfFame + ld bc, HOF_TEAM + jr HallOfFame_Copy + +.shiftHOFTeams +; if the space designated for HOF teams is full, then shift all HOF teams to the next slot, making space for the new HOF team +; this deletes the last HOF team though + ld hl, sHallOfFame + HOF_TEAM + ld de, sHallOfFame + ld bc, HOF_TEAM * (HOF_TEAM_CAPACITY - 1) + call HallOfFame_Copy + ld hl, wHallOfFame + ld de, sHallOfFame + HOF_TEAM * (HOF_TEAM_CAPACITY - 1) + ld bc, HOF_TEAM + jr HallOfFame_Copy + +LoadHallOfFameTeams: + ld hl, sHallOfFame + ld bc, HOF_TEAM + ld a, [wHoFTeamIndex] + call AddNTimes + ld de, wHallOfFame + ld bc, HOF_TEAM + ; fallthrough + +HallOfFame_Copy: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + xor a + ld [MBC1SRamBank], a + call CopyData + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +ClearSAV: + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + ld a, $1 + ld [MBC1SRamBankingMode], a + xor a + call PadSRAM_FF + ld a, $1 + call PadSRAM_FF + ld a, $2 + call PadSRAM_FF + ld a, $3 + call PadSRAM_FF + xor a + ld [MBC1SRamBankingMode], a + ld [MBC1SRamEnable], a + ret + +PadSRAM_FF: + ld [MBC1SRamBank], a + ld hl, $a000 + ld bc, $2000 + ld a, $ff + jp FillMemory diff --git a/engine/menus/start_sub_menus.asm b/engine/menus/start_sub_menus.asm new file mode 100755 index 00000000..b81769a2 --- /dev/null +++ b/engine/menus/start_sub_menus.asm @@ -0,0 +1,808 @@ +StartMenu_Pokedex:: + predef ShowPokedexMenu + call LoadScreenTilesFromBuffer2 ; restore saved screen + call Delay3 + call LoadGBPal + call UpdateSprites + jp RedisplayStartMenu + +StartMenu_Pokemon:: + ld a, [wPartyCount] + and a + jp z, RedisplayStartMenu + xor a + ld [wMenuItemToSwap], a + ld [wPartyMenuTypeOrMessageID], a + ld [wUpdateSpritesEnabled], a + call DisplayPartyMenu + jr .checkIfPokemonChosen +.loop + xor a + ld [wMenuItemToSwap], a + ld [wPartyMenuTypeOrMessageID], a + call GoBackToPartyMenu +.checkIfPokemonChosen + jr nc, .chosePokemon +.exitMenu + call GBPalWhiteOutWithDelay3 + call RestoreScreenTilesAndReloadTilePatterns + call LoadGBPal + jp RedisplayStartMenu +.chosePokemon + call SaveScreenTilesToBuffer1 + ld a, FIELD_MOVE_MON_MENU + ld [wTextBoxID], a + call DisplayTextBoxID ; display pokemon menu options + ld hl, wFieldMoves + lb bc, 2, 12 ; max menu item ID, top menu item Y + ld e, 5 +.adjustMenuVariablesLoop + dec e + jr z, .storeMenuVariables + ld a, [hli] + and a ; end of field moves? + jr z, .storeMenuVariables + inc b + dec c + dec c + jr .adjustMenuVariablesLoop +.storeMenuVariables + ld hl, wTopMenuItemY + ld a, c + ld [hli], a ; top menu item Y + ld a, [hFieldMoveMonMenuTopMenuItemX] + ld [hli], a ; top menu item X + xor a + ld [hli], a ; current menu item ID + inc hl + ld a, b + ld [hli], a ; max menu item ID + ld a, A_BUTTON | B_BUTTON + ld [hli], a ; menu watched keys + xor a + ld [hl], a + call HandleMenuInput + push af + call LoadScreenTilesFromBuffer1 ; restore saved screen + pop af + bit 1, a ; was the B button pressed? + jp nz, .loop +; if the B button wasn't pressed + ld a, [wMaxMenuItem] + ld b, a + ld a, [wCurrentMenuItem] ; menu selection + cp b + jp z, .exitMenu ; if the player chose Cancel + dec b + cp b + jr z, .choseSwitch + dec b + cp b + jp z, .choseStats + ld c, a + ld b, 0 + ld hl, wFieldMoves + add hl, bc + jp .choseOutOfBattleMove +.choseSwitch + ld a, [wPartyCount] + cp 2 ; is there more than one pokemon in the party? + jp c, StartMenu_Pokemon ; if not, no switching + call SwitchPartyMon_InitVarOrSwapData ; init [wMenuItemToSwap] + ld a, SWAP_MONS_PARTY_MENU + ld [wPartyMenuTypeOrMessageID], a + call GoBackToPartyMenu + jp .checkIfPokemonChosen +.choseStats + call ClearSprites + xor a ; PLAYER_PARTY_DATA + ld [wMonDataLocation], a + predef StatusScreen + predef StatusScreen2 + call ReloadMapData + jp StartMenu_Pokemon +.choseOutOfBattleMove + push hl + ld a, [wWhichPokemon] + ld hl, wPartyMonNicks + call GetPartyMonName + pop hl + ld a, [hl] + dec a + add a + ld b, 0 + ld c, a + ld hl, .outOfBattleMovePointers + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [wObtainedBadges] ; badges obtained + jp hl +.outOfBattleMovePointers + dw .cut + dw .fly + dw .surf + dw .surf + dw .strength + dw .flash + dw .dig + dw .teleport + dw .softboiled +.fly + bit 2, a ; does the player have the Thunder Badge? + jp z, .newBadgeRequired + call CheckIfInOutsideMap + jr z, .canFly + ld a, [wWhichPokemon] + ld hl, wPartyMonNicks + call GetPartyMonName + ld hl, .cannotFlyHereText + call PrintText + jp .loop +.canFly + call ChooseFlyDestination + ld a, [wd732] + bit 3, a ; did the player decide to fly? + jp nz, .goBackToMap + call LoadFontTilePatterns + ld hl, wd72e + set 1, [hl] + jp StartMenu_Pokemon +.cut + bit 1, a ; does the player have the Cascade Badge? + jp z, .newBadgeRequired + predef UsedCut + ld a, [wActionResultOrTookBattleTurn] + and a + jp z, .loop + jp CloseTextDisplay +.surf + bit 4, a ; does the player have the Soul Badge? + jp z, .newBadgeRequired + callba IsSurfingAllowed + ld hl, wd728 + bit 1, [hl] + res 1, [hl] + jp z, .loop + ld a, SURFBOARD + ld [wcf91], a + ld [wPseudoItemID], a + call UseItem + ld a, [wActionResultOrTookBattleTurn] + and a + jp z, .loop + call GBPalWhiteOutWithDelay3 + jp .goBackToMap +.strength + bit 3, a ; does the player have the Rainbow Badge? + jp z, .newBadgeRequired + predef PrintStrengthTxt + call GBPalWhiteOutWithDelay3 + jp .goBackToMap +.flash + bit 0, a ; does the player have the Boulder Badge? + jp z, .newBadgeRequired + xor a + ld [wMapPalOffset], a + ld hl, .flashLightsAreaText + call PrintText + call GBPalWhiteOutWithDelay3 + jp .goBackToMap +.flashLightsAreaText + TX_FAR _FlashLightsAreaText + db "@" +.dig + ld a, ESCAPE_ROPE + ld [wcf91], a + ld [wPseudoItemID], a + call UseItem + ld a, [wActionResultOrTookBattleTurn] + and a + jp z, .loop + call GBPalWhiteOutWithDelay3 + jp .goBackToMap +.teleport + call CheckIfInOutsideMap + jr z, .canTeleport + ld a, [wWhichPokemon] + ld hl, wPartyMonNicks + call GetPartyMonName + ld hl, .cannotUseTeleportNowText + call PrintText + jp .loop +.canTeleport + ld hl, .warpToLastPokemonCenterText + call PrintText + ld hl, wd732 + set 3, [hl] + set 6, [hl] + ld hl, wd72e + set 1, [hl] + res 4, [hl] + ld c, 60 + call DelayFrames + call GBPalWhiteOutWithDelay3 + jp .goBackToMap +.warpToLastPokemonCenterText + TX_FAR _WarpToLastPokemonCenterText + db "@" +.cannotUseTeleportNowText + TX_FAR _CannotUseTeleportNowText + db "@" +.cannotFlyHereText + TX_FAR _CannotFlyHereText + db "@" +.softboiled + ld hl, wPartyMon1MaxHP + ld a, [wWhichPokemon] + ld bc, wPartyMon2 - wPartyMon1 + call AddNTimes + ld a, [hli] + ld [H_DIVIDEND], a + ld a, [hl] + ld [H_DIVIDEND + 1], a + ld a, 5 + ld [H_DIVISOR], a + ld b, 2 ; number of bytes + call Divide + ld bc, wPartyMon1HP - wPartyMon1MaxHP + add hl, bc + ld a, [hld] + ld b, a + ld a, [H_QUOTIENT + 3] + sub b + ld b, [hl] + ld a, [H_QUOTIENT + 2] + sbc b + jp nc, .notHealthyEnough + ld a, [wPartyAndBillsPCSavedMenuItem] + push af + ld a, POTION + ld [wcf91], a + ld [wPseudoItemID], a + call UseItem + pop af + ld [wPartyAndBillsPCSavedMenuItem], a + jp .loop +.notHealthyEnough ; if current HP is less than 1/5 of max HP + ld hl, .notHealthyEnoughText + call PrintText + jp .loop +.notHealthyEnoughText + TX_FAR _NotHealthyEnoughText + db "@" +.goBackToMap + call RestoreScreenTilesAndReloadTilePatterns + jp CloseTextDisplay +.newBadgeRequired + ld hl, .newBadgeRequiredText + call PrintText + jp .loop +.newBadgeRequiredText + TX_FAR _NewBadgeRequiredText + db "@" + +; writes a blank tile to all possible menu cursor positions on the party menu +ErasePartyMenuCursors:: + coord hl, 0, 1 + ld bc, 2 * 20 ; menu cursor positions are 2 rows apart + ld a, 6 ; 6 menu cursor positions +.loop + ld [hl], " " + add hl, bc + dec a + jr nz, .loop + ret + +ItemMenuLoop: + call LoadScreenTilesFromBuffer2DisableBGTransfer ; restore saved screen + call RunDefaultPaletteCommand + +StartMenu_Item:: + ld a, [wLinkState] + dec a ; is the player in the Colosseum or Trade Centre? + jr nz, .notInCableClubRoom + ld hl, CannotUseItemsHereText + call PrintText + jr .exitMenu +.notInCableClubRoom + ld bc, wNumBagItems + ld hl, wListPointer + ld a, c + ld [hli], a + ld [hl], b ; store item bag pointer in wListPointer (for DisplayListMenuID) + xor a + ld [wPrintItemPrices], a + ld a, ITEMLISTMENU + ld [wListMenuID], a + ld a, [wBagSavedMenuItem] + ld [wCurrentMenuItem], a + call DisplayListMenuID + ld a, [wCurrentMenuItem] + ld [wBagSavedMenuItem], a + jr nc, .choseItem +.exitMenu + call LoadScreenTilesFromBuffer2 ; restore saved screen + call LoadTextBoxTilePatterns + call UpdateSprites + jp RedisplayStartMenu +.choseItem +; erase menu cursor (blank each tile in front of an item name) + ld a, " " + Coorda 5, 4 + Coorda 5, 6 + Coorda 5, 8 + Coorda 5, 10 + call PlaceUnfilledArrowMenuCursor + xor a + ld [wMenuItemToSwap], a + ld a, [wcf91] + cp BICYCLE + jp z, .useOrTossItem +.notBicycle1 + ld a, USE_TOSS_MENU_TEMPLATE + ld [wTextBoxID], a + call DisplayTextBoxID + ld hl, wTopMenuItemY + ld a, 11 + ld [hli], a ; top menu item Y + ld a, 14 + ld [hli], a ; top menu item X + xor a + ld [hli], a ; current menu item ID + inc hl + inc a ; a = 1 + ld [hli], a ; max menu item ID + ld a, A_BUTTON | B_BUTTON + ld [hli], a ; menu watched keys + xor a + ld [hl], a ; old menu item id + call HandleMenuInput + call PlaceUnfilledArrowMenuCursor + bit 1, a ; was the B button pressed? + jr z, .useOrTossItem + jp ItemMenuLoop +.useOrTossItem ; if the player made the choice to use or toss the item + ld a, [wcf91] + ld [wd11e], a + call GetItemName + call CopyStringToCF4B ; copy name to wcf4b + ld a, [wcf91] + cp BICYCLE + jr nz, .notBicycle2 + ld a, [wd732] + bit 5, a + jr z, .useItem_closeMenu + ld hl, CannotGetOffHereText + call PrintText + jp ItemMenuLoop +.notBicycle2 + ld a, [wCurrentMenuItem] + and a + jr nz, .tossItem +; use item + ld [wPseudoItemID], a ; a must be 0 due to above conditional jump + ld a, [wcf91] + cp HM_01 + jr nc, .useItem_partyMenu + ld hl, UsableItems_CloseMenu + ld de, 1 + call IsInArray + jr c, .useItem_closeMenu + ld a, [wcf91] + ld hl, UsableItems_PartyMenu + ld de, 1 + call IsInArray + jr c, .useItem_partyMenu + call UseItem + jp ItemMenuLoop +.useItem_closeMenu + xor a + ld [wPseudoItemID], a + call UseItem + ld a, [wActionResultOrTookBattleTurn] + and a + jp z, ItemMenuLoop + jp CloseStartMenu +.useItem_partyMenu + ld a, [wUpdateSpritesEnabled] + push af + call UseItem + ld a, [wActionResultOrTookBattleTurn] + cp $02 + jp z, .partyMenuNotDisplayed + call GBPalWhiteOutWithDelay3 + call RestoreScreenTilesAndReloadTilePatterns + pop af + ld [wUpdateSpritesEnabled], a + jp StartMenu_Item +.partyMenuNotDisplayed + pop af + ld [wUpdateSpritesEnabled], a + jp ItemMenuLoop +.tossItem + call IsKeyItem + ld a, [wIsKeyItem] + and a + jr nz, .skipAskingQuantity + ld a, [wcf91] + call IsItemHM + jr c, .skipAskingQuantity + call DisplayChooseQuantityMenu + inc a + jr z, .tossZeroItems +.skipAskingQuantity + ld hl, wNumBagItems + call TossItem +.tossZeroItems + jp ItemMenuLoop + +CannotUseItemsHereText: + TX_FAR _CannotUseItemsHereText + db "@" + +CannotGetOffHereText: + TX_FAR _CannotGetOffHereText + db "@" + +INCLUDE "data/party_items.asm" + +INCLUDE "data/overworld_items.asm" + +StartMenu_TrainerInfo:: + call GBPalWhiteOut + call ClearScreen + call UpdateSprites + ld a, [hTilesetType] + push af + xor a + ld [hTilesetType], a + call DrawTrainerInfo + predef DrawBadges ; draw badges + ld b, SET_PAL_TRAINER_CARD + call RunPaletteCommand + call GBPalNormal + call WaitForTextScrollButtonPress ; wait for button press + call GBPalWhiteOut + call LoadFontTilePatterns + call LoadScreenTilesFromBuffer2 ; restore saved screen + call RunDefaultPaletteCommand + call ReloadMapData + call LoadGBPal + pop af + ld [hTilesetType], a + jp RedisplayStartMenu + +; loads tile patterns and draws everything except for gym leader faces / badges +DrawTrainerInfo: + ld de, RedPicFront + lb bc, BANK(RedPicFront), $01 + predef DisplayPicCenteredOrUpperRight + call DisableLCD + coord hl, 0, 2 + ld a, " " + call TrainerInfo_DrawVerticalLine + coord hl, 1, 2 + call TrainerInfo_DrawVerticalLine + ld hl, vChars2 + $70 + ld de, vChars2 + ld bc, $70 * 4 + call CopyData + ld hl, TrainerInfoTextBoxTileGraphics ; trainer info text box tile patterns + ld de, vChars2 + $770 + ld bc, $0080 + push bc + call TrainerInfo_FarCopyData + ld hl, BlankLeaderNames + ld de, vChars2 + $600 + ld bc, $0170 + call TrainerInfo_FarCopyData + pop bc + ld hl, BadgeNumbersTileGraphics ; badge number tile patterns + ld de, vChars1 + $580 + call TrainerInfo_FarCopyData + ld hl, GymLeaderFaceAndBadgeTileGraphics ; gym leader face and badge tile patterns + ld de, vChars2 + $200 + ld bc, $0400 + ld a, $03 + call FarCopyData2 + ld hl, TextBoxGraphics + ld de, $00d0 + add hl, de ; hl = colon tile pattern + ld de, vChars1 + $560 + ld bc, $0010 + ld a, $04 + push bc + call FarCopyData2 + pop bc + ld hl, TrainerInfoTextBoxTileGraphics + $80 ; background tile pattern + ld de, vChars1 + $570 + call TrainerInfo_FarCopyData + call EnableLCD + ld hl, wTrainerInfoTextBoxWidthPlus1 + ld a, 18 + 1 + ld [hli], a + dec a + ld [hli], a + ld [hl], 1 + coord hl, 0, 0 + call TrainerInfo_DrawTextBox + ld hl, wTrainerInfoTextBoxWidthPlus1 + ld a, 16 + 1 + ld [hli], a + dec a + ld [hli], a + ld [hl], 3 + coord hl, 1, 10 + call TrainerInfo_DrawTextBox + coord hl, 0, 10 + ld a, $d7 + call TrainerInfo_DrawVerticalLine + coord hl, 19, 10 + call TrainerInfo_DrawVerticalLine + coord hl, 6, 9 + ld de, TrainerInfo_BadgesText + call PlaceString + coord hl, 2, 2 + ld de, TrainerInfo_NameMoneyTimeText + call PlaceString + coord hl, 7, 2 + ld de, wPlayerName + call PlaceString + coord hl, 8, 4 + ld de, wPlayerMoney + ld c, $e3 + call PrintBCDNumber + coord hl, 9, 6 + ld de, wPlayTimeHours ; hours + lb bc, LEFT_ALIGN | 1, 3 + call PrintNumber + ld [hl], $d6 ; colon tile ID + inc hl + ld de, wPlayTimeMinutes ; minutes + lb bc, LEADING_ZEROES | 1, 2 + jp PrintNumber + +TrainerInfo_FarCopyData: + ld a, BANK(TrainerInfoTextBoxTileGraphics) + jp FarCopyData2 + +TrainerInfo_NameMoneyTimeText: + db "NAME/" + next "MONEY/" + next "TIME/@" + +; $76 is a circle tile +TrainerInfo_BadgesText: + db $76,"BADGES",$76,"@" + +; draws a text box on the trainer info screen +; height is always 6 +; INPUT: +; hl = destination address +; [wTrainerInfoTextBoxWidthPlus1] = width +; [wTrainerInfoTextBoxWidth] = width - 1 +; [wTrainerInfoTextBoxNextRowOffset] = distance from the end of a text box row to the start of the next +TrainerInfo_DrawTextBox: + ld a, $79 ; upper left corner tile ID + lb de, $7a, $7b ; top edge and upper right corner tile ID's + call TrainerInfo_DrawHorizontalEdge ; draw top edge + call TrainerInfo_NextTextBoxRow + ld a, [wTrainerInfoTextBoxWidthPlus1] + ld e, a + ld d, 0 + ld c, 6 ; height of the text box +.loop + ld [hl], $7c ; left edge tile ID + add hl, de + ld [hl], $78 ; right edge tile ID + call TrainerInfo_NextTextBoxRow + dec c + jr nz, .loop + ld a, $7d ; lower left corner tile ID + lb de, $77, $7e ; bottom edge and lower right corner tile ID's + +TrainerInfo_DrawHorizontalEdge: + ld [hli], a ; place left corner tile + ld a, [wTrainerInfoTextBoxWidth] + ld c, a + ld a, d +.loop + ld [hli], a ; place edge tile + dec c + jr nz, .loop + ld a, e + ld [hl], a ; place right corner tile + ret + +TrainerInfo_NextTextBoxRow: + ld a, [wTrainerInfoTextBoxNextRowOffset] ; distance to the start of the next row +.loop + inc hl + dec a + jr nz, .loop + ret + +; draws a vertical line +; INPUT: +; hl = address of top tile in the line +; a = tile ID +TrainerInfo_DrawVerticalLine: + ld de, SCREEN_WIDTH + ld c, 8 +.loop + ld [hl], a + add hl, de + dec c + jr nz, .loop + ret + +StartMenu_SaveReset:: + ld a, [wd72e] + bit 6, a ; is the player using the link feature? + jp nz, Init + predef SaveSAV ; save the game + call LoadScreenTilesFromBuffer2 ; restore saved screen + jp HoldTextDisplayOpen + +StartMenu_Option:: + xor a + ld [H_AUTOBGTRANSFERENABLED], a + call ClearScreen + call UpdateSprites + callab DisplayOptionMenu + call LoadScreenTilesFromBuffer2 ; restore saved screen + call LoadTextBoxTilePatterns + call UpdateSprites + jp RedisplayStartMenu + +SwitchPartyMon:: + call SwitchPartyMon_InitVarOrSwapData ; swap data + ld a, [wSwappedMenuItem] + call SwitchPartyMon_ClearGfx + ld a, [wCurrentMenuItem] + call SwitchPartyMon_ClearGfx + jp RedrawPartyMenu_ + +SwitchPartyMon_ClearGfx: + push af + coord hl, 0, 0 + ld bc, SCREEN_WIDTH * 2 + call AddNTimes + ld c, SCREEN_WIDTH * 2 + ld a, " " +.clearMonBGLoop ; clear the mon's row in the party menu + ld [hli], a + dec c + jr nz, .clearMonBGLoop + pop af + ld hl, wOAMBuffer + ld bc, $10 + call AddNTimes + ld de, $4 + ld c, e +.clearMonOAMLoop + ld [hl], $a0 + add hl, de + dec c + jr nz, .clearMonOAMLoop + call WaitForSoundToFinish + ld a, SFX_SWAP + jp PlaySound + +SwitchPartyMon_InitVarOrSwapData: +; This is used to initialise [wMenuItemToSwap] and to actually swap the data. + ld a, [wMenuItemToSwap] + and a ; has [wMenuItemToSwap] been initialised yet? + jr nz, .pickedMonsToSwap +; If not, initialise [wMenuItemToSwap] so that it matches the current mon. + ld a, [wWhichPokemon] + inc a ; [wMenuItemToSwap] counts from 1 + ld [wMenuItemToSwap], a + ret +.pickedMonsToSwap + xor a + ld [wPartyMenuTypeOrMessageID], a + ld a, [wMenuItemToSwap] + dec a + ld b, a + ld a, [wCurrentMenuItem] + ld [wSwappedMenuItem], a + cp b ; swapping a mon with itself? + jr nz, .swappingDifferentMons +; can't swap a mon with itself + xor a + ld [wMenuItemToSwap], a + ld [wPartyMenuTypeOrMessageID], a + ret +.swappingDifferentMons + ld a, b + ld [wMenuItemToSwap], a + push hl + push de + ld hl, wPartySpecies + ld d, h + ld e, l + ld a, [wCurrentMenuItem] + add l + ld l, a + jr nc, .noCarry + inc h +.noCarry + ld a, [wMenuItemToSwap] + add e + ld e, a + jr nc, .noCarry2 + inc d +.noCarry2 + ld a, [hl] + ld [hSwapTemp], a + ld a, [de] + ld [hl], a + ld a, [hSwapTemp] + ld [de], a + ld hl, wPartyMons + ld bc, wPartyMon2 - wPartyMon1 + ld a, [wCurrentMenuItem] + call AddNTimes + push hl + ld de, wSwitchPartyMonTempBuffer + ld bc, wPartyMon2 - wPartyMon1 + call CopyData + ld hl, wPartyMons + ld bc, wPartyMon2 - wPartyMon1 + ld a, [wMenuItemToSwap] + call AddNTimes + pop de + push hl + ld bc, wPartyMon2 - wPartyMon1 + call CopyData + pop de + ld hl, wSwitchPartyMonTempBuffer + ld bc, wPartyMon2 - wPartyMon1 + call CopyData + ld hl, wPartyMonOT + ld a, [wCurrentMenuItem] + call SkipFixedLengthTextEntries + push hl + ld de, wSwitchPartyMonTempBuffer + ld bc, NAME_LENGTH + call CopyData + ld hl, wPartyMonOT + ld a, [wMenuItemToSwap] + call SkipFixedLengthTextEntries + pop de + push hl + ld bc, NAME_LENGTH + call CopyData + pop de + ld hl, wSwitchPartyMonTempBuffer + ld bc, NAME_LENGTH + call CopyData + ld hl, wPartyMonNicks + ld a, [wCurrentMenuItem] + call SkipFixedLengthTextEntries + push hl + ld de, wSwitchPartyMonTempBuffer + ld bc, NAME_LENGTH + call CopyData + ld hl, wPartyMonNicks + ld a, [wMenuItemToSwap] + call SkipFixedLengthTextEntries + pop de + push hl + ld bc, NAME_LENGTH + call CopyData + pop de + ld hl, wSwitchPartyMonTempBuffer + ld bc, NAME_LENGTH + call CopyData + ld a, [wMenuItemToSwap] + ld [wSwappedMenuItem], a + xor a + ld [wMenuItemToSwap], a + ld [wPartyMenuTypeOrMessageID], a + pop de + pop hl + ret diff --git a/engine/menus/swap_items.asm b/engine/menus/swap_items.asm new file mode 100644 index 00000000..826fe60b --- /dev/null +++ b/engine/menus/swap_items.asm @@ -0,0 +1,149 @@ +HandleItemListSwapping:: + ld a, [wListMenuID] + cp ITEMLISTMENU + jp nz, DisplayListMenuIDLoop ; only rearrange item list menus + push hl + ld hl, wListPointer + ld a, [hli] + ld h, [hl] + ld l, a + inc hl ; hl = beginning of list entries + ld a, [wCurrentMenuItem] + ld b, a + ld a, [wListScrollOffset] + add b + add a + ld c, a + ld b, 0 + add hl, bc ; hl = address of currently selected item entry + ld a, [hl] + pop hl + inc a + jp z, DisplayListMenuIDLoop ; ignore attempts to swap the Cancel menu item + ld a, [wMenuItemToSwap] ; ID of item chosen for swapping (counts from 1) + and a ; has the first item to swap already been chosen? + jr nz, .swapItems +; if not, set the currently selected item as the first item + ld a, [wCurrentMenuItem] + inc a + ld b, a + ld a, [wListScrollOffset] ; index of top (visible) menu item within the list + add b + ld [wMenuItemToSwap], a ; ID of item chosen for swapping (counts from 1) + ld c, 20 + call DelayFrames + jp DisplayListMenuIDLoop +.swapItems + ld a, [wCurrentMenuItem] + inc a + ld b, a + ld a, [wListScrollOffset] + add b + ld b, a + ld a, [wMenuItemToSwap] ; ID of item chosen for swapping (counts from 1) + cp b ; is the currently selected item the same as the first item to swap? + jp z, DisplayListMenuIDLoop ; ignore attempts to swap an item with itself + dec a + ld [wMenuItemToSwap], a ; ID of item chosen for swapping (counts from 1) + ld c, 20 + call DelayFrames + push hl + push de + ld hl, wListPointer + ld a, [hli] + ld h, [hl] + ld l, a + inc hl ; hl = beginning of list entries + ld d, h + ld e, l ; de = beginning of list entries + ld a, [wCurrentMenuItem] + ld b, a + ld a, [wListScrollOffset] + add b + add a + ld c, a + ld b, 0 + add hl, bc ; hl = address of currently selected item entry + ld a, [wMenuItemToSwap] ; ID of item chosen for swapping (counts from 1) + add a + add e + ld e, a + jr nc, .noCarry + inc d +.noCarry ; de = address of first item to swap + ld a, [de] + ld b, a + ld a, [hli] + cp b + jr z, .swapSameItemType +.swapDifferentItems + ld [$ff95], a ; [$ff95] = second item ID + ld a, [hld] + ld [$ff96], a ; [$ff96] = 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, [$ff96] + ld [de], a ; put second item quantity in first item slot + dec de + ld a, [$ff95] + ld [de], a ; put second item ID in first item slot + xor a + ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped + pop de + pop hl + jp DisplayListMenuIDLoop +.swapSameItemType + inc de + ld a, [hl] + ld b, a + ld a, [de] + add b ; a = sum of both item quantities + cp 100 ; is the sum too big for one item slot? + jr c, .combineItemSlots +; swap enough items from the first slot to max out the second slot if they can't be combined + sub 99 + ld [de], a + ld a, 99 + ld [hl], a + jr .done +.combineItemSlots + ld [hl], a ; put the sum in the second item slot + ld hl, wListPointer + ld a, [hli] + ld h, [hl] + ld l, a + dec [hl] ; decrease the number of items + ld a, [hl] + ld [wListCount], a ; update number of items variable + cp 1 + jr nz, .skipSettingMaxMenuItemID + ld [wMaxMenuItem], a ; if the number of items is only one now, update the max menu item ID +.skipSettingMaxMenuItemID + dec de + ld h, d + ld l, e + inc hl + inc hl ; hl = address of item after first item to swap +.moveItemsUpLoop ; erase the first item slot and move up all the following item slots to fill the gap + ld a, [hli] + ld [de], a + inc de + inc a ; reached the $ff terminator? + jr z, .afterMovingItemsUp + ld a, [hli] + ld [de], a + inc de + jr .moveItemsUpLoop +.afterMovingItemsUp + xor a + ld [wListScrollOffset], a + ld [wCurrentMenuItem], a +.done + xor a + ld [wMenuItemToSwap], a ; 0 means no item is currently being swapped + pop de + pop hl + jp DisplayListMenuIDLoop diff --git a/engine/menus/text_box.asm b/engine/menus/text_box.asm new file mode 100644 index 00000000..00045959 --- /dev/null +++ b/engine/menus/text_box.asm @@ -0,0 +1,767 @@ +; function to draw various text boxes +DisplayTextBoxID_:: + ld a, [wTextBoxID] + cp TWO_OPTION_MENU + jp z, DisplayTwoOptionMenu + ld c, a + ld hl, TextBoxFunctionTable + ld de, 3 + call SearchTextBoxTable + jr c, .functionTableMatch + ld hl, TextBoxCoordTable + ld de, 5 + call SearchTextBoxTable + jr c, .coordTableMatch + ld hl, TextBoxTextAndCoordTable + ld de, 9 + call SearchTextBoxTable + jr c, .textAndCoordTableMatch +.done + ret +.functionTableMatch + ld a, [hli] + ld h, [hl] + ld l, a ; hl = address of function + ld de, .done + push de + jp hl ; jump to the function +.coordTableMatch + call GetTextBoxIDCoords + call GetAddressOfScreenCoords + call TextBoxBorder + ret +.textAndCoordTableMatch + call GetTextBoxIDCoords + push hl + call GetAddressOfScreenCoords + call TextBoxBorder + pop hl + call GetTextBoxIDText + ld a, [wd730] + push af + ld a, [wd730] + set 6, a ; no pauses between printing each letter + ld [wd730], a + call PlaceString + pop af + ld [wd730], a + call UpdateSprites + ret + +; function to search a table terminated with $ff for a byte matching c in increments of de +; sets carry flag if a match is found and clears carry flag if not +SearchTextBoxTable: + dec de +.loop + ld a, [hli] + cp $ff + jr z, .notFound + cp c + jr z, .found + add hl, de + jr .loop +.found + scf +.notFound + ret + +; function to load coordinates from the TextBoxCoordTable or the TextBoxTextAndCoordTable +; INPUT: +; hl = address of coordinates +; OUTPUT: +; b = height +; c = width +; d = row of upper left corner +; e = column of upper left corner +GetTextBoxIDCoords: + ld a, [hli] ; column of upper left corner + ld e, a + ld a, [hli] ; row of upper left corner + ld d, a + ld a, [hli] ; column of lower right corner + sub e + dec a + ld c, a ; c = width + ld a, [hli] ; row of lower right corner + sub d + dec a + ld b, a ; b = height + ret + +; function to load a text address and text coordinates from the TextBoxTextAndCoordTable +GetTextBoxIDText: + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a ; de = address of text + push de ; save text address + ld a, [hli] + ld e, a ; column of upper left corner of text + ld a, [hl] + ld d, a ; row of upper left corner of text + call GetAddressOfScreenCoords + pop de ; restore text address + ret + +; function to point hl to the screen coordinates +; INPUT: +; d = row +; e = column +; OUTPUT: +; hl = address of upper left corner of text box +GetAddressOfScreenCoords: + push bc + coord hl, 0, 0 + ld bc, 20 +.loop ; loop to add d rows to the base address + ld a, d + and a + jr z, .addedRows + add hl, bc + dec d + jr .loop +.addedRows + pop bc + add hl, de + ret + +; Format: +; 00: text box ID +; 01-02: function address +TextBoxFunctionTable: + dbw MONEY_BOX, DisplayMoneyBox + dbw BUY_SELL_QUIT_MENU, DoBuySellQuitMenu + dbw FIELD_MOVE_MON_MENU, DisplayFieldMoveMonMenu + db $ff ; terminator + +; Format: +; 00: text box ID +; 01: column of upper left corner +; 02: row of upper left corner +; 03: column of lower right corner +; 04: row of lower right corner +TextBoxCoordTable: + db MESSAGE_BOX, 0, 12, 19, 17 + db $03, 0, 0, 19, 14 + db $07, 0, 0, 11, 6 + db LIST_MENU_BOX, 4, 2, 19, 12 + db $10, 7, 0, 19, 17 + db MON_SPRITE_POPUP, 6, 4, 14, 13 + db $ff ; terminator + +; Format: +; 00: text box ID +; 01: column of upper left corner +; 02: row of upper left corner +; 03: column of lower right corner +; 04: row of lower right corner +; 05-06: address of text +; 07: column of beginning of text +; 08: row of beginning of text +; table of window positions and corresponding text [key, start column, start row, end column, end row, text pointer [2 bytes], text column, text row] +TextBoxTextAndCoordTable: + db JP_MOCHIMONO_MENU_TEMPLATE + db 0,0,14,17 ; text box coordinates + dw JapaneseMochimonoText + db 3,0 ; text coordinates + + db USE_TOSS_MENU_TEMPLATE + db 13,10,19,14 ; text box coordinates + dw UseTossText + db 15,11 ; text coordinates + + db JP_SAVE_MESSAGE_MENU_TEMPLATE + db 0,0,7,5 ; text box coordinates + dw JapaneseSaveMessageText + db 2,2 ; text coordinates + + db JP_SPEED_OPTIONS_MENU_TEMPLATE + db 0,6,5,10 ; text box coordinates + dw JapaneseSpeedOptionsText + db 2,7 ; text coordinates + + db BATTLE_MENU_TEMPLATE + db 8,12,19,17 ; text box coordinates + dw BattleMenuText + db 10,14 ; text coordinates + + db SAFARI_BATTLE_MENU_TEMPLATE + db 0,12,19,17 ; text box coordinates + dw SafariZoneBattleMenuText + db 2,14 ; text coordinates + + db SWITCH_STATS_CANCEL_MENU_TEMPLATE + db 11,11,19,17 ; text box coordinates + dw SwitchStatsCancelText + db 13,12 ; text coordinates + + db BUY_SELL_QUIT_MENU_TEMPLATE + db 0,0,10,6 ; text box coordinates + dw BuySellQuitText + db 2,1 ; text coordinates + + db MONEY_BOX_TEMPLATE + db 11,0,19,2 ; text box coordinates + dw MoneyText + db 13,0 ; text coordinates + + db JP_AH_MENU_TEMPLATE + db 7,6,11,10 ; text box coordinates + dw JapaneseAhText + db 8,8 ; text coordinates + + db JP_POKEDEX_MENU_TEMPLATE + db 11,8,19,17 ; text box coordinates + dw JapanesePokedexMenu + db 12,10 ; text coordinates + +; note that there is no terminator + +BuySellQuitText: + db "BUY" + next "SELL" + next "QUIT@@" + +UseTossText: + db "USE" + next "TOSS@" + +JapaneseSaveMessageText: + db "きろく" + next "メッセージ@" + +JapaneseSpeedOptionsText: + db "はやい" + next "おそい@" + +MoneyText: + db "MONEY@" + +JapaneseMochimonoText: + db "もちもの@" + +JapaneseMainMenuText: + db "つづきから" + next "さいしょから@" + +BattleMenuText: + db "FIGHT ",$E1,$E2 + next "ITEM RUN@" + +SafariZoneBattleMenuText: + db "BALL× BAIT" + next "THROW ROCK RUN@" + +SwitchStatsCancelText: + db "SWITCH" + next "STATS" + next "CANCEL@" + +JapaneseAhText: + db "アッ!@" + +JapanesePokedexMenu: + db "データをみる" + next "なきごえ" + next "ぶんぷをみる" + next "キャンセル@" + +DisplayMoneyBox: + ld hl, wd730 + set 6, [hl] + ld a, MONEY_BOX_TEMPLATE + ld [wTextBoxID], a + call DisplayTextBoxID + coord hl, 13, 1 + ld b, 1 + ld c, 6 + call ClearScreenArea + coord hl, 12, 1 + ld de, wPlayerMoney + ld c, $a3 + call PrintBCDNumber + ld hl, wd730 + res 6, [hl] + ret + +CurrencyString: + db " ¥@" + +DoBuySellQuitMenu: + ld a, [wd730] + set 6, a ; no printing delay + ld [wd730], a + xor a + ld [wChosenMenuItem], a + ld a, BUY_SELL_QUIT_MENU_TEMPLATE + ld [wTextBoxID], a + call DisplayTextBoxID + ld a, A_BUTTON | B_BUTTON + ld [wMenuWatchedKeys], a + ld a, $2 + ld [wMaxMenuItem], a + ld a, $1 + ld [wTopMenuItemY], a + ld a, $1 + ld [wTopMenuItemX], a + xor a + ld [wCurrentMenuItem], a + ld [wLastMenuItem], a + ld [wMenuWatchMovingOutOfBounds], a + ld a, [wd730] + res 6, a ; turn on the printing delay + ld [wd730], a + call HandleMenuInput + call PlaceUnfilledArrowMenuCursor + bit 0, a ; was A pressed? + jr nz, .pressedA + bit 1, a ; was B pressed? (always true since only A/B are watched) + jr z, .pressedA + ld a, CANCELLED_MENU + ld [wMenuExitMethod], a + jr .quit +.pressedA + ld a, CHOSE_MENU_ITEM + ld [wMenuExitMethod], a + ld a, [wCurrentMenuItem] + ld [wChosenMenuItem], a + ld b, a + ld a, [wMaxMenuItem] + cp b + jr z, .quit + ret +.quit + ld a, CANCELLED_MENU + ld [wMenuExitMethod], a + ld a, [wCurrentMenuItem] + ld [wChosenMenuItem], a + scf + ret + +; displays a menu with two options to choose from +; b = Y of upper left corner of text region +; c = X of upper left corner of text region +; hl = address where the text box border should be drawn +DisplayTwoOptionMenu: + push hl + ld a, [wd730] + set 6, a ; no printing delay + ld [wd730], a + +; pointless because both values are overwritten before they are read + xor a + ld [wChosenMenuItem], a + ld [wMenuExitMethod], a + + ld a, A_BUTTON | B_BUTTON + ld [wMenuWatchedKeys], a + ld a, $1 + ld [wMaxMenuItem], a + ld a, b + ld [wTopMenuItemY], a + ld a, c + ld [wTopMenuItemX], a + xor a + ld [wLastMenuItem], a + ld [wMenuWatchMovingOutOfBounds], a + push hl + ld hl, wTwoOptionMenuID + bit 7, [hl] ; select second menu item by default? + res 7, [hl] + jr z, .storeCurrentMenuItem + inc a +.storeCurrentMenuItem + ld [wCurrentMenuItem], a + pop hl + push hl + push hl + call TwoOptionMenu_SaveScreenTiles + ld a, [wTwoOptionMenuID] + ld hl, TwoOptionMenuStrings + ld e, a + ld d, $0 + ld a, $5 +.menuStringLoop + add hl, de + dec a + jr nz, .menuStringLoop + ld a, [hli] + ld c, a + ld a, [hli] + ld b, a + ld e, l + ld d, h + pop hl + push de + ld a, [wTwoOptionMenuID] + cp TRADE_CANCEL_MENU + jr nz, .notTradeCancelMenu + call CableClub_TextBoxBorder + jr .afterTextBoxBorder +.notTradeCancelMenu + call TextBoxBorder +.afterTextBoxBorder + call UpdateSprites + pop hl + ld a, [hli] + and a ; put blank line before first menu item? + ld bc, 20 + 2 + jr z, .noBlankLine + ld bc, 2 * 20 + 2 +.noBlankLine + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + pop hl + add hl, bc + call PlaceString + ld hl, wd730 + res 6, [hl] ; turn on the printing delay + ld a, [wTwoOptionMenuID] + cp NO_YES_MENU + jr nz, .notNoYesMenu +; No/Yes menu +; this menu type ignores the B button +; it only seems to be used when confirming the deletion of a save file + xor a + ld [wTwoOptionMenuID], a + ld a, [wFlags_0xcd60] + push af + push hl + ld hl, wFlags_0xcd60 + bit 5, [hl] + set 5, [hl] ; don't play sound when A or B is pressed in menu + pop hl +.noYesMenuInputLoop + call HandleMenuInput + bit 1, a ; A button pressed? + jr nz, .noYesMenuInputLoop ; try again if A was not pressed + pop af + pop hl + ld [wFlags_0xcd60], a + ld a, SFX_PRESS_AB + call PlaySound + jr .pressedAButton +.notNoYesMenu + xor a + ld [wTwoOptionMenuID], a + call HandleMenuInput + pop hl + bit 1, a ; A button pressed? + jr nz, .choseSecondMenuItem ; automatically choose the second option if B is pressed +.pressedAButton + ld a, [wCurrentMenuItem] + ld [wChosenMenuItem], a + and a + jr nz, .choseSecondMenuItem +; chose first menu item + ld a, CHOSE_FIRST_ITEM + ld [wMenuExitMethod], a + ld c, 15 + call DelayFrames + call TwoOptionMenu_RestoreScreenTiles + and a + ret +.choseSecondMenuItem + ld a, 1 + ld [wCurrentMenuItem], a + ld [wChosenMenuItem], a + ld a, CHOSE_SECOND_ITEM + ld [wMenuExitMethod], a + ld c, 15 + call DelayFrames + call TwoOptionMenu_RestoreScreenTiles + scf + ret + +; Some of the wider/taller two option menus will not have the screen areas +; they cover be fully saved/restored by the two functions below. +; The bottom and right edges of the menu may remain after the function returns. + +TwoOptionMenu_SaveScreenTiles: + ld de, wBuffer + lb bc, 5, 6 +.loop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .loop + push bc + ld bc, SCREEN_WIDTH - 6 + add hl, bc + pop bc + ld c, $6 + dec b + jr nz, .loop + ret + +TwoOptionMenu_RestoreScreenTiles: + ld de, wBuffer + lb bc, 5, 6 +.loop + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .loop + push bc + ld bc, SCREEN_WIDTH - 6 + add hl, bc + pop bc + ld c, 6 + dec b + jr nz, .loop + call UpdateSprites + ret + +; Format: +; 00: byte width +; 01: byte height +; 02: byte put blank line before first menu item +; 03: word text pointer +TwoOptionMenuStrings: + db 4,3,0 + dw .YesNoMenu + db 6,3,0 + dw .NorthWestMenu + db 6,3,0 + dw .SouthEastMenu + db 6,3,0 + dw .YesNoMenu + db 6,3,0 + dw .NorthEastMenu + db 7,3,0 + dw .TradeCancelMenu + db 7,4,1 + dw .HealCancelMenu + db 4,3,0 + dw .NoYesMenu + +.NoYesMenu + db "NO" + next "YES@" +.YesNoMenu + db "YES" + next "NO@" +.NorthWestMenu + db "NORTH" + next "WEST@" +.SouthEastMenu + db "SOUTH" + next "EAST@" +.NorthEastMenu + db "NORTH" + next "EAST@" +.TradeCancelMenu + db "TRADE" + next "CANCEL@" +.HealCancelMenu + db "HEAL" + next "CANCEL@" + +DisplayFieldMoveMonMenu: + xor a + ld hl, wFieldMoves + ld [hli], a ; wFieldMoves + ld [hli], a ; wFieldMoves + 1 + ld [hli], a ; wFieldMoves + 2 + ld [hli], a ; wFieldMoves + 3 + ld [hli], a ; wNumFieldMoves + ld [hl], 12 ; wFieldMovesLeftmostXCoord + call GetMonFieldMoves + ld a, [wNumFieldMoves] + and a + jr nz, .fieldMovesExist + +; no field moves + coord hl, 11, 11 + ld b, 5 + ld c, 7 + call TextBoxBorder + call UpdateSprites + ld a, 12 + ld [hFieldMoveMonMenuTopMenuItemX], a + coord hl, 13, 12 + ld de, PokemonMenuEntries + jp PlaceString + +.fieldMovesExist + push af + +; Calculate the text box position and dimensions based on the leftmost X coord +; of the field move names before adjusting for the number of field moves. + coord hl, 0, 11 + ld a, [wFieldMovesLeftmostXCoord] + dec a + ld e, a + ld d, 0 + add hl, de + ld b, 5 + ld a, 18 + sub e + ld c, a + pop af + +; For each field move, move the top of the text box up 2 rows while the leaving +; the bottom of the text box at the bottom of the screen. + ld de, -SCREEN_WIDTH * 2 +.textBoxHeightLoop + add hl, de + inc b + inc b + dec a + jr nz, .textBoxHeightLoop + +; Make space for an extra blank row above the top field move. + ld de, -SCREEN_WIDTH + add hl, de + inc b + + call TextBoxBorder + call UpdateSprites + +; Calculate the position of the first field move name to print. + coord hl, 0, 12 + ld a, [wFieldMovesLeftmostXCoord] + inc a + ld e, a + ld d, 0 + add hl, de + ld de, -SCREEN_WIDTH * 2 + ld a, [wNumFieldMoves] +.calcFirstFieldMoveYLoop + add hl, de + dec a + jr nz, .calcFirstFieldMoveYLoop + + xor a + ld [wNumFieldMoves], a + ld de, wFieldMoves +.printNamesLoop + push hl + ld hl, FieldMoveNames + ld a, [de] + and a + jr z, .donePrintingNames + inc de + ld b, a ; index of name +.skipNamesLoop ; skip past names before the name we want + dec b + jr z, .reachedName +.skipNameLoop ; skip past current name + ld a, [hli] + cp "@" + jr nz, .skipNameLoop + jr .skipNamesLoop +.reachedName + ld b, h + ld c, l + pop hl + push de + ld d, b + ld e, c + call PlaceString + ld bc, SCREEN_WIDTH * 2 + add hl, bc + pop de + jr .printNamesLoop + +.donePrintingNames + pop hl + ld a, [wFieldMovesLeftmostXCoord] + ld [hFieldMoveMonMenuTopMenuItemX], a + coord hl, 0, 12 + ld a, [wFieldMovesLeftmostXCoord] + inc a + ld e, a + ld d, 0 + add hl, de + ld de, PokemonMenuEntries + jp PlaceString + +FieldMoveNames: + db "CUT@" + db "FLY@" + db "@" + db "SURF@" + db "STRENGTH@" + db "FLASH@" + db "DIG@" + db "TELEPORT@" + db "SOFTBOILED@" + +PokemonMenuEntries: + db "STATS" + next "SWITCH" + next "CANCEL@" + +GetMonFieldMoves: + ld a, [wWhichPokemon] + ld hl, wPartyMon1Moves + ld bc, wPartyMon2 - wPartyMon1 + call AddNTimes + ld d, h + ld e, l + ld c, NUM_MOVES + 1 + ld hl, wFieldMoves +.loop + push hl +.nextMove + dec c + jr z, .done + ld a, [de] ; move ID + and a + jr z, .done + ld b, a + inc de + ld hl, FieldMoveDisplayData +.fieldMoveLoop + ld a, [hli] + cp $ff + jr z, .nextMove ; if the move is not a field move + cp b + jr z, .foundFieldMove + inc hl + inc hl + jr .fieldMoveLoop +.foundFieldMove + ld a, b + ld [wLastFieldMoveID], a + ld a, [hli] ; field move name index + ld b, [hl] ; field move leftmost X coordinate + pop hl + ld [hli], a ; store name index in wFieldMoves + ld a, [wNumFieldMoves] + inc a + ld [wNumFieldMoves], a + ld a, [wFieldMovesLeftmostXCoord] + cp b + jr c, .skipUpdatingLeftmostXCoord + ld a, b + ld [wFieldMovesLeftmostXCoord], a +.skipUpdatingLeftmostXCoord + ld a, [wLastFieldMoveID] + ld b, a + jr .loop +.done + pop hl + ret + +; Format: [Move id], [name index], [leftmost tile] +; Move id = id of move +; Name index = index of name in FieldMoveNames +; Leftmost tile = -1 + tile column in which the first letter of the move's name should be displayed +; "SOFTBOILED" is $08 because it has 4 more letters than "SURF", for example, whose value is $0C +FieldMoveDisplayData: + db CUT, $01, $0C + db FLY, $02, $0C + db $B4, $03, $0C ; unused field move + db SURF, $04, $0C + db STRENGTH, $05, $0A + db FLASH, $06, $0C + db DIG, $07, $0C + db TELEPORT, $08, $0A + db SOFTBOILED, $09, $08 + db $ff ; list terminator |