shithub: pokered

Download patch

ref: c2381bb3e624e966c323610df762a299468a1728
parent: 6f1ac06e03cd046f61fb2370ff7379a29932bbb1
author: YamaArashi <shadow962@live.com>
date: Sat Sep 17 14:17:57 EDT 2016

split bank 1 into files

--- /dev/null
+++ b/engine/black_out.asm
@@ -1,0 +1,46 @@
+ResetStatusAndHalveMoneyOnBlackout::
+; Reset player status on blackout.
+	xor a
+	ld [wBattleResult], a
+	ld [wWalkBikeSurfState], a
+	ld [wIsInBattle], a
+	ld [wMapPalOffset], a
+	ld [wNPCMovementScriptFunctionNum], a
+	ld [hJoyHeld], a
+	ld [wNPCMovementScriptPointerTableNum], a
+	ld [wFlags_0xcd60], a
+
+	ld [hMoney], a
+	ld [hMoney + 1], a
+	ld [hMoney + 2], a
+	call HasEnoughMoney
+	jr c, .lostmoney ; never happens
+
+	; Halve the player's money.
+	ld a, [wPlayerMoney]
+	ld [hMoney], a
+	ld a, [wPlayerMoney + 1]
+	ld [hMoney + 1], a
+	ld a, [wPlayerMoney + 2]
+	ld [hMoney + 2], a
+	xor a
+	ld [hDivideBCDDivisor], a
+	ld [hDivideBCDDivisor + 1], a
+	ld a, 2
+	ld [hDivideBCDDivisor + 2], a
+	predef DivideBCDPredef3
+	ld a, [hDivideBCDQuotient]
+	ld [wPlayerMoney], a
+	ld a, [hDivideBCDQuotient + 1]
+	ld [wPlayerMoney + 1], a
+	ld a, [hDivideBCDQuotient + 2]
+	ld [wPlayerMoney + 2], a
+
+.lostmoney
+	ld hl, wd732
+	set 2, [hl]
+	res 3, [hl]
+	set 6, [hl]
+	ld a, %11111111
+	ld [wJoyIgnore], a
+	predef_jump HealParty
--- /dev/null
+++ b/engine/debug1.asm
@@ -1,0 +1,33 @@
+; This function appears to never be used.
+; It is likely a debugging feature to give the player Tsunekazu Ishihara's
+; favorite Pokemon. This is indicated by the overpowered Exeggutor, which
+; Ishihara (president of Creatures Inc.) said was his favorite Pokemon in an ABC
+; interview on February 8, 2000.
+; "Exeggutor is my favorite. That's because I was always using this character
+; while I was debugging the program."
+; http://www.ign.com/articles/2000/02/09/abc-news-pokamon-chat-transcript
+
+SetIshiharaTeam:
+	ld de, IshiharaTeam
+.loop
+	ld a, [de]
+	cp $ff
+	ret z
+	ld [wcf91], a
+	inc de
+	ld a, [de]
+	ld [wCurEnemyLVL], a
+	inc de
+	call AddPartyMon
+	jr .loop
+
+IshiharaTeam:
+	db EXEGGUTOR,90
+	db MEW,20
+	db JOLTEON,56
+	db DUGTRIO,56
+	db ARTICUNO,57
+	db $FF
+
+EmptyFunc:
+	ret
--- /dev/null
+++ b/engine/display_pokedex.asm
@@ -1,0 +1,19 @@
+_DisplayPokedex:
+	ld hl, wd730
+	set 6, [hl]
+	predef ShowPokedexData
+	ld hl, wd730
+	res 6, [hl]
+	call ReloadMapData
+	ld c, 10
+	call DelayFrames
+	predef IndexToPokedex
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_SET
+	ld hl, wPokedexSeen
+	predef FlagActionPredef
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ret
--- /dev/null
+++ b/engine/display_text_id_init.asm
@@ -1,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 a,$ff ; is the sprite visible?
+	jr z,.nextSprite
+; if it is visible
+	and a,$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
--- /dev/null
+++ b/engine/menu/draw_start_menu.asm
@@ -1,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 pokdex
+	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
--- /dev/null
+++ b/engine/menu/swap_items.asm
@@ -1,0 +1,149 @@
+HandleItemListSwapping:
+	ld a,[wListMenuID]
+	cp a,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 a,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 a,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 a,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
--- /dev/null
+++ b/engine/menu/text_box.asm
@@ -1,0 +1,767 @@
+; function to draw various text boxes
+DisplayTextBoxID_:
+	ld a,[wTextBoxID]
+	cp a,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 a,$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
--- /dev/null
+++ b/engine/overworld/set_blackout_map.asm
@@ -1,0 +1,29 @@
+SetLastBlackoutMap:
+; Set the map to return to when
+; blacking out or using Teleport or Dig.
+; Safari rest houses don't count.
+
+	push hl
+	ld hl, SafariZoneRestHouses
+	ld a, [wCurMap]
+	ld b, a
+.loop
+	ld a, [hli]
+	cp -1
+	jr z, .notresthouse
+	cp b
+	jr nz, .loop
+	jr .done
+
+.notresthouse
+	ld a, [wLastMap]
+	ld [wLastBlackoutMap], a
+.done
+	pop hl
+	ret
+
+SafariZoneRestHouses:
+	db SAFARI_ZONE_REST_HOUSE_2
+	db SAFARI_ZONE_REST_HOUSE_3
+	db SAFARI_ZONE_REST_HOUSE_4
+	db -1
--- /dev/null
+++ b/engine/remove_pokemon.asm
@@ -1,0 +1,95 @@
+_RemovePokemon:
+	ld hl, wPartyCount
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7b74
+	ld hl, wNumInBox
+.asm_7b74
+	ld a, [hl]
+	dec a
+	ld [hli], a
+	ld a, [wWhichPokemon]
+	ld c, a
+	ld b, $0
+	add hl, bc
+	ld e, l
+	ld d, h
+	inc de
+.asm_7b81
+	ld a, [de]
+	inc de
+	ld [hli], a
+	inc a
+	jr nz, .asm_7b81
+	ld hl, wPartyMonOT
+	ld d, $5
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7b97
+	ld hl, wBoxMonOT
+	ld d, $13
+.asm_7b97
+	ld a, [wWhichPokemon]
+	call SkipFixedLengthTextEntries
+	ld a, [wWhichPokemon]
+	cp d
+	jr nz, .asm_7ba6
+	ld [hl], $ff
+	ret
+.asm_7ba6
+	ld d, h
+	ld e, l
+	ld bc, NAME_LENGTH
+	add hl, bc
+	ld bc, wPartyMonNicks
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7bb8
+	ld bc, wBoxMonNicks
+.asm_7bb8
+	call CopyDataUntil
+	ld hl, wPartyMons
+	ld bc, wPartyMon2 - wPartyMon1
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7bcd
+	ld hl, wBoxMons
+	ld bc, wBoxMon2 - wBoxMon1
+.asm_7bcd
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld d, h
+	ld e, l
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7be4
+	ld bc, wBoxMon2 - wBoxMon1
+	add hl, bc
+	ld bc, wBoxMonOT
+	jr .asm_7beb
+.asm_7be4
+	ld bc, wPartyMon2 - wPartyMon1
+	add hl, bc
+	ld bc, wPartyMonOT
+.asm_7beb
+	call CopyDataUntil
+	ld hl, wPartyMonNicks
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7bfa
+	ld hl, wBoxMonNicks
+.asm_7bfa
+	ld bc, NAME_LENGTH
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld d, h
+	ld e, l
+	ld bc, NAME_LENGTH
+	add hl, bc
+	ld bc, wPokedexOwned
+	ld a, [wRemoveMonFromBox]
+	and a
+	jr z, .asm_7c15
+	ld bc, wBoxMonNicksEnd
+.asm_7c15
+	jp CopyDataUntil
--- /dev/null
+++ b/engine/special_warps.asm
@@ -1,0 +1,149 @@
+SpecialWarpIn:
+	call LoadSpecialWarpData
+	predef LoadTilesetHeader
+	ld hl,wd732
+	bit 2,[hl] ; dungeon warp or fly warp?
+	res 2,[hl]
+	jr z,.next
+; if dungeon warp or fly warp
+	ld a,[wDestinationMap]
+	jr .next2
+.next
+	bit 1,[hl]
+	jr z,.next3
+	call EmptyFunc
+.next3
+	ld a,0
+.next2
+	ld b,a
+	ld a,[wd72d]
+	and a
+	jr nz,.next4
+	ld a,b
+.next4
+	ld hl,wd732
+	bit 4,[hl] ; dungeon warp?
+	ret nz
+; if not dungeon warp
+	ld [wLastMap],a
+	ret
+
+; gets the map ID, tile block map view pointer, tileset, and coordinates
+LoadSpecialWarpData:
+	ld a, [wd72d]
+	cp TRADE_CENTER
+	jr nz, .notTradeCenter
+	ld hl, TradeCenterSpec1
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK ; which gameboy is clocking determines who is on the left and who is on the right
+	jr z, .copyWarpData
+	ld hl, TradeCenterSpec2
+	jr .copyWarpData
+.notTradeCenter
+	cp COLOSSEUM
+	jr nz, .notColosseum
+	ld hl, ColosseumSpec1
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK
+	jr z, .copyWarpData
+	ld hl, ColosseumSpec2
+	jr .copyWarpData
+.notColosseum
+	ld a, [wd732]
+	bit 1, a
+	jr nz, .notFirstMap
+	bit 2, a
+	jr nz, .notFirstMap
+	ld hl, FirstMapSpec
+.copyWarpData
+	ld de, wCurMap
+	ld c, $7
+.copyWarpDataLoop
+	ld a, [hli]
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .copyWarpDataLoop
+	ld a, [hli]
+	ld [wCurMapTileset], a
+	xor a
+	jr .done
+.notFirstMap
+	ld a, [wLastMap] ; this value is overwritten before it's ever read
+	ld hl, wd732
+	bit 4, [hl] ; used dungeon warp (jumped down hole/waterfall)?
+	jr nz, .usedDunegonWarp
+	bit 6, [hl] ; return to last pokemon center (or player's house)?
+	res 6, [hl]
+	jr z, .otherDestination
+; return to last pokemon center or player's house
+	ld a, [wLastBlackoutMap]
+	jr .usedFlyWarp
+.usedDunegonWarp
+	ld hl, wd72d
+	res 4, [hl]
+	ld a, [wDungeonWarpDestinationMap]
+	ld b, a
+	ld [wCurMap], a
+	ld a, [wWhichDungeonWarp]
+	ld c, a
+	ld hl, DungeonWarpList
+	ld de, 0
+	ld a, 6
+	ld [wDungeonWarpDataEntrySize], a
+.dungeonWarpListLoop
+	ld a, [hli]
+	cp b
+	jr z, .matchedDungeonWarpDestinationMap
+	inc hl
+	jr .nextDungeonWarp
+.matchedDungeonWarpDestinationMap
+	ld a, [hli]
+	cp c
+	jr z, .matchedDungeonWarpID
+.nextDungeonWarp
+	ld a, [wDungeonWarpDataEntrySize]
+	add e
+	ld e, a
+	jr .dungeonWarpListLoop
+.matchedDungeonWarpID
+	ld hl, DungeonWarpData
+	add hl, de
+	jr .copyWarpData2
+.otherDestination
+	ld a, [wDestinationMap]
+.usedFlyWarp
+	ld b, a
+	ld [wCurMap], a
+	ld hl, FlyWarpDataPtr
+.flyWarpDataPtrLoop
+	ld a, [hli]
+	inc hl
+	cp b
+	jr z, .foundFlyWarpMatch
+	inc hl
+	inc hl
+	jr .flyWarpDataPtrLoop
+.foundFlyWarpMatch
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+.copyWarpData2
+	ld de, wCurrentTileBlockMapViewPointer
+	ld c, $6
+.copyWarpDataLoop2
+	ld a, [hli]
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .copyWarpDataLoop2
+	xor a ; OVERWORLD
+	ld [wCurMapTileset], a
+.done
+	ld [wYOffsetSinceLastSpecialWarp], a
+	ld [wXOffsetSinceLastSpecialWarp], a
+	ld a, $ff ; the player's coordinates have already been updated using a special warp, so don't use any of the normal warps
+	ld [wDestinationWarpID], a
+	ret
+
+INCLUDE "data/special_warps.asm"
--- /dev/null
+++ b/engine/subtract_paid_money.asm
@@ -1,0 +1,17 @@
+; subtracts the amount the player paid from their money
+; sets carry flag if there is enough money and unsets carry flag if not
+SubtractAmountPaidFromMoney_:
+	ld de,wPlayerMoney
+	ld hl,hMoney ; total price of items
+	ld c,3 ; length of money in bytes
+	call StringCmp
+	ret c
+	ld de,wPlayerMoney + 2
+	ld hl,hMoney + 2 ; total price of items
+	ld c,3 ; length of money in bytes
+	predef SubBCDPredef ; subtract total price from money
+	ld a,MONEY_BOX
+	ld [wTextBoxID],a
+	call DisplayTextBoxID ; redraw money text box
+	and a
+	ret
--- a/main.asm
+++ b/main.asm
@@ -18,54 +18,8 @@
 
 INCLUDE "data/facing.asm"
 
-ResetStatusAndHalveMoneyOnBlackout::
-; Reset player status on blackout.
-	xor a
-	ld [wBattleResult], a
-	ld [wWalkBikeSurfState], a
-	ld [wIsInBattle], a
-	ld [wMapPalOffset], a
-	ld [wNPCMovementScriptFunctionNum], a
-	ld [hJoyHeld], a
-	ld [wNPCMovementScriptPointerTableNum], a
-	ld [wFlags_0xcd60], a
+INCLUDE "engine/black_out.asm"
 
-	ld [hMoney], a
-	ld [hMoney + 1], a
-	ld [hMoney + 2], a
-	call HasEnoughMoney
-	jr c, .lostmoney ; never happens
-
-	; Halve the player's money.
-	ld a, [wPlayerMoney]
-	ld [hMoney], a
-	ld a, [wPlayerMoney + 1]
-	ld [hMoney + 1], a
-	ld a, [wPlayerMoney + 2]
-	ld [hMoney + 2], a
-	xor a
-	ld [hDivideBCDDivisor], a
-	ld [hDivideBCDDivisor + 1], a
-	ld a, 2
-	ld [hDivideBCDDivisor + 2], a
-	predef DivideBCDPredef3
-	ld a, [hDivideBCDQuotient]
-	ld [wPlayerMoney], a
-	ld a, [hDivideBCDQuotient + 1]
-	ld [wPlayerMoney + 1], a
-	ld a, [hDivideBCDQuotient + 2]
-	ld [wPlayerMoney + 2], a
-
-.lostmoney
-	ld hl, wd732
-	set 2, [hl]
-	res 3, [hl]
-	set 6, [hl]
-	ld a, %11111111
-	ld [wJoyIgnore], a
-	predef_jump HealParty
-
-
 MewPicFront:: INCBIN "pic/bmon/mew.pic"
 MewPicBack::  INCBIN "pic/monback/mewb.pic"
 INCLUDE "data/baseStats/mew.asm"
@@ -97,361 +51,17 @@
 
 INCLUDE "engine/oak_speech.asm"
 
-SpecialWarpIn:
-	call LoadSpecialWarpData
-	predef LoadTilesetHeader
-	ld hl,wd732
-	bit 2,[hl] ; dungeon warp or fly warp?
-	res 2,[hl]
-	jr z,.next
-; if dungeon warp or fly warp
-	ld a,[wDestinationMap]
-	jr .next2
-.next
-	bit 1,[hl]
-	jr z,.next3
-	call EmptyFunc
-.next3
-	ld a,0
-.next2
-	ld b,a
-	ld a,[wd72d]
-	and a
-	jr nz,.next4
-	ld a,b
-.next4
-	ld hl,wd732
-	bit 4,[hl] ; dungeon warp?
-	ret nz
-; if not dungeon warp
-	ld [wLastMap],a
-	ret
+INCLUDE "engine/special_warps.asm"
 
-; gets the map ID, tile block map view pointer, tileset, and coordinates
-LoadSpecialWarpData:
-	ld a, [wd72d]
-	cp TRADE_CENTER
-	jr nz, .notTradeCenter
-	ld hl, TradeCenterSpec1
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK ; which gameboy is clocking determines who is on the left and who is on the right
-	jr z, .copyWarpData
-	ld hl, TradeCenterSpec2
-	jr .copyWarpData
-.notTradeCenter
-	cp COLOSSEUM
-	jr nz, .notColosseum
-	ld hl, ColosseumSpec1
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK
-	jr z, .copyWarpData
-	ld hl, ColosseumSpec2
-	jr .copyWarpData
-.notColosseum
-	ld a, [wd732]
-	bit 1, a
-	jr nz, .notFirstMap
-	bit 2, a
-	jr nz, .notFirstMap
-	ld hl, FirstMapSpec
-.copyWarpData
-	ld de, wCurMap
-	ld c, $7
-.copyWarpDataLoop
-	ld a, [hli]
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .copyWarpDataLoop
-	ld a, [hli]
-	ld [wCurMapTileset], a
-	xor a
-	jr .done
-.notFirstMap
-	ld a, [wLastMap] ; this value is overwritten before it's ever read
-	ld hl, wd732
-	bit 4, [hl] ; used dungeon warp (jumped down hole/waterfall)?
-	jr nz, .usedDunegonWarp
-	bit 6, [hl] ; return to last pokemon center (or player's house)?
-	res 6, [hl]
-	jr z, .otherDestination
-; return to last pokemon center or player's house
-	ld a, [wLastBlackoutMap]
-	jr .usedFlyWarp
-.usedDunegonWarp
-	ld hl, wd72d
-	res 4, [hl]
-	ld a, [wDungeonWarpDestinationMap]
-	ld b, a
-	ld [wCurMap], a
-	ld a, [wWhichDungeonWarp]
-	ld c, a
-	ld hl, DungeonWarpList
-	ld de, 0
-	ld a, 6
-	ld [wDungeonWarpDataEntrySize], a
-.dungeonWarpListLoop
-	ld a, [hli]
-	cp b
-	jr z, .matchedDungeonWarpDestinationMap
-	inc hl
-	jr .nextDungeonWarp
-.matchedDungeonWarpDestinationMap
-	ld a, [hli]
-	cp c
-	jr z, .matchedDungeonWarpID
-.nextDungeonWarp
-	ld a, [wDungeonWarpDataEntrySize]
-	add e
-	ld e, a
-	jr .dungeonWarpListLoop
-.matchedDungeonWarpID
-	ld hl, DungeonWarpData
-	add hl, de
-	jr .copyWarpData2
-.otherDestination
-	ld a, [wDestinationMap]
-.usedFlyWarp
-	ld b, a
-	ld [wCurMap], a
-	ld hl, FlyWarpDataPtr
-.flyWarpDataPtrLoop
-	ld a, [hli]
-	inc hl
-	cp b
-	jr z, .foundFlyWarpMatch
-	inc hl
-	inc hl
-	jr .flyWarpDataPtrLoop
-.foundFlyWarpMatch
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-.copyWarpData2
-	ld de, wCurrentTileBlockMapViewPointer
-	ld c, $6
-.copyWarpDataLoop2
-	ld a, [hli]
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .copyWarpDataLoop2
-	xor a ; OVERWORLD
-	ld [wCurMapTileset], a
-.done
-	ld [wYOffsetSinceLastSpecialWarp], a
-	ld [wXOffsetSinceLastSpecialWarp], a
-	ld a, $ff ; the player's coordinates have already been updated using a special warp, so don't use any of the normal warps
-	ld [wDestinationWarpID], a
-	ret
+INCLUDE "engine/debug1.asm"
 
-INCLUDE "data/special_warps.asm"
-
-; This function appears to never be used.
-; It is likely a debugging feature to give the player Tsunekazu Ishihara's
-; favorite Pokemon. This is indicated by the overpowered Exeggutor, which
-; Ishihara (president of Creatures Inc.) said was his favorite Pokemon in an ABC
-; interview on February 8, 2000.
-; "Exeggutor is my favorite. That's because I was always using this character
-; while I was debugging the program."
-; http://www.ign.com/articles/2000/02/09/abc-news-pokamon-chat-transcript
-
-SetIshiharaTeam:
-	ld de, IshiharaTeam
-.loop
-	ld a, [de]
-	cp $ff
-	ret z
-	ld [wcf91], a
-	inc de
-	ld a, [de]
-	ld [wCurEnemyLVL], a
-	inc de
-	call AddPartyMon
-	jr .loop
-
-IshiharaTeam:
-	db EXEGGUTOR,90
-	db MEW,20
-	db JOLTEON,56
-	db DUGTRIO,56
-	db ARTICUNO,57
-	db $FF
-
-EmptyFunc:
-	ret
-
 INCLUDE "engine/menu/naming_screen.asm"
 
 INCLUDE "engine/oak_speech2.asm"
 
-; subtracts the amount the player paid from their money
-; sets carry flag if there is enough money and unsets carry flag if not
-SubtractAmountPaidFromMoney_:
-	ld de,wPlayerMoney
-	ld hl,hMoney ; total price of items
-	ld c,3 ; length of money in bytes
-	call StringCmp
-	ret c
-	ld de,wPlayerMoney + 2
-	ld hl,hMoney + 2 ; total price of items
-	ld c,3 ; length of money in bytes
-	predef SubBCDPredef ; subtract total price from money
-	ld a,MONEY_BOX
-	ld [wTextBoxID],a
-	call DisplayTextBoxID ; redraw money text box
-	and a
-	ret
+INCLUDE "engine/subtract_paid_money.asm"
 
-HandleItemListSwapping:
-	ld a,[wListMenuID]
-	cp a,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 a,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 a,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 a,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
+INCLUDE "engine/menu/swap_items.asm"
 
 INCLUDE "engine/overworld/pokemart.asm"
 
@@ -459,1096 +69,22 @@
 
 INCLUDE "engine/overworld/pokecenter.asm"
 
-SetLastBlackoutMap:
-; Set the map to return to when
-; blacking out or using Teleport or Dig.
-; Safari rest houses don't count.
+INCLUDE "engine/overworld/set_blackout_map.asm"
 
-	push hl
-	ld hl, SafariZoneRestHouses
-	ld a, [wCurMap]
-	ld b, a
-.loop
-	ld a, [hli]
-	cp -1
-	jr z, .notresthouse
-	cp b
-	jr nz, .loop
-	jr .done
+INCLUDE "engine/display_text_id_init.asm"
+INCLUDE "engine/menu/draw_start_menu.asm"
 
-.notresthouse
-	ld a, [wLastMap]
-	ld [wLastBlackoutMap], a
-.done
-	pop hl
-	ret
-
-SafariZoneRestHouses:
-	db SAFARI_ZONE_REST_HOUSE_2
-	db SAFARI_ZONE_REST_HOUSE_3
-	db SAFARI_ZONE_REST_HOUSE_4
-	db -1
-
-; 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 a,$ff ; is the sprite visible?
-	jr z,.nextSprite
-; if it is visible
-	and a,$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
-
-; 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 pokdex
-	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
-
 INCLUDE "engine/overworld/cable_club_npc.asm"
 
-; function to draw various text boxes
-DisplayTextBoxID_:
-	ld a,[wTextBoxID]
-	cp a,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
+INCLUDE "engine/menu/text_box.asm"
 
-; 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 a,$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
-
-
 INCLUDE "engine/battle/moveEffects/drain_hp_effect.asm"
 
 INCLUDE "engine/menu/players_pc.asm"
 
-_RemovePokemon:
-	ld hl, wPartyCount
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7b74
-	ld hl, wNumInBox
-.asm_7b74
-	ld a, [hl]
-	dec a
-	ld [hli], a
-	ld a, [wWhichPokemon]
-	ld c, a
-	ld b, $0
-	add hl, bc
-	ld e, l
-	ld d, h
-	inc de
-.asm_7b81
-	ld a, [de]
-	inc de
-	ld [hli], a
-	inc a
-	jr nz, .asm_7b81
-	ld hl, wPartyMonOT
-	ld d, $5
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7b97
-	ld hl, wBoxMonOT
-	ld d, $13
-.asm_7b97
-	ld a, [wWhichPokemon]
-	call SkipFixedLengthTextEntries
-	ld a, [wWhichPokemon]
-	cp d
-	jr nz, .asm_7ba6
-	ld [hl], $ff
-	ret
-.asm_7ba6
-	ld d, h
-	ld e, l
-	ld bc, NAME_LENGTH
-	add hl, bc
-	ld bc, wPartyMonNicks
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7bb8
-	ld bc, wBoxMonNicks
-.asm_7bb8
-	call CopyDataUntil
-	ld hl, wPartyMons
-	ld bc, wPartyMon2 - wPartyMon1
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7bcd
-	ld hl, wBoxMons
-	ld bc, wBoxMon2 - wBoxMon1
-.asm_7bcd
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld d, h
-	ld e, l
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7be4
-	ld bc, wBoxMon2 - wBoxMon1
-	add hl, bc
-	ld bc, wBoxMonOT
-	jr .asm_7beb
-.asm_7be4
-	ld bc, wPartyMon2 - wPartyMon1
-	add hl, bc
-	ld bc, wPartyMonOT
-.asm_7beb
-	call CopyDataUntil
-	ld hl, wPartyMonNicks
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7bfa
-	ld hl, wBoxMonNicks
-.asm_7bfa
-	ld bc, NAME_LENGTH
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld d, h
-	ld e, l
-	ld bc, NAME_LENGTH
-	add hl, bc
-	ld bc, wPokedexOwned
-	ld a, [wRemoveMonFromBox]
-	and a
-	jr z, .asm_7c15
-	ld bc, wBoxMonNicksEnd
-.asm_7c15
-	jp CopyDataUntil
+INCLUDE "engine/remove_pokemon.asm"
 
-_DisplayPokedex:
-	ld hl, wd730
-	set 6, [hl]
-	predef ShowPokedexData
-	ld hl, wd730
-	res 6, [hl]
-	call ReloadMapData
-	ld c, 10
-	call DelayFrames
-	predef IndexToPokedex
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_SET
-	ld hl, wPokedexSeen
-	predef FlagActionPredef
-	ld a, $1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ret
-
+INCLUDE "engine/display_pokedex.asm"
 
 SECTION "bank3",ROMX,BANK[$3]