ref: cdfab2e8194eff4559a02aac0fb7c9aa6b989686
dir: /engine/menus/text_box.asm/
; 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