shithub: pokered

Download patch

ref: c85050497c1bd062e9cd40bf5b32fa3beca366cc
parent: 5559d51c863b6fb529ea0494d857950a36fe85b7
parent: 87ef75c173b5d5f227912860487600b6f53d1d1f
author: Rangi <35663410+Rangi42@users.noreply.github.com>
date: Fri Jul 3 05:38:52 EDT 2020

Merge pull request #256 from Rangi42/master

Add subdirectories to engine/ similar to pokecrystal

diff: cannot open a/engine/battle/moveEffects//null: file does not exist: 'a/engine/battle/moveEffects//null' diff: cannot open b/engine/battle/move_effects//null: file does not exist: 'b/engine/battle/move_effects//null' diff: cannot open b/engine/debug//null: file does not exist: 'b/engine/debug//null' diff: cannot open b/engine/events//null: file does not exist: 'b/engine/events//null' diff: cannot open b/engine/gfx//null: file does not exist: 'b/engine/gfx//null' diff: cannot open b/engine/link//null: file does not exist: 'b/engine/link//null' diff: cannot open b/engine/math//null: file does not exist: 'b/engine/math//null' diff: cannot open a/engine/menu//null: file does not exist: 'a/engine/menu//null' diff: cannot open b/engine/menus//null: file does not exist: 'b/engine/menus//null' diff: cannot open b/engine/movie/oak_speech//null: file does not exist: 'b/engine/movie/oak_speech//null' diff: cannot open b/engine/movie//null: file does not exist: 'b/engine/movie//null' diff: cannot open b/engine/pokemon//null: file does not exist: 'b/engine/pokemon//null' diff: cannot open b/engine/slots//null: file does not exist: 'b/engine/slots//null'
--- a/audio.asm
+++ b/audio.asm
@@ -352,248 +352,30 @@
 INCLUDE "audio/sfx/cry22_3.asm"
 
 
-
 SECTION "Audio Engine 1", ROMX
 
-PlayBattleMusic::
-	xor a
-	ld [wAudioFadeOutControl], a
-	ld [wLowHealthAlarm], a
-	dec a
-	ld [wNewSoundID], a
-	call PlaySound ; stop music
-	call DelayFrame
-	ld c, BANK(Music_GymLeaderBattle)
-	ld a, [wGymLeaderNo]
-	and a
-	jr z, .notGymLeaderBattle
-	ld a, MUSIC_GYM_LEADER_BATTLE
-	jr .playSong
-.notGymLeaderBattle
-	ld a, [wCurOpponent]
-	cp OPP_ID_OFFSET
-	jr c, .wildBattle
-	cp OPP_SONY3
-	jr z, .finalBattle
-	cp OPP_LANCE
-	jr nz, .normalTrainerBattle
-	ld a, MUSIC_GYM_LEADER_BATTLE ; lance also plays gym leader theme
-	jr .playSong
-.normalTrainerBattle
-	ld a, MUSIC_TRAINER_BATTLE
-	jr .playSong
-.finalBattle
-	ld a, MUSIC_FINAL_BATTLE
-	jr .playSong
-.wildBattle
-	ld a, MUSIC_WILD_BATTLE
-.playSong
-	jp PlayMusic
-
-
+INCLUDE "audio/play_battle_music.asm"
 INCLUDE "audio/engine_1.asm"
+INCLUDE "audio/alternate_tempo.asm"
 
 
-; an alternate start for MeetRival which has a different first measure
-Music_RivalAlternateStart::
-	ld c, BANK(Music_MeetRival)
-	ld a, MUSIC_MEET_RIVAL
-	call PlayMusic
-	ld hl, wChannelCommandPointers
-	ld de, Music_MeetRival_branch_b1a2
-	call Audio1_OverwriteChannelPointer
-	ld de, Music_MeetRival_branch_b21d
-	call Audio1_OverwriteChannelPointer
-	ld de, Music_MeetRival_branch_b2b5
+SECTION "Low Health Alarm (Audio Engine 2)", ROMX
 
-Audio1_OverwriteChannelPointer:
-	ld a, e
-	ld [hli], a
-	ld a, d
-	ld [hli], a
-	ret
+INCLUDE "audio/low_health_alarm.asm"
 
-; an alternate tempo for MeetRival which is slightly slower
-Music_RivalAlternateTempo::
-	ld c, BANK(Music_MeetRival)
-	ld a, MUSIC_MEET_RIVAL
-	call PlayMusic
-	ld hl, wChannelCommandPointers
-	ld de, Music_MeetRival_branch_b119
-	jp Audio1_OverwriteChannelPointer
 
-; applies both the alternate start and alternate tempo
-Music_RivalAlternateStartAndTempo::
-	call Music_RivalAlternateStart
-	ld hl, wChannelCommandPointers
-	ld de, Music_MeetRival_branch_b19b
-	jp Audio1_OverwriteChannelPointer
-
-; an alternate tempo for Cities1 which is used for the Hall of Fame room
-Music_Cities1AlternateTempo::
-	ld a, 10
-	ld [wAudioFadeOutCounterReloadValue], a
-	ld [wAudioFadeOutCounter], a
-	ld a, $ff ; stop playing music after the fade-out is finished
-	ld [wAudioFadeOutControl], a
-	ld c, 100
-	call DelayFrames ; wait for the fade-out to finish
-	ld c, BANK(Music_Cities1)
-	ld a, MUSIC_CITIES1
-	call PlayMusic
-	ld hl, wChannelCommandPointers
-	ld de, Music_Cities1_branch_aa6f
-	jp Audio1_OverwriteChannelPointer
-
-
 SECTION "Audio Engine 2", ROMX
 
-Music_DoLowHealthAlarm::
-	ld a, [wLowHealthAlarm]
-	cp $ff
-	jr z, .disableAlarm
-
-	bit 7, a  ;alarm enabled?
-	ret z     ;nope
-
-	and $7f   ;low 7 bits are the timer.
-	jr nz, .asm_21383 ;if timer > 0, play low tone.
-
-	call .playToneHi
-	ld a, 30 ;keep this tone for 30 frames.
-	jr .asm_21395 ;reset the timer.
-
-.asm_21383
-	cp 20
-	jr nz, .asm_2138a ;if timer == 20,
-	call .playToneLo  ;actually set the sound registers.
-
-.asm_2138a
-	ld a, $86
-	ld [wChannelSoundIDs + Ch5], a ;disable sound channel?
-	ld a, [wLowHealthAlarm]
-	and $7f ;decrement alarm timer.
-	dec a
-
-.asm_21395
-	; reset the timer and enable flag.
-	set 7, a
-	ld [wLowHealthAlarm], a
-	ret
-
-.disableAlarm
-	xor a
-	ld [wLowHealthAlarm], a  ;disable alarm
-	ld [wChannelSoundIDs + Ch5], a  ;re-enable sound channel?
-	ld de, .toneDataSilence
-	jr .playTone
-
-;update the sound registers to change the frequency.
-;the tone set here stays until we change it.
-.playToneHi
-	ld de, .toneDataHi
-	jr .playTone
-
-.playToneLo
-	ld de, .toneDataLo
-
-;update sound channel 1 to play the alarm, overriding all other sounds.
-.playTone
-	ld hl, rNR10 ;channel 1 sound register
-	ld c, $5
-	xor a
-
-.copyLoop
-	ld [hli], a
-	ld a, [de]
-	inc de
-	dec c
-	jr nz, .copyLoop
-	ret
-
-;bytes to write to sound channel 1 registers for health alarm.
-;starting at FF11 (FF10 is always zeroed), so these bytes are:
-;length, envelope, freq lo, freq hi
-.toneDataHi
-	db $A0,$E2,$50,$87
-
-.toneDataLo
-	db $B0,$E2,$EE,$86
-
-;written to stop the alarm
-.toneDataSilence
-	db $00,$00,$00,$80
-
-
-INCLUDE "engine/menu/bills_pc.asm"
-
 INCLUDE "audio/engine_2.asm"
+INCLUDE "audio/poke_flute.asm"
 
 
-Music_PokeFluteInBattle::
-	; begin playing the "caught mon" sound effect
-	ld a, SFX_CAUGHT_MON
-	call PlaySoundWaitForCurrent
-	; then immediately overwrite the channel pointers
-	ld hl, wChannelCommandPointers + Ch5 * 2
-	ld de, SFX_Pokeflute_Ch5
-	call Audio2_OverwriteChannelPointer
-	ld de, SFX_Pokeflute_Ch6
-	call Audio2_OverwriteChannelPointer
-	ld de, SFX_Pokeflute_Ch7
-
-Audio2_OverwriteChannelPointer:
-	ld a, e
-	ld [hli], a
-	ld a, d
-	ld [hli], a
-	ret
-
-
 SECTION "Audio Engine 3", ROMX
 
-PlayPokedexRatingSfx::
-	ld a, [$ffdc]
-	ld c, $0
-	ld hl, OwnedMonValues
-.getSfxPointer
-	cp [hl]
-	jr c, .gotSfxPointer
-	inc c
-	inc hl
-	jr .getSfxPointer
-.gotSfxPointer
-	push bc
-	ld a, $ff
-	ld [wNewSoundID], a
-	call PlaySoundWaitForCurrent
-	pop bc
-	ld b, $0
-	ld hl, PokedexRatingSfxPointers
-	add hl, bc
-	add hl, bc
-	ld a, [hli]
-	ld c, [hl]
-	call PlayMusic
-	jp PlayDefaultMusic
-
-PokedexRatingSfxPointers:
-	db SFX_DENIED,         BANK(SFX_Denied_3)
-	db SFX_POKEDEX_RATING, BANK(SFX_Pokedex_Rating_1)
-	db SFX_GET_ITEM_1,     BANK(SFX_Get_Item1_1)
-	db SFX_CAUGHT_MON,     BANK(SFX_Caught_Mon)
-	db SFX_LEVEL_UP,       BANK(SFX_Level_Up)
-	db SFX_GET_KEY_ITEM,   BANK(SFX_Get_Key_Item_1)
-	db SFX_GET_ITEM_2,     BANK(SFX_Get_Item2_1)
-
-OwnedMonValues:
-	db 10, 40, 60, 90, 120, 150, $ff
-
-
+INCLUDE "audio/pokedex_rating_sfx.asm"
 INCLUDE "audio/engine_3.asm"
 
 
-
 SECTION "Music 1", ROMX
 
 INCLUDE "audio/music/pkmnhealed.asm"
@@ -663,4 +445,3 @@
 INCLUDE "audio/music/jigglypuffsong.asm"
 INCLUDE "audio/music/halloffame.asm"
 INCLUDE "audio/music/credits.asm"
-
--- /dev/null
+++ b/audio/alternate_tempo.asm
@@ -1,0 +1,50 @@
+; an alternate start for MeetRival which has a different first measure
+Music_RivalAlternateStart::
+	ld c, BANK(Music_MeetRival)
+	ld a, MUSIC_MEET_RIVAL
+	call PlayMusic
+	ld hl, wChannelCommandPointers
+	ld de, Music_MeetRival_branch_b1a2
+	call Audio1_OverwriteChannelPointer
+	ld de, Music_MeetRival_branch_b21d
+	call Audio1_OverwriteChannelPointer
+	ld de, Music_MeetRival_branch_b2b5
+
+Audio1_OverwriteChannelPointer:
+	ld a, e
+	ld [hli], a
+	ld a, d
+	ld [hli], a
+	ret
+
+; an alternate tempo for MeetRival which is slightly slower
+Music_RivalAlternateTempo::
+	ld c, BANK(Music_MeetRival)
+	ld a, MUSIC_MEET_RIVAL
+	call PlayMusic
+	ld hl, wChannelCommandPointers
+	ld de, Music_MeetRival_branch_b119
+	jp Audio1_OverwriteChannelPointer
+
+; applies both the alternate start and alternate tempo
+Music_RivalAlternateStartAndTempo::
+	call Music_RivalAlternateStart
+	ld hl, wChannelCommandPointers
+	ld de, Music_MeetRival_branch_b19b
+	jp Audio1_OverwriteChannelPointer
+
+; an alternate tempo for Cities1 which is used for the Hall of Fame room
+Music_Cities1AlternateTempo::
+	ld a, 10
+	ld [wAudioFadeOutCounterReloadValue], a
+	ld [wAudioFadeOutCounter], a
+	ld a, $ff ; stop playing music after the fade-out is finished
+	ld [wAudioFadeOutControl], a
+	ld c, 100
+	call DelayFrames ; wait for the fade-out to finish
+	ld c, BANK(Music_Cities1)
+	ld a, MUSIC_CITIES1
+	call PlayMusic
+	ld hl, wChannelCommandPointers
+	ld de, Music_Cities1_branch_aa6f
+	jp Audio1_OverwriteChannelPointer
--- /dev/null
+++ b/audio/low_health_alarm.asm
@@ -1,0 +1,75 @@
+Music_DoLowHealthAlarm::
+	ld a, [wLowHealthAlarm]
+	cp $ff
+	jr z, .disableAlarm
+
+	bit 7, a  ;alarm enabled?
+	ret z     ;nope
+
+	and $7f   ;low 7 bits are the timer.
+	jr nz, .asm_21383 ;if timer > 0, play low tone.
+
+	call .playToneHi
+	ld a, 30 ;keep this tone for 30 frames.
+	jr .asm_21395 ;reset the timer.
+
+.asm_21383
+	cp 20
+	jr nz, .asm_2138a ;if timer == 20,
+	call .playToneLo  ;actually set the sound registers.
+
+.asm_2138a
+	ld a, $86
+	ld [wChannelSoundIDs + Ch5], a ;disable sound channel?
+	ld a, [wLowHealthAlarm]
+	and $7f ;decrement alarm timer.
+	dec a
+
+.asm_21395
+	; reset the timer and enable flag.
+	set 7, a
+	ld [wLowHealthAlarm], a
+	ret
+
+.disableAlarm
+	xor a
+	ld [wLowHealthAlarm], a  ;disable alarm
+	ld [wChannelSoundIDs + Ch5], a  ;re-enable sound channel?
+	ld de, .toneDataSilence
+	jr .playTone
+
+;update the sound registers to change the frequency.
+;the tone set here stays until we change it.
+.playToneHi
+	ld de, .toneDataHi
+	jr .playTone
+
+.playToneLo
+	ld de, .toneDataLo
+
+;update sound channel 1 to play the alarm, overriding all other sounds.
+.playTone
+	ld hl, rNR10 ;channel 1 sound register
+	ld c, $5
+	xor a
+
+.copyLoop
+	ld [hli], a
+	ld a, [de]
+	inc de
+	dec c
+	jr nz, .copyLoop
+	ret
+
+;bytes to write to sound channel 1 registers for health alarm.
+;starting at FF11 (FF10 is always zeroed), so these bytes are:
+;length, envelope, freq lo, freq hi
+.toneDataHi
+	db $A0,$E2,$50,$87
+
+.toneDataLo
+	db $B0,$E2,$EE,$86
+
+;written to stop the alarm
+.toneDataSilence
+	db $00,$00,$00,$80
--- a/audio/notes.asm
+++ b/audio/notes.asm
@@ -1,3 +1,5 @@
+; This file is INCLUDEd three times, once in each audio engine.
+
 	dw $F82C ; C_
 	dw $F89D ; C#
 	dw $F907 ; D_
--- /dev/null
+++ b/audio/play_battle_music.asm
@@ -1,0 +1,34 @@
+PlayBattleMusic::
+	xor a
+	ld [wAudioFadeOutControl], a
+	ld [wLowHealthAlarm], a
+	dec a
+	ld [wNewSoundID], a
+	call PlaySound ; stop music
+	call DelayFrame
+	ld c, BANK(Music_GymLeaderBattle)
+	ld a, [wGymLeaderNo]
+	and a
+	jr z, .notGymLeaderBattle
+	ld a, MUSIC_GYM_LEADER_BATTLE
+	jr .playSong
+.notGymLeaderBattle
+	ld a, [wCurOpponent]
+	cp OPP_ID_OFFSET
+	jr c, .wildBattle
+	cp OPP_SONY3
+	jr z, .finalBattle
+	cp OPP_LANCE
+	jr nz, .normalTrainerBattle
+	ld a, MUSIC_GYM_LEADER_BATTLE ; lance also plays gym leader theme
+	jr .playSong
+.normalTrainerBattle
+	ld a, MUSIC_TRAINER_BATTLE
+	jr .playSong
+.finalBattle
+	ld a, MUSIC_FINAL_BATTLE
+	jr .playSong
+.wildBattle
+	ld a, MUSIC_WILD_BATTLE
+.playSong
+	jp PlayMusic
--- /dev/null
+++ b/audio/poke_flute.asm
@@ -1,0 +1,18 @@
+Music_PokeFluteInBattle::
+	; begin playing the "caught mon" sound effect
+	ld a, SFX_CAUGHT_MON
+	call PlaySoundWaitForCurrent
+	; then immediately overwrite the channel pointers
+	ld hl, wChannelCommandPointers + Ch5 * 2
+	ld de, SFX_Pokeflute_Ch5
+	call Audio2_OverwriteChannelPointer
+	ld de, SFX_Pokeflute_Ch6
+	call Audio2_OverwriteChannelPointer
+	ld de, SFX_Pokeflute_Ch7
+
+Audio2_OverwriteChannelPointer:
+	ld a, e
+	ld [hli], a
+	ld a, d
+	ld [hli], a
+	ret
--- /dev/null
+++ b/audio/pokedex_rating_sfx.asm
@@ -1,0 +1,36 @@
+PlayPokedexRatingSfx::
+	ld a, [$ffdc]
+	ld c, $0
+	ld hl, OwnedMonValues
+.getSfxPointer
+	cp [hl]
+	jr c, .gotSfxPointer
+	inc c
+	inc hl
+	jr .getSfxPointer
+.gotSfxPointer
+	push bc
+	ld a, $ff
+	ld [wNewSoundID], a
+	call PlaySoundWaitForCurrent
+	pop bc
+	ld b, $0
+	ld hl, PokedexRatingSfxPointers
+	add hl, bc
+	add hl, bc
+	ld a, [hli]
+	ld c, [hl]
+	call PlayMusic
+	jp PlayDefaultMusic
+
+PokedexRatingSfxPointers:
+	db SFX_DENIED,         BANK(SFX_Denied_3)
+	db SFX_POKEDEX_RATING, BANK(SFX_Pokedex_Rating_1)
+	db SFX_GET_ITEM_1,     BANK(SFX_Get_Item1_1)
+	db SFX_CAUGHT_MON,     BANK(SFX_Caught_Mon)
+	db SFX_LEVEL_UP,       BANK(SFX_Level_Up)
+	db SFX_GET_KEY_ITEM,   BANK(SFX_Get_Key_Item_1)
+	db SFX_GET_ITEM_2,     BANK(SFX_Get_Item2_1)
+
+OwnedMonValues:
+	db 10, 40, 60, 90, 120, 150, $ff
--- a/audio/wave_instruments.asm
+++ b/audio/wave_instruments.asm
@@ -1,3 +1,5 @@
+; This file is INCLUDEd three times, once for each audio engine.
+
 	dw .wave0
 	dw .wave1
 	dw .wave2
--- /dev/null
+++ b/data/hm_moves.asm
@@ -1,0 +1,7 @@
+HMMoveArray:
+	db CUT
+	db FLY
+	db SURF
+	db STRENGTH
+	db FLASH
+	db -1
--- a/engine/HoF_room_pc.asm
+++ /dev/null
@@ -1,270 +1,0 @@
-HallOfFamePC:
-	callba AnimateHallOfFame
-	call ClearScreen
-	ld c, 100
-	call DelayFrames
-	call DisableLCD
-	ld hl, vFont
-	ld bc, $800 / 2
-	call ZeroMemory
-	ld hl, vChars2 + $600
-	ld bc, $200 / 2
-	call ZeroMemory
-	ld hl, vChars2 + $7e0
-	ld bc, $10
-	ld a, $ff
-	call FillMemory
-	coord hl, 0, 0
-	call FillFourRowsWithBlack
-	coord hl, 0, 14
-	call FillFourRowsWithBlack
-	ld a, %11000000
-	ld [rBGP], a
-	call EnableLCD
-	ld a, $ff
-	call PlaySoundWaitForCurrent
-	ld c, BANK(Music_Credits)
-	ld a, MUSIC_CREDITS
-	call PlayMusic
-	ld c, 128
-	call DelayFrames
-	xor a
-	ld [wUnusedCD3D], a ; not read
-	ld [wNumCreditsMonsDisplayed], a
-	jp Credits
-
-FadeInCreditsText:
-	ld hl, HoFGBPalettes
-	ld b, 4
-.loop
-	ld a, [hli]
-	ld [rBGP], a
-	ld c, 5
-	call DelayFrames
-	dec b
-	jr nz, .loop
-	ret
-
-DisplayCreditsMon:
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call SaveScreenTilesToBuffer1
-	call FillMiddleOfScreenWithWhite
-
-	; display the next monster from CreditsMons
-	ld hl, wNumCreditsMonsDisplayed
-	ld c, [hl] ; how many monsters have we displayed so far?
-	inc [hl]
-	ld b, 0
-	ld hl, CreditsMons
-	add hl, bc ; go that far in the list of monsters and get the next one
-	ld a, [hl]
-	ld [wcf91], a
-	ld [wd0b5], a
-	coord hl, 8, 6
-	call GetMonHeader
-	call LoadFrontSpriteByMonIndex
-	ld hl, vBGMap0 + $c
-	call CreditsCopyTileMapToVRAM
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call LoadScreenTilesFromBuffer1
-	ld hl, vBGMap0
-	call CreditsCopyTileMapToVRAM
-	ld a, $A7
-	ld [rWX], a
-	ld hl, vBGMap1
-	call CreditsCopyTileMapToVRAM
-	call FillMiddleOfScreenWithWhite
-	ld a, %11111100 ; make the mon a black silhouette
-	ld [rBGP], a
-
-; scroll the mon left by one tile 7 times
-	ld bc, 7
-.scrollLoop1
-	call ScrollCreditsMonLeft
-	dec c
-	jr nz, .scrollLoop1
-
-; scroll the mon left by one tile 20 times
-; This time, we have to move the window left too in order to hide the text that
-; is wrapping around to the right side of the screen.
-	ld c, 20
-.scrollLoop2
-	call ScrollCreditsMonLeft
-	ld a, [rWX]
-	sub 8
-	ld [rWX], a
-	dec c
-	jr nz, .scrollLoop2
-
-	xor a
-	ld [hWY], a
-	ld a, %11000000
-	ld [rBGP], a
-	ret
-
-INCLUDE "data/credit_mons.asm"
-
-ScrollCreditsMonLeft:
-	ld h, b
-	ld l, $20
-	call ScrollCreditsMonLeft_SetSCX
-	ld h, $0
-	ld l, $70
-	call ScrollCreditsMonLeft_SetSCX
-	ld a, b
-	add $8
-	ld b, a
-	ret
-
-ScrollCreditsMonLeft_SetSCX:
-	ld a, [rLY]
-	cp l
-	jr nz, ScrollCreditsMonLeft_SetSCX
-	ld a, h
-	ld [rSCX], a
-.loop
-	ld a, [rLY]
-	cp h
-	jr z, .loop
-	ret
-
-HoFGBPalettes:
-	db %11000000
-	db %11010000
-	db %11100000
-	db %11110000
-
-CreditsCopyTileMapToVRAM:
-	ld a, l
-	ld [H_AUTOBGTRANSFERDEST], a
-	ld a, h
-	ld [H_AUTOBGTRANSFERDEST + 1], a
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	jp Delay3
-
-ZeroMemory:
-; zero bc bytes at hl
-	ld [hl], 0
-	inc hl
-	inc hl
-	dec bc
-	ld a, b
-	or c
-	jr nz, ZeroMemory
-	ret
-
-FillFourRowsWithBlack:
-	ld bc, SCREEN_WIDTH * 4
-	ld a, $7e
-	jp FillMemory
-
-FillMiddleOfScreenWithWhite:
-	coord hl, 0, 4
-	ld bc, SCREEN_WIDTH * 10
-	ld a, " "
-	jp FillMemory
-
-Credits:
-	ld de, CreditsOrder
-	push de
-.nextCreditsScreen
-	pop de
-	coord hl, 9, 6
-	push hl
-	call FillMiddleOfScreenWithWhite
-	pop hl
-.nextCreditsCommand
-	ld a, [de]
-	inc de
-	push de
-	cp $ff
-	jr z, .fadeInTextAndShowMon
-	cp $fe
-	jr z, .showTextAndShowMon
-	cp $fd
-	jr z, .fadeInText
-	cp $fc
-	jr z, .showText
-	cp $fb
-	jr z, .showCopyrightText
-	cp $fa
-	jr z, .showTheEnd
-	push hl
-	push hl
-	ld hl, CreditsTextPointers
-	add a
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld e, [hl]
-	inc hl
-	ld d, [hl]
-	ld a, [de]
-	inc de
-	ld c, a
-	ld b, $ff
-	pop hl
-	add hl, bc
-	call PlaceString
-	pop hl
-	ld bc, SCREEN_WIDTH * 2
-	add hl, bc
-	pop de
-	jr .nextCreditsCommand
-.fadeInTextAndShowMon
-	call FadeInCreditsText
-	ld c, 90
-	jr .next1
-.showTextAndShowMon
-	ld c, 110
-.next1
-	call DelayFrames
-	call DisplayCreditsMon
-	jr .nextCreditsScreen
-.fadeInText
-	call FadeInCreditsText
-	ld c, 120
-	jr .next2
-.showText
-	ld c, 140
-.next2
-	call DelayFrames
-	jr .nextCreditsScreen
-.showCopyrightText
-	push de
-	callba LoadCopyrightTiles
-	pop de
-	pop de
-	jr .nextCreditsCommand
-.showTheEnd
-	ld c, 16
-	call DelayFrames
-	call FillMiddleOfScreenWithWhite
-	pop de
-	ld de, TheEndGfx
-	ld hl, vChars2 + $600
-	lb bc, BANK(TheEndGfx), (TheEndGfxEnd - TheEndGfx) / $10
-	call CopyVideoData
-	coord hl, 4, 8
-	ld de, TheEndTextString
-	call PlaceString
-	coord hl, 4, 9
-	inc de
-	call PlaceString
-	jp FadeInCreditsText
-
-TheEndTextString:
-; "T H E  E N D"
-	db $60," ",$62," ",$64,"  ",$64," ",$66," ",$68,"@"
-	db $61," ",$63," ",$65,"  ",$65," ",$67," ",$69,"@"
-
-INCLUDE "data/credits_order.asm"
-
-INCLUDE "text/credits_text.asm"
-
-TheEndGfx:
-	INCBIN "gfx/intro_credits/the_end.2bpp"
-TheEndGfxEnd:
--- a/engine/add_mon.asm
+++ /dev/null
@@ -1,516 +1,0 @@
-_AddPartyMon::
-; Adds a new mon to the player's or enemy's party.
-; [wMonDataLocation] is used in an unusual way in this function.
-; If the lower nybble is 0, the mon is added to the player's party, else the enemy's.
-; If the entire value is 0, then the player is allowed to name the mon.
-	ld de, wPartyCount
-	ld a, [wMonDataLocation]
-	and $f
-	jr z, .next
-	ld de, wEnemyPartyCount
-.next
-	ld a, [de]
-	inc a
-	cp PARTY_LENGTH + 1
-	ret nc ; return if the party is already full
-	ld [de], a
-	ld a, [de]
-	ld [hNewPartyLength], a
-	add e
-	ld e, a
-	jr nc, .noCarry
-	inc d
-.noCarry
-	ld a, [wcf91]
-	ld [de], a ; write species of new mon in party list
-	inc de
-	ld a, $ff ; terminator
-	ld [de], a
-	ld hl, wPartyMonOT
-	ld a, [wMonDataLocation]
-	and $f
-	jr z, .next2
-	ld hl, wEnemyMonOT
-.next2
-	ld a, [hNewPartyLength]
-	dec a
-	call SkipFixedLengthTextEntries
-	ld d, h
-	ld e, l
-	ld hl, wPlayerName
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld a, [wMonDataLocation]
-	and a
-	jr nz, .skipNaming
-	ld hl, wPartyMonNicks
-	ld a, [hNewPartyLength]
-	dec a
-	call SkipFixedLengthTextEntries
-	ld a, NAME_MON_SCREEN
-	ld [wNamingScreenType], a
-	predef AskName
-.skipNaming
-	ld hl, wPartyMons
-	ld a, [wMonDataLocation]
-	and $f
-	jr z, .next3
-	ld hl, wEnemyMons
-.next3
-	ld a, [hNewPartyLength]
-	dec a
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld e, l
-	ld d, h
-	push hl
-	ld a, [wcf91]
-	ld [wd0b5], a
-	call GetMonHeader
-	ld hl, wMonHeader
-	ld a, [hli]
-	ld [de], a ; species
-	inc de
-	pop hl
-	push hl
-	ld a, [wMonDataLocation]
-	and $f
-	ld a, $98     ; set enemy trainer mon IVs to fixed average values
-	ld b, $88
-	jr nz, .next4
-
-; If the mon is being added to the player's party, update the pokedex.
-	ld a, [wcf91]
-	ld [wd11e], a
-	push de
-	predef IndexToPokedex
-	pop de
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_TEST
-	ld hl, wPokedexOwned
-	call FlagAction
-	ld a, c ; whether the mon was already flagged as owned
-	ld [wUnusedD153], a ; not read
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_SET
-	push bc
-	call FlagAction
-	pop bc
-	ld hl, wPokedexSeen
-	call FlagAction
-
-	pop hl
-	push hl
-
-	ld a, [wIsInBattle]
-	and a ; is this a wild mon caught in battle?
-	jr nz, .copyEnemyMonData
-
-; Not wild.
-	call Random ; generate random IVs
-	ld b, a
-	call Random
-
-.next4
-	push bc
-	ld bc, wPartyMon1DVs - wPartyMon1
-	add hl, bc
-	pop bc
-	ld [hli], a
-	ld [hl], b         ; write IVs
-	ld bc, (wPartyMon1HPExp - 1) - (wPartyMon1DVs + 1)
-	add hl, bc
-	ld a, 1
-	ld c, a
-	xor a
-	ld b, a
-	call CalcStat      ; calc HP stat (set cur Hp to max HP)
-	ld a, [H_MULTIPLICAND+1]
-	ld [de], a
-	inc de
-	ld a, [H_MULTIPLICAND+2]
-	ld [de], a
-	inc de
-	xor a
-	ld [de], a         ; box level
-	inc de
-	ld [de], a         ; status ailments
-	inc de
-	jr .copyMonTypesAndMoves
-.copyEnemyMonData
-	ld bc, wEnemyMon1DVs - wEnemyMon1
-	add hl, bc
-	ld a, [wEnemyMonDVs] ; copy IVs from cur enemy mon
-	ld [hli], a
-	ld a, [wEnemyMonDVs + 1]
-	ld [hl], a
-	ld a, [wEnemyMonHP]    ; copy HP from cur enemy mon
-	ld [de], a
-	inc de
-	ld a, [wEnemyMonHP+1]
-	ld [de], a
-	inc de
-	xor a
-	ld [de], a                ; box level
-	inc de
-	ld a, [wEnemyMonStatus]   ; copy status ailments from cur enemy mon
-	ld [de], a
-	inc de
-.copyMonTypesAndMoves
-	ld hl, wMonHTypes
-	ld a, [hli]       ; type 1
-	ld [de], a
-	inc de
-	ld a, [hli]       ; type 2
-	ld [de], a
-	inc de
-	ld a, [hli]       ; catch rate (held item in gen 2)
-	ld [de], a
-	ld hl, wMonHMoves
-	ld a, [hli]
-	inc de
-	push de
-	ld [de], a
-	ld a, [hli]
-	inc de
-	ld [de], a
-	ld a, [hli]
-	inc de
-	ld [de], a
-	ld a, [hli]
-	inc de
-	ld [de], a
-	push de
-	dec de
-	dec de
-	dec de
-	xor a
-	ld [wLearningMovesFromDayCare], a
-	predef WriteMonMoves
-	pop de
-	ld a, [wPlayerID]  ; set trainer ID to player ID
-	inc de
-	ld [de], a
-	ld a, [wPlayerID + 1]
-	inc de
-	ld [de], a
-	push de
-	ld a, [wCurEnemyLVL]
-	ld d, a
-	callab CalcExperience
-	pop de
-	inc de
-	ld a, [hExperience] ; write experience
-	ld [de], a
-	inc de
-	ld a, [hExperience + 1]
-	ld [de], a
-	inc de
-	ld a, [hExperience + 2]
-	ld [de], a
-	xor a
-	ld b, NUM_STATS * 2
-.writeEVsLoop              ; set all EVs to 0
-	inc de
-	ld [de], a
-	dec b
-	jr nz, .writeEVsLoop
-	inc de
-	inc de
-	pop hl
-	call AddPartyMon_WriteMovePP
-	inc de
-	ld a, [wCurEnemyLVL]
-	ld [de], a
-	inc de
-	ld a, [wIsInBattle]
-	dec a
-	jr nz, .calcFreshStats
-	ld hl, wEnemyMonMaxHP
-	ld bc, $a
-	call CopyData          ; copy stats of cur enemy mon
-	pop hl
-	jr .done
-.calcFreshStats
-	pop hl
-	ld bc, wPartyMon1HPExp - 1 - wPartyMon1
-	add hl, bc
-	ld b, $0
-	call CalcStats         ; calculate fresh set of stats
-.done
-	scf
-	ret
-
-LoadMovePPs:
-	call GetPredefRegisters
-	; fallthrough
-AddPartyMon_WriteMovePP:
-	ld b, NUM_MOVES
-.pploop
-	ld a, [hli]     ; read move ID
-	and a
-	jr z, .empty
-	dec a
-	push hl
-	push de
-	push bc
-	ld hl, Moves
-	ld bc, MoveEnd - Moves
-	call AddNTimes
-	ld de, wcd6d
-	ld a, BANK(Moves)
-	call FarCopyData
-	pop bc
-	pop de
-	pop hl
-	ld a, [wcd6d + 5] ; PP is byte 5 of move data
-.empty
-	inc de
-	ld [de], a
-	dec b
-	jr nz, .pploop ; there are still moves to read
-	ret
-
-; adds enemy mon [wcf91] (at position [wWhichPokemon] in enemy list) to own party
-; used in the cable club trade center
-_AddEnemyMonToPlayerParty::
-	ld hl, wPartyCount
-	ld a, [hl]
-	cp PARTY_LENGTH
-	scf
-	ret z            ; party full, return failure
-	inc a
-	ld [hl], a       ; add 1 to party members
-	ld c, a
-	ld b, $0
-	add hl, bc
-	ld a, [wcf91]
-	ld [hli], a      ; add mon as last list entry
-	ld [hl], $ff     ; write new sentinel
-	ld hl, wPartyMons
-	ld a, [wPartyCount]
-	dec a
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld e, l
-	ld d, h
-	ld hl, wLoadedMon
-	call CopyData    ; write new mon's data (from wLoadedMon)
-	ld hl, wPartyMonOT
-	ld a, [wPartyCount]
-	dec a
-	call SkipFixedLengthTextEntries
-	ld d, h
-	ld e, l
-	ld hl, wEnemyMonOT
-	ld a, [wWhichPokemon]
-	call SkipFixedLengthTextEntries
-	ld bc, NAME_LENGTH
-	call CopyData    ; write new mon's OT name (from an enemy mon)
-	ld hl, wPartyMonNicks
-	ld a, [wPartyCount]
-	dec a
-	call SkipFixedLengthTextEntries
-	ld d, h
-	ld e, l
-	ld hl, wEnemyMonNicks
-	ld a, [wWhichPokemon]
-	call SkipFixedLengthTextEntries
-	ld bc, NAME_LENGTH
-	call CopyData    ; write new mon's nickname (from an enemy mon)
-	ld a, [wcf91]
-	ld [wd11e], a
-	predef IndexToPokedex
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_SET
-	ld hl, wPokedexOwned
-	push bc
-	call FlagAction ; add to owned pokemon
-	pop bc
-	ld hl, wPokedexSeen
-	call FlagAction ; add to seen pokemon
-	and a
-	ret                  ; return success
-
-_MoveMon::
-	ld a, [wMoveMonType]
-	and a   ; BOX_TO_PARTY
-	jr z, .checkPartyMonSlots
-	cp DAYCARE_TO_PARTY
-	jr z, .checkPartyMonSlots
-	cp PARTY_TO_DAYCARE
-	ld hl, wDayCareMon
-	jr z, .findMonDataSrc
-	; else it's PARTY_TO_BOX
-	ld hl, wNumInBox
-	ld a, [hl]
-	cp MONS_PER_BOX
-	jr nz, .partyOrBoxNotFull
-	jr .boxFull
-.checkPartyMonSlots
-	ld hl, wPartyCount
-	ld a, [hl]
-	cp PARTY_LENGTH
-	jr nz, .partyOrBoxNotFull
-.boxFull
-	scf
-	ret
-.partyOrBoxNotFull
-	inc a
-	ld [hl], a           ; increment number of mons in party/box
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [wMoveMonType]
-	cp DAYCARE_TO_PARTY
-	ld a, [wDayCareMon]
-	jr z, .copySpecies
-	ld a, [wcf91]
-.copySpecies
-	ld [hli], a          ; write new mon ID
-	ld [hl], $ff         ; write new sentinel
-.findMonDataDest
-	ld a, [wMoveMonType]
-	dec a
-	ld hl, wPartyMons
-	ld bc, wPartyMon2 - wPartyMon1 ; $2c
-	ld a, [wPartyCount]
-	jr nz, .addMonOffset
-	; if it's PARTY_TO_BOX
-	ld hl, wBoxMons
-	ld bc, wBoxMon2 - wBoxMon1 ; $21
-	ld a, [wNumInBox]
-.addMonOffset
-	dec a
-	call AddNTimes
-.findMonDataSrc
-	push hl
-	ld e, l
-	ld d, h
-	ld a, [wMoveMonType]
-	and a
-	ld hl, wBoxMons
-	ld bc, wBoxMon2 - wBoxMon1 ; $21
-	jr z, .addMonOffset2
-	cp DAYCARE_TO_PARTY
-	ld hl, wDayCareMon
-	jr z, .copyMonData
-	ld hl, wPartyMons
-	ld bc, wPartyMon2 - wPartyMon1 ; $2c
-.addMonOffset2
-	ld a, [wWhichPokemon]
-	call AddNTimes
-.copyMonData
-	push hl
-	push de
-	ld bc, wBoxMon2 - wBoxMon1
-	call CopyData
-	pop de
-	pop hl
-	ld a, [wMoveMonType]
-	and a ; BOX_TO_PARTY
-	jr z, .findOTdest
-	cp DAYCARE_TO_PARTY
-	jr z, .findOTdest
-	ld bc, wBoxMon2 - wBoxMon1
-	add hl, bc
-	ld a, [hl] ; hl = Level
-	inc de
-	inc de
-	inc de
-	ld [de], a ; de = BoxLevel
-.findOTdest
-	ld a, [wMoveMonType]
-	cp PARTY_TO_DAYCARE
-	ld de, wDayCareMonOT
-	jr z, .findOTsrc
-	dec a 
-	ld hl, wPartyMonOT
-	ld a, [wPartyCount]
-	jr nz, .addOToffset
-	ld hl, wBoxMonOT
-	ld a, [wNumInBox]
-.addOToffset
-	dec a
-	call SkipFixedLengthTextEntries
-	ld d, h
-	ld e, l
-.findOTsrc
-	ld hl, wBoxMonOT
-	ld a, [wMoveMonType]
-	and a
-	jr z, .addOToffset2
-	ld hl, wDayCareMonOT
-	cp DAYCARE_TO_PARTY
-	jr z, .copyOT
-	ld hl, wPartyMonOT
-.addOToffset2
-	ld a, [wWhichPokemon]
-	call SkipFixedLengthTextEntries
-.copyOT
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld a, [wMoveMonType]
-.findNickDest
-	cp PARTY_TO_DAYCARE
-	ld de, wDayCareMonName
-	jr z, .findNickSrc
-	dec a
-	ld hl, wPartyMonNicks
-	ld a, [wPartyCount]
-	jr nz, .addNickOffset
-	ld hl, wBoxMonNicks
-	ld a, [wNumInBox]
-.addNickOffset
-	dec a
-	call SkipFixedLengthTextEntries
-	ld d, h
-	ld e, l
-.findNickSrc
-	ld hl, wBoxMonNicks
-	ld a, [wMoveMonType]
-	and a
-	jr z, .addNickOffset2
-	ld hl, wDayCareMonName
-	cp DAYCARE_TO_PARTY
-	jr z, .copyNick
-	ld hl, wPartyMonNicks
-.addNickOffset2
-	ld a, [wWhichPokemon]
-	call SkipFixedLengthTextEntries
-.copyNick
-	ld bc, NAME_LENGTH
-	call CopyData
-	pop hl
-	ld a, [wMoveMonType]
-	cp PARTY_TO_BOX
-	jr z, .done
-	cp PARTY_TO_DAYCARE
-	jr z, .done
-	push hl
-	srl a
-	add $2
-	ld [wMonDataLocation], a
-	call LoadMonData
-	callba CalcLevelFromExperience
-	ld a, d
-	ld [wCurEnemyLVL], a
-	pop hl
-	ld bc, wBoxMon2 - wBoxMon1
-	add hl, bc
-	ld [hli], a
-	ld d, h
-	ld e, l
-	ld bc, -18
-	add hl, bc
-	ld b, $1
-	call CalcStats
-.done
-	and a
-	ret
--- a/engine/battle/bank_e_misc.asm
+++ /dev/null
@@ -1,122 +1,0 @@
-; formats a string at wMovesString that lists the moves at wMoves
-FormatMovesString:
-	ld hl, wMoves
-	ld de, wMovesString
-	ld b, $0
-.printMoveNameLoop
-	ld a, [hli]
-	and a ; end of move list?
-	jr z, .printDashLoop ; print dashes when no moves are left
-	push hl
-	ld [wd0b5], a
-	ld a, BANK(MoveNames)
-	ld [wPredefBank], a
-	ld a, MOVE_NAME
-	ld [wNameListType], a
-	call GetName
-	ld hl, wcd6d
-.copyNameLoop
-	ld a, [hli]
-	cp $50
-	jr z, .doneCopyingName
-	ld [de], a
-	inc de
-	jr .copyNameLoop
-.doneCopyingName
-	ld a, b
-	ld [wNumMovesMinusOne], a
-	inc b
-	ld a, $4e ; line break
-	ld [de], a
-	inc de
-	pop hl
-	ld a, b
-	cp NUM_MOVES
-	jr z, .done
-	jr .printMoveNameLoop
-.printDashLoop
-	ld a, "-"
-	ld [de], a
-	inc de
-	inc b
-	ld a, b
-	cp NUM_MOVES
-	jr z, .done
-	ld a, $4e ; line break
-	ld [de], a
-	inc de
-	jr .printDashLoop
-.done
-	ld a, "@"
-	ld [de], a
-	ret
-
-; XXX this is called in a few places, but it doesn't appear to do anything useful
-InitList:
-	ld a, [wInitListType]
-	cp INIT_ENEMYOT_LIST
-	jr nz, .notEnemy
-	ld hl, wEnemyPartyCount
-	ld de, wEnemyMonOT
-	ld a, ENEMYOT_NAME
-	jr .done
-.notEnemy
-	cp INIT_PLAYEROT_LIST
-	jr nz, .notPlayer
-	ld hl, wPartyCount
-	ld de, wPartyMonOT
-	ld a, PLAYEROT_NAME
-	jr .done
-.notPlayer
-	cp INIT_MON_LIST
-	jr nz, .notMonster
-	ld hl, wItemList
-	ld de, MonsterNames
-	ld a, MONSTER_NAME
-	jr .done
-.notMonster
-	cp INIT_BAG_ITEM_LIST
-	jr nz, .notBag
-	ld hl, wNumBagItems
-	ld de, ItemNames
-	ld a, ITEM_NAME
-	jr .done
-.notBag
-	ld hl, wItemList
-	ld de, ItemNames
-	ld a, ITEM_NAME
-.done
-	ld [wNameListType], a
-	ld a, l
-	ld [wListPointer], a
-	ld a, h
-	ld [wListPointer + 1], a
-	ld a, e
-	ld [wUnusedCF8D], a
-	ld a, d
-	ld [wUnusedCF8D + 1], a
-	ld bc, ItemPrices
-	ld a, c
-	ld [wItemPrices], a
-	ld a, b
-	ld [wItemPrices + 1], a
-	ret
-
-; get species of mon e in list [wMonDataLocation] for LoadMonData
-GetMonSpecies:
-	ld hl, wPartySpecies
-	ld a, [wMonDataLocation]
-	and a
-	jr z, .getSpecies
-	dec a
-	jr z, .enemyParty
-	ld hl, wBoxSpecies
-	jr .getSpecies
-.enemyParty
-	ld hl, wEnemyPartyMons
-.getSpecies
-	ld d, 0
-	add hl, de
-	ld a, [hl]
-	ld [wcf91], a
-	ret
--- /dev/null
+++ b/engine/battle/misc.asm
@@ -1,0 +1,122 @@
+; formats a string at wMovesString that lists the moves at wMoves
+FormatMovesString:
+	ld hl, wMoves
+	ld de, wMovesString
+	ld b, $0
+.printMoveNameLoop
+	ld a, [hli]
+	and a ; end of move list?
+	jr z, .printDashLoop ; print dashes when no moves are left
+	push hl
+	ld [wd0b5], a
+	ld a, BANK(MoveNames)
+	ld [wPredefBank], a
+	ld a, MOVE_NAME
+	ld [wNameListType], a
+	call GetName
+	ld hl, wcd6d
+.copyNameLoop
+	ld a, [hli]
+	cp $50
+	jr z, .doneCopyingName
+	ld [de], a
+	inc de
+	jr .copyNameLoop
+.doneCopyingName
+	ld a, b
+	ld [wNumMovesMinusOne], a
+	inc b
+	ld a, $4e ; line break
+	ld [de], a
+	inc de
+	pop hl
+	ld a, b
+	cp NUM_MOVES
+	jr z, .done
+	jr .printMoveNameLoop
+.printDashLoop
+	ld a, "-"
+	ld [de], a
+	inc de
+	inc b
+	ld a, b
+	cp NUM_MOVES
+	jr z, .done
+	ld a, $4e ; line break
+	ld [de], a
+	inc de
+	jr .printDashLoop
+.done
+	ld a, "@"
+	ld [de], a
+	ret
+
+; XXX this is called in a few places, but it doesn't appear to do anything useful
+InitList:
+	ld a, [wInitListType]
+	cp INIT_ENEMYOT_LIST
+	jr nz, .notEnemy
+	ld hl, wEnemyPartyCount
+	ld de, wEnemyMonOT
+	ld a, ENEMYOT_NAME
+	jr .done
+.notEnemy
+	cp INIT_PLAYEROT_LIST
+	jr nz, .notPlayer
+	ld hl, wPartyCount
+	ld de, wPartyMonOT
+	ld a, PLAYEROT_NAME
+	jr .done
+.notPlayer
+	cp INIT_MON_LIST
+	jr nz, .notMonster
+	ld hl, wItemList
+	ld de, MonsterNames
+	ld a, MONSTER_NAME
+	jr .done
+.notMonster
+	cp INIT_BAG_ITEM_LIST
+	jr nz, .notBag
+	ld hl, wNumBagItems
+	ld de, ItemNames
+	ld a, ITEM_NAME
+	jr .done
+.notBag
+	ld hl, wItemList
+	ld de, ItemNames
+	ld a, ITEM_NAME
+.done
+	ld [wNameListType], a
+	ld a, l
+	ld [wListPointer], a
+	ld a, h
+	ld [wListPointer + 1], a
+	ld a, e
+	ld [wUnusedCF8D], a
+	ld a, d
+	ld [wUnusedCF8D + 1], a
+	ld bc, ItemPrices
+	ld a, c
+	ld [wItemPrices], a
+	ld a, b
+	ld [wItemPrices + 1], a
+	ret
+
+; get species of mon e in list [wMonDataLocation] for LoadMonData
+GetMonSpecies:
+	ld hl, wPartySpecies
+	ld a, [wMonDataLocation]
+	and a
+	jr z, .getSpecies
+	dec a
+	jr z, .enemyParty
+	ld hl, wBoxSpecies
+	jr .getSpecies
+.enemyParty
+	ld hl, wEnemyPartyMons
+.getSpecies
+	ld d, 0
+	add hl, de
+	ld a, [hl]
+	ld [wcf91], a
+	ret
--- a/engine/battle/moveEffects/conversion_effect.asm
+++ /dev/null
@@ -1,35 +1,0 @@
-ConversionEffect_:
-	ld hl, wEnemyMonType1
-	ld de, wBattleMonType1
-	ld a, [H_WHOSETURN]
-	and a
-	ld a, [wEnemyBattleStatus1]
-	jr z, .conversionEffect
-	push hl
-	ld h, d
-	ld l, e
-	pop de
-	ld a, [wPlayerBattleStatus1]
-.conversionEffect
-	bit INVULNERABLE, a ; is mon immune to typical attacks (dig/fly)
-	jr nz, PrintButItFailedText
-; copy target's types to user
-	ld a, [hli]
-	ld [de], a
-	inc de
-	ld a, [hl]
-	ld [de], a
-	ld hl, PlayCurrentMoveAnimation
-	call CallBankF
-	ld hl, ConvertedTypeText
-	jp PrintText
-
-ConvertedTypeText:
-	TX_FAR _ConvertedTypeText
-	db "@"
-
-PrintButItFailedText:
-	ld hl, PrintButItFailedText_
-CallBankF:
-	ld b, BANK(PrintButItFailedText_)
-	jp Bankswitch
--- a/engine/battle/moveEffects/drain_hp_effect.asm
+++ /dev/null
@@ -1,104 +1,0 @@
-DrainHPEffect_:
-	ld hl, wDamage
-	ld a, [hl]
-	srl a ; divide damage by 2
-	ld [hli], a
-	ld a, [hl]
-	rr a
-	ld [hld], a
-	or [hl] ; is damage 0?
-	jr nz, .getAttackerHP
-; if damage is 0, increase to 1 so that the attacker gains at least 1 HP
-	inc hl
-	inc [hl]
-.getAttackerHP
-	ld hl, wBattleMonHP
-	ld de, wBattleMonMaxHP
-	ld a, [H_WHOSETURN]
-	and a
-	jp z, .addDamageToAttackerHP
-	ld hl, wEnemyMonHP
-	ld de, wEnemyMonMaxHP
-.addDamageToAttackerHP
-	ld bc, wHPBarOldHP+1
-; copy current HP to wHPBarOldHP
-	ld a, [hli]
-	ld [bc], a
-	ld a, [hl]
-	dec bc
-	ld [bc], a
-; copy max HP to wHPBarMaxHP
-	ld a, [de]
-	dec bc
-	ld [bc], a
-	inc de
-	ld a, [de]
-	dec bc
-	ld [bc], a
-; add damage to attacker's HP and copy new HP to wHPBarNewHP
-	ld a, [wDamage + 1]
-	ld b, [hl]
-	add b
-	ld [hld], a
-	ld [wHPBarNewHP], a
-	ld a, [wDamage]
-	ld b, [hl]
-	adc b
-	ld [hli], a
-	ld [wHPBarNewHP+1], a
-	jr c, .capToMaxHP ; if HP > 65,535, cap to max HP
-; compare HP with max HP
-	ld a, [hld]
-	ld b, a
-	ld a, [de]
-	dec de
-	sub b
-	ld a, [hli]
-	ld b, a
-	ld a, [de]
-	inc de
-	sbc b
-	jr nc, .next
-.capToMaxHP
-	ld a, [de]
-	ld [hld], a
-	ld [wHPBarNewHP], a
-	dec de
-	ld a, [de]
-	ld [hli], a
-	ld [wHPBarNewHP+1], a
-	inc de
-.next
-	ld a, [H_WHOSETURN]
-	and a
-	coord hl, 10, 9
-	ld a, $1
-	jr z, .next2
-	coord hl, 2, 2
-	xor a
-.next2
-	ld [wHPBarType], a
-	predef UpdateHPBar2
-	predef DrawPlayerHUDAndHPBar
-	predef DrawEnemyHUDAndHPBar
-	callab ReadPlayerMonCurHPAndStatus
-	ld hl, SuckedHealthText
-	ld a, [H_WHOSETURN]
-	and a
-	ld a, [wPlayerMoveEffect]
-	jr z, .next3
-	ld a, [wEnemyMoveEffect]
-.next3
-	cp DREAM_EATER_EFFECT
-	jr nz, .printText
-	ld hl, DreamWasEatenText
-.printText
-	jp PrintText
-
-SuckedHealthText:
-	TX_FAR _SuckedHealthText
-	db "@"
-
-DreamWasEatenText:
-	TX_FAR _DreamWasEatenText
-	db "@"
--- a/engine/battle/moveEffects/focus_energy_effect.asm
+++ /dev/null
@@ -1,22 +1,0 @@
-FocusEnergyEffect_:
-	ld hl, wPlayerBattleStatus2
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .notEnemy
-	ld hl, wEnemyBattleStatus2
-.notEnemy
-	bit GETTING_PUMPED, [hl] ; is mon already using focus energy?
-	jr nz, .alreadyUsing
-	set GETTING_PUMPED, [hl] ; mon is now using focus energy
-	callab PlayCurrentMoveAnimation
-	ld hl, GettingPumpedText
-	jp PrintText
-.alreadyUsing
-	ld c, 50
-	call DelayFrames
-	jpab PrintButItFailedText_
-
-GettingPumpedText:
-	TX_DELAY
-	TX_FAR _GettingPumpedText
-	db "@"
--- a/engine/battle/moveEffects/haze_effect.asm
+++ /dev/null
@@ -1,81 +1,0 @@
-HazeEffect_:
-	ld a, $7
-; store 7 on every stat mod
-	ld hl, wPlayerMonAttackMod
-	call ResetStatMods
-	ld hl, wEnemyMonAttackMod
-	call ResetStatMods
-; copy unmodified stats to battle stats
-	ld hl, wPlayerMonUnmodifiedAttack
-	ld de, wBattleMonAttack
-	call ResetStats
-	ld hl, wEnemyMonUnmodifiedAttack
-	ld de, wEnemyMonAttack
-	call ResetStats
-; cure non-volatile status, but only for the target
-	ld hl, wEnemyMonStatus
-	ld de, wEnemySelectedMove
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .cureStatuses
-	ld hl, wBattleMonStatus
-	dec de ; wPlayerSelectedMove
-
-.cureStatuses
-	ld a, [hl]
-	ld [hl], $0
-	and SLP | (1 << FRZ)
-	jr z, .cureVolatileStatuses
-; prevent the Pokemon from executing a move if it was asleep or frozen
-	ld a, $ff
-	ld [de], a
-
-.cureVolatileStatuses
-	xor a
-	ld [wPlayerDisabledMove], a
-	ld [wEnemyDisabledMove], a
-	ld hl, wPlayerDisabledMoveNumber
-	ld [hli], a
-	ld [hl], a
-	ld hl, wPlayerBattleStatus1
-	call CureVolatileStatuses
-	ld hl, wEnemyBattleStatus1
-	call CureVolatileStatuses
-	ld hl, PlayCurrentMoveAnimation
-	call CallBankF
-	ld hl, StatusChangesEliminatedText
-	jp PrintText
-
-CureVolatileStatuses:
-	res CONFUSED, [hl]
-	inc hl ; BATTSTATUS2
-	ld a, [hl]
-	; clear USING_X_ACCURACY, PROTECTED_BY_MIST, GETTING_PUMPED, and SEEDED statuses
-	and $ff ^((1 << USING_X_ACCURACY) | (1 << PROTECTED_BY_MIST) | (1 << GETTING_PUMPED) | (1 << SEEDED))
-	ld [hli], a ; BATTSTATUS3
-	ld a, [hl]
-	and %11110000 | (1 << TRANSFORMED) ; clear Bad Poison, Reflect and Light Screen statuses
-	ld [hl], a
-	ret
-
-ResetStatMods:
-	ld b, $8
-.loop
-	ld [hli], a
-	dec b
-	jr nz, .loop
-	ret
-
-ResetStats:
-	ld b, $8
-.loop
-	ld a, [hli]
-	ld [de], a
-	inc de
-	dec b
-	jr nz, .loop
-	ret
-
-StatusChangesEliminatedText:
-	TX_FAR _StatusChangesEliminatedText
-	db "@"
--- a/engine/battle/moveEffects/heal_effect.asm
+++ /dev/null
@@ -1,120 +1,0 @@
-HealEffect_:
-	ld a, [H_WHOSETURN]
-	and a
-	ld de, wBattleMonHP
-	ld hl, wBattleMonMaxHP
-	ld a, [wPlayerMoveNum]
-	jr z, .healEffect
-	ld de, wEnemyMonHP
-	ld hl, wEnemyMonMaxHP
-	ld a, [wEnemyMoveNum]
-.healEffect
-	ld b, a
-	ld a, [de]
-	cp [hl] ; most significant bytes comparison is ignored
-	        ; causes the move to miss if max HP is 255 or 511 points higher than the current HP
-	inc de
-	inc hl
-	ld a, [de]
-	sbc [hl]
-	jp z, .failed ; no effect if user's HP is already at its maximum
-	ld a, b
-	cp REST
-	jr nz, .healHP
-	push hl
-	push de
-	push af
-	ld c, 50
-	call DelayFrames
-	ld hl, wBattleMonStatus
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .restEffect
-	ld hl, wEnemyMonStatus
-.restEffect
-	ld a, [hl]
-	and a
-	ld [hl], 2 ; clear status and set number of turns asleep to 2
-	ld hl, StartedSleepingEffect ; if mon didn't have an status
-	jr z, .printRestText
-	ld hl, FellAsleepBecameHealthyText ; if mon had an status
-.printRestText
-	call PrintText
-	pop af
-	pop de
-	pop hl
-.healHP
-	ld a, [hld]
-	ld [wHPBarMaxHP], a
-	ld c, a
-	ld a, [hl]
-	ld [wHPBarMaxHP+1], a
-	ld b, a
-	jr z, .gotHPAmountToHeal
-; Recover and Softboiled only heal for half the mon's max HP
-	srl b
-	rr c
-.gotHPAmountToHeal
-; update HP
-	ld a, [de]
-	ld [wHPBarOldHP], a
-	add c
-	ld [de], a
-	ld [wHPBarNewHP], a
-	dec de
-	ld a, [de]
-	ld [wHPBarOldHP+1], a
-	adc b
-	ld [de], a
-	ld [wHPBarNewHP+1], a
-	inc hl
-	inc de
-	ld a, [de]
-	dec de
-	sub [hl]
-	dec hl
-	ld a, [de]
-	sbc [hl]
-	jr c, .playAnim
-; copy max HP to current HP if an overflow occurred
-	ld a, [hli]
-	ld [de], a
-	ld [wHPBarNewHP+1], a
-	inc de
-	ld a, [hl]
-	ld [de], a
-	ld [wHPBarNewHP], a
-.playAnim
-	ld hl, PlayCurrentMoveAnimation
-	call BankswitchEtoF
-	ld a, [H_WHOSETURN]
-	and a
-	coord hl, 10, 9
-	ld a, $1
-	jr z, .updateHPBar
-	coord hl, 2, 2
-	xor a
-.updateHPBar
-	ld [wHPBarType], a
-	predef UpdateHPBar2
-	ld hl, DrawHUDsAndHPBars
-	call BankswitchEtoF
-	ld hl, RegainedHealthText
-	jp PrintText
-.failed
-	ld c, 50
-	call DelayFrames
-	ld hl, PrintButItFailedText_
-	jp BankswitchEtoF
-
-StartedSleepingEffect:
-	TX_FAR _StartedSleepingEffect
-	db "@"
-
-FellAsleepBecameHealthyText:
-	TX_FAR _FellAsleepBecameHealthyText
-	db "@"
-
-RegainedHealthText:
-	TX_FAR _RegainedHealthText
-	db "@"
--- a/engine/battle/moveEffects/leech_seed_effect.asm
+++ /dev/null
@@ -1,40 +1,0 @@
-LeechSeedEffect_:
-	callab MoveHitTest
-	ld a, [wMoveMissed]
-	and a
-	jr nz, .moveMissed
-	ld hl, wEnemyBattleStatus2
-	ld de, wEnemyMonType1
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .leechSeedEffect
-	ld hl, wPlayerBattleStatus2
-	ld de, wBattleMonType1
-.leechSeedEffect
-; miss if the target is grass-type or already seeded
-	ld a, [de]
-	cp GRASS
-	jr z, .moveMissed
-	inc de
-	ld a, [de]
-	cp GRASS
-	jr z, .moveMissed
-	bit SEEDED, [hl]
-	jr nz, .moveMissed
-	set SEEDED, [hl]
-	callab PlayCurrentMoveAnimation
-	ld hl, WasSeededText
-	jp PrintText
-.moveMissed
-	ld c, 50
-	call DelayFrames
-	ld hl, EvadedAttackText
-	jp PrintText
-
-WasSeededText:
-	TX_FAR _WasSeededText
-	db "@"
-
-EvadedAttackText:
-	TX_FAR _EvadedAttackText
-	db "@"
--- a/engine/battle/moveEffects/mist_effect.asm
+++ /dev/null
@@ -1,19 +1,0 @@
-MistEffect_:
-	ld hl, wPlayerBattleStatus2
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .mistEffect
-	ld hl, wEnemyBattleStatus2
-.mistEffect
-	bit PROTECTED_BY_MIST, [hl] ; is mon protected by mist?
-	jr nz, .mistAlreadyInUse
-	set PROTECTED_BY_MIST, [hl] ; mon is now protected by mist
-	callab PlayCurrentMoveAnimation
-	ld hl, ShroudedInMistText
-	jp PrintText
-.mistAlreadyInUse
-	jpab PrintButItFailedText_
-
-ShroudedInMistText:
-	TX_FAR _ShroudedInMistText
-	db "@"
--- a/engine/battle/moveEffects/one_hit_ko_effect.asm
+++ /dev/null
@@ -1,38 +1,0 @@
-OneHitKOEffect_:
-	ld hl, wDamage
-	xor a
-	ld [hli], a
-	ld [hl], a ; set the damage output to zero
-	dec a
-	ld [wCriticalHitOrOHKO], a
-	ld hl, wBattleMonSpeed + 1
-	ld de, wEnemyMonSpeed + 1
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .compareSpeed
-	ld hl, wEnemyMonSpeed + 1
-	ld de, wBattleMonSpeed + 1
-.compareSpeed
-; set damage to 65535 and OHKO flag is the user's current speed is higher than the target's
-	ld a, [de]
-	dec de
-	ld b, a
-	ld a, [hld]
-	sub b
-	ld a, [de]
-	ld b, a
-	ld a, [hl]
-	sbc b
-	jr c, .userIsSlower
-	ld hl, wDamage
-	ld a, $ff
-	ld [hli], a
-	ld [hl], a
-	ld a, $2
-	ld [wCriticalHitOrOHKO], a
-	ret
-.userIsSlower
-; keep damage at 0 and set move missed flag if target's current speed is higher instead
-	ld a, $1
-	ld [wMoveMissed], a
-	ret
--- a/engine/battle/moveEffects/paralyze_effect.asm
+++ /dev/null
@@ -1,47 +1,0 @@
-ParalyzeEffect_:
-	ld hl, wEnemyMonStatus
-	ld de, wPlayerMoveType
-	ld a, [H_WHOSETURN]
-	and a
-	jp z, .next
-	ld hl, wBattleMonStatus
-	ld de, wEnemyMoveType
-.next
-	ld a, [hl]
-	and a ; does the target already have a status ailment?
-	jr nz, .didntAffect
-; check if the target is immune due to types
-	ld a, [de]
-	cp ELECTRIC
-	jr nz, .hitTest
-	ld b, h
-	ld c, l
-	inc bc
-	ld a, [bc]
-	cp GROUND
-	jr z, .doesntAffect
-	inc bc
-	ld a, [bc]
-	cp GROUND
-	jr z, .doesntAffect
-.hitTest
-	push hl
-	callab MoveHitTest
-	pop hl
-	ld a, [wMoveMissed]
-	and a
-	jr nz, .didntAffect
-	set PAR, [hl]
-	callab QuarterSpeedDueToParalysis
-	ld c, 30
-	call DelayFrames
-	callab PlayCurrentMoveAnimation
-	jpab PrintMayNotAttackText
-.didntAffect
-	ld c, 50
-	call DelayFrames
-	jpab PrintDidntAffectText
-.doesntAffect
-	ld c, 50
-	call DelayFrames
-	jpab PrintDoesntAffectText
--- a/engine/battle/moveEffects/pay_day_effect.asm
+++ /dev/null
@@ -1,45 +1,0 @@
-PayDayEffect_:
-	xor a
-	ld hl, wcd6d
-	ld [hli], a
-	ld a, [H_WHOSETURN]
-	and a
-	ld a, [wBattleMonLevel]
-	jr z, .payDayEffect
-	ld a, [wEnemyMonLevel]
-.payDayEffect
-; level * 2
-	add a
-	ld [H_DIVIDEND + 3], a
-	xor a
-	ld [H_DIVIDEND], a
-	ld [H_DIVIDEND + 1], a
-	ld [H_DIVIDEND + 2], a
-; convert to BCD
-	ld a, 100
-	ld [H_DIVISOR], a
-	ld b, $4
-	call Divide
-	ld a, [H_QUOTIENT + 3]
-	ld [hli], a
-	ld a, [H_REMAINDER]
-	ld [H_DIVIDEND + 3], a
-	ld a, 10
-	ld [H_DIVISOR], a
-	ld b, $4
-	call Divide
-	ld a, [H_QUOTIENT + 3]
-	swap a
-	ld b, a
-	ld a, [H_REMAINDER]
-	add b
-	ld [hl], a
-	ld de, wTotalPayDayMoney + 2
-	ld c, $3
-	predef AddBCDPredef
-	ld hl, CoinsScatteredText
-	jp PrintText
-
-CoinsScatteredText:
-	TX_FAR _CoinsScatteredText
-	db "@"
--- a/engine/battle/moveEffects/recoil_effect.asm
+++ /dev/null
@@ -1,70 +1,0 @@
-RecoilEffect_:
-	ld a, [H_WHOSETURN]
-	and a
-	ld a, [wPlayerMoveNum]
-	ld hl, wBattleMonMaxHP
-	jr z, .recoilEffect
-	ld a, [wEnemyMoveNum]
-	ld hl, wEnemyMonMaxHP
-.recoilEffect
-	ld d, a
-	ld a, [wDamage]
-	ld b, a
-	ld a, [wDamage + 1]
-	ld c, a
-	srl b
-	rr c
-	ld a, d
-	cp STRUGGLE ; struggle deals 50% recoil damage
-	jr z, .gotRecoilDamage
-	srl b
-	rr c
-.gotRecoilDamage
-	ld a, b
-	or c
-	jr nz, .updateHP
-	inc c ; minimum recoil damage is 1
-.updateHP
-; subtract HP from user due to the recoil damage
-	ld a, [hli]
-	ld [wHPBarMaxHP+1], a
-	ld a, [hl]
-	ld [wHPBarMaxHP], a
-	push bc
-	ld bc, wBattleMonHP - wBattleMonMaxHP
-	add hl, bc
-	pop bc
-	ld a, [hl]
-	ld [wHPBarOldHP], a
-	sub c
-	ld [hld], a
-	ld [wHPBarNewHP], a
-	ld a, [hl]
-	ld [wHPBarOldHP+1], a
-	sbc b
-	ld [hl], a
-	ld [wHPBarNewHP+1], a
-	jr nc, .getHPBarCoords
-; if recoil damage is higher than the Pokemon's HP, set its HP to 0
-	xor a
-	ld [hli], a
-	ld [hl], a
-	ld hl, wHPBarNewHP
-	ld [hli], a
-	ld [hl], a
-.getHPBarCoords
-	coord hl, 10, 9
-	ld a, [H_WHOSETURN]
-	and a
-	ld a, $1
-	jr z, .updateHPBar
-	coord hl, 2, 2
-	xor a
-.updateHPBar
-	ld [wHPBarType], a
-	predef UpdateHPBar2
-	ld hl, HitWithRecoilText
-	jp PrintText
-HitWithRecoilText:
-	TX_FAR _HitWithRecoilText
-	db "@"
--- a/engine/battle/moveEffects/reflect_light_screen_effect.asm
+++ /dev/null
@@ -1,45 +1,0 @@
-ReflectLightScreenEffect_:
-	ld hl, wPlayerBattleStatus3
-	ld de, wPlayerMoveEffect
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .reflectLightScreenEffect
-	ld hl, wEnemyBattleStatus3
-	ld de, wEnemyMoveEffect
-.reflectLightScreenEffect
-	ld a, [de]
-	cp LIGHT_SCREEN_EFFECT
-	jr nz, .reflect
-	bit HAS_LIGHT_SCREEN_UP, [hl] ; is mon already protected by light screen?
-	jr nz, .moveFailed
-	set HAS_LIGHT_SCREEN_UP, [hl] ; mon is now protected by light screen
-	ld hl, LightScreenProtectedText
-	jr .playAnim
-.reflect
-	bit HAS_REFLECT_UP, [hl] ; is mon already protected by reflect?
-	jr nz, .moveFailed
-	set HAS_REFLECT_UP, [hl] ; mon is now protected by reflect
-	ld hl, ReflectGainedArmorText
-.playAnim
-	push hl
-	ld hl, PlayCurrentMoveAnimation
-	call BankswitchEtoF
-	pop hl
-	jp PrintText
-.moveFailed
-	ld c, 50
-	call DelayFrames
-	ld hl, PrintButItFailedText_
-	jp BankswitchEtoF
-
-LightScreenProtectedText:
-	TX_FAR _LightScreenProtectedText
-	db "@"
-
-ReflectGainedArmorText:
-	TX_FAR _ReflectGainedArmorText
-	db "@"
-
-BankswitchEtoF:
-	ld b, BANK(BattleCore)
-	jp Bankswitch
--- a/engine/battle/moveEffects/substitute_effect.asm
+++ /dev/null
@@ -1,77 +1,0 @@
-SubstituteEffect_:
-	ld c, 50
-	call DelayFrames
-	ld hl, wBattleMonMaxHP
-	ld de, wPlayerSubstituteHP
-	ld bc, wPlayerBattleStatus2
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .notEnemy
-	ld hl, wEnemyMonMaxHP
-	ld de, wEnemySubstituteHP
-	ld bc, wEnemyBattleStatus2
-.notEnemy
-	ld a, [bc]
-	bit HAS_SUBSTITUTE_UP, a ; user already has substitute?
-	jr nz, .alreadyHasSubstitute
-; quarter health to remove from user
-; assumes max HP is 1023 or lower
-	push bc
-	ld a, [hli]
-	ld b, [hl]
-	srl a
-	rr b
-	srl a
-	rr b ; max hp / 4
-	push de
-	ld de, wBattleMonHP - wBattleMonMaxHP
-	add hl, de ; point hl to current HP low byte
-	pop de
-	ld a, b
-	ld [de], a ; save copy of HP to subtract in wPlayerSubstituteHP/wEnemySubstituteHP
-	ld a, [hld]
-; subtract [max hp / 4] to current HP
-	sub b
-	ld d, a
-	ld a, [hl]
-	sbc 0
-	pop bc
-	jr c, .notEnoughHP ; underflow means user would be left with negative health
-                           ; bug: since it only branches on carry, it will possibly leave user with 0 HP
-.userHasZeroOrMoreHP
-	ldi [hl], a ; save resulting HP after subtraction into current HP
-	ld [hl], d
-	ld h, b
-	ld l, c
-	set HAS_SUBSTITUTE_UP, [hl]
-	ld a, [wOptions]
-	bit 7, a ; battle animation is enabled?
-	ld hl, PlayCurrentMoveAnimation
-	ld b, BANK(PlayCurrentMoveAnimation)
-	jr z, .animationEnabled
-	ld hl, AnimationSubstitute
-	ld b, BANK(AnimationSubstitute)
-.animationEnabled
-	call Bankswitch ; jump to routine depending on animation setting
-	ld hl, SubstituteText
-	call PrintText
-	jpab DrawHUDsAndHPBars
-.alreadyHasSubstitute
-	ld hl, HasSubstituteText
-	jr .printText
-.notEnoughHP
-	ld hl, TooWeakSubstituteText
-.printText
-	jp PrintText
-
-SubstituteText:
-	TX_FAR _SubstituteText
-	db "@"
-
-HasSubstituteText:
-	TX_FAR _HasSubstituteText
-	db "@"
-
-TooWeakSubstituteText:
-	TX_FAR _TooWeakSubstituteText
-	db "@"
--- a/engine/battle/moveEffects/transform_effect.asm
+++ /dev/null
@@ -1,148 +1,0 @@
-TransformEffect_:
-	ld hl, wBattleMonSpecies
-	ld de, wEnemyMonSpecies
-	ld bc, wEnemyBattleStatus3
-	ld a, [wEnemyBattleStatus1]
-	ld a, [H_WHOSETURN]
-	and a
-	jr nz, .hitTest
-	ld hl, wEnemyMonSpecies
-	ld de, wBattleMonSpecies
-	ld bc, wPlayerBattleStatus3
-	ld [wPlayerMoveListIndex], a
-	ld a, [wPlayerBattleStatus1]
-.hitTest
-	bit INVULNERABLE, a ; is mon invulnerable to typical attacks? (fly/dig)
-	jp nz, .failed
-	push hl
-	push de
-	push bc
-	ld hl, wPlayerBattleStatus2
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .transformEffect
-	ld hl, wEnemyBattleStatus2
-.transformEffect
-; animation(s) played are different if target has Substitute up
-	bit HAS_SUBSTITUTE_UP, [hl]
-	push af
-	ld hl, HideSubstituteShowMonAnim
-	ld b, BANK(HideSubstituteShowMonAnim)
-	call nz, Bankswitch
-	ld a, [wOptions]
-	add a
-	ld hl, PlayCurrentMoveAnimation
-	ld b, BANK(PlayCurrentMoveAnimation)
-	jr nc, .gotAnimToPlay
-	ld hl, AnimationTransformMon
-	ld b, BANK(AnimationTransformMon)
-.gotAnimToPlay
-	call Bankswitch
-	ld hl, ReshowSubstituteAnim
-	ld b, BANK(ReshowSubstituteAnim)
-	pop af
-	call nz, Bankswitch
-	pop bc
-	ld a, [bc]
-	set TRANSFORMED, a ; mon is now transformed
-	ld [bc], a
-	pop de
-	pop hl
-	push hl
-; transform user into opposing Pokemon
-; species
-	ld a, [hl]
-	ld [de], a
-; type 1, type 2, catch rate, and moves
-	ld bc, $5
-	add hl, bc
-	inc de
-	inc de
-	inc de
-	inc de
-	inc de
-	inc bc
-	inc bc
-	call CopyData
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .next
-; save enemy mon DVs at wTransformedEnemyMonOriginalDVs
-	ld a, [de]
-	ld [wTransformedEnemyMonOriginalDVs], a
-	inc de
-	ld a, [de]
-	ld [wTransformedEnemyMonOriginalDVs + 1], a
-	dec de
-.next
-; DVs
-	ld a, [hli]
-	ld [de], a
-	inc de
-	ld a, [hli]
-	ld [de], a
-	inc de
-; Attack, Defense, Speed, and Special stats
-	inc hl
-	inc hl
-	inc hl
-	inc de
-	inc de
-	inc de
-	ld bc, $8
-	call CopyData
-	ld bc, wBattleMonMoves - wBattleMonPP
-	add hl, bc ; ld hl, wBattleMonMoves
-	ld b, NUM_MOVES
-.copyPPLoop
-; 5 PP for all moves
-	ld a, [hli]
-	and a
-	jr z, .lessThanFourMoves
-	ld a, $5
-	ld [de], a
-	inc de
-	dec b
-	jr nz, .copyPPLoop
-	jr .copyStats
-.lessThanFourMoves
-; 0 PP for blank moves
-	xor a
-	ld [de], a
-	inc de
-	dec b
-	jr nz, .lessThanFourMoves
-.copyStats
-; original (unmodified) stats and stat mods
-	pop hl
-	ld a, [hl]
-	ld [wd11e], a
-	call GetMonName
-	ld hl, wEnemyMonUnmodifiedAttack
-	ld de, wPlayerMonUnmodifiedAttack
-	call .copyBasedOnTurn ; original (unmodified) stats
-	ld hl, wEnemyMonStatMods
-	ld de, wPlayerMonStatMods
-	call .copyBasedOnTurn ; stat mods
-	ld hl, TransformedText
-	jp PrintText
-
-.copyBasedOnTurn
-	ld a, [H_WHOSETURN]
-	and a
-	jr z, .gotStatsOrModsToCopy
-	push hl
-	ld h, d
-	ld l, e
-	pop de
-.gotStatsOrModsToCopy
-	ld bc, $8
-	jp CopyData
-
-.failed
-	ld hl, PrintButItFailedText_
-	jp BankswitchEtoF
-
-TransformedText:
-	TX_FAR _TransformedText
-	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/conversion.asm
@@ -1,0 +1,35 @@
+ConversionEffect_:
+	ld hl, wEnemyMonType1
+	ld de, wBattleMonType1
+	ld a, [H_WHOSETURN]
+	and a
+	ld a, [wEnemyBattleStatus1]
+	jr z, .conversionEffect
+	push hl
+	ld h, d
+	ld l, e
+	pop de
+	ld a, [wPlayerBattleStatus1]
+.conversionEffect
+	bit INVULNERABLE, a ; is mon immune to typical attacks (dig/fly)
+	jr nz, PrintButItFailedText
+; copy target's types to user
+	ld a, [hli]
+	ld [de], a
+	inc de
+	ld a, [hl]
+	ld [de], a
+	ld hl, PlayCurrentMoveAnimation
+	call CallBankF
+	ld hl, ConvertedTypeText
+	jp PrintText
+
+ConvertedTypeText:
+	TX_FAR _ConvertedTypeText
+	db "@"
+
+PrintButItFailedText:
+	ld hl, PrintButItFailedText_
+CallBankF:
+	ld b, BANK(PrintButItFailedText_)
+	jp Bankswitch
--- /dev/null
+++ b/engine/battle/move_effects/drain_hp.asm
@@ -1,0 +1,104 @@
+DrainHPEffect_:
+	ld hl, wDamage
+	ld a, [hl]
+	srl a ; divide damage by 2
+	ld [hli], a
+	ld a, [hl]
+	rr a
+	ld [hld], a
+	or [hl] ; is damage 0?
+	jr nz, .getAttackerHP
+; if damage is 0, increase to 1 so that the attacker gains at least 1 HP
+	inc hl
+	inc [hl]
+.getAttackerHP
+	ld hl, wBattleMonHP
+	ld de, wBattleMonMaxHP
+	ld a, [H_WHOSETURN]
+	and a
+	jp z, .addDamageToAttackerHP
+	ld hl, wEnemyMonHP
+	ld de, wEnemyMonMaxHP
+.addDamageToAttackerHP
+	ld bc, wHPBarOldHP+1
+; copy current HP to wHPBarOldHP
+	ld a, [hli]
+	ld [bc], a
+	ld a, [hl]
+	dec bc
+	ld [bc], a
+; copy max HP to wHPBarMaxHP
+	ld a, [de]
+	dec bc
+	ld [bc], a
+	inc de
+	ld a, [de]
+	dec bc
+	ld [bc], a
+; add damage to attacker's HP and copy new HP to wHPBarNewHP
+	ld a, [wDamage + 1]
+	ld b, [hl]
+	add b
+	ld [hld], a
+	ld [wHPBarNewHP], a
+	ld a, [wDamage]
+	ld b, [hl]
+	adc b
+	ld [hli], a
+	ld [wHPBarNewHP+1], a
+	jr c, .capToMaxHP ; if HP > 65,535, cap to max HP
+; compare HP with max HP
+	ld a, [hld]
+	ld b, a
+	ld a, [de]
+	dec de
+	sub b
+	ld a, [hli]
+	ld b, a
+	ld a, [de]
+	inc de
+	sbc b
+	jr nc, .next
+.capToMaxHP
+	ld a, [de]
+	ld [hld], a
+	ld [wHPBarNewHP], a
+	dec de
+	ld a, [de]
+	ld [hli], a
+	ld [wHPBarNewHP+1], a
+	inc de
+.next
+	ld a, [H_WHOSETURN]
+	and a
+	coord hl, 10, 9
+	ld a, $1
+	jr z, .next2
+	coord hl, 2, 2
+	xor a
+.next2
+	ld [wHPBarType], a
+	predef UpdateHPBar2
+	predef DrawPlayerHUDAndHPBar
+	predef DrawEnemyHUDAndHPBar
+	callab ReadPlayerMonCurHPAndStatus
+	ld hl, SuckedHealthText
+	ld a, [H_WHOSETURN]
+	and a
+	ld a, [wPlayerMoveEffect]
+	jr z, .next3
+	ld a, [wEnemyMoveEffect]
+.next3
+	cp DREAM_EATER_EFFECT
+	jr nz, .printText
+	ld hl, DreamWasEatenText
+.printText
+	jp PrintText
+
+SuckedHealthText:
+	TX_FAR _SuckedHealthText
+	db "@"
+
+DreamWasEatenText:
+	TX_FAR _DreamWasEatenText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/focus_energy.asm
@@ -1,0 +1,22 @@
+FocusEnergyEffect_:
+	ld hl, wPlayerBattleStatus2
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .notEnemy
+	ld hl, wEnemyBattleStatus2
+.notEnemy
+	bit GETTING_PUMPED, [hl] ; is mon already using focus energy?
+	jr nz, .alreadyUsing
+	set GETTING_PUMPED, [hl] ; mon is now using focus energy
+	callab PlayCurrentMoveAnimation
+	ld hl, GettingPumpedText
+	jp PrintText
+.alreadyUsing
+	ld c, 50
+	call DelayFrames
+	jpab PrintButItFailedText_
+
+GettingPumpedText:
+	TX_DELAY
+	TX_FAR _GettingPumpedText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/haze.asm
@@ -1,0 +1,81 @@
+HazeEffect_:
+	ld a, $7
+; store 7 on every stat mod
+	ld hl, wPlayerMonAttackMod
+	call ResetStatMods
+	ld hl, wEnemyMonAttackMod
+	call ResetStatMods
+; copy unmodified stats to battle stats
+	ld hl, wPlayerMonUnmodifiedAttack
+	ld de, wBattleMonAttack
+	call ResetStats
+	ld hl, wEnemyMonUnmodifiedAttack
+	ld de, wEnemyMonAttack
+	call ResetStats
+; cure non-volatile status, but only for the target
+	ld hl, wEnemyMonStatus
+	ld de, wEnemySelectedMove
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .cureStatuses
+	ld hl, wBattleMonStatus
+	dec de ; wPlayerSelectedMove
+
+.cureStatuses
+	ld a, [hl]
+	ld [hl], $0
+	and SLP | (1 << FRZ)
+	jr z, .cureVolatileStatuses
+; prevent the Pokemon from executing a move if it was asleep or frozen
+	ld a, $ff
+	ld [de], a
+
+.cureVolatileStatuses
+	xor a
+	ld [wPlayerDisabledMove], a
+	ld [wEnemyDisabledMove], a
+	ld hl, wPlayerDisabledMoveNumber
+	ld [hli], a
+	ld [hl], a
+	ld hl, wPlayerBattleStatus1
+	call CureVolatileStatuses
+	ld hl, wEnemyBattleStatus1
+	call CureVolatileStatuses
+	ld hl, PlayCurrentMoveAnimation
+	call CallBankF
+	ld hl, StatusChangesEliminatedText
+	jp PrintText
+
+CureVolatileStatuses:
+	res CONFUSED, [hl]
+	inc hl ; BATTSTATUS2
+	ld a, [hl]
+	; clear USING_X_ACCURACY, PROTECTED_BY_MIST, GETTING_PUMPED, and SEEDED statuses
+	and $ff ^((1 << USING_X_ACCURACY) | (1 << PROTECTED_BY_MIST) | (1 << GETTING_PUMPED) | (1 << SEEDED))
+	ld [hli], a ; BATTSTATUS3
+	ld a, [hl]
+	and %11110000 | (1 << TRANSFORMED) ; clear Bad Poison, Reflect and Light Screen statuses
+	ld [hl], a
+	ret
+
+ResetStatMods:
+	ld b, $8
+.loop
+	ld [hli], a
+	dec b
+	jr nz, .loop
+	ret
+
+ResetStats:
+	ld b, $8
+.loop
+	ld a, [hli]
+	ld [de], a
+	inc de
+	dec b
+	jr nz, .loop
+	ret
+
+StatusChangesEliminatedText:
+	TX_FAR _StatusChangesEliminatedText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/heal.asm
@@ -1,0 +1,120 @@
+HealEffect_:
+	ld a, [H_WHOSETURN]
+	and a
+	ld de, wBattleMonHP
+	ld hl, wBattleMonMaxHP
+	ld a, [wPlayerMoveNum]
+	jr z, .healEffect
+	ld de, wEnemyMonHP
+	ld hl, wEnemyMonMaxHP
+	ld a, [wEnemyMoveNum]
+.healEffect
+	ld b, a
+	ld a, [de]
+	cp [hl] ; most significant bytes comparison is ignored
+	        ; causes the move to miss if max HP is 255 or 511 points higher than the current HP
+	inc de
+	inc hl
+	ld a, [de]
+	sbc [hl]
+	jp z, .failed ; no effect if user's HP is already at its maximum
+	ld a, b
+	cp REST
+	jr nz, .healHP
+	push hl
+	push de
+	push af
+	ld c, 50
+	call DelayFrames
+	ld hl, wBattleMonStatus
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .restEffect
+	ld hl, wEnemyMonStatus
+.restEffect
+	ld a, [hl]
+	and a
+	ld [hl], 2 ; clear status and set number of turns asleep to 2
+	ld hl, StartedSleepingEffect ; if mon didn't have an status
+	jr z, .printRestText
+	ld hl, FellAsleepBecameHealthyText ; if mon had an status
+.printRestText
+	call PrintText
+	pop af
+	pop de
+	pop hl
+.healHP
+	ld a, [hld]
+	ld [wHPBarMaxHP], a
+	ld c, a
+	ld a, [hl]
+	ld [wHPBarMaxHP+1], a
+	ld b, a
+	jr z, .gotHPAmountToHeal
+; Recover and Softboiled only heal for half the mon's max HP
+	srl b
+	rr c
+.gotHPAmountToHeal
+; update HP
+	ld a, [de]
+	ld [wHPBarOldHP], a
+	add c
+	ld [de], a
+	ld [wHPBarNewHP], a
+	dec de
+	ld a, [de]
+	ld [wHPBarOldHP+1], a
+	adc b
+	ld [de], a
+	ld [wHPBarNewHP+1], a
+	inc hl
+	inc de
+	ld a, [de]
+	dec de
+	sub [hl]
+	dec hl
+	ld a, [de]
+	sbc [hl]
+	jr c, .playAnim
+; copy max HP to current HP if an overflow occurred
+	ld a, [hli]
+	ld [de], a
+	ld [wHPBarNewHP+1], a
+	inc de
+	ld a, [hl]
+	ld [de], a
+	ld [wHPBarNewHP], a
+.playAnim
+	ld hl, PlayCurrentMoveAnimation
+	call BankswitchEtoF
+	ld a, [H_WHOSETURN]
+	and a
+	coord hl, 10, 9
+	ld a, $1
+	jr z, .updateHPBar
+	coord hl, 2, 2
+	xor a
+.updateHPBar
+	ld [wHPBarType], a
+	predef UpdateHPBar2
+	ld hl, DrawHUDsAndHPBars
+	call BankswitchEtoF
+	ld hl, RegainedHealthText
+	jp PrintText
+.failed
+	ld c, 50
+	call DelayFrames
+	ld hl, PrintButItFailedText_
+	jp BankswitchEtoF
+
+StartedSleepingEffect:
+	TX_FAR _StartedSleepingEffect
+	db "@"
+
+FellAsleepBecameHealthyText:
+	TX_FAR _FellAsleepBecameHealthyText
+	db "@"
+
+RegainedHealthText:
+	TX_FAR _RegainedHealthText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/leech_seed.asm
@@ -1,0 +1,40 @@
+LeechSeedEffect_:
+	callab MoveHitTest
+	ld a, [wMoveMissed]
+	and a
+	jr nz, .moveMissed
+	ld hl, wEnemyBattleStatus2
+	ld de, wEnemyMonType1
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .leechSeedEffect
+	ld hl, wPlayerBattleStatus2
+	ld de, wBattleMonType1
+.leechSeedEffect
+; miss if the target is grass-type or already seeded
+	ld a, [de]
+	cp GRASS
+	jr z, .moveMissed
+	inc de
+	ld a, [de]
+	cp GRASS
+	jr z, .moveMissed
+	bit SEEDED, [hl]
+	jr nz, .moveMissed
+	set SEEDED, [hl]
+	callab PlayCurrentMoveAnimation
+	ld hl, WasSeededText
+	jp PrintText
+.moveMissed
+	ld c, 50
+	call DelayFrames
+	ld hl, EvadedAttackText
+	jp PrintText
+
+WasSeededText:
+	TX_FAR _WasSeededText
+	db "@"
+
+EvadedAttackText:
+	TX_FAR _EvadedAttackText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/mist.asm
@@ -1,0 +1,19 @@
+MistEffect_:
+	ld hl, wPlayerBattleStatus2
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .mistEffect
+	ld hl, wEnemyBattleStatus2
+.mistEffect
+	bit PROTECTED_BY_MIST, [hl] ; is mon protected by mist?
+	jr nz, .mistAlreadyInUse
+	set PROTECTED_BY_MIST, [hl] ; mon is now protected by mist
+	callab PlayCurrentMoveAnimation
+	ld hl, ShroudedInMistText
+	jp PrintText
+.mistAlreadyInUse
+	jpab PrintButItFailedText_
+
+ShroudedInMistText:
+	TX_FAR _ShroudedInMistText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/one_hit_ko.asm
@@ -1,0 +1,38 @@
+OneHitKOEffect_:
+	ld hl, wDamage
+	xor a
+	ld [hli], a
+	ld [hl], a ; set the damage output to zero
+	dec a
+	ld [wCriticalHitOrOHKO], a
+	ld hl, wBattleMonSpeed + 1
+	ld de, wEnemyMonSpeed + 1
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .compareSpeed
+	ld hl, wEnemyMonSpeed + 1
+	ld de, wBattleMonSpeed + 1
+.compareSpeed
+; set damage to 65535 and OHKO flag is the user's current speed is higher than the target's
+	ld a, [de]
+	dec de
+	ld b, a
+	ld a, [hld]
+	sub b
+	ld a, [de]
+	ld b, a
+	ld a, [hl]
+	sbc b
+	jr c, .userIsSlower
+	ld hl, wDamage
+	ld a, $ff
+	ld [hli], a
+	ld [hl], a
+	ld a, $2
+	ld [wCriticalHitOrOHKO], a
+	ret
+.userIsSlower
+; keep damage at 0 and set move missed flag if target's current speed is higher instead
+	ld a, $1
+	ld [wMoveMissed], a
+	ret
--- /dev/null
+++ b/engine/battle/move_effects/paralyze.asm
@@ -1,0 +1,47 @@
+ParalyzeEffect_:
+	ld hl, wEnemyMonStatus
+	ld de, wPlayerMoveType
+	ld a, [H_WHOSETURN]
+	and a
+	jp z, .next
+	ld hl, wBattleMonStatus
+	ld de, wEnemyMoveType
+.next
+	ld a, [hl]
+	and a ; does the target already have a status ailment?
+	jr nz, .didntAffect
+; check if the target is immune due to types
+	ld a, [de]
+	cp ELECTRIC
+	jr nz, .hitTest
+	ld b, h
+	ld c, l
+	inc bc
+	ld a, [bc]
+	cp GROUND
+	jr z, .doesntAffect
+	inc bc
+	ld a, [bc]
+	cp GROUND
+	jr z, .doesntAffect
+.hitTest
+	push hl
+	callab MoveHitTest
+	pop hl
+	ld a, [wMoveMissed]
+	and a
+	jr nz, .didntAffect
+	set PAR, [hl]
+	callab QuarterSpeedDueToParalysis
+	ld c, 30
+	call DelayFrames
+	callab PlayCurrentMoveAnimation
+	jpab PrintMayNotAttackText
+.didntAffect
+	ld c, 50
+	call DelayFrames
+	jpab PrintDidntAffectText
+.doesntAffect
+	ld c, 50
+	call DelayFrames
+	jpab PrintDoesntAffectText
--- /dev/null
+++ b/engine/battle/move_effects/pay_day.asm
@@ -1,0 +1,45 @@
+PayDayEffect_:
+	xor a
+	ld hl, wcd6d
+	ld [hli], a
+	ld a, [H_WHOSETURN]
+	and a
+	ld a, [wBattleMonLevel]
+	jr z, .payDayEffect
+	ld a, [wEnemyMonLevel]
+.payDayEffect
+; level * 2
+	add a
+	ld [H_DIVIDEND + 3], a
+	xor a
+	ld [H_DIVIDEND], a
+	ld [H_DIVIDEND + 1], a
+	ld [H_DIVIDEND + 2], a
+; convert to BCD
+	ld a, 100
+	ld [H_DIVISOR], a
+	ld b, $4
+	call Divide
+	ld a, [H_QUOTIENT + 3]
+	ld [hli], a
+	ld a, [H_REMAINDER]
+	ld [H_DIVIDEND + 3], a
+	ld a, 10
+	ld [H_DIVISOR], a
+	ld b, $4
+	call Divide
+	ld a, [H_QUOTIENT + 3]
+	swap a
+	ld b, a
+	ld a, [H_REMAINDER]
+	add b
+	ld [hl], a
+	ld de, wTotalPayDayMoney + 2
+	ld c, $3
+	predef AddBCDPredef
+	ld hl, CoinsScatteredText
+	jp PrintText
+
+CoinsScatteredText:
+	TX_FAR _CoinsScatteredText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/recoil.asm
@@ -1,0 +1,70 @@
+RecoilEffect_:
+	ld a, [H_WHOSETURN]
+	and a
+	ld a, [wPlayerMoveNum]
+	ld hl, wBattleMonMaxHP
+	jr z, .recoilEffect
+	ld a, [wEnemyMoveNum]
+	ld hl, wEnemyMonMaxHP
+.recoilEffect
+	ld d, a
+	ld a, [wDamage]
+	ld b, a
+	ld a, [wDamage + 1]
+	ld c, a
+	srl b
+	rr c
+	ld a, d
+	cp STRUGGLE ; struggle deals 50% recoil damage
+	jr z, .gotRecoilDamage
+	srl b
+	rr c
+.gotRecoilDamage
+	ld a, b
+	or c
+	jr nz, .updateHP
+	inc c ; minimum recoil damage is 1
+.updateHP
+; subtract HP from user due to the recoil damage
+	ld a, [hli]
+	ld [wHPBarMaxHP+1], a
+	ld a, [hl]
+	ld [wHPBarMaxHP], a
+	push bc
+	ld bc, wBattleMonHP - wBattleMonMaxHP
+	add hl, bc
+	pop bc
+	ld a, [hl]
+	ld [wHPBarOldHP], a
+	sub c
+	ld [hld], a
+	ld [wHPBarNewHP], a
+	ld a, [hl]
+	ld [wHPBarOldHP+1], a
+	sbc b
+	ld [hl], a
+	ld [wHPBarNewHP+1], a
+	jr nc, .getHPBarCoords
+; if recoil damage is higher than the Pokemon's HP, set its HP to 0
+	xor a
+	ld [hli], a
+	ld [hl], a
+	ld hl, wHPBarNewHP
+	ld [hli], a
+	ld [hl], a
+.getHPBarCoords
+	coord hl, 10, 9
+	ld a, [H_WHOSETURN]
+	and a
+	ld a, $1
+	jr z, .updateHPBar
+	coord hl, 2, 2
+	xor a
+.updateHPBar
+	ld [wHPBarType], a
+	predef UpdateHPBar2
+	ld hl, HitWithRecoilText
+	jp PrintText
+HitWithRecoilText:
+	TX_FAR _HitWithRecoilText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/reflect_light_screen.asm
@@ -1,0 +1,45 @@
+ReflectLightScreenEffect_:
+	ld hl, wPlayerBattleStatus3
+	ld de, wPlayerMoveEffect
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .reflectLightScreenEffect
+	ld hl, wEnemyBattleStatus3
+	ld de, wEnemyMoveEffect
+.reflectLightScreenEffect
+	ld a, [de]
+	cp LIGHT_SCREEN_EFFECT
+	jr nz, .reflect
+	bit HAS_LIGHT_SCREEN_UP, [hl] ; is mon already protected by light screen?
+	jr nz, .moveFailed
+	set HAS_LIGHT_SCREEN_UP, [hl] ; mon is now protected by light screen
+	ld hl, LightScreenProtectedText
+	jr .playAnim
+.reflect
+	bit HAS_REFLECT_UP, [hl] ; is mon already protected by reflect?
+	jr nz, .moveFailed
+	set HAS_REFLECT_UP, [hl] ; mon is now protected by reflect
+	ld hl, ReflectGainedArmorText
+.playAnim
+	push hl
+	ld hl, PlayCurrentMoveAnimation
+	call BankswitchEtoF
+	pop hl
+	jp PrintText
+.moveFailed
+	ld c, 50
+	call DelayFrames
+	ld hl, PrintButItFailedText_
+	jp BankswitchEtoF
+
+LightScreenProtectedText:
+	TX_FAR _LightScreenProtectedText
+	db "@"
+
+ReflectGainedArmorText:
+	TX_FAR _ReflectGainedArmorText
+	db "@"
+
+BankswitchEtoF:
+	ld b, BANK(BattleCore)
+	jp Bankswitch
--- /dev/null
+++ b/engine/battle/move_effects/substitute.asm
@@ -1,0 +1,77 @@
+SubstituteEffect_:
+	ld c, 50
+	call DelayFrames
+	ld hl, wBattleMonMaxHP
+	ld de, wPlayerSubstituteHP
+	ld bc, wPlayerBattleStatus2
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .notEnemy
+	ld hl, wEnemyMonMaxHP
+	ld de, wEnemySubstituteHP
+	ld bc, wEnemyBattleStatus2
+.notEnemy
+	ld a, [bc]
+	bit HAS_SUBSTITUTE_UP, a ; user already has substitute?
+	jr nz, .alreadyHasSubstitute
+; quarter health to remove from user
+; assumes max HP is 1023 or lower
+	push bc
+	ld a, [hli]
+	ld b, [hl]
+	srl a
+	rr b
+	srl a
+	rr b ; max hp / 4
+	push de
+	ld de, wBattleMonHP - wBattleMonMaxHP
+	add hl, de ; point hl to current HP low byte
+	pop de
+	ld a, b
+	ld [de], a ; save copy of HP to subtract in wPlayerSubstituteHP/wEnemySubstituteHP
+	ld a, [hld]
+; subtract [max hp / 4] to current HP
+	sub b
+	ld d, a
+	ld a, [hl]
+	sbc 0
+	pop bc
+	jr c, .notEnoughHP ; underflow means user would be left with negative health
+                           ; bug: since it only branches on carry, it will possibly leave user with 0 HP
+.userHasZeroOrMoreHP
+	ldi [hl], a ; save resulting HP after subtraction into current HP
+	ld [hl], d
+	ld h, b
+	ld l, c
+	set HAS_SUBSTITUTE_UP, [hl]
+	ld a, [wOptions]
+	bit 7, a ; battle animation is enabled?
+	ld hl, PlayCurrentMoveAnimation
+	ld b, BANK(PlayCurrentMoveAnimation)
+	jr z, .animationEnabled
+	ld hl, AnimationSubstitute
+	ld b, BANK(AnimationSubstitute)
+.animationEnabled
+	call Bankswitch ; jump to routine depending on animation setting
+	ld hl, SubstituteText
+	call PrintText
+	jpab DrawHUDsAndHPBars
+.alreadyHasSubstitute
+	ld hl, HasSubstituteText
+	jr .printText
+.notEnoughHP
+	ld hl, TooWeakSubstituteText
+.printText
+	jp PrintText
+
+SubstituteText:
+	TX_FAR _SubstituteText
+	db "@"
+
+HasSubstituteText:
+	TX_FAR _HasSubstituteText
+	db "@"
+
+TooWeakSubstituteText:
+	TX_FAR _TooWeakSubstituteText
+	db "@"
--- /dev/null
+++ b/engine/battle/move_effects/transform.asm
@@ -1,0 +1,148 @@
+TransformEffect_:
+	ld hl, wBattleMonSpecies
+	ld de, wEnemyMonSpecies
+	ld bc, wEnemyBattleStatus3
+	ld a, [wEnemyBattleStatus1]
+	ld a, [H_WHOSETURN]
+	and a
+	jr nz, .hitTest
+	ld hl, wEnemyMonSpecies
+	ld de, wBattleMonSpecies
+	ld bc, wPlayerBattleStatus3
+	ld [wPlayerMoveListIndex], a
+	ld a, [wPlayerBattleStatus1]
+.hitTest
+	bit INVULNERABLE, a ; is mon invulnerable to typical attacks? (fly/dig)
+	jp nz, .failed
+	push hl
+	push de
+	push bc
+	ld hl, wPlayerBattleStatus2
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .transformEffect
+	ld hl, wEnemyBattleStatus2
+.transformEffect
+; animation(s) played are different if target has Substitute up
+	bit HAS_SUBSTITUTE_UP, [hl]
+	push af
+	ld hl, HideSubstituteShowMonAnim
+	ld b, BANK(HideSubstituteShowMonAnim)
+	call nz, Bankswitch
+	ld a, [wOptions]
+	add a
+	ld hl, PlayCurrentMoveAnimation
+	ld b, BANK(PlayCurrentMoveAnimation)
+	jr nc, .gotAnimToPlay
+	ld hl, AnimationTransformMon
+	ld b, BANK(AnimationTransformMon)
+.gotAnimToPlay
+	call Bankswitch
+	ld hl, ReshowSubstituteAnim
+	ld b, BANK(ReshowSubstituteAnim)
+	pop af
+	call nz, Bankswitch
+	pop bc
+	ld a, [bc]
+	set TRANSFORMED, a ; mon is now transformed
+	ld [bc], a
+	pop de
+	pop hl
+	push hl
+; transform user into opposing Pokemon
+; species
+	ld a, [hl]
+	ld [de], a
+; type 1, type 2, catch rate, and moves
+	ld bc, $5
+	add hl, bc
+	inc de
+	inc de
+	inc de
+	inc de
+	inc de
+	inc bc
+	inc bc
+	call CopyData
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .next
+; save enemy mon DVs at wTransformedEnemyMonOriginalDVs
+	ld a, [de]
+	ld [wTransformedEnemyMonOriginalDVs], a
+	inc de
+	ld a, [de]
+	ld [wTransformedEnemyMonOriginalDVs + 1], a
+	dec de
+.next
+; DVs
+	ld a, [hli]
+	ld [de], a
+	inc de
+	ld a, [hli]
+	ld [de], a
+	inc de
+; Attack, Defense, Speed, and Special stats
+	inc hl
+	inc hl
+	inc hl
+	inc de
+	inc de
+	inc de
+	ld bc, $8
+	call CopyData
+	ld bc, wBattleMonMoves - wBattleMonPP
+	add hl, bc ; ld hl, wBattleMonMoves
+	ld b, NUM_MOVES
+.copyPPLoop
+; 5 PP for all moves
+	ld a, [hli]
+	and a
+	jr z, .lessThanFourMoves
+	ld a, $5
+	ld [de], a
+	inc de
+	dec b
+	jr nz, .copyPPLoop
+	jr .copyStats
+.lessThanFourMoves
+; 0 PP for blank moves
+	xor a
+	ld [de], a
+	inc de
+	dec b
+	jr nz, .lessThanFourMoves
+.copyStats
+; original (unmodified) stats and stat mods
+	pop hl
+	ld a, [hl]
+	ld [wd11e], a
+	call GetMonName
+	ld hl, wEnemyMonUnmodifiedAttack
+	ld de, wPlayerMonUnmodifiedAttack
+	call .copyBasedOnTurn ; original (unmodified) stats
+	ld hl, wEnemyMonStatMods
+	ld de, wPlayerMonStatMods
+	call .copyBasedOnTurn ; stat mods
+	ld hl, TransformedText
+	jp PrintText
+
+.copyBasedOnTurn
+	ld a, [H_WHOSETURN]
+	and a
+	jr z, .gotStatsOrModsToCopy
+	push hl
+	ld h, d
+	ld l, e
+	pop de
+.gotStatsOrModsToCopy
+	ld bc, $8
+	jp CopyData
+
+.failed
+	ld hl, PrintButItFailedText_
+	jp BankswitchEtoF
+
+TransformedText:
+	TX_FAR _TransformedText
+	db "@"
--- a/engine/battle/trainer_ai.asm
+++ b/engine/battle/trainer_ai.asm
@@ -279,7 +279,7 @@
 
 INCLUDE "text/trainer_names.asm"
 
-INCLUDE "engine/battle/bank_e_misc.asm"
+INCLUDE "engine/battle/misc.asm"
 
 INCLUDE "engine/battle/read_trainer_party.asm"
 
--- a/engine/bcd.asm
+++ /dev/null
@@ -1,214 +1,0 @@
-DivideBCDPredef::
-DivideBCDPredef2::
-DivideBCDPredef3::
-DivideBCDPredef4::
-	call GetPredefRegisters
-
-DivideBCD::
-	xor a
-	ld [hDivideBCDBuffer], a
-	ld [hDivideBCDBuffer+1], a
-	ld [hDivideBCDBuffer+2], a
-	ld d, $1
-.mulBy10Loop 
-; multiply the divisor by 10 until the leading digit is nonzero
-; to set up the standard long division algorithm
-	ld a, [hDivideBCDDivisor]
-	and $f0
-	jr nz, .next
-	inc d
-	ld a, [hDivideBCDDivisor]
-	swap a
-	and $f0
-	ld b, a
-	ld a, [hDivideBCDDivisor+1]
-	swap a
-	ld [hDivideBCDDivisor+1], a
-	and $f
-	or b
-	ld [hDivideBCDDivisor], a
-	ld a, [hDivideBCDDivisor+1]
-	and $f0
-	ld b, a
-	ld a, [hDivideBCDDivisor+2]
-	swap a
-	ld [hDivideBCDDivisor+2], a
-	and $f
-	or b
-	ld [hDivideBCDDivisor+1], a
-	ld a, [hDivideBCDDivisor+2]
-	and $f0
-	ld [hDivideBCDDivisor+2], a
-	jr .mulBy10Loop
-.next
-	push de
-	push de
-	call DivideBCD_getNextDigit
-	pop de
-	ld a, b
-	swap a
-	and $f0
-	ld [hDivideBCDBuffer], a
-	dec d
-	jr z, .next2
-	push de
-	call DivideBCD_divDivisorBy10
-	call DivideBCD_getNextDigit
-	pop de
-	ld a, [hDivideBCDBuffer]
-	or b
-	ld [hDivideBCDBuffer], a
-	dec d
-	jr z, .next2
-	push de
-	call DivideBCD_divDivisorBy10
-	call DivideBCD_getNextDigit
-	pop de
-	ld a, b
-	swap a
-	and $f0
-	ld [hDivideBCDBuffer+1], a
-	dec d
-	jr z, .next2
-	push de
-	call DivideBCD_divDivisorBy10
-	call DivideBCD_getNextDigit
-	pop de
-	ld a, [hDivideBCDBuffer+1]
-	or b
-	ld [hDivideBCDBuffer+1], a
-	dec d
-	jr z, .next2
-	push de
-	call DivideBCD_divDivisorBy10
-	call DivideBCD_getNextDigit
-	pop de
-	ld a, b
-	swap a
-	and $f0
-	ld [hDivideBCDBuffer+2], a
-	dec d
-	jr z, .next2
-	push de
-	call DivideBCD_divDivisorBy10
-	call DivideBCD_getNextDigit
-	pop de
-	ld a, [hDivideBCDBuffer+2]
-	or b
-	ld [hDivideBCDBuffer+2], a
-.next2
-	ld a, [hDivideBCDBuffer]
-	ld [hDivideBCDQuotient], a ; the same memory location as hDivideBCDDivisor
-	ld a, [hDivideBCDBuffer+1]
-	ld [hDivideBCDQuotient+1], a
-	ld a, [hDivideBCDBuffer+2]
-	ld [hDivideBCDQuotient+2], a
-	pop de
-	ld a, $6 
-	sub d
-	and a
-	ret z
-.divResultBy10loop
-	push af
-	call DivideBCD_divDivisorBy10
-	pop af
-	dec a
-	jr nz, .divResultBy10loop
-	ret
-
-DivideBCD_divDivisorBy10:
-	ld a, [hDivideBCDDivisor+2]
-	swap a
-	and $f
-	ld b, a
-	ld a, [hDivideBCDDivisor+1]
-	swap a
-	ld [hDivideBCDDivisor+1], a
-	and $f0
-	or b
-	ld [hDivideBCDDivisor+2], a
-	ld a, [hDivideBCDDivisor+1]
-	and $f
-	ld b, a
-	ld a, [hDivideBCDDivisor]
-	swap a
-	ld [hDivideBCDDivisor], a
-	and $f0
-	or b
-	ld [hDivideBCDDivisor+1], a
-	ld a, [hDivideBCDDivisor]
-	and $f
-	ld [hDivideBCDDivisor], a
-	ret
-
-DivideBCD_getNextDigit:
-	ld bc, $3
-.loop
-	ld de, hMoney ; the dividend
-	ld hl, hDivideBCDDivisor
-	push bc
-	call StringCmp
-	pop bc
-	ret c
-	inc b
-	ld de, hMoney+2 ; since SubBCD works starting from the least significant digit
-	ld hl, hDivideBCDDivisor+2  
-	push bc
-	call SubBCD
-	pop bc
-	jr .loop
-
-
-AddBCDPredef::
-	call GetPredefRegisters
-
-AddBCD::
-	and a
-	ld b, c
-.add
-	ld a, [de]
-	adc [hl]
-	daa
-	ld [de], a
-	dec de
-	dec hl
-	dec c
-	jr nz, .add
-	jr nc, .done
-	ld a, $99
-	inc de
-.fill
-	ld [de], a
-	inc de
-	dec b
-	jr nz, .fill
-.done
-	ret
-
-
-SubBCDPredef::
-	call GetPredefRegisters
-
-SubBCD::
-	and a
-	ld b, c
-.sub
-	ld a, [de]
-	sbc [hl]
-	daa
-	ld [de], a
-	dec de
-	dec hl
-	dec c
-	jr nz, .sub
-	jr nc, .done
-	ld a, $00
-	inc de
-.fill
-	ld [de], a
-	inc de
-	dec b
-	jr nz, .fill
-	scf
-.done
-	ret
--- a/engine/black_out.asm
+++ /dev/null
@@ -1,46 +1,0 @@
-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
--- a/engine/cable_club.asm
+++ /dev/null
@@ -1,977 +1,0 @@
-; performs the appropriate action when the player uses the gameboy on the table in the Colosseum or Trade Center
-; In the Colosseum, it starts a battle. In the Trade Center, it displays the trade selection screen.
-; Before doing either action, it swaps random numbers, trainer names and party data with the other gameboy.
-CableClub_DoBattleOrTrade:
-	ld c, 80
-	call DelayFrames
-	call ClearScreen
-	call UpdateSprites
-	call LoadFontTilePatterns
-	call LoadHpBarAndStatusTilePatterns
-	call LoadTrainerInfoTextBoxTiles
-	coord hl, 3, 8
-	ld b, 2
-	ld c, 12
-	call CableClub_TextBoxBorder
-	coord hl, 4, 10
-	ld de, PleaseWaitString
-	call PlaceString
-	ld hl, wPlayerNumHits
-	xor a
-	ld [hli], a
-	ld [hl], $50
-	; fall through
-
-; This is called after completing a trade.
-CableClub_DoBattleOrTradeAgain:
-	ld hl, wSerialPlayerDataBlock
-	ld a, SERIAL_PREAMBLE_BYTE
-	ld b, 6
-.writePlayerDataBlockPreambleLoop
-	ld [hli], a
-	dec b
-	jr nz, .writePlayerDataBlockPreambleLoop
-	ld hl, wSerialRandomNumberListBlock
-	ld a, SERIAL_PREAMBLE_BYTE
-	ld b, 7
-.writeRandomNumberListPreambleLoop
-	ld [hli], a
-	dec b
-	jr nz, .writeRandomNumberListPreambleLoop
-	ld b, 10
-.generateRandomNumberListLoop
-	call Random
-	cp SERIAL_PREAMBLE_BYTE ; all the random numbers have to be less than the preamble byte
-	jr nc, .generateRandomNumberListLoop
-	ld [hli], a
-	dec b
-	jr nz, .generateRandomNumberListLoop
-	ld hl, wSerialPartyMonsPatchList
-	ld a, SERIAL_PREAMBLE_BYTE
-	ld [hli], a
-	ld [hli], a
-	ld [hli], a
-	ld b, $c8
-	xor a
-.zeroPlayerDataPatchListLoop
-	ld [hli], a
-	dec b
-	jr nz, .zeroPlayerDataPatchListLoop
-	ld hl, wGrassRate
-	ld bc, wTrainerHeaderPtr - wGrassRate
-.zeroEnemyPartyLoop
-	xor a
-	ld [hli], a
-	dec bc
-	ld a, b
-	or c
-	jr nz, .zeroEnemyPartyLoop
-	ld hl, wPartyMons - 1
-	ld de, wSerialPartyMonsPatchList + 10
-	ld bc, 0
-.patchPartyMonsLoop
-	inc c
-	ld a, c
-	cp SERIAL_PREAMBLE_BYTE
-	jr z, .startPatchListPart2
-	ld a, b
-	dec a ; are we in part 2 of the patch list?
-	jr nz, .checkPlayerDataByte ; jump if in part 1
-; if we're in part 2
-	ld a, c
-	cp (wPartyMonOT - (wPartyMons - 1)) - (SERIAL_PREAMBLE_BYTE - 1)
-	jr z, .finishedPatchingPlayerData
-.checkPlayerDataByte
-	inc hl
-	ld a, [hl]
-	cp SERIAL_NO_DATA_BYTE
-	jr nz, .patchPartyMonsLoop
-; if the player data byte matches SERIAL_NO_DATA_BYTE, patch it with $FF and record the offset in the patch list
-	ld a, c
-	ld [de], a
-	inc de
-	ld [hl], $ff
-	jr .patchPartyMonsLoop
-.startPatchListPart2
-	ld a, SERIAL_PATCH_LIST_PART_TERMINATOR
-	ld [de], a ; end of part 1
-	inc de
-	lb bc, 1, 0
-	jr .patchPartyMonsLoop
-.finishedPatchingPlayerData
-	ld a, SERIAL_PATCH_LIST_PART_TERMINATOR
-	ld [de], a ; end of part 2
-	call Serial_SyncAndExchangeNybble
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK
-	jr nz, .skipSendingTwoZeroBytes
-; if using internal clock
-; send two zero bytes for syncing purposes?
-	call Delay3
-	xor a
-	ld [hSerialSendData], a
-	ld a, START_TRANSFER_INTERNAL_CLOCK
-	ld [rSC], a
-	call DelayFrame
-	xor a
-	ld [hSerialSendData], a
-	ld a, START_TRANSFER_INTERNAL_CLOCK
-	ld [rSC], a
-.skipSendingTwoZeroBytes
-	call Delay3
-	ld a, (1 << SERIAL)
-	ld [rIE], a
-	ld hl, wSerialRandomNumberListBlock
-	ld de, wSerialOtherGameboyRandomNumberListBlock
-	ld bc, $11
-	call Serial_ExchangeBytes
-	ld a, SERIAL_NO_DATA_BYTE
-	ld [de], a
-	ld hl, wSerialPlayerDataBlock
-	ld de, wSerialEnemyDataBlock
-	ld bc, $1a8
-	call Serial_ExchangeBytes
-	ld a, SERIAL_NO_DATA_BYTE
-	ld [de], a
-	ld hl, wSerialPartyMonsPatchList
-	ld de, wSerialEnemyMonsPatchList
-	ld bc, $c8
-	call Serial_ExchangeBytes
-	ld a, (1 << SERIAL) | (1 << TIMER) | (1 << VBLANK)
-	ld [rIE], a
-	ld a, $ff
-	call PlaySound
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK
-	jr z, .skipCopyingRandomNumberList ; the list generated by the gameboy clocking the connection is used by both gameboys
-	ld hl, wSerialOtherGameboyRandomNumberListBlock
-.findStartOfRandomNumberListLoop
-	ld a, [hli]
-	and a
-	jr z, .findStartOfRandomNumberListLoop
-	cp SERIAL_PREAMBLE_BYTE
-	jr z, .findStartOfRandomNumberListLoop
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .findStartOfRandomNumberListLoop
-	dec hl
-	ld de, wLinkBattleRandomNumberList
-	ld c, 10
-.copyRandomNumberListLoop
-	ld a, [hli]
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .copyRandomNumberListLoop
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .copyRandomNumberListLoop
-.skipCopyingRandomNumberList
-	ld hl, wSerialEnemyDataBlock + 3
-.findStartOfEnemyNameLoop
-	ld a, [hli]
-	and a
-	jr z, .findStartOfEnemyNameLoop
-	cp SERIAL_PREAMBLE_BYTE
-	jr z, .findStartOfEnemyNameLoop
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .findStartOfEnemyNameLoop
-	dec hl
-	ld de, wLinkEnemyTrainerName
-	ld c, NAME_LENGTH
-.copyEnemyNameLoop
-	ld a, [hli]
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .copyEnemyNameLoop
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .copyEnemyNameLoop
-	ld de, wEnemyPartyCount
-	ld bc, wTrainerHeaderPtr - wEnemyPartyCount
-.copyEnemyPartyLoop
-	ld a, [hli]
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .copyEnemyPartyLoop
-	ld [de], a
-	inc de
-	dec bc
-	ld a, b
-	or c
-	jr nz, .copyEnemyPartyLoop
-	ld de, wSerialPartyMonsPatchList
-	ld hl, wPartyMons
-	ld c, 2 ; patch list has 2 parts
-.unpatchPartyMonsLoop
-	ld a, [de]
-	inc de
-	and a
-	jr z, .unpatchPartyMonsLoop
-	cp SERIAL_PREAMBLE_BYTE
-	jr z, .unpatchPartyMonsLoop
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .unpatchPartyMonsLoop
-	cp SERIAL_PATCH_LIST_PART_TERMINATOR
-	jr z, .finishedPartyMonsPatchListPart
-	push hl
-	push bc
-	ld b, 0
-	dec a
-	ld c, a
-	add hl, bc
-	ld a, SERIAL_NO_DATA_BYTE
-	ld [hl], a
-	pop bc
-	pop hl
-	jr .unpatchPartyMonsLoop
-.finishedPartyMonsPatchListPart
-	ld hl, wPartyMons + (SERIAL_PREAMBLE_BYTE - 1)
-	dec c ; is there another part?
-	jr nz, .unpatchPartyMonsLoop
-	ld de, wSerialEnemyMonsPatchList
-	ld hl, wEnemyMons
-	ld c, 2 ; patch list has 2 parts
-.unpatchEnemyMonsLoop
-	ld a, [de]
-	inc de
-	and a
-	jr z, .unpatchEnemyMonsLoop
-	cp SERIAL_PREAMBLE_BYTE
-	jr z, .unpatchEnemyMonsLoop
-	cp SERIAL_NO_DATA_BYTE
-	jr z, .unpatchEnemyMonsLoop
-	cp SERIAL_PATCH_LIST_PART_TERMINATOR
-	jr z, .finishedEnemyMonsPatchListPart
-	push hl
-	push bc
-	ld b, 0
-	dec a
-	ld c, a
-	add hl, bc
-	ld a, SERIAL_NO_DATA_BYTE
-	ld [hl], a
-	pop bc
-	pop hl
-	jr .unpatchEnemyMonsLoop
-.finishedEnemyMonsPatchListPart
-	ld hl, wEnemyMons + (SERIAL_PREAMBLE_BYTE - 1)
-	dec c
-	jr nz, .unpatchEnemyMonsLoop
-	ld a, wEnemyMonOT % $100
-	ld [wUnusedCF8D], a
-	ld a, wEnemyMonOT / $100
-	ld [wUnusedCF8D + 1], a
-	xor a
-	ld [wTradeCenterPointerTableIndex], a
-	ld a, $ff
-	call PlaySound
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK
-	ld c, 66
-	call z, DelayFrames ; delay if using internal clock
-	ld a, [wLinkState]
-	cp LINK_STATE_START_BATTLE
-	ld a, LINK_STATE_TRADING
-	ld [wLinkState], a
-	jr nz, .trading
-	ld a, LINK_STATE_BATTLING
-	ld [wLinkState], a
-	ld a, OPP_SONY1
-	ld [wCurOpponent], a
-	call ClearScreen
-	call Delay3
-	ld hl, wOptions
-	res 7, [hl]
-	predef InitOpponent
-	predef HealParty
-	jp ReturnToCableClubRoom
-.trading
-	ld c, BANK(Music_GameCorner)
-	ld a, MUSIC_GAME_CORNER
-	call PlayMusic
-	jr CallCurrentTradeCenterFunction
-
-PleaseWaitString:
-	db "PLEASE WAIT!@"
-
-CallCurrentTradeCenterFunction:
-	ld hl, TradeCenterPointerTable
-	ld b, 0
-	ld a, [wTradeCenterPointerTableIndex]
-	cp $ff
-	jp z, DisplayTitleScreen
-	add a
-	ld c, a
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	jp hl
-
-TradeCenter_SelectMon:
-	call ClearScreen
-	call LoadTrainerInfoTextBoxTiles
-	call TradeCenter_DrawPartyLists
-	call TradeCenter_DrawCancelBox
-	xor a
-	ld hl, wSerialSyncAndExchangeNybbleReceiveData
-	ld [hli], a
-	ld [hli], a
-	ld [hli], a
-	ld [hl], a
-	ld [wMenuWatchMovingOutOfBounds], a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld [wMenuJoypadPollCount], a
-	inc a
-	ld [wSerialExchangeNybbleSendData], a
-	jp .playerMonMenu
-.enemyMonMenu
-	xor a
-	ld [wMenuWatchMovingOutOfBounds], a
-	inc a
-	ld [wWhichTradeMonSelectionMenu], a
-	ld a, D_DOWN | D_LEFT | A_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, [wEnemyPartyCount]
-	ld [wMaxMenuItem], a
-	ld a, 9
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-.enemyMonMenu_HandleInput
-	ld hl, hFlags_0xFFF6
-	set 1, [hl]
-	call HandleMenuInput
-	ld hl, hFlags_0xFFF6
-	res 1, [hl]
-	and a
-	jp z, .getNewInput
-	bit 0, a ; A button pressed?
-	jr z, .enemyMonMenu_ANotPressed
-; if A button pressed
-	ld a, [wMaxMenuItem]
-	ld c, a
-	ld a, [wCurrentMenuItem]
-	cp c
-	jr c, .displayEnemyMonStats
-	ld a, [wMaxMenuItem]
-	dec a
-	ld [wCurrentMenuItem], a
-.displayEnemyMonStats
-	ld a, INIT_ENEMYOT_LIST
-	ld [wInitListType], a
-	callab InitList ; the list isn't used
-	ld hl, wEnemyMons
-	call TradeCenter_DisplayStats
-	jp .getNewInput
-.enemyMonMenu_ANotPressed
-	bit 5, a ; Left pressed?
-	jr z, .enemyMonMenu_LeftNotPressed
-; if Left pressed, switch back to the player mon menu
-	xor a ; player mon menu
-	ld [wWhichTradeMonSelectionMenu], a
-	ld a, [wMenuCursorLocation]
-	ld l, a
-	ld a, [wMenuCursorLocation + 1]
-	ld h, a
-	ld a, [wTileBehindCursor]
-	ld [hl], a
-	ld a, [wCurrentMenuItem]
-	ld b, a
-	ld a, [wPartyCount]
-	dec a
-	cp b
-	jr nc, .playerMonMenu
-	ld [wCurrentMenuItem], a
-	jr .playerMonMenu
-.enemyMonMenu_LeftNotPressed
-	bit 7, a ; Down pressed?
-	jp z, .getNewInput
-	jp .selectedCancelMenuItem ; jump if Down pressed
-.playerMonMenu
-	xor a ; player mon menu
-	ld [wWhichTradeMonSelectionMenu], a
-	ld [wMenuWatchMovingOutOfBounds], a
-	ld a, D_DOWN | D_RIGHT | A_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, [wPartyCount]
-	ld [wMaxMenuItem], a
-	ld a, 1
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-	coord hl, 1, 1
-	lb bc, 6, 1
-	call ClearScreenArea
-.playerMonMenu_HandleInput
-	ld hl, hFlags_0xFFF6
-	set 1, [hl]
-	call HandleMenuInput
-	ld hl, hFlags_0xFFF6
-	res 1, [hl]
-	and a ; was anything pressed?
-	jr nz, .playerMonMenu_SomethingPressed
-	jp .getNewInput
-.playerMonMenu_SomethingPressed
-	bit 0, a ; A button pressed?
-	jr z, .playerMonMenu_ANotPressed
-	jp .chosePlayerMon ; jump if A button pressed
-; unreachable code
-	ld a, INIT_PLAYEROT_LIST
-	ld [wInitListType], a
-	callab InitList ; the list isn't used
-	call TradeCenter_DisplayStats
-	jp .getNewInput
-.playerMonMenu_ANotPressed
-	bit 4, a ; Right pressed?
-	jr z, .playerMonMenu_RightNotPressed
-; if Right pressed, switch to the enemy mon menu
-	ld a, $1 ; enemy mon menu
-	ld [wWhichTradeMonSelectionMenu], a
-	ld a, [wMenuCursorLocation]
-	ld l, a
-	ld a, [wMenuCursorLocation + 1]
-	ld h, a
-	ld a, [wTileBehindCursor]
-	ld [hl], a
-	ld a, [wCurrentMenuItem]
-	ld b, a
-	ld a, [wEnemyPartyCount]
-	dec a
-	cp b
-	jr nc, .notPastLastEnemyMon
-; when switching to the enemy mon menu, if the menu selection would be past the last enemy mon, select the last enemy mon
-	ld [wCurrentMenuItem], a
-.notPastLastEnemyMon
-	jp .enemyMonMenu
-.playerMonMenu_RightNotPressed
-	bit 7, a ; Down pressed?
-	jr z, .getNewInput
-	jp .selectedCancelMenuItem ; jump if Down pressed
-.getNewInput
-	ld a, [wWhichTradeMonSelectionMenu]
-	and a
-	jp z, .playerMonMenu_HandleInput
-	jp .enemyMonMenu_HandleInput
-.chosePlayerMon
-	call SaveScreenTilesToBuffer1
-	call PlaceUnfilledArrowMenuCursor
-	ld a, [wMaxMenuItem]
-	ld c, a
-	ld a, [wCurrentMenuItem]
-	cp c
-	jr c, .displayStatsTradeMenu
-	ld a, [wMaxMenuItem]
-	dec a
-.displayStatsTradeMenu
-	push af
-	coord hl, 0, 14
-	ld b, 2
-	ld c, 18
-	call CableClub_TextBoxBorder
-	coord hl, 2, 16
-	ld de, .statsTrade
-	call PlaceString
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld [wMenuJoypadPollCount], a
-	ld [wMaxMenuItem], a
-	ld a, 16
-	ld [wTopMenuItemY], a
-.selectStatsMenuItem
-	ld a, " "
-	Coorda 11, 16
-	ld a, D_RIGHT | B_BUTTON | A_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-	call HandleMenuInput
-	bit 4, a ; Right pressed?
-	jr nz, .selectTradeMenuItem
-	bit 1, a ; B button pressed?
-	jr z, .displayPlayerMonStats
-.cancelPlayerMonChoice
-	pop af
-	ld [wCurrentMenuItem], a
-	call LoadScreenTilesFromBuffer1
-	jp .playerMonMenu
-.selectTradeMenuItem
-	ld a, " "
-	Coorda 1, 16
-	ld a, D_LEFT | B_BUTTON | A_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 11
-	ld [wTopMenuItemX], a
-	call HandleMenuInput
-	bit 5, a ; Left pressed?
-	jr nz, .selectStatsMenuItem
-	bit 1, a ; B button pressed?
-	jr nz, .cancelPlayerMonChoice
-	jr .choseTrade
-.displayPlayerMonStats
-	pop af
-	ld [wCurrentMenuItem], a
-	ld a, INIT_PLAYEROT_LIST
-	ld [wInitListType], a
-	callab InitList ; the list isn't used
-	call TradeCenter_DisplayStats
-	call LoadScreenTilesFromBuffer1
-	jp .playerMonMenu
-.choseTrade
-	call PlaceUnfilledArrowMenuCursor
-	pop af
-	ld [wCurrentMenuItem], a
-	ld [wTradingWhichPlayerMon], a
-	ld [wSerialExchangeNybbleSendData], a
-	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
-	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
-	cp $f
-	jp z, CallCurrentTradeCenterFunction ; go back to the beginning of the trade selection menu if the other person cancelled
-	ld [wTradingWhichEnemyMon], a
-	call TradeCenter_PlaceSelectedEnemyMonMenuCursor
-	ld a, $1 ; TradeCenter_Trade
-	ld [wTradeCenterPointerTableIndex], a
-	jp CallCurrentTradeCenterFunction
-.statsTrade
-	db "STATS     TRADE@"
-.selectedCancelMenuItem
-	ld a, [wCurrentMenuItem]
-	ld b, a
-	ld a, [wMaxMenuItem]
-	cp b
-	jp nz, .getNewInput
-	ld a, [wMenuCursorLocation]
-	ld l, a
-	ld a, [wMenuCursorLocation + 1]
-	ld h, a
-	ld a, " "
-	ld [hl], a
-.cancelMenuItem_Loop
-	ld a, "▶" ; filled arrow cursor
-	Coorda 1, 16
-.cancelMenuItem_JoypadLoop
-	call JoypadLowSensitivity
-	ld a, [hJoy5]
-	and a ; pressed anything?
-	jr z, .cancelMenuItem_JoypadLoop
-	bit 0, a ; A button pressed?
-	jr nz, .cancelMenuItem_APressed
-	bit 6, a ; Up pressed?
-	jr z, .cancelMenuItem_JoypadLoop
-; if Up pressed
-	ld a, " "
-	Coorda 1, 16
-	ld a, [wPartyCount]
-	dec a
-	ld [wCurrentMenuItem], a
-	jp .playerMonMenu
-.cancelMenuItem_APressed
-	ld a, "▷" ; unfilled arrow cursor
-	Coorda 1, 16
-	ld a, $f
-	ld [wSerialExchangeNybbleSendData], a
-	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
-	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
-	cp $f ; did the other person choose Cancel too?
-	jr nz, .cancelMenuItem_Loop
-	; fall through
-
-ReturnToCableClubRoom:
-	call GBPalWhiteOutWithDelay3
-	ld hl, wFontLoaded
-	ld a, [hl]
-	push af
-	push hl
-	res 0, [hl]
-	xor a
-	ld [wd72d], a
-	dec a
-	ld [wDestinationWarpID], a
-	call LoadMapData
-	callba ClearVariablesOnEnterMap
-	pop hl
-	pop af
-	ld [hl], a
-	call GBFadeInFromWhite
-	ret
-
-TradeCenter_DrawCancelBox:
-	coord hl, 11, 15
-	ld a, $7e
-	ld bc, 2 * SCREEN_WIDTH + 9
-	call FillMemory
-	coord hl, 0, 15
-	ld b, 1
-	ld c, 9
-	call CableClub_TextBoxBorder
-	coord hl, 2, 16
-	ld de, CancelTextString
-	jp PlaceString
-
-CancelTextString:
-	db "CANCEL@"
-
-TradeCenter_PlaceSelectedEnemyMonMenuCursor:
-	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
-	coord hl, 1, 9
-	ld bc, SCREEN_WIDTH
-	call AddNTimes
-	ld [hl], "▷" ; cursor
-	ret
-
-TradeCenter_DisplayStats:
-	ld a, [wCurrentMenuItem]
-	ld [wWhichPokemon], a
-	predef StatusScreen
-	predef StatusScreen2
-	call GBPalNormal
-	call LoadTrainerInfoTextBoxTiles
-	call TradeCenter_DrawPartyLists
-	jp TradeCenter_DrawCancelBox
-
-TradeCenter_DrawPartyLists:
-	coord hl, 0, 0
-	ld b, 6
-	ld c, 18
-	call CableClub_TextBoxBorder
-	coord hl, 0, 8
-	ld b, 6
-	ld c, 18
-	call CableClub_TextBoxBorder
-	coord hl, 5, 0
-	ld de, wPlayerName
-	call PlaceString
-	coord hl, 5, 8
-	ld de, wLinkEnemyTrainerName
-	call PlaceString
-	coord hl, 2, 1
-	ld de, wPartySpecies
-	call TradeCenter_PrintPartyListNames
-	coord hl, 2, 9
-	ld de, wEnemyPartyMons
-	; fall through
-
-TradeCenter_PrintPartyListNames:
-	ld c, $0
-.loop
-	ld a, [de]
-	cp $ff
-	ret z
-	ld [wd11e], a
-	push bc
-	push hl
-	push de
-	push hl
-	ld a, c
-	ld [$ff95], a
-	call GetMonName
-	pop hl
-	call PlaceString
-	pop de
-	inc de
-	pop hl
-	ld bc, 20
-	add hl, bc
-	pop bc
-	inc c
-	jr .loop
-
-TradeCenter_Trade:
-	ld c, 100
-	call DelayFrames
-	xor a
-	ld [wSerialExchangeNybbleSendData + 1], a ; unnecessary
-	ld [wSerialExchangeNybbleReceiveData], a
-	ld [wMenuWatchMovingOutOfBounds], a
-	ld [wMenuJoypadPollCount], a
-	coord hl, 0, 12
-	ld b, 4
-	ld c, 18
-	call CableClub_TextBoxBorder
-	ld a, [wTradingWhichPlayerMon]
-	ld hl, wPartySpecies
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hl]
-	ld [wd11e], a
-	call GetMonName
-	ld hl, wcd6d
-	ld de, wNameOfPlayerMonToBeTraded
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld a, [wTradingWhichEnemyMon]
-	ld hl, wEnemyPartyMons
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hl]
-	ld [wd11e], a
-	call GetMonName
-	ld hl, WillBeTradedText
-	coord bc, 1, 14
-	call TextCommandProcessor
-	call SaveScreenTilesToBuffer1
-	coord hl, 10, 7
-	lb bc, 8, 11
-	ld a, TRADE_CANCEL_MENU
-	ld [wTwoOptionMenuID], a
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	call LoadScreenTilesFromBuffer1
-	ld a, [wCurrentMenuItem]
-	and a
-	jr z, .tradeConfirmed
-; if trade cancelled
-	ld a, $1
-	ld [wSerialExchangeNybbleSendData], a
-	coord hl, 0, 12
-	ld b, 4
-	ld c, 18
-	call CableClub_TextBoxBorder
-	coord hl, 1, 14
-	ld de, TradeCanceled
-	call PlaceString
-	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
-	jp .tradeCancelled
-.tradeConfirmed
-	ld a, $2
-	ld [wSerialExchangeNybbleSendData], a
-	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
-	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
-	dec a ; did the other person cancel?
-	jr nz, .doTrade
-; if the other person cancelled
-	coord hl, 0, 12
-	ld b, 4
-	ld c, 18
-	call CableClub_TextBoxBorder
-	coord hl, 1, 14
-	ld de, TradeCanceled
-	call PlaceString
-	jp .tradeCancelled
-.doTrade
-	ld a, [wTradingWhichPlayerMon]
-	ld hl, wPartyMonOT
-	call SkipFixedLengthTextEntries
-	ld de, wTradedPlayerMonOT
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, wPartyMon1Species
-	ld a, [wTradingWhichPlayerMon]
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld bc, wPartyMon1OTID - wPartyMon1
-	add hl, bc
-	ld a, [hli]
-	ld [wTradedPlayerMonOTID], a
-	ld a, [hl]
-	ld [wTradedPlayerMonOTID + 1], a
-	ld a, [wTradingWhichEnemyMon]
-	ld hl, wEnemyMonOT
-	call SkipFixedLengthTextEntries
-	ld de, wTradedEnemyMonOT
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, wEnemyMons
-	ld a, [wTradingWhichEnemyMon]
-	ld bc, wEnemyMon2 - wEnemyMon1
-	call AddNTimes
-	ld bc, wEnemyMon1OTID - wEnemyMon1
-	add hl, bc
-	ld a, [hli]
-	ld [wTradedEnemyMonOTID], a
-	ld a, [hl]
-	ld [wTradedEnemyMonOTID + 1], a
-	ld a, [wTradingWhichPlayerMon]
-	ld [wWhichPokemon], a
-	ld hl, wPartySpecies
-	ld b, 0
-	ld c, a
-	add hl, bc
-	ld a, [hl]
-	ld [wTradedPlayerMonSpecies], a
-	xor a
-	ld [wRemoveMonFromBox], a
-	call RemovePokemon
-	ld a, [wTradingWhichEnemyMon]
-	ld c, a
-	ld [wWhichPokemon], a
-	ld hl, wEnemyPartyMons
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hl]
-	ld [wcf91], a
-	ld hl, wEnemyMons
-	ld a, c
-	ld bc, wEnemyMon2 - wEnemyMon1
-	call AddNTimes
-	ld de, wLoadedMon
-	ld bc, wEnemyMon2 - wEnemyMon1
-	call CopyData
-	call AddEnemyMonToPlayerParty
-	ld a, [wPartyCount]
-	dec a
-	ld [wWhichPokemon], a
-	ld a, $1
-	ld [wForceEvolution], a
-	ld a, [wTradingWhichEnemyMon]
-	ld hl, wEnemyPartyMons
-	ld b, 0
-	ld c, a
-	add hl, bc
-	ld a, [hl]
-	ld [wTradedEnemyMonSpecies], a
-	ld a, 10
-	ld [wAudioFadeOutControl], a
-	ld a, BANK(Music_SafariZone)
-	ld [wAudioSavedROMBank], a
-	ld a, MUSIC_SAFARI_ZONE
-	ld [wNewSoundID], a
-	call PlaySound
-	ld c, 100
-	call DelayFrames
-	call ClearScreen
-	call LoadHpBarAndStatusTilePatterns
-	xor a
-	ld [wUnusedCC5B], a
-	ld a, [hSerialConnectionStatus]
-	cp USING_EXTERNAL_CLOCK
-	jr z, .usingExternalClock
-	predef InternalClockTradeAnim
-	jr .tradeCompleted
-.usingExternalClock
-	predef ExternalClockTradeAnim
-.tradeCompleted
-	callab TryEvolvingMon
-	call ClearScreen
-	call LoadTrainerInfoTextBoxTiles
-	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
-	ld c, 40
-	call DelayFrames
-	coord hl, 0, 12
-	ld b, 4
-	ld c, 18
-	call CableClub_TextBoxBorder
-	coord hl, 1, 14
-	ld de, TradeCompleted
-	call PlaceString
-	predef SaveSAVtoSRAM2
-	ld c, 50
-	call DelayFrames
-	xor a
-	ld [wTradeCenterPointerTableIndex], a
-	jp CableClub_DoBattleOrTradeAgain
-.tradeCancelled
-	ld c, 100
-	call DelayFrames
-	xor a ; TradeCenter_SelectMon
-	ld [wTradeCenterPointerTableIndex], a
-	jp CallCurrentTradeCenterFunction
-
-WillBeTradedText:
-	TX_FAR _WillBeTradedText
-	db "@"
-
-TradeCompleted:
-	db "Trade completed!@"
-
-TradeCanceled:
-	db   "Too bad! The trade"
-	next "was canceled!@"
-
-TradeCenterPointerTable:
-	dw TradeCenter_SelectMon
-	dw TradeCenter_Trade
-
-CableClub_Run:
-	ld a, [wLinkState]
-	cp LINK_STATE_START_TRADE
-	jr z, .doBattleOrTrade
-	cp LINK_STATE_START_BATTLE
-	jr z, .doBattleOrTrade
-	cp LINK_STATE_RESET ; this is never used
-	ret nz
-	predef EmptyFunc3
-	jp Init
-.doBattleOrTrade
-	call CableClub_DoBattleOrTrade
-	ld hl, Club_GFX
-	ld a, h
-	ld [wTilesetGfxPtr + 1], a
-	ld a, l
-	ld [wTilesetGfxPtr], a
-	ld a, Bank(Club_GFX)
-	ld [wTilesetBank], a
-	ld hl, Club_Coll
-	ld a, h
-	ld [wTilesetCollisionPtr + 1], a
-	ld a, l
-	ld [wTilesetCollisionPtr], a
-	xor a
-	ld [wGrassRate], a
-	inc a ; LINK_STATE_IN_CABLE_CLUB
-	ld [wLinkState], a
-	ld [hJoy5], a
-	ld a, 10
-	ld [wAudioFadeOutControl], a
-	ld a, BANK(Music_Celadon)
-	ld [wAudioSavedROMBank], a
-	ld a, MUSIC_CELADON
-	ld [wNewSoundID], a
-	jp PlaySound
-
-EmptyFunc3:
-	ret
-
-Diploma_TextBoxBorder:
-	call GetPredefRegisters
-
-; b = height
-; c = width
-CableClub_TextBoxBorder:
-	push hl
-	ld a, $78 ; border upper left corner tile
-	ld [hli], a
-	inc a ; border top horizontal line tile
-	call CableClub_DrawHorizontalLine
-	inc a ; border upper right corner tile
-	ld [hl], a
-	pop hl
-	ld de, 20
-	add hl, de
-.loop
-	push hl
-	ld a, $7b ; border left vertical line tile
-	ld [hli], a
-	ld a, " "
-	call CableClub_DrawHorizontalLine
-	ld [hl], $77 ; border right vertical line tile
-	pop hl
-	ld de, 20
-	add hl, de
-	dec b
-	jr nz, .loop
-	ld a, $7c ; border lower left corner tile
-	ld [hli], a
-	ld a, $76 ; border bottom horizontal line tile
-	call CableClub_DrawHorizontalLine
-	ld [hl], $7d ; border lower right corner tile
-	ret
-
-; c = width
-CableClub_DrawHorizontalLine:
-	ld d, c
-.loop
-	ld [hli], a
-	dec d
-	jr nz, .loop
-	ret
-
-LoadTrainerInfoTextBoxTiles:
-	ld de, TrainerInfoTextBoxTileGraphics
-	ld hl, vChars2 + $760
-	lb bc, BANK(TrainerInfoTextBoxTileGraphics), (TrainerInfoTextBoxTileGraphicsEnd - TrainerInfoTextBoxTileGraphics) / $10
-	jp CopyVideoData
--- a/engine/clear_save.asm
+++ /dev/null
@@ -1,23 +1,0 @@
-DoClearSaveDialogue:
-	call ClearScreen
-	call RunDefaultPaletteCommand
-	call LoadFontTilePatterns
-	call LoadTextBoxTilePatterns
-	ld hl, ClearSaveDataText
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, NO_YES_MENU
-	ld [wTwoOptionMenuID], a
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	ld a, [wCurrentMenuItem]
-	and a
-	jp z, Init
-	callba ClearSAV
-	jp Init
-
-ClearSaveDataText:
-	TX_FAR _ClearSaveDataText
-	db "@"
--- /dev/null
+++ b/engine/debug/debug_party.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/debug/test_battle.asm
@@ -1,0 +1,45 @@
+TestBattle:
+	ret
+
+.loop
+	call GBPalNormal
+
+	; Don't mess around
+	; with obedience.
+	ld a, %10000000 ; EARTHBADGE
+	ld [wObtainedBadges], a
+
+	ld hl, wFlags_D733
+	set BIT_TEST_BATTLE, [hl]
+
+	; Reset the party.
+	ld hl, wPartyCount
+	xor a
+	ld [hli], a
+	dec a
+	ld [hl], a
+
+	; Give the player a
+	; level 20 Rhydon.
+	ld a, RHYDON
+	ld [wcf91], a
+	ld a, 20
+	ld [wCurEnemyLVL], a
+	xor a
+	ld [wMonDataLocation], a
+	ld [wCurMap], a
+	call AddPartyMon
+
+	; Fight against a
+	; level 20 Rhydon.
+	ld a, RHYDON
+	ld [wCurOpponent], a
+
+	predef InitOpponent
+
+	; When the battle ends,
+	; do it all again.
+	ld a, 1
+	ld [wUpdateSpritesEnabled], a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	jr .loop
--- a/engine/debug1.asm
+++ /dev/null
@@ -1,33 +1,0 @@
-; 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
--- a/engine/display_pokedex.asm
+++ /dev/null
@@ -1,19 +1,0 @@
-_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
--- a/engine/display_text_id_init.asm
+++ /dev/null
@@ -1,78 +1,0 @@
-; 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
--- /dev/null
+++ b/engine/events/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/events/card_key.asm
@@ -1,0 +1,112 @@
+PrintCardKeyText:
+	ld hl, SilphCoMapList
+	ld a, [wCurMap]
+	ld b, a
+.silphCoMapListLoop
+	ld a, [hli]
+	cp $ff
+	ret z
+	cp b
+	jr nz, .silphCoMapListLoop
+	predef GetTileAndCoordsInFrontOfPlayer
+	ld a, [wTileInFrontOfPlayer]
+	cp $18
+	jr z, .cardKeyDoorInFrontOfPlayer
+	cp $24
+	jr z, .cardKeyDoorInFrontOfPlayer
+	ld b, a
+	ld a, [wCurMap]
+	cp SILPH_CO_11F
+	ret nz
+	ld a, b
+	cp $5e
+	ret nz
+.cardKeyDoorInFrontOfPlayer
+	ld b, CARD_KEY
+	call IsItemInBag
+	jr z, .noCardKey
+	call GetCoordsInFrontOfPlayer
+	push de
+	tx_pre_id CardKeySuccessText
+	ld [hSpriteIndexOrTextID], a
+	call PrintPredefTextID
+	pop de
+	srl d
+	ld a, d
+	ld b, a
+	ld [wCardKeyDoorY], a
+	srl e
+	ld a, e
+	ld c, a
+	ld [wCardKeyDoorX], a
+	ld a, [wCurMap]
+	cp SILPH_CO_11F
+	jr nz, .notSilphCo11F
+	ld a, $3
+	jr .replaceCardKeyDoorTileBlock
+.notSilphCo11F
+	ld a, $e
+.replaceCardKeyDoorTileBlock
+	ld [wNewTileBlockID], a
+	predef ReplaceTileBlock
+	ld hl, wCurrentMapScriptFlags
+	set 5, [hl]
+	ld a, SFX_GO_INSIDE
+	jp PlaySound
+.noCardKey
+	tx_pre_id CardKeyFailText
+	ld [hSpriteIndexOrTextID], a
+	jp PrintPredefTextID
+
+SilphCoMapList:
+	db SILPH_CO_2F
+	db SILPH_CO_3F
+	db SILPH_CO_4F
+	db SILPH_CO_5F
+	db SILPH_CO_6F
+	db SILPH_CO_7F
+	db SILPH_CO_8F
+	db SILPH_CO_9F
+	db SILPH_CO_10F
+	db SILPH_CO_11F
+	db $FF
+
+CardKeySuccessText::
+	TX_FAR _CardKeySuccessText1
+	TX_SFX_ITEM_1
+	TX_FAR _CardKeySuccessText2
+	db "@"
+
+CardKeyFailText::
+	TX_FAR _CardKeyFailText
+	db "@"
+
+; d = Y
+; e = X
+GetCoordsInFrontOfPlayer:
+	ld a, [wYCoord]
+	ld d, a
+	ld a, [wXCoord]
+	ld e, a
+	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+	and a
+	jr nz, .notFacingDown
+; facing down
+	inc d
+	ret
+.notFacingDown
+	cp SPRITE_FACING_UP
+	jr nz, .notFacingUp
+; facing up
+	dec d
+	ret
+.notFacingUp
+	cp SPRITE_FACING_LEFT
+	jr nz, .notFacingLeft
+; facing left
+	dec e
+	ret
+.notFacingLeft
+; facing right
+	inc e
+	ret
--- /dev/null
+++ b/engine/events/cinnabar_lab.asm
@@ -1,0 +1,123 @@
+GiveFossilToCinnabarLab::
+	ld hl, wd730
+	set 6, [hl]
+	xor a
+	ld [wCurrentMenuItem], a
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, [wFilteredBagItemsCount]
+	dec a
+	ld [wMaxMenuItem], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+	ld a, [wFilteredBagItemsCount]
+	dec a
+	ld bc, 2
+	ld hl, 3
+	call AddNTimes
+	dec l
+	ld b, l
+	ld c, $d
+	coord hl, 0, 0
+	call TextBoxBorder
+	call UpdateSprites
+	call PrintFossilsInBag
+	ld hl, wd730
+	res 6, [hl]
+	call HandleMenuInput
+	bit 1, a ; pressed B?
+	jr nz, .cancelledGivingFossil
+	ld hl, wFilteredBagItems
+	ld a, [wCurrentMenuItem]
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hl]
+	ld [$ffdb], a
+	cp DOME_FOSSIL
+	jr z, .choseDomeFossil
+	cp HELIX_FOSSIL
+	jr z, .choseHelixFossil
+	ld b, AERODACTYL
+	jr .fossilSelected
+.choseHelixFossil
+	ld b, OMANYTE
+	jr .fossilSelected
+.choseDomeFossil
+	ld b, KABUTO
+.fossilSelected
+	ld [wFossilItem], a
+	ld a, b
+	ld [wFossilMon], a
+	call LoadFossilItemAndMonName
+	ld hl, LabFossil_610ae
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .cancelledGivingFossil
+	ld hl, LabFossil_610b3
+	call PrintText
+	ld a, [wFossilItem]
+	ld [hItemToRemoveID], a
+	callba RemoveItemByID
+	ld hl, LabFossil_610b8
+	call PrintText
+	SetEvents EVENT_GAVE_FOSSIL_TO_LAB, EVENT_LAB_STILL_REVIVING_FOSSIL
+	ret
+.cancelledGivingFossil
+	ld hl, LabFossil_610bd
+	call PrintText
+	ret
+
+LabFossil_610ae:
+	TX_FAR _Lab4Text_610ae
+	db "@"
+
+LabFossil_610b3:
+	TX_FAR _Lab4Text_610b3
+	db "@"
+
+LabFossil_610b8:
+	TX_FAR _Lab4Text_610b8
+	db "@"
+
+LabFossil_610bd:
+	TX_FAR _Lab4Text_610bd
+	db "@"
+
+PrintFossilsInBag:
+; Prints each fossil in the player's bag on a separate line in the menu.
+	ld hl, wFilteredBagItems
+	xor a
+	ld [hItemCounter], a
+.loop
+	ld a, [hli]
+	cp $ff
+	ret z
+	push hl
+	ld [wd11e], a
+	call GetItemName
+	coord hl, 2, 2
+	ld a, [hItemCounter]
+	ld bc, SCREEN_WIDTH * 2
+	call AddNTimes
+	ld de, wcd6d
+	call PlaceString
+	ld hl, hItemCounter
+	inc [hl]
+	pop hl
+	jr .loop
+
+; loads the names of the fossil item and the resulting mon
+LoadFossilItemAndMonName::
+	ld a, [wFossilMon]
+	ld [wd11e], a
+	call GetMonName
+	call CopyStringToCF4B
+	ld a, [wFossilItem]
+	ld [wd11e], a
+	call GetItemName
+	ret
--- /dev/null
+++ b/engine/events/diploma.asm
@@ -1,0 +1,113 @@
+DisplayDiploma::
+	call SaveScreenTilesToBuffer2
+	call GBPalWhiteOutWithDelay3
+	call ClearScreen
+	xor a
+	ld [wUpdateSpritesEnabled], a
+	ld hl, wd730
+	set 6, [hl]
+	call DisableLCD
+	ld hl, CircleTile
+	ld de, vChars2 + $700
+	ld bc, $0010
+	ld a, BANK(CircleTile)
+	call FarCopyData2
+	coord hl, 0, 0
+	lb bc, 16, 18
+	predef Diploma_TextBoxBorder
+	ld hl, DiplomaTextPointersAndCoords
+	ld c, $5
+.asm_56715
+	push bc
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	ld a, [hli]
+	push hl
+	ld h, [hl]
+	ld l, a
+	call PlaceString
+	pop hl
+	inc hl
+	pop bc
+	dec c
+	jr nz, .asm_56715
+	coord hl, 10, 4
+	ld de, wPlayerName
+	call PlaceString
+	callba DrawPlayerCharacter
+
+; Move the player 33 pixels right and set the priority bit so he appears
+; behind the background layer.
+	ld hl, wOAMBuffer + $01
+	lb bc, $80, $28
+.adjustPlayerGfxLoop
+	ld a, [hl] ; X
+	add 33
+	ld [hli], a
+	inc hl
+	ld a, b
+	ld [hli], a ; attributes
+	inc hl
+	dec c
+	jr nz, .adjustPlayerGfxLoop
+
+	call EnableLCD
+	callba LoadTrainerInfoTextBoxTiles
+	ld b, SET_PAL_GENERIC
+	call RunPaletteCommand
+	call Delay3
+	call GBPalNormal
+	ld a, $90
+	ld [rOBP0], a
+	call WaitForTextScrollButtonPress
+	ld hl, wd730
+	res 6, [hl]
+	call GBPalWhiteOutWithDelay3
+	call RestoreScreenTilesAndReloadTilePatterns
+	call Delay3
+	jp GBPalNormal
+
+UnusedPlayerNameLengthFunc:
+; Unused function that does a calculation involving the length of the player's
+; name.
+	ld hl, wPlayerName
+	ld bc, $ff00
+.loop
+	ld a, [hli]
+	cp "@"
+	ret z
+	dec c
+	jr .loop
+
+DiplomaTextPointersAndCoords:
+	dw DiplomaText
+	dwCoord 5, 2
+	dw DiplomaPlayer
+	dwCoord 3, 4
+	dw DiplomaEmptyText
+	dwCoord 15, 4
+	dw DiplomaCongrats
+	dwCoord 2, 6
+	dw DiplomaGameFreak
+	dwCoord 9, 16
+
+DiplomaText:
+	db $70,"Diploma",$70,"@"
+
+DiplomaPlayer:
+	db "Player@"
+
+DiplomaEmptyText:
+	db "@"
+
+DiplomaCongrats:
+	db   "Congrats! This"
+	next "diploma certifies"
+	next "that you have"
+	next "completed your"
+	next "#DEX.@"
+
+DiplomaGameFreak:
+	db "GAME FREAK@"
--- /dev/null
+++ b/engine/events/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/events/elevator.asm
@@ -1,0 +1,48 @@
+DisplayElevatorFloorMenu:
+	ld hl, WhichFloorText
+	call PrintText
+	ld hl, wItemList
+	ld a, l
+	ld [wListPointer], a
+	ld a, h
+	ld [wListPointer + 1], a
+	ld a, [wListScrollOffset]
+	push af
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wListScrollOffset], a
+	ld [wPrintItemPrices], a
+	ld a, SPECIALLISTMENU
+	ld [wListMenuID], a
+	call DisplayListMenuID
+	pop bc
+	ld a, b
+	ld [wListScrollOffset], a
+	ret c
+	ld hl, wCurrentMapScriptFlags
+	set 7, [hl]
+	ld hl, wElevatorWarpMaps
+	ld a, [wWhichPokemon]
+	add a
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hli]
+	ld b, a
+	ld a, [hl]
+	ld c, a
+	ld hl, wWarpEntries
+	call .UpdateWarp
+
+.UpdateWarp
+	inc hl
+	inc hl
+	ld a, b
+	ld [hli], a ; destination warp ID
+	ld a, c
+	ld [hli], a ; destination map ID
+	ret
+
+WhichFloorText:
+	TX_FAR _WhichFloorText
+	db "@"
--- /dev/null
+++ b/engine/events/evolve_trade.asm
@@ -1,0 +1,44 @@
+EvolveTradeMon:
+; Verify the TradeMon's species name before
+; attempting to initiate a trade evolution.
+
+; The names of the trade evolutions in Blue (JP)
+; are checked. In that version, TradeMons that
+; can evolve are Graveler and Haunter.
+
+; In localization, this check was translated
+; before monster names were finalized.
+; Then, Haunter's name was "Spectre".
+; Since its name no longer starts with
+; "SP", it is prevented from evolving.
+
+; This may have been why Red/Green's trades
+; were used instead, where none can evolve.
+
+; This was fixed in Yellow.
+
+	ld a, [wInGameTradeReceiveMonName]
+
+	; GRAVELER
+	cp "G"
+	jr z, .ok
+
+	; "SPECTRE" (HAUNTER)
+	cp "S"
+	ret nz
+	ld a, [wInGameTradeReceiveMonName + 1]
+	cp "P"
+	ret nz
+
+.ok
+	ld a, [wPartyCount]
+	dec a
+	ld [wWhichPokemon], a
+	ld a, $1
+	ld [wForceEvolution], a
+	ld a, LINK_STATE_TRADING
+	ld [wLinkState], a
+	callab TryEvolvingMon
+	xor a ; LINK_STATE_NONE
+	ld [wLinkState], a
+	jp PlayDefaultMusic
--- /dev/null
+++ b/engine/events/give_pokemon.asm
@@ -1,0 +1,82 @@
+_GivePokemon::
+; returns success in carry
+; and whether the mon was added to the party in [wAddedToParty]
+	call EnableAutoTextBoxDrawing
+	xor a
+	ld [wAddedToParty], a
+	ld a, [wPartyCount]
+	cp PARTY_LENGTH
+	jr c, .addToParty
+	ld a, [wNumInBox]
+	cp MONS_PER_BOX
+	jr nc, .boxFull
+; add to box
+	xor a
+	ld [wEnemyBattleStatus3], a
+	ld a, [wcf91]
+	ld [wEnemyMonSpecies2], a
+	callab LoadEnemyMonData
+	call SetPokedexOwnedFlag
+	callab SendNewMonToBox
+	ld hl, wcf4b
+	ld a, [wCurrentBoxNum]
+	and $7f
+	cp 9
+	jr c, .singleDigitBoxNum
+	sub 9
+	ld [hl], "1"
+	inc hl
+	add "0"
+	jr .next
+.singleDigitBoxNum
+	add "1"
+.next
+	ld [hli], a
+	ld [hl], "@"
+	ld hl, SentToBoxText
+	call PrintText
+	scf
+	ret
+.boxFull
+	ld hl, BoxIsFullText
+	call PrintText
+	and a
+	ret
+.addToParty
+	call SetPokedexOwnedFlag
+	call AddPartyMon
+	ld a, 1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld [wAddedToParty], a
+	scf
+	ret
+
+SetPokedexOwnedFlag:
+	ld a, [wcf91]
+	push af
+	ld [wd11e], a
+	predef IndexToPokedex
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld hl, wPokedexOwned
+	ld b, FLAG_SET
+	predef FlagActionPredef
+	pop af
+	ld [wd11e], a
+	call GetMonName
+	ld hl, GotMonText
+	jp PrintText
+
+GotMonText:
+	TX_FAR _GotMonText
+	TX_SFX_ITEM_1
+	db "@"
+
+SentToBoxText:
+	TX_FAR _SentToBoxText
+	db "@"
+
+BoxIsFullText:
+	TX_FAR _BoxIsFullText
+	db "@"
--- /dev/null
+++ b/engine/events/heal_party.asm
@@ -1,0 +1,99 @@
+HealParty:
+; Restore HP and PP.
+
+	ld hl, wPartySpecies
+	ld de, wPartyMon1HP
+.healmon
+	ld a, [hli]
+	cp $ff
+	jr z, .done
+
+	push hl
+	push de
+
+	ld hl, wPartyMon1Status - wPartyMon1HP
+	add hl, de
+	xor a
+	ld [hl], a
+
+	push de
+	ld b, NUM_MOVES ; A Pokémon has 4 moves
+.pp
+	ld hl, wPartyMon1Moves - wPartyMon1HP
+	add hl, de
+
+	ld a, [hl]
+	and a
+	jr z, .nextmove
+
+	dec a
+	ld hl, wPartyMon1PP - wPartyMon1HP
+	add hl, de
+
+	push hl
+	push de
+	push bc
+
+	ld hl, Moves
+	ld bc, MoveEnd - Moves
+	call AddNTimes
+	ld de, wcd6d
+	ld a, BANK(Moves)
+	call FarCopyData
+	ld a, [wcd6d + 5] ; PP is byte 5 of move data
+
+	pop bc
+	pop de
+	pop hl
+
+	inc de
+	push bc
+	ld b, a
+	ld a, [hl]
+	and $c0
+	add b
+	ld [hl], a
+	pop bc
+
+.nextmove
+	dec b
+	jr nz, .pp
+	pop de
+
+	ld hl, wPartyMon1MaxHP - wPartyMon1HP
+	add hl, de
+	ld a, [hli]
+	ld [de], a
+	inc de
+	ld a, [hl]
+	ld [de], a
+
+	pop de
+	pop hl
+
+	push hl
+	ld bc, wPartyMon2 - wPartyMon1
+	ld h, d
+	ld l, e
+	add hl, bc
+	ld d, h
+	ld e, l
+	pop hl
+	jr .healmon
+
+.done
+	xor a
+	ld [wWhichPokemon], a
+	ld [wd11e], a
+
+	ld a, [wPartyCount]
+	ld b, a
+.ppup
+	push bc
+	call RestoreBonusPP
+	pop bc
+	ld hl, wWhichPokemon
+	inc [hl]
+	dec b
+	jr nz, .ppup
+	ret
--- /dev/null
+++ b/engine/events/hidden_items.asm
@@ -1,0 +1,161 @@
+HiddenItems:
+	ld hl, HiddenItemCoords
+	call FindHiddenItemOrCoinsIndex
+	ld [wHiddenItemOrCoinsIndex], a
+	ld hl, wObtainedHiddenItemsFlags
+	ld a, [wHiddenItemOrCoinsIndex]
+	ld c, a
+	ld b, FLAG_TEST
+	predef FlagActionPredef
+	ld a, c
+	and a
+	ret nz
+	call EnableAutoTextBoxDrawing
+	ld a, 1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld a, [wHiddenObjectFunctionArgument] ; item ID
+	ld [wd11e], a
+	call GetItemName
+	tx_pre_jump FoundHiddenItemText
+
+INCLUDE "data/hidden_item_coords.asm"
+
+FoundHiddenItemText::
+	TX_FAR _FoundHiddenItemText
+	TX_ASM
+	ld a, [wHiddenObjectFunctionArgument] ; item ID
+	ld b, a
+	ld c, 1
+	call GiveItem
+	jr nc, .bagFull
+	ld hl, wObtainedHiddenItemsFlags
+	ld a, [wHiddenItemOrCoinsIndex]
+	ld c, a
+	ld b, FLAG_SET
+	predef FlagActionPredef
+	ld a, SFX_GET_ITEM_2
+	call PlaySoundWaitForCurrent
+	call WaitForSoundToFinish
+	jp TextScriptEnd
+.bagFull
+	call WaitForTextScrollButtonPress ; wait for button press
+	xor a
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld hl, HiddenItemBagFullText
+	call PrintText
+	jp TextScriptEnd
+
+HiddenItemBagFullText::
+	TX_FAR _HiddenItemBagFullText
+	db "@"
+
+HiddenCoins:
+	ld b, COIN_CASE
+	predef GetQuantityOfItemInBag
+	ld a, b
+	and a
+	ret z
+	ld hl, HiddenCoinCoords
+	call FindHiddenItemOrCoinsIndex
+	ld [wHiddenItemOrCoinsIndex], a
+	ld hl, wObtainedHiddenCoinsFlags
+	ld a, [wHiddenItemOrCoinsIndex]
+	ld c, a
+	ld b, FLAG_TEST
+	predef FlagActionPredef
+	ld a, c
+	and a
+	ret nz
+	xor a
+	ld [hUnusedCoinsByte], a
+	ld [hCoins], a
+	ld [hCoins + 1], a
+	ld a, [wHiddenObjectFunctionArgument]
+	sub COIN
+	cp 10
+	jr z, .bcd10
+	cp 20
+	jr z, .bcd20
+	cp 40
+	jr z, .bcd20 ; should be bcd40
+	jr .bcd100
+.bcd10
+	ld a, $10
+	ld [hCoins + 1], a
+	jr .bcdDone
+.bcd20
+	ld a, $20
+	ld [hCoins + 1], a
+	jr .bcdDone
+.bcd40 ; due to a typo, this is never used
+	ld a, $40
+	ld [hCoins + 1], a
+	jr .bcdDone
+.bcd100
+	ld a, $1
+	ld [hCoins], a
+.bcdDone
+	ld de, wPlayerCoins + 1
+	ld hl, hCoins + 1
+	ld c, $2
+	predef AddBCDPredef
+	ld hl, wObtainedHiddenCoinsFlags
+	ld a, [wHiddenItemOrCoinsIndex]
+	ld c, a
+	ld b, FLAG_SET
+	predef FlagActionPredef
+	call EnableAutoTextBoxDrawing
+	ld a, [wPlayerCoins]
+	cp $99
+	jr nz, .roomInCoinCase
+	ld a, [wPlayerCoins + 1]
+	cp $99
+	jr nz, .roomInCoinCase
+	tx_pre_id DroppedHiddenCoinsText
+	jr .done
+.roomInCoinCase
+	tx_pre_id FoundHiddenCoinsText
+.done
+	jp PrintPredefTextID
+
+INCLUDE "data/hidden_coins.asm"
+
+FoundHiddenCoinsText::
+	TX_FAR _FoundHiddenCoinsText
+	TX_SFX_ITEM_2
+	db "@"
+
+DroppedHiddenCoinsText::
+	TX_FAR _FoundHiddenCoins2Text
+	TX_SFX_ITEM_2
+	TX_FAR _DroppedHiddenCoinsText
+	db "@"
+
+FindHiddenItemOrCoinsIndex:
+	ld a, [wHiddenObjectY]
+	ld d, a
+	ld a, [wHiddenObjectX]
+	ld e, a
+	ld a, [wCurMap]
+	ld b, a
+	ld c, -1
+.loop
+	inc c
+	ld a, [hli]
+	cp $ff ; end of the list?
+	ret z  ; if so, we're done here
+	cp b
+	jr nz, .next1
+	ld a, [hli]
+	cp d
+	jr nz, .next2
+	ld a, [hli]
+	cp e
+	jr nz, .loop
+	ld a, c
+	ret
+.next1
+	inc hl
+.next2
+	inc hl
+	jr .loop
--- /dev/null
+++ b/engine/events/hidden_object_functions14.asm
@@ -1,0 +1,100 @@
+PrintNotebookText:
+	call EnableAutoTextBoxDrawing
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld a, [wHiddenObjectFunctionArgument]
+	jp PrintPredefTextID
+
+TMNotebook::
+	TX_FAR TMNotebookText
+	TX_WAIT
+	db "@"
+
+ViridianSchoolNotebook::
+	TX_ASM
+	ld hl, ViridianSchoolNotebookText1
+	call PrintText
+	call TurnPageSchoolNotebook
+	jr nz, .doneReading
+	ld hl, ViridianSchoolNotebookText2
+	call PrintText
+	call TurnPageSchoolNotebook
+	jr nz, .doneReading
+	ld hl, ViridianSchoolNotebookText3
+	call PrintText
+	call TurnPageSchoolNotebook
+	jr nz, .doneReading
+	ld hl, ViridianSchoolNotebookText4
+	call PrintText
+	ld hl, ViridianSchoolNotebookText5
+	call PrintText
+.doneReading
+	jp TextScriptEnd
+
+TurnPageSchoolNotebook:
+	ld hl, TurnPageText
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	ret
+
+TurnPageText:
+	TX_FAR _TurnPageText
+	db "@"
+
+ViridianSchoolNotebookText5:
+	TX_FAR _ViridianSchoolNotebookText5
+	TX_WAIT
+	db "@"
+
+ViridianSchoolNotebookText1:
+	TX_FAR _ViridianSchoolNotebookText1
+	db "@"
+
+ViridianSchoolNotebookText2:
+	TX_FAR _ViridianSchoolNotebookText2
+	db "@"
+
+ViridianSchoolNotebookText3:
+	TX_FAR _ViridianSchoolNotebookText3
+	db "@"
+
+ViridianSchoolNotebookText4:
+	TX_FAR _ViridianSchoolNotebookText4
+	db "@"
+
+PrintFightingDojoText2:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump EnemiesOnEverySideText
+
+EnemiesOnEverySideText::
+	TX_FAR _EnemiesOnEverySideText
+	db "@"
+
+PrintFightingDojoText3:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump WhatGoesAroundComesAroundText
+
+WhatGoesAroundComesAroundText::
+	TX_FAR _WhatGoesAroundComesAroundText
+	db "@"
+
+PrintFightingDojoText:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump FightingDojoText
+
+FightingDojoText::
+	TX_FAR _FightingDojoText
+	db "@"
+
+PrintIndigoPlateauHQText:
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP
+	ret nz
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump IndigoPlateauHQText
+
+IndigoPlateauHQText::
+	TX_FAR _IndigoPlateauHQText
+	db "@"
--- /dev/null
+++ b/engine/events/hidden_object_functions17.asm
@@ -1,0 +1,475 @@
+PrintRedSNESText:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump RedBedroomSNESText
+
+RedBedroomSNESText::
+	TX_FAR _RedBedroomSNESText
+	db "@"
+
+OpenRedsPC:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump RedBedroomPCText
+
+RedBedroomPCText::
+	TX_PLAYERS_PC
+
+Route15GateLeftBinoculars:
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP
+	ret nz
+	call EnableAutoTextBoxDrawing
+	tx_pre Route15UpstairsBinocularsText
+	ld a, ARTICUNO
+	ld [wcf91], a
+	call PlayCry
+	jp DisplayMonFrontSpriteInBox
+
+Route15UpstairsBinocularsText::
+	TX_FAR _Route15UpstairsBinocularsText
+	db "@"
+
+AerodactylFossil:
+	ld a, FOSSIL_AERODACTYL
+	ld [wcf91], a
+	call DisplayMonFrontSpriteInBox
+	call EnableAutoTextBoxDrawing
+	tx_pre AerodactylFossilText
+	ret
+
+AerodactylFossilText::
+	TX_FAR _AerodactylFossilText
+	db "@"
+
+KabutopsFossil:
+	ld a, FOSSIL_KABUTOPS
+	ld [wcf91], a
+	call DisplayMonFrontSpriteInBox
+	call EnableAutoTextBoxDrawing
+	tx_pre KabutopsFossilText
+	ret
+
+KabutopsFossilText::
+	TX_FAR _KabutopsFossilText
+	db "@"
+
+DisplayMonFrontSpriteInBox:
+; Displays a pokemon's front sprite in a pop-up window.
+; [wcf91] = pokemon internal id number
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Delay3
+	xor a
+	ld [hWY], a
+	call SaveScreenTilesToBuffer1
+	ld a, MON_SPRITE_POPUP
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	call UpdateSprites
+	ld a, [wcf91]
+	ld [wd0b5], a
+	call GetMonHeader
+	ld de, vChars1 + $310
+	call LoadMonFrontSprite
+	ld a, $80
+	ld [hStartTileID], a
+	coord hl, 10, 11
+	predef AnimateSendingOutMon
+	call WaitForTextScrollButtonPress
+	call LoadScreenTilesFromBuffer1
+	call Delay3
+	ld a, $90
+	ld [hWY], a
+	ret
+
+PrintBlackboardLinkCableText:
+	call EnableAutoTextBoxDrawing
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld a, [wHiddenObjectFunctionArgument]
+	call PrintPredefTextID
+	ret
+
+LinkCableHelp::
+	TX_ASM
+	call SaveScreenTilesToBuffer1
+	ld hl, LinkCableHelpText1
+	call PrintText
+	xor a
+	ld [wMenuItemOffset], a ; not used
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 3
+	ld [wMaxMenuItem], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+.linkHelpLoop
+	ld hl, wd730
+	set 6, [hl]
+	coord hl, 0, 0
+	ld b, 8
+	ld c, 13
+	call TextBoxBorder
+	coord hl, 2, 2
+	ld de, HowToLinkText
+	call PlaceString
+	ld hl, LinkCableHelpText2
+	call PrintText
+	call HandleMenuInput
+	bit 1, a ; pressed b
+	jr nz, .exit
+	ld a, [wCurrentMenuItem]
+	cp 3 ; pressed a on "STOP READING"
+	jr z, .exit
+	ld hl, wd730
+	res 6, [hl]
+	ld hl, LinkCableInfoTexts
+	add a
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	call PrintText
+	jp .linkHelpLoop
+.exit
+	ld hl, wd730
+	res 6, [hl]
+	call LoadScreenTilesFromBuffer1
+	jp TextScriptEnd
+
+LinkCableHelpText1:
+	TX_FAR _LinkCableHelpText1
+	db "@"
+
+LinkCableHelpText2:
+	TX_FAR _LinkCableHelpText2
+	db "@"
+
+HowToLinkText:
+	db   "HOW TO LINK"
+	next "COLOSSEUM"
+	next "TRADE CENTER"
+	next "STOP READING@"
+
+LinkCableInfoTexts:
+	dw LinkCableInfoText1
+	dw LinkCableInfoText2
+	dw LinkCableInfoText3
+
+LinkCableInfoText1:
+	TX_FAR _LinkCableInfoText1
+	db "@"
+
+LinkCableInfoText2:
+	TX_FAR _LinkCableInfoText2
+	db "@"
+
+LinkCableInfoText3:
+	TX_FAR _LinkCableInfoText3
+	db "@"
+
+ViridianSchoolBlackboard::
+	TX_ASM
+	call SaveScreenTilesToBuffer1
+	ld hl, ViridianSchoolBlackboardText1
+	call PrintText
+	xor a
+	ld [wMenuItemOffset], a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld a, D_LEFT | D_RIGHT | A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 2
+	ld [wMaxMenuItem], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+.blackboardLoop
+	ld hl, wd730
+	set 6, [hl]
+	coord hl, 0, 0
+	lb bc, 6, 10
+	call TextBoxBorder
+	coord hl, 1, 2
+	ld de, StatusAilmentText1
+	call PlaceString
+	coord hl, 6, 2
+	ld de, StatusAilmentText2
+	call PlaceString
+	ld hl, ViridianSchoolBlackboardText2
+	call PrintText
+	call HandleMenuInput ; pressing up and down is handled in here
+	bit 1, a ; pressed b
+	jr nz, .exitBlackboard
+	bit 4, a ; pressed right
+	jr z, .didNotPressRight
+	; move cursor to right column
+	ld a, 2
+	ld [wMaxMenuItem], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 6
+	ld [wTopMenuItemX], a
+	ld a, 3 ; in the the right column, use an offset to prevent overlap
+	ld [wMenuItemOffset], a
+	jr .blackboardLoop
+.didNotPressRight
+	bit 5, a ; pressed left
+	jr z, .didNotPressLeftOrRight
+	; move cursor to left column
+	ld a, 2
+	ld [wMaxMenuItem], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+	xor a
+	ld [wMenuItemOffset], a
+	jr .blackboardLoop
+.didNotPressLeftOrRight
+	ld a, [wCurrentMenuItem]
+	ld b, a
+	ld a, [wMenuItemOffset]
+	add b
+	cp 5 ; cursor is pointing to "QUIT"
+	jr z, .exitBlackboard
+	; we must have pressed a on a status condition
+	; so print the text
+	ld hl, wd730
+	res 6, [hl]
+	ld hl, ViridianBlackboardStatusPointers
+	add a
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	call PrintText
+	jp .blackboardLoop
+.exitBlackboard
+	ld hl, wd730
+	res 6, [hl]
+	call LoadScreenTilesFromBuffer1
+	jp TextScriptEnd
+
+ViridianSchoolBlackboardText1:
+	TX_FAR _ViridianSchoolBlackboardText1
+	db "@"
+
+ViridianSchoolBlackboardText2:
+	TX_FAR _ViridianSchoolBlackboardText2
+	db "@"
+
+StatusAilmentText1:
+	db   " SLP"
+	next " PSN"
+	next " PAR@"
+
+StatusAilmentText2:
+	db   " BRN"
+	next " FRZ"
+	next " QUIT@@"
+
+ViridianBlackboardStatusPointers:
+	dw ViridianBlackboardSleepText
+	dw ViridianBlackboardPoisonText
+	dw ViridianBlackboardPrlzText
+	dw ViridianBlackboardBurnText
+	dw ViridianBlackboardFrozenText
+
+ViridianBlackboardSleepText:
+	TX_FAR _ViridianBlackboardSleepText
+	db "@"
+
+ViridianBlackboardPoisonText:
+	TX_FAR _ViridianBlackboardPoisonText
+	db "@"
+
+ViridianBlackboardPrlzText:
+	TX_FAR _ViridianBlackboardPrlzText
+	db "@"
+
+ViridianBlackboardBurnText:
+	TX_FAR _ViridianBlackboardBurnText
+	db "@"
+
+ViridianBlackboardFrozenText:
+	TX_FAR _ViridianBlackboardFrozenText
+	db "@"
+
+PrintTrashText:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump VermilionGymTrashText
+
+VermilionGymTrashText::
+	TX_FAR _VermilionGymTrashText
+	db "@"
+
+GymTrashScript:
+	call EnableAutoTextBoxDrawing
+	ld a, [wHiddenObjectFunctionArgument]
+	ld [wGymTrashCanIndex], a
+
+; Don't do the trash can puzzle if it's already been done.
+	CheckEvent EVENT_2ND_LOCK_OPENED
+	jr z, .ok
+
+	tx_pre_jump VermilionGymTrashText
+
+.ok
+	CheckEventReuseA EVENT_1ST_LOCK_OPENED
+	jr nz, .trySecondLock
+
+	ld a, [wFirstLockTrashCanIndex]
+	ld b, a
+	ld a, [wGymTrashCanIndex]
+	cp b
+	jr z, .openFirstLock
+
+	tx_pre_id VermilionGymTrashText
+	jr .done
+
+.openFirstLock
+; Next can is trying for the second switch.
+	SetEvent EVENT_1ST_LOCK_OPENED
+
+	ld hl, GymTrashCans
+	ld a, [wGymTrashCanIndex]
+	; * 5
+	ld b, a
+	add a
+	add a
+	add b
+
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hli]
+
+; There is a bug in this code. It should calculate a value in the range [0, 3]
+; but if the mask and random number don't have any 1 bits in common, then
+; the result of the AND will be 0. When 1 is subtracted from that, the value
+; will become $ff. This will result in 255 being added to hl, which will cause
+; hl to point to one of the zero bytes that pad the end of the ROM bank.
+; Trash can 0 was intended to be able to have the second lock only when the
+; first lock was in trash can 1 or 3. However, due to this bug, trash can 0 can
+; have the second lock regardless of which trash can had the first lock.
+
+	ld [hGymTrashCanRandNumMask], a
+	push hl
+	call Random
+	swap a
+	ld b, a
+	ld a, [hGymTrashCanRandNumMask]
+	and b
+	dec a
+	pop hl
+
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hl]
+	and $f
+	ld [wSecondLockTrashCanIndex], a
+
+	tx_pre_id VermilionGymTrashSuccessText1
+	jr .done
+
+.trySecondLock
+	ld a, [wSecondLockTrashCanIndex]
+	ld b, a
+	ld a, [wGymTrashCanIndex]
+	cp b
+	jr z, .openSecondLock
+
+; Reset the cans.
+	ResetEvent EVENT_1ST_LOCK_OPENED
+	call Random
+
+	and $e
+	ld [wFirstLockTrashCanIndex], a
+
+	tx_pre_id VermilionGymTrashFailText
+	jr .done
+
+.openSecondLock
+; Completed the trash can puzzle.
+	SetEvent EVENT_2ND_LOCK_OPENED
+	ld hl, wCurrentMapScriptFlags
+	set 6, [hl]
+
+	tx_pre_id VermilionGymTrashSuccessText3
+
+.done
+	jp PrintPredefTextID
+
+GymTrashCans:
+; byte 0: mask for random number
+; bytes 1-4: indices of the trash cans that can have the second lock
+;            (but see the comment above explaining a bug regarding this)
+; Note that the mask is simply the number of valid trash can indices that
+; follow. The remaining bytes are filled with 0 to pad the length of each entry
+; to 5 bytes.
+	db 2,  1,  3,  0,  0 ; 0
+	db 3,  0,  2,  4,  0 ; 1
+	db 2,  1,  5,  0,  0 ; 2
+	db 3,  0,  4,  6,  0 ; 3
+	db 4,  1,  3,  5,  7 ; 4
+	db 3,  2,  4,  8,  0 ; 5
+	db 3,  3,  7,  9,  0 ; 6
+	db 4,  4,  6,  8, 10 ; 7
+	db 3,  5,  7, 11,  0 ; 8
+	db 3,  6, 10, 12,  0 ; 9
+	db 4,  7,  9, 11, 13 ; 10
+	db 3,  8, 10, 14,  0 ; 11
+	db 2,  9, 13,  0,  0 ; 12
+	db 3, 10, 12, 14,  0 ; 13
+	db 2, 11, 13,  0,  0 ; 14
+
+VermilionGymTrashSuccessText1::
+	TX_FAR _VermilionGymTrashSuccessText1
+	TX_ASM
+	call WaitForSoundToFinish
+	ld a, SFX_SWITCH
+	call PlaySound
+	call WaitForSoundToFinish
+	jp TextScriptEnd
+
+; unused
+VermilionGymTrashSuccessText2::
+	TX_FAR _VermilionGymTrashSuccessText2
+	db "@"
+
+; unused
+VermilionGymTrashSuccesPlaySfx:
+	TX_ASM
+	call WaitForSoundToFinish
+	ld a, SFX_SWITCH
+	call PlaySound
+	call WaitForSoundToFinish
+	jp TextScriptEnd
+
+VermilionGymTrashSuccessText3::
+	TX_FAR _VermilionGymTrashSuccessText3
+	TX_ASM
+	call WaitForSoundToFinish
+	ld a, SFX_GO_INSIDE
+	call PlaySound
+	call WaitForSoundToFinish
+	jp TextScriptEnd
+
+VermilionGymTrashFailText::
+	TX_FAR _VermilionGymTrashFailText
+	TX_ASM
+	call WaitForSoundToFinish
+	ld a, SFX_DENIED
+	call PlaySound
+	call WaitForSoundToFinish
+	jp TextScriptEnd
--- /dev/null
+++ b/engine/events/hidden_object_functions18.asm
@@ -1,0 +1,198 @@
+GymStatues:
+; if in a gym and have the corresponding badge, a = GymStatueText2_id and jp PrintPredefTextID
+; if in a gym and don’t have the corresponding badge, a = GymStatueText1_id and jp PrintPredefTextID
+; else ret
+	call EnableAutoTextBoxDrawing
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP
+	ret nz
+	ld hl, .BadgeFlags
+	ld a, [wCurMap]
+	ld b, a
+.loop
+	ld a, [hli]
+	cp $ff
+	ret z
+	cp b
+	jr z, .match
+	inc hl
+	jr .loop
+.match
+	ld b, [hl]
+	ld a, [wBeatGymFlags]
+	and b
+	cp b
+	tx_pre_id GymStatueText2
+	jr z, .haveBadge
+	tx_pre_id GymStatueText1
+.haveBadge
+	jp PrintPredefTextID
+
+.BadgeFlags:
+	db PEWTER_GYM,   %00000001
+	db CERULEAN_GYM, %00000010
+	db VERMILION_GYM,%00000100
+	db CELADON_GYM,  %00001000
+	db FUCHSIA_GYM,  %00010000
+	db SAFFRON_GYM,  %00100000
+	db CINNABAR_GYM, %01000000
+	db VIRIDIAN_GYM, %10000000
+	db $ff
+
+GymStatueText1::
+	TX_FAR _GymStatueText1
+	db "@"
+
+GymStatueText2::
+	TX_FAR _GymStatueText2
+	db "@"
+
+PrintBenchGuyText:
+	call EnableAutoTextBoxDrawing
+	ld hl, BenchGuyTextPointers
+	ld a, [wCurMap]
+	ld b, a
+.loop
+	ld a, [hli]
+	cp $ff
+	ret z
+	cp b
+	jr z, .match
+	inc hl
+	inc hl
+	jr .loop
+.match
+	ld a, [hli]
+	ld b, a
+	ld a, [wSpriteStateData1 + 9]
+	cp b
+	jr nz, .loop ; player isn't facing left at the bench guy
+	ld a, [hl]
+	jp PrintPredefTextID
+
+; format: db map id, player sprite facing direction, text id of PredefTextIDPointerTable
+BenchGuyTextPointers:
+	db VIRIDIAN_POKECENTER,   SPRITE_FACING_LEFT
+	db_tx_pre ViridianCityPokecenterBenchGuyText
+	db PEWTER_POKECENTER,     SPRITE_FACING_LEFT
+	db_tx_pre PewterCityPokecenterBenchGuyText
+	db CERULEAN_POKECENTER,   SPRITE_FACING_LEFT
+	db_tx_pre CeruleanCityPokecenterBenchGuyText
+	db LAVENDER_POKECENTER,   SPRITE_FACING_LEFT
+	db_tx_pre LavenderCityPokecenterBenchGuyText
+	db VERMILION_POKECENTER,  SPRITE_FACING_LEFT
+	db_tx_pre VermilionCityPokecenterBenchGuyText
+	db CELADON_POKECENTER,    SPRITE_FACING_LEFT
+	db_tx_pre CeladonCityPokecenterBenchGuyText
+	db CELADON_HOTEL,         SPRITE_FACING_LEFT
+	db_tx_pre CeladonCityHotelText
+	db FUCHSIA_POKECENTER,    SPRITE_FACING_LEFT
+	db_tx_pre FuchsiaCityPokecenterBenchGuyText
+	db CINNABAR_POKECENTER,   SPRITE_FACING_LEFT
+	db_tx_pre CinnabarIslandPokecenterBenchGuyText
+	db SAFFRON_POKECENTER,    SPRITE_FACING_LEFT
+	db_tx_pre SaffronCityPokecenterBenchGuyText
+	db MT_MOON_POKECENTER,    SPRITE_FACING_LEFT
+	db_tx_pre MtMoonPokecenterBenchGuyText
+	db ROCK_TUNNEL_POKECENTER,SPRITE_FACING_LEFT
+	db_tx_pre RockTunnelPokecenterBenchGuyText
+	db $FF
+
+ViridianCityPokecenterBenchGuyText::
+	TX_FAR _ViridianCityPokecenterGuyText
+	db "@"
+
+PewterCityPokecenterBenchGuyText::
+	TX_FAR _PewterCityPokecenterGuyText
+	db "@"
+
+CeruleanCityPokecenterBenchGuyText::
+	TX_FAR _CeruleanPokecenterGuyText
+	db "@"
+
+LavenderCityPokecenterBenchGuyText::
+	TX_FAR _LavenderPokecenterGuyText
+	db "@"
+
+MtMoonPokecenterBenchGuyText::
+	TX_FAR _MtMoonPokecenterBenchGuyText
+	db "@"
+
+RockTunnelPokecenterBenchGuyText::
+	TX_FAR _RockTunnelPokecenterGuyText
+	db "@"
+
+UnusedBenchGuyText1::
+	TX_FAR _UnusedBenchGuyText1
+	db "@"
+
+UnusedBenchGuyText2::
+	TX_FAR _UnusedBenchGuyText2
+	db "@"
+
+UnusedBenchGuyText3::
+	TX_FAR _UnusedBenchGuyText3
+	db "@"
+
+VermilionCityPokecenterBenchGuyText::
+	TX_FAR _VermilionPokecenterGuyText
+	db "@"
+
+CeladonCityPokecenterBenchGuyText::
+	TX_FAR _CeladonCityPokecenterGuyText
+	db "@"
+
+FuchsiaCityPokecenterBenchGuyText::
+	TX_FAR _FuchsiaCityPokecenterGuyText
+	db "@"
+
+CinnabarIslandPokecenterBenchGuyText::
+	TX_FAR _CinnabarPokecenterGuyText
+	db "@"
+
+SaffronCityPokecenterBenchGuyText::
+	TX_ASM
+	CheckEvent EVENT_BEAT_SILPH_CO_GIOVANNI
+	ld hl, SaffronCityPokecenterBenchGuyText2
+	jr nz, .asm_624f2
+	ld hl, SaffronCityPokecenterBenchGuyText1
+.asm_624f2
+	call PrintText
+	jp TextScriptEnd
+
+SaffronCityPokecenterBenchGuyText1:
+	TX_FAR _SaffronCityPokecenterGuyText1
+	db "@"
+
+SaffronCityPokecenterBenchGuyText2:
+	TX_FAR _SaffronCityPokecenterGuyText2
+	db "@"
+
+CeladonCityHotelText::
+	TX_FAR _CeladonCityHotelText
+	db "@"
+
+	ret
+
+UnusedPredefText::
+	db "@"
+
+PrintBookcaseText:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump BookcaseText
+
+BookcaseText::
+	TX_FAR _BookcaseText
+	db "@"
+
+OpenPokemonCenterPC:
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP ; check to see if player is facing up
+	ret nz
+	call EnableAutoTextBoxDrawing
+	ld a, $1
+	ld [wAutoTextBoxDrawingControl], a
+	tx_pre_jump PokemonCenterPCText
+
+PokemonCenterPCText::
+	TX_POKECENTER_PC
--- /dev/null
+++ b/engine/events/hidden_object_functions3.asm
@@ -1,0 +1,117 @@
+; prints text for bookshelves in buildings without sign events
+PrintBookshelfText::
+	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+	cp SPRITE_FACING_UP
+	jr nz, .noMatch
+; facing up
+	ld a, [wCurMapTileset]
+	ld b, a
+	aCoord 8, 7
+	ld c, a
+	ld hl, BookshelfTileIDs
+.loop
+	ld a, [hli]
+	cp $ff
+	jr z, .noMatch
+	cp b
+	jr nz, .nextBookshelfEntry1
+	ld a, [hli]
+	cp c
+	jr nz, .nextBookshelfEntry2
+	ld a, [hl]
+	push af
+	call EnableAutoTextBoxDrawing
+	pop af
+	call PrintPredefTextID
+	xor a
+	ld [$ffdb], a
+	ret
+.nextBookshelfEntry1
+	inc hl
+.nextBookshelfEntry2
+	inc hl
+	jr .loop
+.noMatch
+	ld a, $ff
+	ld [$ffdb], a
+	jpba PrintCardKeyText
+
+INCLUDE "data/bookshelf_tile_ids.asm"
+
+IndigoPlateauStatues::
+	TX_ASM
+	ld hl, IndigoPlateauStatuesText1
+	call PrintText
+	ld a, [wXCoord]
+	bit 0, a
+	ld hl, IndigoPlateauStatuesText2
+	jr nz, .ok
+	ld hl, IndigoPlateauStatuesText3
+.ok
+	call PrintText
+	jp TextScriptEnd
+
+IndigoPlateauStatuesText1:
+	TX_FAR _IndigoPlateauStatuesText1
+	db "@"
+
+IndigoPlateauStatuesText2:
+	TX_FAR _IndigoPlateauStatuesText2
+	db "@"
+
+IndigoPlateauStatuesText3:
+	TX_FAR _IndigoPlateauStatuesText3
+	db "@"
+
+BookOrSculptureText::
+	TX_ASM
+	ld hl, PokemonBooksText
+	ld a, [wCurMapTileset]
+	cp MANSION ; Celadon Mansion tileset
+	jr nz, .ok
+	aCoord 8, 6
+	cp $38
+	jr nz, .ok
+	ld hl, DiglettSculptureText
+.ok
+	call PrintText
+	jp TextScriptEnd
+
+PokemonBooksText:
+	TX_FAR _PokemonBooksText
+	db "@"
+
+DiglettSculptureText:
+	TX_FAR _DiglettSculptureText
+	db "@"
+
+ElevatorText::
+	TX_FAR _ElevatorText
+	db "@"
+
+TownMapText::
+	TX_FAR _TownMapText
+	TX_BLINK
+	TX_ASM
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld hl, wd730
+	set 6, [hl]
+	call GBPalWhiteOutWithDelay3
+	xor a
+	ld [hWY], a
+	inc a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call LoadFontTilePatterns
+	callba DisplayTownMap
+	ld hl, wd730
+	res 6, [hl]
+	ld de, TextScriptEnd
+	push de
+	ld a, [H_LOADEDROMBANK]
+	push af
+	jp CloseTextDisplay
+
+PokemonStuffText::
+	TX_FAR _PokemonStuffText
+	db "@"
--- /dev/null
+++ b/engine/events/hidden_object_functions7.asm
@@ -1,0 +1,467 @@
+PrintNewBikeText:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump NewBicycleText
+
+NewBicycleText::
+	TX_FAR _NewBicycleText
+	db "@"
+
+DisplayOakLabLeftPoster:
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump PushStartText
+
+PushStartText::
+	TX_FAR _PushStartText
+	db "@"
+
+DisplayOakLabRightPoster:
+	call EnableAutoTextBoxDrawing
+	ld hl, wPokedexOwned
+	ld b, wPokedexOwnedEnd - wPokedexOwned
+	call CountSetBits
+	ld a, [wNumSetBits]
+	cp 2
+	tx_pre_id SaveOptionText
+	jr c, .ownLessThanTwo
+	; own two or more mon
+	tx_pre_id StrengthsAndWeaknessesText
+.ownLessThanTwo
+	jp PrintPredefTextID
+
+SaveOptionText::
+	TX_FAR _SaveOptionText
+	db "@"
+
+StrengthsAndWeaknessesText::
+	TX_FAR _StrengthsAndWeaknessesText
+	db "@"
+
+SafariZoneCheck::
+	CheckEventHL EVENT_IN_SAFARI_ZONE ; if we are not in the Safari Zone,
+	jr z, SafariZoneGameStillGoing ; don't bother printing game over text
+	ld a, [wNumSafariBalls]
+	and a
+	jr z, SafariZoneGameOver
+	jr SafariZoneGameStillGoing
+
+SafariZoneCheckSteps::
+	ld a, [wSafariSteps]
+	ld b, a
+	ld a, [wSafariSteps + 1]
+	ld c, a
+	or b
+	jr z, SafariZoneGameOver
+	dec bc
+	ld a, b
+	ld [wSafariSteps], a
+	ld a, c
+	ld [wSafariSteps + 1], a
+SafariZoneGameStillGoing:
+	xor a
+	ld [wSafariZoneGameOver], a
+	ret
+
+SafariZoneGameOver:
+	call EnableAutoTextBoxDrawing
+	xor a
+	ld [wAudioFadeOutControl], a
+	dec a
+	call PlaySound
+	ld c, BANK(SFX_Safari_Zone_PA)
+	ld a, SFX_SAFARI_ZONE_PA
+	call PlayMusic
+.waitForMusicToPlay
+	ld a, [wChannelSoundIDs + Ch5]
+	cp SFX_SAFARI_ZONE_PA
+	jr nz, .waitForMusicToPlay
+	ld a, TEXT_SAFARI_GAME_OVER
+	ld [hSpriteIndexOrTextID], a
+	call DisplayTextID
+	xor a
+	ld [wPlayerMovingDirection], a
+	ld a, SAFARI_ZONE_GATE
+	ld [hWarpDestinationMap], a
+	ld a, $3
+	ld [wDestinationWarpID], a
+	ld a, $5
+	ld [wSafariZoneGateCurScript], a
+	SetEvent EVENT_SAFARI_GAME_OVER
+	ld a, 1
+	ld [wSafariZoneGameOver], a
+	ret
+
+PrintSafariGameOverText::
+	xor a
+	ld [wJoyIgnore], a
+	ld hl, SafariGameOverText
+	jp PrintText
+
+SafariGameOverText:
+	TX_ASM
+	ld a, [wNumSafariBalls]
+	and a
+	jr z, .noMoreSafariBalls
+	ld hl, TimesUpText
+	call PrintText
+.noMoreSafariBalls
+	ld hl, GameOverText
+	call PrintText
+	jp TextScriptEnd
+
+TimesUpText:
+	TX_FAR _TimesUpText
+	db "@"
+
+GameOverText:
+	TX_FAR _GameOverText
+	db "@"
+
+PrintCinnabarQuiz:
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP
+	ret nz
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump CinnabarGymQuiz
+
+CinnabarGymQuiz::
+	TX_ASM
+	xor a
+	ld [wOpponentAfterWrongAnswer], a
+	ld a, [wHiddenObjectFunctionArgument]
+	push af
+	and $f
+	ld [hGymGateIndex], a
+	pop af
+	and $f0
+	swap a
+	ld [$ffdc], a
+	ld hl, CinnabarGymQuizIntroText
+	call PrintText
+	ld a, [hGymGateIndex]
+	dec a
+	add a
+	ld d, 0
+	ld e, a
+	ld hl, CinnabarQuizQuestions
+	add hl, de
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	call PrintText
+	ld a, 1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	call CinnabarGymQuiz_1ea92
+	jp TextScriptEnd
+
+CinnabarGymQuizIntroText:
+	TX_FAR _CinnabarGymQuizIntroText
+	db "@"
+
+CinnabarQuizQuestions:
+	dw CinnabarQuizQuestionsText1
+	dw CinnabarQuizQuestionsText2
+	dw CinnabarQuizQuestionsText3
+	dw CinnabarQuizQuestionsText4
+	dw CinnabarQuizQuestionsText5
+	dw CinnabarQuizQuestionsText6
+
+CinnabarQuizQuestionsText1:
+	TX_FAR _CinnabarQuizQuestionsText1
+	db "@"
+
+CinnabarQuizQuestionsText2:
+	TX_FAR _CinnabarQuizQuestionsText2
+	db "@"
+
+CinnabarQuizQuestionsText3:
+	TX_FAR _CinnabarQuizQuestionsText3
+	db "@"
+
+CinnabarQuizQuestionsText4:
+	TX_FAR _CinnabarQuizQuestionsText4
+	db "@"
+
+CinnabarQuizQuestionsText5:
+	TX_FAR _CinnabarQuizQuestionsText5
+	db "@"
+
+CinnabarQuizQuestionsText6:
+	TX_FAR _CinnabarQuizQuestionsText6
+	db "@"
+
+CinnabarGymGateFlagAction:
+	EventFlagAddress hl, EVENT_CINNABAR_GYM_GATE0_UNLOCKED
+	predef_jump FlagActionPredef
+
+CinnabarGymQuiz_1ea92:
+	call YesNoChoice
+	ld a, [$ffdc]
+	ld c, a
+	ld a, [wCurrentMenuItem]
+	cp c
+	jr nz, .wrongAnswer
+	ld hl, wCurrentMapScriptFlags
+	set 5, [hl]
+	ld a, [hGymGateIndex]
+	ld [$ffe0], a
+	ld hl, CinnabarGymQuizCorrectText
+	call PrintText
+	ld a, [$ffe0]
+	AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
+	ld c, a
+	ld b, FLAG_SET
+	call CinnabarGymGateFlagAction
+	jp UpdateCinnabarGymGateTileBlocks_
+.wrongAnswer
+	call WaitForSoundToFinish
+	ld a, SFX_DENIED
+	call PlaySound
+	call WaitForSoundToFinish
+	ld hl, CinnabarGymQuizIncorrectText
+	call PrintText
+	ld a, [hGymGateIndex]
+	add $2
+	AdjustEventBit EVENT_BEAT_CINNABAR_GYM_TRAINER_0, 2
+	ld c, a
+	ld b, FLAG_TEST
+	EventFlagAddress hl, EVENT_BEAT_CINNABAR_GYM_TRAINER_0
+	predef FlagActionPredef
+	ld a, c
+	and a
+	ret nz
+	ld a, [hGymGateIndex]
+	add $2
+	ld [wOpponentAfterWrongAnswer], a
+	ret
+
+CinnabarGymQuizCorrectText:
+	TX_SFX_ITEM_1
+	TX_FAR _CinnabarGymQuizCorrectText
+	TX_BLINK
+	TX_ASM
+
+	ld a, [$ffe0]
+	AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
+	ld c, a
+	ld b, FLAG_TEST
+	call CinnabarGymGateFlagAction
+	ld a, c
+	and a
+	jp nz, TextScriptEnd
+	call WaitForSoundToFinish
+	ld a, SFX_GO_INSIDE
+	call PlaySound
+	call WaitForSoundToFinish
+	jp TextScriptEnd
+
+CinnabarGymQuizIncorrectText:
+	TX_FAR _CinnabarGymQuizIncorrectText
+	db "@"
+
+UpdateCinnabarGymGateTileBlocks_::
+; Update the overworld map with open floor blocks or locked gate blocks
+; depending on event flags.
+	ld a, 6
+	ld [hGymGateIndex], a
+.loop
+	ld a, [hGymGateIndex]
+	dec a
+	add a
+	add a
+	ld d, 0
+	ld e, a
+	ld hl, CinnabarGymGateCoords
+	add hl, de
+	ld a, [hli]
+	ld b, [hl]
+	ld c, a
+	inc hl
+	ld a, [hl]
+	ld [wGymGateTileBlock], a
+	push bc
+	ld a, [hGymGateIndex]
+	ld [$ffe0], a
+	AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
+	ld c, a
+	ld b, FLAG_TEST
+	call CinnabarGymGateFlagAction
+	ld a, c
+	and a
+	jr nz, .unlocked
+	ld a, [wGymGateTileBlock]
+	jr .next
+.unlocked
+	ld a, $e
+.next
+	pop bc
+	ld [wNewTileBlockID], a
+	predef ReplaceTileBlock
+	ld hl, hGymGateIndex
+	dec [hl]
+	jr nz, .loop
+	ret
+
+CinnabarGymGateCoords:
+	; format: x-coord, y-coord, direction, padding
+	; direction: $54 = horizontal gate, $5f = vertical gate
+	db $09,$03,$54,$00
+	db $06,$03,$54,$00
+	db $06,$06,$54,$00
+	db $03,$08,$5f,$00
+	db $02,$06,$54,$00
+	db $02,$03,$54,$00
+
+PrintMagazinesText:
+	call EnableAutoTextBoxDrawing
+	tx_pre MagazinesText
+	ret
+
+MagazinesText::
+	TX_FAR _MagazinesText
+	db "@"
+
+BillsHousePC:
+	call EnableAutoTextBoxDrawing
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP
+	ret nz
+	CheckEvent EVENT_LEFT_BILLS_HOUSE_AFTER_HELPING
+	jr nz, .displayBillsHousePokemonList
+	CheckEventReuseA EVENT_USED_CELL_SEPARATOR_ON_BILL
+	jr nz, .displayBillsHouseMonitorText
+	CheckEventReuseA EVENT_BILL_SAID_USE_CELL_SEPARATOR
+	jr nz, .doCellSeparator
+.displayBillsHouseMonitorText
+	tx_pre_jump BillsHouseMonitorText
+.doCellSeparator
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	tx_pre BillsHouseInitiatedText
+	ld c, 32
+	call DelayFrames
+	ld a, SFX_TINK
+	call PlaySound
+	call WaitForSoundToFinish
+	ld c, 80
+	call DelayFrames
+	ld a, SFX_SHRINK
+	call PlaySound
+	call WaitForSoundToFinish
+	ld c, 48
+	call DelayFrames
+	ld a, SFX_TINK
+	call PlaySound
+	call WaitForSoundToFinish
+	ld c, 32
+	call DelayFrames
+	ld a, SFX_GET_ITEM_1
+	call PlaySound
+	call WaitForSoundToFinish
+	call PlayDefaultMusic
+	SetEvent EVENT_USED_CELL_SEPARATOR_ON_BILL
+	ret
+.displayBillsHousePokemonList
+	ld a, $1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	tx_pre BillsHousePokemonList
+	ret
+
+BillsHouseMonitorText::
+	TX_FAR _BillsHouseMonitorText
+	db "@"
+
+BillsHouseInitiatedText::
+	TX_FAR _BillsHouseInitiatedText
+	TX_BLINK
+	TX_ASM
+	ld a, $ff
+	ld [wNewSoundID], a
+	call PlaySound
+	ld c, 16
+	call DelayFrames
+	ld a, SFX_SWITCH
+	call PlaySound
+	call WaitForSoundToFinish
+	ld c, 60
+	call DelayFrames
+	jp TextScriptEnd
+
+BillsHousePokemonList::
+	TX_ASM
+	call SaveScreenTilesToBuffer1
+	ld hl, BillsHousePokemonListText1
+	call PrintText
+	xor a
+	ld [wMenuItemOffset], a ; not used
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 4
+	ld [wMaxMenuItem], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+.billsPokemonLoop
+	ld hl, wd730
+	set 6, [hl]
+	coord hl, 0, 0
+	ld b, 10
+	ld c, 9
+	call TextBoxBorder
+	coord hl, 2, 2
+	ld de, BillsMonListText
+	call PlaceString
+	ld hl, BillsHousePokemonListText2
+	call PrintText
+	call SaveScreenTilesToBuffer2
+	call HandleMenuInput
+	bit 1, a ; pressed b
+	jr nz, .cancel
+	ld a, [wCurrentMenuItem]
+	add EEVEE
+	cp EEVEE
+	jr z, .displayPokedex
+	cp FLAREON
+	jr z, .displayPokedex
+	cp JOLTEON
+	jr z, .displayPokedex
+	cp VAPOREON
+	jr z, .displayPokedex
+	jr .cancel
+.displayPokedex
+	call DisplayPokedex
+	call LoadScreenTilesFromBuffer2
+	jr .billsPokemonLoop
+.cancel
+	ld hl, wd730
+	res 6, [hl]
+	call LoadScreenTilesFromBuffer2
+	jp TextScriptEnd
+
+BillsHousePokemonListText1:
+	TX_FAR _BillsHousePokemonListText1
+	db "@"
+
+BillsMonListText:
+	db   "EEVEE"
+	next "FLAREON"
+	next "JOLTEON"
+	next "VAPOREON"
+	next "CANCEL@"
+
+BillsHousePokemonListText2:
+	TX_FAR _BillsHousePokemonListText2
+	db "@"
+
+DisplayOakLabEmailText:
+	ld a, [wSpriteStateData1 + 9]
+	cp SPRITE_FACING_UP
+	ret nz
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump OakLabEmailText
+
+OakLabEmailText::
+	TX_FAR _OakLabEmailText
+	db "@"
--- /dev/null
+++ b/engine/events/in_game_trades.asm
@@ -1,0 +1,330 @@
+DoInGameTradeDialogue:
+; trigger the trade offer/action specified by wWhichTrade
+	call SaveScreenTilesToBuffer2
+	ld hl, TradeMons
+	ld a, [wWhichTrade]
+	ld b, a
+	swap a
+	sub b
+	sub b
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hli]
+	ld [wInGameTradeGiveMonSpecies], a
+	ld a, [hli]
+	ld [wInGameTradeReceiveMonSpecies], a
+	ld a, [hli]
+	push af
+	ld de, wInGameTradeMonNick
+	ld bc, NAME_LENGTH
+	call CopyData
+	pop af
+	ld l, a
+	ld h, 0
+	ld de, InGameTradeTextPointers
+	add hl, hl
+	add hl, de
+	ld a, [hli]
+	ld [wInGameTradeTextPointerTablePointer], a
+	ld a, [hl]
+	ld [wInGameTradeTextPointerTablePointer + 1], a
+	ld a, [wInGameTradeGiveMonSpecies]
+	ld de, wInGameTradeGiveMonName
+	call InGameTrade_GetMonName
+	ld a, [wInGameTradeReceiveMonSpecies]
+	ld de, wInGameTradeReceiveMonName
+	call InGameTrade_GetMonName
+	ld hl, wCompletedInGameTradeFlags
+	ld a, [wWhichTrade]
+	ld c, a
+	ld b, FLAG_TEST
+	predef FlagActionPredef
+	ld a, c
+	and a
+	ld a, $4
+	ld [wInGameTradeTextPointerTableIndex], a
+	jr nz, .printText
+; if the trade hasn't been done yet
+	xor a
+	ld [wInGameTradeTextPointerTableIndex], a
+	call .printText
+	ld a, $1
+	ld [wInGameTradeTextPointerTableIndex], a
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .printText
+	call InGameTrade_DoTrade
+	jr c, .printText
+	ld hl, TradedForText
+	call PrintText
+.printText
+	ld hl, wInGameTradeTextPointerTableIndex
+	ld a, [hld] ; wInGameTradeTextPointerTableIndex
+	ld e, a
+	ld d, 0
+	ld a, [hld] ; wInGameTradeTextPointerTablePointer + 1
+	ld l, [hl] ; wInGameTradeTextPointerTablePointer
+	ld h, a
+	add hl, de
+	add hl, de
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	jp PrintText
+
+; copies name of species a to hl
+InGameTrade_GetMonName:
+	push de
+	ld [wd11e], a
+	call GetMonName
+	ld hl, wcd6d
+	pop de
+	ld bc, NAME_LENGTH
+	jp CopyData
+
+INCLUDE "data/trades.asm"
+
+InGameTrade_DoTrade:
+	xor a ; NORMAL_PARTY_MENU
+	ld [wPartyMenuTypeOrMessageID], a
+	dec a
+	ld [wUpdateSpritesEnabled], a
+	call DisplayPartyMenu
+	push af
+	call InGameTrade_RestoreScreen
+	pop af
+	ld a, $1
+	jp c, .tradeFailed ; jump if the player didn't select a pokemon
+	ld a, [wInGameTradeGiveMonSpecies]
+	ld b, a
+	ld a, [wcf91]
+	cp b
+	ld a, $2
+	jr nz, .tradeFailed ; jump if the selected mon's species is not the required one
+	ld a, [wWhichPokemon]
+	ld hl, wPartyMon1Level
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld a, [hl]
+	ld [wCurEnemyLVL], a
+	ld hl, wCompletedInGameTradeFlags
+	ld a, [wWhichTrade]
+	ld c, a
+	ld b, FLAG_SET
+	predef FlagActionPredef
+	ld hl, ConnectCableText
+	call PrintText
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wCurEnemyLVL]
+	push af
+	call LoadHpBarAndStatusTilePatterns
+	call InGameTrade_PrepareTradeData
+	predef InternalClockTradeAnim
+	pop af
+	ld [wCurEnemyLVL], a
+	pop af
+	ld [wWhichPokemon], a
+	ld a, [wInGameTradeReceiveMonSpecies]
+	ld [wcf91], a
+	xor a
+	ld [wMonDataLocation], a ; not used
+	ld [wRemoveMonFromBox], a
+	call RemovePokemon
+	ld a, $80 ; prevent the player from naming the mon
+	ld [wMonDataLocation], a
+	call AddPartyMon
+	call InGameTrade_CopyDataToReceivedMon
+	callab EvolveTradeMon
+	call ClearScreen
+	call InGameTrade_RestoreScreen
+	callba RedrawMapView
+	and a
+	ld a, $3
+	jr .tradeSucceeded
+.tradeFailed
+	scf
+.tradeSucceeded
+	ld [wInGameTradeTextPointerTableIndex], a
+	ret
+
+InGameTrade_RestoreScreen:
+	call GBPalWhiteOutWithDelay3
+	call RestoreScreenTilesAndReloadTilePatterns
+	call ReloadTilesetTilePatterns
+	call LoadScreenTilesFromBuffer2
+	call Delay3
+	call LoadGBPal
+	ld c, 10
+	call DelayFrames
+	jpba LoadWildData
+
+InGameTrade_PrepareTradeData:
+	ld hl, wTradedPlayerMonSpecies
+	ld a, [wInGameTradeGiveMonSpecies]
+	ld [hli], a ; wTradedPlayerMonSpecies
+	ld a, [wInGameTradeReceiveMonSpecies]
+	ld [hl], a ; wTradedEnemyMonSpecies
+	ld hl, wPartyMonOT
+	ld bc, NAME_LENGTH
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld de, wTradedPlayerMonOT
+	ld bc, NAME_LENGTH
+	call InGameTrade_CopyData
+	ld hl, InGameTrade_TrainerString
+	ld de, wTradedEnemyMonOT
+	call InGameTrade_CopyData
+	ld de, wLinkEnemyTrainerName
+	call InGameTrade_CopyData
+	ld hl, wPartyMon1OTID
+	ld bc, wPartyMon2 - wPartyMon1
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld de, wTradedPlayerMonOTID
+	ld bc, $2
+	call InGameTrade_CopyData
+	call Random
+	ld hl, hRandomAdd
+	ld de, wTradedEnemyMonOTID
+	jp CopyData
+
+InGameTrade_CopyData:
+	push hl
+	push bc
+	call CopyData
+	pop bc
+	pop hl
+	ret
+
+InGameTrade_CopyDataToReceivedMon:
+	ld hl, wPartyMonNicks
+	ld bc, NAME_LENGTH
+	call InGameTrade_GetReceivedMonPointer
+	ld hl, wInGameTradeMonNick
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, wPartyMonOT
+	ld bc, NAME_LENGTH
+	call InGameTrade_GetReceivedMonPointer
+	ld hl, InGameTrade_TrainerString
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, wPartyMon1OTID
+	ld bc, wPartyMon2 - wPartyMon1
+	call InGameTrade_GetReceivedMonPointer
+	ld hl, wTradedEnemyMonOTID
+	ld bc, $2
+	jp CopyData
+
+; the received mon's index is (partyCount - 1),
+; so this adds bc to hl (partyCount - 1) times and moves the result to de
+InGameTrade_GetReceivedMonPointer:
+	ld a, [wPartyCount]
+	dec a
+	call AddNTimes
+	ld e, l
+	ld d, h
+	ret
+
+InGameTrade_TrainerString:
+	; "TRAINER@@@@@@@@@@"
+	db $5d, "@@@@@@@@@@"
+
+InGameTradeTextPointers:
+	dw TradeTextPointers1
+	dw TradeTextPointers2
+	dw TradeTextPointers3
+
+TradeTextPointers1:
+	dw WannaTrade1Text
+	dw NoTrade1Text
+	dw WrongMon1Text
+	dw Thanks1Text
+	dw AfterTrade1Text
+
+TradeTextPointers2:
+	dw WannaTrade2Text
+	dw NoTrade2Text
+	dw WrongMon2Text
+	dw Thanks2Text
+	dw AfterTrade2Text
+
+TradeTextPointers3:
+	dw WannaTrade3Text
+	dw NoTrade3Text
+	dw WrongMon3Text
+	dw Thanks3Text
+	dw AfterTrade3Text
+
+ConnectCableText:
+	TX_FAR _ConnectCableText
+	db "@"
+
+TradedForText:
+	TX_FAR _TradedForText
+	TX_SFX_KEY_ITEM
+	TX_DELAY
+	db "@"
+
+WannaTrade1Text:
+	TX_FAR _WannaTrade1Text
+	db "@"
+
+NoTrade1Text:
+	TX_FAR _NoTrade1Text
+	db "@"
+
+WrongMon1Text:
+	TX_FAR _WrongMon1Text
+	db "@"
+
+Thanks1Text:
+	TX_FAR _Thanks1Text
+	db "@"
+
+AfterTrade1Text:
+	TX_FAR _AfterTrade1Text
+	db "@"
+
+WannaTrade2Text:
+	TX_FAR _WannaTrade2Text
+	db "@"
+
+NoTrade2Text:
+	TX_FAR _NoTrade2Text
+	db "@"
+
+WrongMon2Text:
+	TX_FAR _WrongMon2Text
+	db "@"
+
+Thanks2Text:
+	TX_FAR _Thanks2Text
+	db "@"
+
+AfterTrade2Text:
+	TX_FAR _AfterTrade2Text
+	db "@"
+
+WannaTrade3Text:
+	TX_FAR _WannaTrade3Text
+	db "@"
+
+NoTrade3Text:
+	TX_FAR _NoTrade3Text
+	db "@"
+
+WrongMon3Text:
+	TX_FAR _WrongMon3Text
+	db "@"
+
+Thanks3Text:
+	TX_FAR _Thanks3Text
+	db "@"
+
+AfterTrade3Text:
+	TX_FAR _AfterTrade3Text
+	db "@"
--- /dev/null
+++ b/engine/events/oaks_aide.asm
@@ -1,0 +1,71 @@
+OaksAideScript:
+	ld hl, OaksAideHiText
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .choseNo
+	ld hl, wPokedexOwned
+	ld b, wPokedexOwnedEnd - wPokedexOwned
+	call CountSetBits
+	ld a, [wNumSetBits]
+	ld [hOaksAideNumMonsOwned], a
+	ld b, a
+	ld a, [hOaksAideRequirement]
+	cp b
+	jr z, .giveItem
+	jr nc, .notEnoughOwnedMons
+.giveItem
+	ld hl, OaksAideHereYouGoText
+	call PrintText
+	ld a, [hOaksAideRewardItem]
+	ld b, a
+	ld c, 1
+	call GiveItem
+	jr nc, .bagFull
+	ld hl, OaksAideGotItemText
+	call PrintText
+	ld a, $1
+	jr .done
+.bagFull
+	ld hl, OaksAideNoRoomText
+	call PrintText
+	xor a
+	jr .done
+.notEnoughOwnedMons
+	ld hl, OaksAideUhOhText
+	call PrintText
+	ld a, $80
+	jr .done
+.choseNo
+	ld hl, OaksAideComeBackText
+	call PrintText
+	ld a, $ff
+.done
+	ld [hOaksAideResult], a
+	ret
+
+OaksAideHiText:
+	TX_FAR _OaksAideHiText
+	db "@"
+
+OaksAideUhOhText:
+	TX_FAR _OaksAideUhOhText
+	db "@"
+
+OaksAideComeBackText:
+	TX_FAR _OaksAideComeBackText
+	db "@"
+
+OaksAideHereYouGoText:
+	TX_FAR _OaksAideHereYouGoText
+	db "@"
+
+OaksAideGotItemText:
+	TX_FAR _OaksAideGotItemText
+	TX_SFX_ITEM_1
+	db "@"
+
+OaksAideNoRoomText:
+	TX_FAR _OaksAideNoRoomText
+	db "@"
--- /dev/null
+++ b/engine/events/pewter_guys.asm
@@ -1,0 +1,102 @@
+PewterGuys:
+	ld hl, wSimulatedJoypadStatesEnd
+	ld a, [wSimulatedJoypadStatesIndex]
+	dec a ; this decrement causes it to overwrite the last byte before $FF in the list
+	ld [wSimulatedJoypadStatesIndex], a
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld d, h
+	ld e, l
+	ld hl, PointerTable_37ce6
+	ld a, [wWhichPewterGuy]
+	add a
+	ld b, 0
+	ld c, a
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld a, [wYCoord]
+	ld b, a
+	ld a, [wXCoord]
+	ld c, a
+.findMatchingCoordsLoop
+	ld a, [hli]
+	cp b
+	jr nz, .nextEntry1
+	ld a, [hli]
+	cp c
+	jr nz, .nextEntry2
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+.copyMovementDataLoop
+	ld a, [hli]
+	cp $ff
+	ret z
+	ld [de], a
+	inc de
+	ld a, [wSimulatedJoypadStatesIndex]
+	inc a
+	ld [wSimulatedJoypadStatesIndex], a
+	jr .copyMovementDataLoop
+.nextEntry1
+	inc hl
+.nextEntry2
+	inc hl
+	inc hl
+	jr .findMatchingCoordsLoop
+
+PointerTable_37ce6:
+	dw PewterMuseumGuyCoords
+	dw PewterGymGuyCoords
+
+; these are the four coordinates of the spaces below, above, to the left and
+; to the right of the museum guy, and pointers to different movements for
+; the player to make to get positioned before the main movement.
+PewterMuseumGuyCoords:
+	db 18, 27
+	dw .down
+	db 16, 27
+	dw .up
+	db 17, 26
+	dw .left
+	db 17, 28
+	dw .right
+
+.down
+	db D_UP, D_UP, $ff
+.up
+	db D_RIGHT, D_LEFT, $ff
+.left
+	db D_UP, D_RIGHT, $ff
+.right
+	db D_UP, D_LEFT, $ff
+
+; these are the five coordinates which trigger the gym guy and pointers to
+; different movements for the player to make to get positioned before the
+; main movement
+; $00 is a pause
+PewterGymGuyCoords:
+	db 16, 34
+	dw .one
+	db 17, 35
+	dw .two
+	db 18, 37
+	dw .three
+	db 19, 37
+	dw .four
+	db 17, 36
+	dw .five
+
+.one
+	db D_LEFT, D_DOWN, D_DOWN, D_RIGHT, $ff
+.two
+	db D_LEFT, D_DOWN, D_RIGHT, D_LEFT, $ff
+.three
+	db D_LEFT, D_LEFT, D_LEFT, $00, $00, $00, $00, $00, $00, $00, $00, $ff
+.four
+	db D_LEFT, D_LEFT, D_UP, D_LEFT, $ff
+.five
+	db D_LEFT, D_DOWN, D_LEFT, $00, $00, $00, $00, $00, $00, $00, $00, $ff
--- /dev/null
+++ b/engine/events/pick_up_item.asm
@@ -1,0 +1,54 @@
+PickUpItem:
+	call EnableAutoTextBoxDrawing
+
+	ld a, [hSpriteIndexOrTextID]
+	ld b, a
+	ld hl, wMissableObjectList
+.missableObjectsListLoop
+	ld a, [hli]
+	cp $ff
+	ret z
+	cp b
+	jr z, .isMissable
+	inc hl
+	jr .missableObjectsListLoop
+
+.isMissable
+	ld a, [hl]
+	ld [$ffdb], a
+
+	ld hl, wMapSpriteExtraData
+	ld a, [hSpriteIndexOrTextID]
+	dec a
+	add a
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hl]
+	ld b, a ; item
+	ld c, 1 ; quantity
+	call GiveItem
+	jr nc, .BagFull
+
+	ld a, [$ffdb]
+	ld [wMissableObjectIndex], a
+	predef HideObject
+	ld a, 1
+	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+	ld hl, FoundItemText
+	jr .print
+
+.BagFull
+	ld hl, NoMoreRoomForItemText
+.print
+	call PrintText
+	ret
+
+FoundItemText:
+	TX_FAR _FoundItemText
+	TX_SFX_ITEM_1
+	db "@"
+
+NoMoreRoomForItemText:
+	TX_FAR _NoMoreRoomForItemText
+	db "@"
--- /dev/null
+++ b/engine/events/poison.asm
@@ -1,0 +1,112 @@
+ApplyOutOfBattlePoisonDamage:
+	ld a, [wd730]
+	add a
+	jp c, .noBlackOut ; no black out if joypad states are being simulated
+	ld a, [wPartyCount]
+	and a
+	jp z, .noBlackOut
+	call IncrementDayCareMonExp
+	ld a, [wStepCounter]
+	and $3 ; is the counter a multiple of 4?
+	jp nz, .noBlackOut ; only apply poison damage every fourth step
+	ld [wWhichPokemon], a
+	ld hl, wPartyMon1Status
+	ld de, wPartySpecies
+.applyDamageLoop
+	ld a, [hl]
+	and (1 << PSN)
+	jr z, .nextMon2 ; not poisoned
+	dec hl
+	dec hl
+	ld a, [hld]
+	ld b, a
+	ld a, [hli]
+	or b
+	jr z, .nextMon ; already fainted
+; subtract 1 from HP
+	ld a, [hl]
+	dec a
+	ld [hld], a
+	inc a
+	jr nz, .noBorrow
+; borrow 1 from upper byte of HP
+	dec [hl]
+	inc hl
+	jr .nextMon
+.noBorrow
+	ld a, [hli]
+	or [hl]
+	jr nz, .nextMon ; didn't faint from damage
+; the mon fainted from the damage
+	push hl
+	inc hl
+	inc hl
+	ld [hl], a
+	ld a, [de]
+	ld [wd11e], a
+	push de
+	ld a, [wWhichPokemon]
+	ld hl, wPartyMonNicks
+	call GetPartyMonName
+	xor a
+	ld [wJoyIgnore], a
+	call EnableAutoTextBoxDrawing
+	ld a, TEXT_MON_FAINTED
+	ld [hSpriteIndexOrTextID], a
+	call DisplayTextID
+	pop de
+	pop hl
+.nextMon
+	inc hl
+	inc hl
+.nextMon2
+	inc de
+	ld a, [de]
+	inc a
+	jr z, .applyDamageLoopDone
+	ld bc, wPartyMon2 - wPartyMon1
+	add hl, bc
+	push hl
+	ld hl, wWhichPokemon
+	inc [hl]
+	pop hl
+	jr .applyDamageLoop
+.applyDamageLoopDone
+	ld hl, wPartyMon1Status
+	ld a, [wPartyCount]
+	ld d, a
+	ld e, 0
+.countPoisonedLoop
+	ld a, [hl]
+	and (1 << PSN)
+	or e
+	ld e, a
+	ld bc, wPartyMon2 - wPartyMon1
+	add hl, bc
+	dec d
+	jr nz, .countPoisonedLoop
+	ld a, e
+	and a ; are any party members poisoned?
+	jr z, .skipPoisonEffectAndSound
+	ld b, $2
+	predef ChangeBGPalColor0_4Frames ; change BG white to dark grey for 4 frames
+	ld a, SFX_POISONED
+	call PlaySound
+.skipPoisonEffectAndSound
+	predef AnyPartyAlive
+	ld a, d
+	and a
+	jr nz, .noBlackOut
+	call EnableAutoTextBoxDrawing
+	ld a, TEXT_BLACKED_OUT
+	ld [hSpriteIndexOrTextID], a
+	call DisplayTextID
+	ld hl, wd72e
+	set 5, [hl]
+	ld a, $ff
+	jr .done
+.noBlackOut
+	xor a
+.done
+	ld [wOutOfBattleBlackout], a
+	ret
--- /dev/null
+++ b/engine/events/pokecenter.asm
@@ -1,0 +1,68 @@
+DisplayPokemonCenterDialogue_::
+	call SaveScreenTilesToBuffer1 ; save screen
+	ld hl, PokemonCenterWelcomeText
+	call PrintText
+	ld hl, wd72e
+	bit 2, [hl]
+	set 1, [hl]
+	set 2, [hl]
+	jr nz, .skipShallWeHealYourPokemon
+	ld hl, ShallWeHealYourPokemonText
+	call PrintText
+.skipShallWeHealYourPokemon
+	call YesNoChoicePokeCenter ; yes/no menu
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .declinedHealing ; if the player chose No
+	call SetLastBlackoutMap
+	call LoadScreenTilesFromBuffer1 ; restore screen
+	ld hl, NeedYourPokemonText
+	call PrintText
+	ld a, $18
+	ld [wSpriteStateData1 + $12], a ; make the nurse turn to face the machine
+	call Delay3
+	predef HealParty
+	callba AnimateHealingMachine ; do the healing machine animation
+	xor a
+	ld [wAudioFadeOutControl], a
+	ld a, [wAudioSavedROMBank]
+	ld [wAudioROMBank], a
+	ld a, [wMapMusicSoundID]
+	ld [wLastMusicSoundID], a
+	ld [wNewSoundID], a
+	call PlaySound
+	ld hl, PokemonFightingFitText
+	call PrintText
+	ld a, $14
+	ld [wSpriteStateData1 + $12], a ; make the nurse bow
+	ld c, a
+	call DelayFrames
+	jr .done
+.declinedHealing
+	call LoadScreenTilesFromBuffer1 ; restore screen
+.done
+	ld hl, PokemonCenterFarewellText
+	call PrintText
+	jp UpdateSprites
+
+PokemonCenterWelcomeText:
+	TX_FAR _PokemonCenterWelcomeText
+	db "@"
+
+ShallWeHealYourPokemonText:
+	TX_DELAY
+	TX_FAR _ShallWeHealYourPokemonText
+	db "@"
+
+NeedYourPokemonText:
+	TX_FAR _NeedYourPokemonText
+	db "@"
+
+PokemonFightingFitText:
+	TX_FAR _PokemonFightingFitText
+	db "@"
+
+PokemonCenterFarewellText:
+	TX_DELAY
+	TX_FAR _PokemonCenterFarewellText
+	db "@"
--- /dev/null
+++ b/engine/events/pokedex_rating.asm
@@ -1,0 +1,154 @@
+DisplayDexRating:
+	ld hl, wPokedexSeen
+	ld b, wPokedexSeenEnd - wPokedexSeen
+	call CountSetBits
+	ld a, [wNumSetBits]
+	ld [hDexRatingNumMonsSeen], a
+	ld hl, wPokedexOwned
+	ld b, wPokedexOwnedEnd - wPokedexOwned
+	call CountSetBits
+	ld a, [wNumSetBits]
+	ld [hDexRatingNumMonsOwned], a
+	ld hl, DexRatingsTable
+.findRating
+	ld a, [hli]
+	ld b, a
+	ld a, [hDexRatingNumMonsOwned]
+	cp b
+	jr c, .foundRating
+	inc hl
+	inc hl
+	jr .findRating
+.foundRating
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a ; load text pointer into hl
+	CheckAndResetEventA EVENT_HALL_OF_FAME_DEX_RATING
+	jr nz, .hallOfFame
+	push hl
+	ld hl, PokedexRatingText_441cc
+	call PrintText
+	pop hl
+	call PrintText
+	callba PlayPokedexRatingSfx
+	jp WaitForTextScrollButtonPress
+.hallOfFame
+	ld de, wDexRatingNumMonsSeen
+	ld a, [hDexRatingNumMonsSeen]
+	ld [de], a
+	inc de
+	ld a, [hDexRatingNumMonsOwned]
+	ld [de], a
+	inc de
+.copyRatingTextLoop
+	ld a, [hli]
+	cp "@"
+	jr z, .doneCopying
+	ld [de], a
+	inc de
+	jr .copyRatingTextLoop
+.doneCopying
+	ld [de], a
+	ret
+
+PokedexRatingText_441cc:
+	TX_FAR _OaksLabText_441cc
+	db "@"
+
+DexRatingsTable:
+	db 10
+	dw PokedexRatingText_44201
+	db 20
+	dw PokedexRatingText_44206
+	db 30
+	dw PokedexRatingText_4420b
+	db 40
+	dw PokedexRatingText_44210
+	db 50
+	dw PokedexRatingText_44215
+	db 60
+	dw PokedexRatingText_4421a
+	db 70
+	dw PokedexRatingText_4421f
+	db 80
+	dw PokedexRatingText_44224
+	db 90
+	dw PokedexRatingText_44229
+	db 100
+	dw PokedexRatingText_4422e
+	db 110
+	dw PokedexRatingText_44233
+	db 120
+	dw PokedexRatingText_44238
+	db 130
+	dw PokedexRatingText_4423d
+	db 140
+	dw PokedexRatingText_44242
+	db 150
+	dw PokedexRatingText_44247
+	db NUM_POKEMON + 1
+	dw PokedexRatingText_4424c
+
+PokedexRatingText_44201:
+	TX_FAR _OaksLabText_44201
+	db "@"
+
+PokedexRatingText_44206:
+	TX_FAR _OaksLabText_44206
+	db "@"
+
+PokedexRatingText_4420b:
+	TX_FAR _OaksLabText_4420b
+	db "@"
+
+PokedexRatingText_44210:
+	TX_FAR _OaksLabText_44210
+	db "@"
+
+PokedexRatingText_44215:
+	TX_FAR _OaksLabText_44215
+	db "@"
+
+PokedexRatingText_4421a:
+	TX_FAR _OaksLabText_4421a
+	db "@"
+
+PokedexRatingText_4421f:
+	TX_FAR _OaksLabText_4421f
+	db "@"
+
+PokedexRatingText_44224:
+	TX_FAR _OaksLabText_44224
+	db "@"
+
+PokedexRatingText_44229:
+	TX_FAR _OaksLabText_44229
+	db "@"
+
+PokedexRatingText_4422e:
+	TX_FAR _OaksLabText_4422e
+	db "@"
+
+PokedexRatingText_44233:
+	TX_FAR _OaksLabText_44233
+	db "@"
+
+PokedexRatingText_44238:
+	TX_FAR _OaksLabText_44238
+	db "@"
+
+PokedexRatingText_4423d:
+	TX_FAR _OaksLabText_4423d
+	db "@"
+
+PokedexRatingText_44242:
+	TX_FAR _OaksLabText_44242
+	db "@"
+
+PokedexRatingText_44247:
+	TX_FAR _OaksLabText_44247
+	db "@"
+
+PokedexRatingText_4424c:
+	TX_FAR _OaksLabText_4424c
+	db "@"
--- /dev/null
+++ b/engine/events/pokemart.asm
@@ -1,0 +1,272 @@
+DisplayPokemartDialogue_::
+	ld a, [wListScrollOffset]
+	ld [wSavedListScrollOffset], a
+	call UpdateSprites
+	xor a
+	ld [wBoughtOrSoldItemInMart], a
+.loop
+	xor a
+	ld [wListScrollOffset], a
+	ld [wCurrentMenuItem], a
+	ld [wPlayerMonNumber], a
+	inc a
+	ld [wPrintItemPrices], a
+	ld a, MONEY_BOX
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	ld a, BUY_SELL_QUIT_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+
+; This code is useless. It copies the address of the pokemart's inventory to hl,
+; but the address is never used.
+	ld hl, wItemListPointer
+	ld a, [hli]
+	ld l, [hl]
+	ld h, a
+
+	ld a, [wMenuExitMethod]
+	cp CANCELLED_MENU
+	jp z, .done
+	ld a, [wChosenMenuItem]
+	and a ; buying?
+	jp z, .buyMenu
+	dec a ; selling?
+	jp z, .sellMenu
+	dec a ; quitting?
+	jp z, .done
+.sellMenu
+
+; the same variables are set again below, so this code has no effect
+	xor a
+	ld [wPrintItemPrices], a
+	ld a, INIT_BAG_ITEM_LIST
+	ld [wInitListType], a
+	callab InitList
+
+	ld a, [wNumBagItems]
+	and a
+	jp z, .bagEmpty
+	ld hl, PokemonSellingGreetingText
+	call PrintText
+	call SaveScreenTilesToBuffer1 ; save screen
+.sellMenuLoop
+	call LoadScreenTilesFromBuffer1 ; restore saved screen
+	ld a, MONEY_BOX
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; draw money text box
+	ld hl, wNumBagItems
+	ld a, l
+	ld [wListPointer], a
+	ld a, h
+	ld [wListPointer + 1], a
+	xor a
+	ld [wPrintItemPrices], a
+	ld [wCurrentMenuItem], a
+	ld a, ITEMLISTMENU
+	ld [wListMenuID], a
+	call DisplayListMenuID
+	jp c, .returnToMainPokemartMenu ; if the player closed the menu
+.confirmItemSale ; if the player is trying to sell a specific item
+	call IsKeyItem
+	ld a, [wIsKeyItem]
+	and a
+	jr nz, .unsellableItem
+	ld a, [wcf91]
+	call IsItemHM
+	jr c, .unsellableItem
+	ld a, PRICEDITEMLISTMENU
+	ld [wListMenuID], a
+	ld [hHalveItemPrices], a ; halve prices when selling
+	call DisplayChooseQuantityMenu
+	inc a
+	jr z, .sellMenuLoop ; if the player closed the choose quantity menu with the B button
+	ld hl, PokemartTellSellPriceText
+	lb bc, 14, 1 ; location that PrintText always prints to, this is useless
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; yes/no menu
+	ld a, [wMenuExitMethod]
+	cp CHOSE_SECOND_ITEM
+	jr z, .sellMenuLoop ; if the player chose No or pressed the B button
+
+; The following code is supposed to check if the player chose No, but the above
+; check already catches it.
+	ld a, [wChosenMenuItem]
+	dec a
+	jr z, .sellMenuLoop
+
+.sellItem
+	ld a, [wBoughtOrSoldItemInMart]
+	and a
+	jr nz, .skipSettingFlag1
+	inc a
+	ld [wBoughtOrSoldItemInMart], a
+.skipSettingFlag1
+	call AddAmountSoldToMoney
+	ld hl, wNumBagItems
+	call RemoveItemFromInventory
+	jp .sellMenuLoop
+.unsellableItem
+	ld hl, PokemartUnsellableItemText
+	call PrintText
+	jp .returnToMainPokemartMenu
+.bagEmpty
+	ld hl, PokemartItemBagEmptyText
+	call PrintText
+	call SaveScreenTilesToBuffer1
+	jp .returnToMainPokemartMenu
+.buyMenu
+
+; the same variables are set again below, so this code has no effect
+	ld a, 1
+	ld [wPrintItemPrices], a
+	ld a, INIT_OTHER_ITEM_LIST
+	ld [wInitListType], a
+	callab InitList
+
+	ld hl, PokemartBuyingGreetingText
+	call PrintText
+	call SaveScreenTilesToBuffer1
+.buyMenuLoop
+	call LoadScreenTilesFromBuffer1
+	ld a, MONEY_BOX
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	ld hl, wItemList
+	ld a, l
+	ld [wListPointer], a
+	ld a, h
+	ld [wListPointer + 1], a
+	xor a
+	ld [wCurrentMenuItem], a
+	inc a
+	ld [wPrintItemPrices], a
+	inc a ; a = 2 (PRICEDITEMLISTMENU)
+	ld [wListMenuID], a
+	call DisplayListMenuID
+	jr c, .returnToMainPokemartMenu ; if the player closed the menu
+	ld a, 99
+	ld [wMaxItemQuantity], a
+	xor a
+	ld [hHalveItemPrices], a ; don't halve item prices when buying
+	call DisplayChooseQuantityMenu
+	inc a
+	jr z, .buyMenuLoop ; if the player closed the choose quantity menu with the B button
+	ld a, [wcf91] ; item ID
+	ld [wd11e], a ; store item ID for GetItemName
+	call GetItemName
+	call CopyStringToCF4B ; copy name to wcf4b
+	ld hl, PokemartTellBuyPriceText
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; yes/no menu
+	ld a, [wMenuExitMethod]
+	cp CHOSE_SECOND_ITEM
+	jp z, .buyMenuLoop ; if the player chose No or pressed the B button
+
+; The following code is supposed to check if the player chose No, but the above
+; check already catches it.
+	ld a, [wChosenMenuItem]
+	dec a
+	jr z, .buyMenuLoop
+
+.buyItem
+	call .isThereEnoughMoney
+	jr c, .notEnoughMoney
+	ld hl, wNumBagItems
+	call AddItemToInventory
+	jr nc, .bagFull
+	call SubtractAmountPaidFromMoney
+	ld a, [wBoughtOrSoldItemInMart]
+	and a
+	jr nz, .skipSettingFlag2
+	ld a, 1
+	ld [wBoughtOrSoldItemInMart], a
+.skipSettingFlag2
+	ld a, SFX_PURCHASE
+	call PlaySoundWaitForCurrent
+	call WaitForSoundToFinish
+	ld hl, PokemartBoughtItemText
+	call PrintText
+	jp .buyMenuLoop
+.returnToMainPokemartMenu
+	call LoadScreenTilesFromBuffer1
+	ld a, MONEY_BOX
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	ld hl, PokemartAnythingElseText
+	call PrintText
+	jp .loop
+.isThereEnoughMoney
+	ld de, wPlayerMoney
+	ld hl, hMoney
+	ld c, 3 ; length of money in bytes
+	jp StringCmp
+.notEnoughMoney
+	ld hl, PokemartNotEnoughMoneyText
+	call PrintText
+	jr .returnToMainPokemartMenu
+.bagFull
+	ld hl, PokemartItemBagFullText
+	call PrintText
+	jr .returnToMainPokemartMenu
+.done
+	ld hl, PokemartThankYouText
+	call PrintText
+	ld a, 1
+	ld [wUpdateSpritesEnabled], a
+	call UpdateSprites
+	ld a, [wSavedListScrollOffset]
+	ld [wListScrollOffset], a
+	ret
+
+PokemartBuyingGreetingText:
+	TX_FAR _PokemartBuyingGreetingText
+	db "@"
+
+PokemartTellBuyPriceText:
+	TX_FAR _PokemartTellBuyPriceText
+	db "@"
+
+PokemartBoughtItemText:
+	TX_FAR _PokemartBoughtItemText
+	db "@"
+
+PokemartNotEnoughMoneyText:
+	TX_FAR _PokemartNotEnoughMoneyText
+	db "@"
+
+PokemartItemBagFullText:
+	TX_FAR _PokemartItemBagFullText
+	db "@"
+
+PokemonSellingGreetingText:
+	TX_FAR _PokemonSellingGreetingText
+	db "@"
+
+PokemartTellSellPriceText:
+	TX_FAR _PokemartTellSellPriceText
+	db "@"
+
+PokemartItemBagEmptyText:
+	TX_FAR _PokemartItemBagEmptyText
+	db "@"
+
+PokemartUnsellableItemText:
+	TX_FAR _PokemartUnsellableItemText
+	db "@"
+
+PokemartThankYouText:
+	TX_FAR _PokemartThankYouText
+	db "@"
+
+PokemartAnythingElseText:
+	TX_FAR _PokemartAnythingElseText
+	db "@"
--- /dev/null
+++ b/engine/events/prize_menu.asm
@@ -1,0 +1,306 @@
+CeladonPrizeMenu::
+	ld b, COIN_CASE
+	call IsItemInBag
+	jr nz, .havingCoinCase
+	ld hl, RequireCoinCaseTextPtr
+	jp PrintText
+.havingCoinCase
+	ld hl, wd730
+	set 6, [hl] ; disable letter-printing delay
+	ld hl, ExchangeCoinsForPrizesTextPtr
+	call PrintText
+; the following are the menu settings
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, $03
+	ld [wMaxMenuItem], a
+	ld a, $04
+	ld [wTopMenuItemY], a
+	ld a, $01
+	ld [wTopMenuItemX], a
+	call PrintPrizePrice
+	coord hl, 0, 2
+	ld b, 8
+	ld c, 16
+	call TextBoxBorder
+	call GetPrizeMenuId
+	call UpdateSprites
+	ld hl, WhichPrizeTextPtr
+	call PrintText
+	call HandleMenuInput ; menu choice handler
+	bit 1, a ; keypress = B (Cancel)
+	jr nz, .noChoice
+	ld a, [wCurrentMenuItem]
+	cp 3 ; "NO,THANKS" choice
+	jr z, .noChoice
+	call HandlePrizeChoice
+.noChoice
+	ld hl, wd730
+	res 6, [hl]
+	ret
+
+RequireCoinCaseTextPtr:
+	TX_FAR _RequireCoinCaseText
+	TX_WAIT
+	db "@"
+
+ExchangeCoinsForPrizesTextPtr:
+	TX_FAR _ExchangeCoinsForPrizesText
+	db "@"
+
+WhichPrizeTextPtr:
+	TX_FAR _WhichPrizeText
+	db "@"
+
+GetPrizeMenuId:
+; determine which one among the three
+; prize-texts has been selected
+; using the text ID (stored in [hSpriteIndexOrTextID])
+; load the three prizes at wd13d-wd13f
+; load the three prices at wd141-wd146
+; display the three prizes' names
+; (distinguishing between Pokemon names
+; and Items (specifically TMs) names)
+	ld a, [hSpriteIndexOrTextID]
+	sub 3       ; prize-texts' id are 3, 4 and 5
+	ld [wWhichPrizeWindow], a    ; prize-texts' id (relative, i.e. 0, 1 or 2)
+	add a
+	add a
+	ld d, 0
+	ld e, a
+	ld hl, PrizeDifferentMenuPtrs
+	add hl, de
+	ld a, [hli]
+	ld d, [hl]
+	ld e, a
+	inc hl
+	push hl
+	ld hl, wPrize1
+	call CopyString
+	pop hl
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld de, wPrize1Price
+	ld bc, 6
+	call CopyData
+	ld a, [wWhichPrizeWindow]
+	cp 2        ;is TM_menu?
+	jr nz, .putMonName
+	ld a, [wPrize1]
+	ld [wd11e], a
+	call GetItemName
+	coord hl, 2, 4
+	call PlaceString
+	ld a, [wPrize2]
+	ld [wd11e], a
+	call GetItemName
+	coord hl, 2, 6
+	call PlaceString
+	ld a, [wPrize3]
+	ld [wd11e], a
+	call GetItemName
+	coord hl, 2, 8
+	call PlaceString
+	jr .putNoThanksText
+.putMonName
+	ld a, [wPrize1]
+	ld [wd11e], a
+	call GetMonName
+	coord hl, 2, 4
+	call PlaceString
+	ld a, [wPrize2]
+	ld [wd11e], a
+	call GetMonName
+	coord hl, 2, 6
+	call PlaceString
+	ld a, [wPrize3]
+	ld [wd11e], a
+	call GetMonName
+	coord hl, 2, 8
+	call PlaceString
+.putNoThanksText
+	coord hl, 2, 10
+	ld de, NoThanksText
+	call PlaceString
+; put prices on the right side of the textbox
+	ld de, wPrize1Price
+	coord hl, 13, 5
+; reg. c:
+; [low nybble] number of bytes
+; [bit 765 = %100] space-padding (not zero-padding)
+	ld c, (1 << 7 | 2)
+; Function $15CD displays BCD value (same routine
+; used by text-command $02)
+	call PrintBCDNumber
+	ld de, wPrize2Price
+	coord hl, 13, 7
+	ld c, (1 << 7 | 2)
+	call PrintBCDNumber
+	ld de, wPrize3Price
+	coord hl, 13, 9
+	ld c, (1 << 7 | 2)
+	jp PrintBCDNumber
+
+INCLUDE "data/prizes.asm"
+
+PrintPrizePrice:
+	coord hl, 11, 0
+	ld b, 1
+	ld c, 7
+	call TextBoxBorder
+	call UpdateSprites
+	coord hl, 12, 0
+	ld de, .CoinString
+	call PlaceString
+	coord hl, 13, 1
+	ld de, .SixSpacesString
+	call PlaceString
+	coord hl, 13, 1
+	ld de, wPlayerCoins
+	ld c, %10000010
+	call PrintBCDNumber
+	ret
+
+.CoinString:
+	db "COIN@"
+
+.SixSpacesString:
+	db "      @"
+
+LoadCoinsToSubtract:
+	ld a, [wWhichPrize]
+	add a
+	ld d, 0
+	ld e, a
+	ld hl, wPrize1Price
+	add hl, de ; get selected prize's price
+	xor a
+	ld [hUnusedCoinsByte], a
+	ld a, [hli]
+	ld [hCoins], a
+	ld a, [hl]
+	ld [hCoins + 1], a
+	ret
+
+HandlePrizeChoice:
+	ld a, [wCurrentMenuItem]
+	ld [wWhichPrize], a
+	ld d, 0
+	ld e, a
+	ld hl, wPrize1
+	add hl, de
+	ld a, [hl]
+	ld [wd11e], a
+	ld a, [wWhichPrizeWindow]
+	cp 2 ; is prize a TM?
+	jr nz, .getMonName
+	call GetItemName
+	jr .givePrize
+.getMonName
+	call GetMonName
+.givePrize
+	ld hl, SoYouWantPrizeTextPtr
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem] ; yes/no answer (Y=0, N=1)
+	and a
+	jr nz, .printOhFineThen
+	call LoadCoinsToSubtract
+	call HasEnoughCoins
+	jr c, .notEnoughCoins
+	ld a, [wWhichPrizeWindow]
+	cp $02
+	jr nz, .giveMon
+	ld a, [wd11e]
+	ld b, a
+	ld a, 1
+	ld c, a
+	call GiveItem
+	jr nc, .bagFull
+	jr .subtractCoins
+.giveMon
+	ld a, [wd11e]
+	ld [wcf91], a
+	push af
+	call GetPrizeMonLevel
+	ld c, a
+	pop af
+	ld b, a
+	call GivePokemon
+
+; If either the party or box was full, wait after displaying message.
+	push af
+	ld a, [wAddedToParty]
+	and a
+	call z, WaitForTextScrollButtonPress
+	pop af
+
+; If the mon couldn't be given to the player (because both the party and box
+; were full), return without subtracting coins.
+	ret nc
+
+.subtractCoins
+	call LoadCoinsToSubtract
+	ld hl, hCoins + 1
+	ld de, wPlayerCoins + 1
+	ld c, $02 ; how many bytes
+	predef SubBCDPredef
+	jp PrintPrizePrice
+.bagFull
+	ld hl, PrizeRoomBagIsFullTextPtr
+	jp PrintText
+.notEnoughCoins
+	ld hl, SorryNeedMoreCoinsText
+	jp PrintText
+.printOhFineThen
+	ld hl, OhFineThenTextPtr
+	jp PrintText
+
+UnknownPrizeData:
+; XXX what's this?
+	db $00,$01,$00,$01,$00,$01,$00,$00,$01
+
+HereYouGoTextPtr:
+	TX_FAR _HereYouGoText
+	TX_WAIT
+	db "@"
+
+SoYouWantPrizeTextPtr:
+	TX_FAR _SoYouWantPrizeText
+	db "@"
+
+SorryNeedMoreCoinsText:
+	TX_FAR _SorryNeedMoreCoinsText
+	TX_WAIT
+	db "@"
+
+PrizeRoomBagIsFullTextPtr:
+	TX_FAR _OopsYouDontHaveEnoughRoomText
+	TX_WAIT
+	db "@"
+
+OhFineThenTextPtr:
+	TX_FAR _OhFineThenText
+	TX_WAIT
+	db "@"
+
+GetPrizeMonLevel:
+	ld a, [wcf91]
+	ld b, a
+	ld hl, PrizeMonLevelDictionary
+.loop
+	ld a, [hli]
+	cp b
+	jr z, .matchFound
+	inc hl
+	jr .loop
+.matchFound
+	ld a, [hl]
+	ld [wCurEnemyLVL], a
+	ret
+
+INCLUDE "data/prize_mon_levels.asm"
--- /dev/null
+++ b/engine/events/saffron_guards.asm
@@ -1,0 +1,15 @@
+RemoveGuardDrink::
+	ld hl, GuardDrinksList
+.drinkLoop
+	ld a, [hli]
+	ld [$ffdb], a
+	and a
+	ret z
+	push hl
+	ld b, a
+	call IsItemInBag
+	pop hl
+	jr z, .drinkLoop
+	jpba RemoveItemByID
+
+INCLUDE "data/guard_drink_items.asm"
--- /dev/null
+++ b/engine/events/set_blackout_map.asm
@@ -1,0 +1,25 @@
+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
+
+INCLUDE "data/rest_house_maps.asm"
--- /dev/null
+++ b/engine/events/starter_dex.asm
@@ -1,0 +1,9 @@
+; this function temporarily makes the starters (and Ivysaur) seen
+; so that the full Pokedex information gets displayed in Oak's lab
+StarterDex:
+	ld a, %01001011 ; set starter flags
+	ld [wPokedexOwned], a
+	predef ShowPokedexData
+	xor a ; unset starter flags
+	ld [wPokedexOwned], a
+	ret
--- /dev/null
+++ b/engine/events/vending_machine.asm
@@ -1,0 +1,133 @@
+VendingMachineMenu::
+	ld hl, VendingMachineText1
+	call PrintText
+	ld a, MONEY_BOX
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 3
+	ld [wMaxMenuItem], a
+	ld a, 5
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+	ld hl, wd730
+	set 6, [hl]
+	coord hl, 0, 3
+	ld b, 8
+	ld c, 12
+	call TextBoxBorder
+	call UpdateSprites
+	coord hl, 2, 5
+	ld de, DrinkText
+	call PlaceString
+	coord hl, 9, 6
+	ld de, DrinkPriceText
+	call PlaceString
+	ld hl, wd730
+	res 6, [hl]
+	call HandleMenuInput
+	bit 1, a ; pressed B?
+	jr nz, .notThirsty
+	ld a, [wCurrentMenuItem]
+	cp 3 ; chose Cancel?
+	jr z, .notThirsty
+	xor a
+	ld [hMoney], a
+	ld [hMoney + 2], a
+	ld a, $2
+	ld [hMoney + 1], a
+	call HasEnoughMoney
+	jr nc, .enoughMoney
+	ld hl, VendingMachineText4
+	jp PrintText
+.enoughMoney
+	call LoadVendingMachineItem
+	ld a, [hVendingMachineItem]
+	ld b, a
+	ld c, 1
+	call GiveItem
+	jr nc, .BagFull
+
+	ld b, 60 ; number of times to play the "brrrrr" sound
+.playDeliverySound
+	ld c, 2
+	call DelayFrames
+	push bc
+	ld a, SFX_PUSH_BOULDER
+	call PlaySound
+	pop bc
+	dec b
+	jr nz, .playDeliverySound
+
+	ld hl, VendingMachineText5
+	call PrintText
+	ld hl, hVendingMachinePrice + 2
+	ld de, wPlayerMoney + 2
+	ld c, $3
+	predef SubBCDPredef
+	ld a, MONEY_BOX
+	ld [wTextBoxID], a
+	jp DisplayTextBoxID
+.BagFull
+	ld hl, VendingMachineText6
+	jp PrintText
+.notThirsty
+	ld hl, VendingMachineText7
+	jp PrintText
+
+VendingMachineText1:
+	TX_FAR _VendingMachineText1
+	db "@"
+
+DrinkText:
+	db   "FRESH WATER"
+	next "SODA POP"
+	next "LEMONADE"
+	next "CANCEL@"
+
+DrinkPriceText:
+	db   "¥200"
+	next "¥300"
+	next "¥350"
+	next "@"
+
+VendingMachineText4:
+	TX_FAR _VendingMachineText4
+	db "@"
+
+VendingMachineText5:
+	TX_FAR _VendingMachineText5
+	db "@"
+
+VendingMachineText6:
+	TX_FAR _VendingMachineText6
+	db "@"
+
+VendingMachineText7:
+	TX_FAR _VendingMachineText7
+	db "@"
+
+LoadVendingMachineItem:
+	ld hl, VendingPrices
+	ld a, [wCurrentMenuItem]
+	add a
+	add a
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hli]
+	ld [hVendingMachineItem], a
+	ld a, [hli]
+	ld [hVendingMachinePrice], a
+	ld a, [hli]
+	ld [hVendingMachinePrice + 1], a
+	ld a, [hl]
+	ld [hVendingMachinePrice + 2], a
+	ret
+
+INCLUDE "data/vending_prices.asm"
--- a/engine/evolution.asm
+++ /dev/null
@@ -1,160 +1,0 @@
-EvolveMon:
-	push hl
-	push de
-	push bc
-	ld a, [wcf91]
-	push af
-	ld a, [wd0b5]
-	push af
-	xor a
-	ld [wLowHealthAlarm], a
-	ld [wChannelSoundIDs + Ch5], a
-	dec a
-	ld [wNewSoundID], a
-	call PlaySound
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, SFX_TINK
-	call PlaySound
-	call Delay3
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld [hTilesetType], a
-	ld a, [wEvoOldSpecies]
-	ld [wWholeScreenPaletteMonSpecies], a
-	ld c, 0
-	call EvolutionSetWholeScreenPalette
-	ld a, [wEvoNewSpecies]
-	ld [wcf91], a
-	ld [wd0b5], a
-	call Evolution_LoadPic
-	ld de, vFrontPic
-	ld hl, vBackPic
-	ld bc, 7 * 7
-	call CopyVideoData
-	ld a, [wEvoOldSpecies]
-	ld [wcf91], a
-	ld [wd0b5], a
-	call Evolution_LoadPic
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, [wEvoOldSpecies]
-	call PlayCry
-	call WaitForSoundToFinish
-	ld c, BANK(Music_SafariZone)
-	ld a, MUSIC_SAFARI_ZONE
-	call PlayMusic
-	ld c, 80
-	call DelayFrames
-	ld c, 1 ; set PAL_BLACK instead of mon palette
-	call EvolutionSetWholeScreenPalette
-	lb bc, $1, $10
-.animLoop
-	push bc
-	call Evolution_CheckForCancel
-	jr c, .evolutionCancelled
-	call Evolution_BackAndForthAnim
-	pop bc
-	inc b
-	dec c
-	dec c
-	jr nz, .animLoop
-	xor a
-	ld [wEvoCancelled], a
-	ld a, $31
-	ld [wEvoMonTileOffset], a
-	call Evolution_ChangeMonPic ; show the new species pic
-	ld a, [wEvoNewSpecies]
-.done
-	ld [wWholeScreenPaletteMonSpecies], a
-	ld a, $ff
-	ld [wNewSoundID], a
-	call PlaySound
-	ld a, [wWholeScreenPaletteMonSpecies]
-	call PlayCry
-	ld c, 0
-	call EvolutionSetWholeScreenPalette
-	pop af
-	ld [wd0b5], a
-	pop af
-	ld [wcf91], a
-	pop bc
-	pop de
-	pop hl
-	ld a, [wEvoCancelled]
-	and a
-	ret z
-	scf
-	ret
-.evolutionCancelled
-	pop bc
-	ld a, 1
-	ld [wEvoCancelled], a
-	ld a, [wEvoOldSpecies]
-	jr .done
-
-EvolutionSetWholeScreenPalette:
-	ld b, SET_PAL_POKEMON_WHOLE_SCREEN
-	jp RunPaletteCommand
-
-Evolution_LoadPic:
-	call GetMonHeader
-	coord hl, 7, 2
-	jp LoadFlippedFrontSpriteByMonIndex
-
-Evolution_BackAndForthAnim:
-; show the mon change back and forth between the new and old species b times
-	ld a, $31
-	ld [wEvoMonTileOffset], a
-	call Evolution_ChangeMonPic
-	ld a, -$31
-	ld [wEvoMonTileOffset], a
-	call Evolution_ChangeMonPic
-	dec b
-	jr nz, Evolution_BackAndForthAnim
-	ret
-
-Evolution_ChangeMonPic:
-	push bc
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	coord hl, 7, 2
-	lb bc, 7, 7
-	ld de, SCREEN_WIDTH - 7
-.loop
-	push bc
-.innerLoop
-	ld a, [wEvoMonTileOffset]
-	add [hl]
-	ld [hli], a
-	dec c
-	jr nz, .innerLoop
-	pop bc
-	add hl, de
-	dec b
-	jr nz, .loop
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Delay3
-	pop bc
-	ret
-
-Evolution_CheckForCancel:
-	call DelayFrame
-	push bc
-	call JoypadLowSensitivity
-	ld a, [hJoy5]
-	pop bc
-	and B_BUTTON
-	jr nz, .pressedB
-.notAllowedToCancel
-	dec c
-	jr nz, Evolution_CheckForCancel
-	and a
-	ret
-.pressedB
-	ld a, [wForceEvolution]
-	and a
-	jr nz, .notAllowedToCancel
-	scf
-	ret
--- a/engine/evolve_trade.asm
+++ /dev/null
@@ -1,44 +1,0 @@
-EvolveTradeMon:
-; Verify the TradeMon's species name before
-; attempting to initiate a trade evolution.
-
-; The names of the trade evolutions in Blue (JP)
-; are checked. In that version, TradeMons that
-; can evolve are Graveler and Haunter.
-
-; In localization, this check was translated
-; before monster names were finalized.
-; Then, Haunter's name was "Spectre".
-; Since its name no longer starts with
-; "SP", it is prevented from evolving.
-
-; This may have been why Red/Green's trades
-; were used instead, where none can evolve.
-
-; This was fixed in Yellow.
-
-	ld a, [wInGameTradeReceiveMonName]
-
-	; GRAVELER
-	cp "G"
-	jr z, .ok
-
-	; "SPECTRE" (HAUNTER)
-	cp "S"
-	ret nz
-	ld a, [wInGameTradeReceiveMonName + 1]
-	cp "P"
-	ret nz
-
-.ok
-	ld a, [wPartyCount]
-	dec a
-	ld [wWhichPokemon], a
-	ld a, $1
-	ld [wForceEvolution], a
-	ld a, LINK_STATE_TRADING
-	ld [wLinkState], a
-	callab TryEvolvingMon
-	xor a ; LINK_STATE_NONE
-	ld [wLinkState], a
-	jp PlayDefaultMusic
--- a/engine/evos_moves.asm
+++ /dev/null
@@ -1,513 +1,0 @@
-; try to evolve the mon in [wWhichPokemon]
-TryEvolvingMon:
-	ld hl, wCanEvolveFlags
-	xor a
-	ld [hl], a
-	ld a, [wWhichPokemon]
-	ld c, a
-	ld b, FLAG_SET
-	call Evolution_FlagAction
-
-; this is only called after battle
-; it is supposed to do level up evolutions, though there is a bug that allows item evolutions to occur
-EvolutionAfterBattle:
-	ld a, [hTilesetType]
-	push af
-	xor a
-	ld [wEvolutionOccurred], a
-	dec a
-	ld [wWhichPokemon], a
-	push hl
-	push bc
-	push de
-	ld hl, wPartyCount
-	push hl
-
-Evolution_PartyMonLoop: ; loop over party mons
-	ld hl, wWhichPokemon
-	inc [hl]
-	pop hl
-	inc hl
-	ld a, [hl]
-	cp $ff ; have we reached the end of the party?
-	jp z, .done
-	ld [wEvoOldSpecies], a
-	push hl
-	ld a, [wWhichPokemon]
-	ld c, a
-	ld hl, wCanEvolveFlags
-	ld b, FLAG_TEST
-	call Evolution_FlagAction
-	ld a, c
-	and a ; is the mon's bit set?
-	jp z, Evolution_PartyMonLoop ; if not, go to the next mon
-	ld a, [wEvoOldSpecies]
-	dec a
-	ld b, 0
-	ld hl, EvosMovesPointerTable
-	add a
-	rl b
-	ld c, a
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	push hl
-	ld a, [wcf91]
-	push af
-	xor a ; PLAYER_PARTY_DATA
-	ld [wMonDataLocation], a
-	call LoadMonData
-	pop af
-	ld [wcf91], a
-	pop hl
-
-.evoEntryLoop ; loop over evolution entries
-	ld a, [hli]
-	and a ; have we reached the end of the evolution data?
-	jr z, Evolution_PartyMonLoop
-	ld b, a ; evolution type
-	cp EV_TRADE
-	jr z, .checkTradeEvo
-; not trade evolution
-	ld a, [wLinkState]
-	cp LINK_STATE_TRADING
-	jr z, Evolution_PartyMonLoop ; if trading, go the next mon
-	ld a, b
-	cp EV_ITEM
-	jr z, .checkItemEvo
-	ld a, [wForceEvolution]
-	and a
-	jr nz, Evolution_PartyMonLoop
-	ld a, b
-	cp EV_LEVEL
-	jr z, .checkLevel
-.checkTradeEvo
-	ld a, [wLinkState]
-	cp LINK_STATE_TRADING
-	jp nz, .nextEvoEntry1 ; if not trading, go to the next evolution entry
-	ld a, [hli] ; level requirement
-	ld b, a
-	ld a, [wLoadedMonLevel]
-	cp b ; is the mon's level greater than the evolution requirement?
-	jp c, Evolution_PartyMonLoop ; if so, go the next mon
-	jr .doEvolution
-.checkItemEvo
-	ld a, [hli]
-	ld b, a ; evolution item
-	ld a, [wcf91] ; this is supposed to be the last item used, but it is also used to hold species numbers
-	cp b ; was the evolution item in this entry used?
-	jp nz, .nextEvoEntry1 ; if not, go to the next evolution entry
-.checkLevel
-	ld a, [hli] ; level requirement
-	ld b, a
-	ld a, [wLoadedMonLevel]
-	cp b ; is the mon's level greater than the evolution requirement?
-	jp c, .nextEvoEntry2 ; if so, go the next evolution entry
-.doEvolution
-	ld [wCurEnemyLVL], a
-	ld a, 1
-	ld [wEvolutionOccurred], a
-	push hl
-	ld a, [hl]
-	ld [wEvoNewSpecies], a
-	ld a, [wWhichPokemon]
-	ld hl, wPartyMonNicks
-	call GetPartyMonName
-	call CopyStringToCF4B
-	ld hl, IsEvolvingText
-	call PrintText
-	ld c, 50
-	call DelayFrames
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	coord hl, 0, 0
-	lb bc, 12, 20
-	call ClearScreenArea
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, $ff
-	ld [wUpdateSpritesEnabled], a
-	call ClearSprites
-	callab EvolveMon
-	jp c, CancelledEvolution
-	ld hl, EvolvedText
-	call PrintText
-	pop hl
-	ld a, [hl]
-	ld [wd0b5], a
-	ld [wLoadedMonSpecies], a
-	ld [wEvoNewSpecies], a
-	ld a, MONSTER_NAME
-	ld [wNameListType], a
-	ld a, BANK(TrainerNames) ; bank is not used for monster names
-	ld [wPredefBank], a
-	call GetName
-	push hl
-	ld hl, IntoText
-	call PrintText_NoCreatingTextBox
-	ld a, SFX_GET_ITEM_2
-	call PlaySoundWaitForCurrent
-	call WaitForSoundToFinish
-	ld c, 40
-	call DelayFrames
-	call ClearScreen
-	call RenameEvolvedMon
-	ld a, [wd11e]
-	push af
-	ld a, [wd0b5]
-	ld [wd11e], a
-	predef IndexToPokedex
-	ld a, [wd11e]
-	dec a
-	ld hl, BaseStats
-	ld bc, MonBaseStatsEnd - MonBaseStats
-	call AddNTimes
-	ld de, wMonHeader
-	call CopyData
-	ld a, [wd0b5]
-	ld [wMonHIndex], a
-	pop af
-	ld [wd11e], a
-	ld hl, wLoadedMonHPExp - 1
-	ld de, wLoadedMonStats
-	ld b, $1
-	call CalcStats
-	ld a, [wWhichPokemon]
-	ld hl, wPartyMon1
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld e, l
-	ld d, h
-	push hl
-	push bc
-	ld bc, wPartyMon1MaxHP - wPartyMon1
-	add hl, bc
-	ld a, [hli]
-	ld b, a
-	ld c, [hl]
-	ld hl, wLoadedMonMaxHP + 1
-	ld a, [hld]
-	sub c
-	ld c, a
-	ld a, [hl]
-	sbc b
-	ld b, a
-	ld hl, wLoadedMonHP + 1
-	ld a, [hl]
-	add c
-	ld [hld], a
-	ld a, [hl]
-	adc b
-	ld [hl], a
-	dec hl
-	pop bc
-	call CopyData
-	ld a, [wd0b5]
-	ld [wd11e], a
-	xor a
-	ld [wMonDataLocation], a
-	call LearnMoveFromLevelUp
-	pop hl
-	predef SetPartyMonTypes
-	ld a, [wIsInBattle]
-	and a
-	call z, Evolution_ReloadTilesetTilePatterns
-	predef IndexToPokedex
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_SET
-	ld hl, wPokedexOwned
-	push bc
-	call Evolution_FlagAction
-	pop bc
-	ld hl, wPokedexSeen
-	call Evolution_FlagAction
-	pop de
-	pop hl
-	ld a, [wLoadedMonSpecies]
-	ld [hl], a
-	push hl
-	ld l, e
-	ld h, d
-	jr .nextEvoEntry2
-
-.nextEvoEntry1
-	inc hl
-
-.nextEvoEntry2
-	inc hl
-	jp .evoEntryLoop
-
-.done
-	pop de
-	pop bc
-	pop hl
-	pop af
-	ld [hTilesetType], a
-	ld a, [wLinkState]
-	cp LINK_STATE_TRADING
-	ret z
-	ld a, [wIsInBattle]
-	and a
-	ret nz
-	ld a, [wEvolutionOccurred]
-	and a
-	call nz, PlayDefaultMusic
-	ret
-
-RenameEvolvedMon:
-; Renames the mon to its new, evolved form's standard name unless it had a
-; nickname, in which case the nickname is kept.
-	ld a, [wd0b5]
-	push af
-	ld a, [wMonHIndex]
-	ld [wd0b5], a
-	call GetName
-	pop af
-	ld [wd0b5], a
-	ld hl, wcd6d
-	ld de, wcf4b
-.compareNamesLoop
-	ld a, [de]
-	inc de
-	cp [hl]
-	inc hl
-	ret nz
-	cp "@"
-	jr nz, .compareNamesLoop
-	ld a, [wWhichPokemon]
-	ld bc, NAME_LENGTH
-	ld hl, wPartyMonNicks
-	call AddNTimes
-	push hl
-	call GetName
-	ld hl, wcd6d
-	pop de
-	jp CopyData
-
-CancelledEvolution:
-	ld hl, StoppedEvolvingText
-	call PrintText
-	call ClearScreen
-	pop hl
-	call Evolution_ReloadTilesetTilePatterns
-	jp Evolution_PartyMonLoop
-
-EvolvedText:
-	TX_FAR _EvolvedText
-	db "@"
-
-IntoText:
-	TX_FAR _IntoText
-	db "@"
-
-StoppedEvolvingText:
-	TX_FAR _StoppedEvolvingText
-	db "@"
-
-IsEvolvingText:
-	TX_FAR _IsEvolvingText
-	db "@"
-
-Evolution_ReloadTilesetTilePatterns:
-	ld a, [wLinkState]
-	cp LINK_STATE_TRADING
-	ret z
-	jp ReloadTilesetTilePatterns
-
-LearnMoveFromLevelUp:
-	ld hl, EvosMovesPointerTable
-	ld a, [wd11e] ; species
-	ld [wcf91], a
-	dec a
-	ld bc, 0
-	ld hl, EvosMovesPointerTable
-	add a
-	rl b
-	ld c, a
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-.skipEvolutionDataLoop ; loop to skip past the evolution data, which comes before the move data
-	ld a, [hli]
-	and a ; have we reached the end of the evolution data?
-	jr nz, .skipEvolutionDataLoop ; if not, jump back up
-.learnSetLoop ; loop over the learn set until we reach a move that is learnt at the current level or the end of the list
-	ld a, [hli]
-	and a ; have we reached the end of the learn set?
-	jr z, .done ; if we've reached the end of the learn set, jump
-	ld b, a ; level the move is learnt at
-	ld a, [wCurEnemyLVL]
-	cp b ; is the move learnt at the mon's current level?
-	ld a, [hli] ; move ID
-	jr nz, .learnSetLoop
-	ld d, a ; ID of move to learn
-	ld a, [wMonDataLocation]
-	and a
-	jr nz, .next
-; If [wMonDataLocation] is 0 (PLAYER_PARTY_DATA), get the address of the mon's
-; current moves in party data. Every call to this function sets
-; [wMonDataLocation] to 0 because other data locations are not supported.
-; If it is not 0, this function will not work properly.
-	ld hl, wPartyMon1Moves
-	ld a, [wWhichPokemon]
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-.next
-	ld b, NUM_MOVES
-.checkCurrentMovesLoop ; check if the move to learn is already known
-	ld a, [hli]
-	cp d
-	jr z, .done ; if already known, jump
-	dec b
-	jr nz, .checkCurrentMovesLoop
-	ld a, d
-	ld [wMoveNum], a
-	ld [wd11e], a
-	call GetMoveName
-	call CopyStringToCF4B
-	predef LearnMove
-.done
-	ld a, [wcf91]
-	ld [wd11e], a
-	ret
-
-; writes the moves a mon has at level [wCurEnemyLVL] to [de]
-; move slots are being filled up sequentially and shifted if all slots are full
-WriteMonMoves:
-	call GetPredefRegisters
-	push hl
-	push de
-	push bc
-	ld hl, EvosMovesPointerTable
-	ld b, 0
-	ld a, [wcf91]  ; cur mon ID
-	dec a
-	add a
-	rl b
-	ld c, a
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-.skipEvoEntriesLoop
-	ld a, [hli]
-	and a
-	jr nz, .skipEvoEntriesLoop
-	jr .firstMove
-.nextMove
-	pop de
-.nextMove2
-	inc hl
-.firstMove
-	ld a, [hli]       ; read level of next move in learnset
-	and a
-	jp z, .done       ; end of list
-	ld b, a
-	ld a, [wCurEnemyLVL]
-	cp b
-	jp c, .done       ; mon level < move level (assumption: learnset is sorted by level)
-	ld a, [wLearningMovesFromDayCare]
-	and a
-	jr z, .skipMinLevelCheck
-	ld a, [wDayCareStartLevel]
-	cp b
-	jr nc, .nextMove2 ; min level >= move level
-
-.skipMinLevelCheck
-
-; check if the move is already known
-	push de
-	ld c, NUM_MOVES
-.alreadyKnowsCheckLoop
-	ld a, [de]
-	inc de
-	cp [hl]
-	jr z, .nextMove
-	dec c
-	jr nz, .alreadyKnowsCheckLoop
-
-; try to find an empty move slot
-	pop de
-	push de
-	ld c, NUM_MOVES
-.findEmptySlotLoop
-	ld a, [de]
-	and a
-	jr z, .writeMoveToSlot2
-	inc de
-	dec c
-	jr nz, .findEmptySlotLoop
-
-; no empty move slots found
-	pop de
-	push de
-	push hl
-	ld h, d
-	ld l, e
-	call WriteMonMoves_ShiftMoveData ; shift all moves one up (deleting move 1)
-	ld a, [wLearningMovesFromDayCare]
-	and a
-	jr z, .writeMoveToSlot
-
-; shift PP as well if learning moves from day care
-	push de
-	ld bc, wPartyMon1PP - (wPartyMon1Moves + 3)
-	add hl, bc
-	ld d, h
-	ld e, l
-	call WriteMonMoves_ShiftMoveData ; shift all move PP data one up
-	pop de
-
-.writeMoveToSlot
-	pop hl
-.writeMoveToSlot2
-	ld a, [hl]
-	ld [de], a
-	ld a, [wLearningMovesFromDayCare]
-	and a
-	jr z, .nextMove
-
-; write move PP value if learning moves from day care
-	push hl
-	ld a, [hl]
-	ld hl, wPartyMon1PP - wPartyMon1Moves
-	add hl, de
-	push hl
-	dec a
-	ld hl, Moves
-	ld bc, MoveEnd - Moves
-	call AddNTimes
-	ld de, wBuffer
-	ld a, BANK(Moves)
-	call FarCopyData
-	ld a, [wBuffer + 5]
-	pop hl
-	ld [hl], a
-	pop hl
-	jr .nextMove
-
-.done
-	pop bc
-	pop de
-	pop hl
-	ret
-
-; shifts all move data one up (freeing 4th move slot)
-WriteMonMoves_ShiftMoveData:
-	ld c, NUM_MOVES - 1
-.loop
-	inc de
-	ld a, [de]
-	ld [hli], a
-	dec c
-	jr nz, .loop
-	ret
-
-Evolution_FlagAction:
-	predef_jump FlagActionPredef
-
-INCLUDE "data/evos_moves.asm"
--- a/engine/experience.asm
+++ /dev/null
@@ -1,149 +1,0 @@
-; calculates the level a mon should be based on its current exp
-CalcLevelFromExperience::
-	ld a, [wLoadedMonSpecies]
-	ld [wd0b5], a
-	call GetMonHeader
-	ld d, $1 ; init level to 1
-.loop
-	inc d ; increment level
-	call CalcExperience
-	push hl
-	ld hl, wLoadedMonExp + 2 ; current exp
-; compare exp needed for level d with current exp
-	ld a, [hExperience + 2]
-	ld c, a
-	ld a, [hld]
-	sub c
-	ld a, [hExperience + 1]
-	ld c, a
-	ld a, [hld]
-	sbc c
-	ld a, [hExperience]
-	ld c, a
-	ld a, [hl]
-	sbc c
-	pop hl
-	jr nc, .loop ; if exp needed for level d is not greater than exp, try the next level
-	dec d ; since the exp was too high on the last loop iteration, go back to the previous value and return
-	ret
-
-; calculates the amount of experience needed for level d
-CalcExperience::
-	ld a, [wMonHGrowthRate]
-	add a
-	add a
-	ld c, a
-	ld b, 0
-	ld hl, GrowthRateTable
-	add hl, bc
-	call CalcDSquared
-	ld a, d
-	ld [H_MULTIPLIER], a
-	call Multiply
-	ld a, [hl]
-	and $f0
-	swap a
-	ld [H_MULTIPLIER], a
-	call Multiply
-	ld a, [hli]
-	and $f
-	ld [H_DIVISOR], a
-	ld b, $4
-	call Divide
-	ld a, [H_QUOTIENT + 1]
-	push af
-	ld a, [H_QUOTIENT + 2]
-	push af
-	ld a, [H_QUOTIENT + 3]
-	push af
-	call CalcDSquared
-	ld a, [hl]
-	and $7f
-	ld [H_MULTIPLIER], a
-	call Multiply
-	ld a, [H_PRODUCT + 1]
-	push af
-	ld a, [H_PRODUCT + 2]
-	push af
-	ld a, [H_PRODUCT + 3]
-	push af
-	ld a, [hli]
-	push af
-	xor a
-	ld [H_MULTIPLICAND], a
-	ld [H_MULTIPLICAND + 1], a
-	ld a, d
-	ld [H_MULTIPLICAND + 2], a
-	ld a, [hli]
-	ld [H_MULTIPLIER], a
-	call Multiply
-	ld b, [hl]
-	ld a, [H_PRODUCT + 3]
-	sub b
-	ld [H_PRODUCT + 3], a
-	ld b, $0
-	ld a, [H_PRODUCT + 2]
-	sbc b
-	ld [H_PRODUCT + 2], a
-	ld a, [H_PRODUCT + 1]
-	sbc b
-	ld [H_PRODUCT + 1], a
-; The difference of the linear term and the constant term consists of 3 bytes
-; starting at H_PRODUCT + 1. Below, hExperience (an alias of that address) will
-; be used instead for the further work of adding or subtracting the squared
-; term and adding the cubed term.
-	pop af
-	and $80
-	jr nz, .subtractSquaredTerm ; check sign
-	pop bc
-	ld a, [hExperience + 2]
-	add b
-	ld [hExperience + 2], a
-	pop bc
-	ld a, [hExperience + 1]
-	adc b
-	ld [hExperience + 1], a
-	pop bc
-	ld a, [hExperience]
-	adc b
-	ld [hExperience], a
-	jr .addCubedTerm
-.subtractSquaredTerm
-	pop bc
-	ld a, [hExperience + 2]
-	sub b
-	ld [hExperience + 2], a
-	pop bc
-	ld a, [hExperience + 1]
-	sbc b
-	ld [hExperience + 1], a
-	pop bc
-	ld a, [hExperience]
-	sbc b
-	ld [hExperience], a
-.addCubedTerm
-	pop bc
-	ld a, [hExperience + 2]
-	add b
-	ld [hExperience + 2], a
-	pop bc
-	ld a, [hExperience + 1]
-	adc b
-	ld [hExperience + 1], a
-	pop bc
-	ld a, [hExperience]
-	adc b
-	ld [hExperience], a
-	ret
-
-; calculates d*d
-CalcDSquared:
-	xor a
-	ld [H_MULTIPLICAND], a
-	ld [H_MULTIPLICAND + 1], a
-	ld a, d
-	ld [H_MULTIPLICAND + 2], a
-	ld [H_MULTIPLIER], a
-	jp Multiply
-
-INCLUDE "data/growth_rates.asm"
--- a/engine/game_corner_slots.asm
+++ /dev/null
@@ -1,54 +1,0 @@
-StartSlotMachine:
-	ld a, [wHiddenObjectFunctionArgument]
-	cp $fd
-	jr z, .printOutOfOrder
-	cp $fe
-	jr z, .printOutToLunch
-	cp $ff
-	jr z, .printSomeonesKeys
-	callba AbleToPlaySlotsCheck
-	ld a, [wCanPlaySlots]
-	and a
-	ret z
-	ld a, [wLuckySlotHiddenObjectIndex]
-	ld b, a
-	ld a, [wHiddenObjectIndex]
-	inc a
-	cp b
-	jr z, .match
-	ld a, 253
-	jr .next
-.match
-	ld a, 250
-.next
-	ld [wSlotMachineSevenAndBarModeChance], a
-	ld a, [H_LOADEDROMBANK]
-	ld [wSlotMachineSavedROMBank], a
-	call PromptUserToPlaySlots
-	ret
-.printOutOfOrder
-	tx_pre_id GameCornerOutOfOrderText
-	jr .printText
-.printOutToLunch
-	tx_pre_id GameCornerOutToLunchText
-	jr .printText
-.printSomeonesKeys
-	tx_pre_id GameCornerSomeonesKeysText
-.printText
-	push af
-	call EnableAutoTextBoxDrawing
-	pop af
-	call PrintPredefTextID
-	ret
-
-GameCornerOutOfOrderText::
-	TX_FAR _GameCornerOutOfOrderText
-	db "@"
-
-GameCornerOutToLunchText::
-	TX_FAR _GameCornerOutToLunchText
-	db "@"
-
-GameCornerSomeonesKeysText::
-	TX_FAR _GameCornerSomeonesKeysText
-	db "@"
--- a/engine/game_corner_slots2.asm
+++ /dev/null
@@ -1,31 +1,0 @@
-AbleToPlaySlotsCheck:
-	ld a, [wSpriteStateData1 + 2]
-	and $8
-	jr z, .done ; not able
-	ld b, COIN_CASE
-	predef GetQuantityOfItemInBag
-	ld a, b
-	and a
-	ld b, (GameCornerCoinCaseText_id - TextPredefs) / 2 + 1
-	jr z, .printCoinCaseRequired
-	ld hl, wPlayerCoins
-	ld a, [hli]
-	or [hl]
-	jr nz, .done ; able to play
-	ld b, (GameCornerNoCoinsText_id - TextPredefs) / 2 + 1
-.printCoinCaseRequired
-	call EnableAutoTextBoxDrawing
-	ld a, b
-	call PrintPredefTextID
-	xor a
-.done
-	ld [wCanPlaySlots], a
-	ret
-
-GameCornerCoinCaseText::
-	TX_FAR _GameCornerCoinCaseText
-	db "@"
-
-GameCornerNoCoinsText::
-	TX_FAR _GameCornerNoCoinsText
-	db "@"
--- a/engine/gamefreak.asm
+++ /dev/null
@@ -1,243 +1,0 @@
-LoadShootingStarGraphics:
-	ld a, $f9
-	ld [rOBP0], a
-	ld a, $a4
-	ld [rOBP1], a
-	ld de, AnimationTileset2 + $30 ; star tile (top left quadrant)
-	ld hl, vChars1 + $200
-	lb bc, BANK(AnimationTileset2), $01
-	call CopyVideoData
-	ld de, AnimationTileset2 + $130 ; star tile (bottom left quadrant)
-	ld hl, vChars1 + $210
-	lb bc, BANK(AnimationTileset2), $01
-	call CopyVideoData
-	ld de, FallingStar
-	ld hl, vChars1 + $220
-	lb bc, BANK(FallingStar), (FallingStarEnd - FallingStar) / $10
-	call CopyVideoData
-	ld hl, GameFreakLogoOAMData
-	ld de, wOAMBuffer + $60
-	ld bc, GameFreakLogoOAMDataEnd - GameFreakLogoOAMData
-	call CopyData
-	ld hl, GameFreakShootingStarOAMData
-	ld de, wOAMBuffer
-	ld bc, GameFreakShootingStarOAMDataEnd - GameFreakShootingStarOAMData
-	jp CopyData
-
-AnimateShootingStar:
-	call LoadShootingStarGraphics
-	ld a, SFX_SHOOTING_STAR
-	call PlaySound
-
-; Move the big star down and left across the screen.
-	ld hl, wOAMBuffer
-	lb bc, $a0, $4
-.bigStarLoop
-	push hl
-	push bc
-.bigStarInnerLoop
-	ld a, [hl] ; Y
-	add 4
-	ld [hli], a
-	ld a, [hl] ; X
-	add -4
-	ld [hli], a
-	inc hl
-	inc hl
-	dec c
-	jr nz, .bigStarInnerLoop
-	ld c, 1
-	call CheckForUserInterruption
-	pop bc
-	pop hl
-	ret c
-	ld a, [hl]
-	cp 80
-	jr nz, .next
-	jr .bigStarLoop
-.next
-	cp b
-	jr nz, .bigStarLoop
-
-; Clear big star OAM.
-	ld hl, wOAMBuffer
-	ld c, 4
-	ld de, 4
-.clearOAMLoop
-	ld [hl], 160
-	add hl, de
-	dec c
-	jr nz, .clearOAMLoop
-
-; Make Gamefreak logo flash.
-	ld b, 3
-.flashLogoLoop
-	ld hl, rOBP0
-	rrc [hl]
-	rrc [hl]
-	ld c, 10
-	call CheckForUserInterruption
-	ret c
-	dec b
-	jr nz, .flashLogoLoop
-
-; Copy 24 instances of the small stars OAM data.
-; Note that their coordinates put them off-screen.
-	ld de, wOAMBuffer
-	ld a, 24
-.initSmallStarsOAMLoop
-	push af
-	ld hl, SmallStarsOAM
-	ld bc, SmallStarsOAMEnd - SmallStarsOAM
-	call CopyData
-	pop af
-	dec a
-	jr nz, .initSmallStarsOAMLoop
-
-; Animate the small stars falling from the Gamefreak logo.
-	xor a
-	ld [wMoveDownSmallStarsOAMCount], a
-	ld hl, SmallStarsWaveCoordsPointerTable
-	ld c, 6
-.smallStarsLoop
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	push bc
-	push hl
-	ld hl, wOAMBuffer + $50
-	ld c, 4
-.smallStarsInnerLoop ; introduce new wave of 4 small stars OAM entries
-	ld a, [de]
-	cp $ff
-	jr z, .next2
-	ld [hli], a ; Y
-	inc de
-	ld a, [de]
-	ld [hli], a ; X
-	inc de
-	inc hl
-	inc hl
-	dec c
-	jr nz, .smallStarsInnerLoop
-	ld a, [wMoveDownSmallStarsOAMCount]
-	cp 24
-	jr z, .next2
-	add 6 ; should be 4, but the extra 2 aren't visible on screen
-	ld [wMoveDownSmallStarsOAMCount], a
-.next2
-	call MoveDownSmallStars
-	push af
-
-; shift the existing OAM entries down to make room for the next wave
-	ld hl, wOAMBuffer + $10
-	ld de, wOAMBuffer
-	ld bc, $50
-	call CopyData
-
-	pop af
-	pop hl
-	pop bc
-	ret c
-	dec c
-	jr nz, .smallStarsLoop
-	and a
-	ret
-
-SmallStarsOAM:
-	db $00,$00,$A2,$90
-SmallStarsOAMEnd:
-
-SmallStarsWaveCoordsPointerTable:
-	dw SmallStarsWave1Coords
-	dw SmallStarsWave2Coords
-	dw SmallStarsWave3Coords
-	dw SmallStarsWave4Coords
-	dw SmallStarsEmptyWave
-	dw SmallStarsEmptyWave
-
-; The stars that fall from the Gamefreak logo come in 4 waves of 4 OAM entries.
-; These arrays contain the Y and X coordinates of each OAM entry.
-
-SmallStarsWave1Coords:
-	db $68,$30
-	db $68,$40
-	db $68,$58
-	db $68,$78
-
-SmallStarsWave2Coords:
-	db $68,$38
-	db $68,$48
-	db $68,$60
-	db $68,$70
-
-SmallStarsWave3Coords:
-	db $68,$34
-	db $68,$4C
-	db $68,$54
-	db $68,$64
-
-SmallStarsWave4Coords:
-	db $68,$3C
-	db $68,$5C
-	db $68,$6C
-	db $68,$74
-
-SmallStarsEmptyWave:
-	db $FF
-
-MoveDownSmallStars:
-	ld b, 8
-.loop
-	ld hl, wOAMBuffer + $5c
-	ld a, [wMoveDownSmallStarsOAMCount]
-	ld de, -4
-	ld c, a
-.innerLoop
-	inc [hl] ; Y
-	add hl, de
-	dec c
-	jr nz, .innerLoop
-; Toggle the palette so that the lower star in the small stars tile blinks in
-; and out.
-	ld a, [rOBP1]
-	xor %10100000
-	ld [rOBP1], a
-
-	ld c, 3
-	call CheckForUserInterruption
-	ret c
-	dec b
-	jr nz, .loop
-	ret
-
-GameFreakLogoOAMData:
-	db $48,$50,$8D,$00
-	db $48,$58,$8E,$00
-	db $50,$50,$8F,$00
-	db $50,$58,$90,$00
-	db $58,$50,$91,$00
-	db $58,$58,$92,$00
-	db $60,$30,$80,$00
-	db $60,$38,$81,$00
-	db $60,$40,$82,$00
-	db $60,$48,$83,$00
-	db $60,$50,$93,$00
-	db $60,$58,$84,$00
-	db $60,$60,$85,$00
-	db $60,$68,$83,$00
-	db $60,$70,$81,$00
-	db $60,$78,$86,$00
-GameFreakLogoOAMDataEnd:
-
-GameFreakShootingStarOAMData:
-	db $00,$A0,$A0,$10
-	db $00,$A8,$A0,$30
-	db $08,$A0,$A1,$10
-	db $08,$A8,$A1,$30
-GameFreakShootingStarOAMDataEnd:
-
-FallingStar:
-	INCBIN "gfx/intro_credits/falling_star.2bpp"
-FallingStarEnd:
--- a/engine/get_bag_item_quantity.asm
+++ /dev/null
@@ -1,18 +1,0 @@
-GetQuantityOfItemInBag:
-; In: b = item ID
-; Out: b = how many of that item are in the bag
-	call GetPredefRegisters
-	ld hl, wNumBagItems
-.loop
-	inc hl
-	ld a, [hli]
-	cp $ff
-	jr z, .notInBag
-	cp b
-	jr nz, .loop
-	ld a, [hl]
-	ld b, a
-	ret
-.notInBag
-	ld b, 0
-	ret
--- /dev/null
+++ b/engine/gfx/hp_bar.asm
@@ -1,0 +1,270 @@
+HPBarLength:
+	call GetPredefRegisters
+
+; calculates bc * 48 / de, the number of pixels the HP bar has
+; the result is always at least 1
+GetHPBarLength:
+	push hl
+	xor a
+	ld hl, H_MULTIPLICAND
+	ld [hli], a
+	ld a, b
+	ld [hli], a
+	ld a, c
+	ld [hli], a
+	ld [hl], $30
+	call Multiply      ; 48 * bc (hp bar is 48 pixels long)
+	ld a, d
+	and a
+	jr z, .maxHPSmaller256
+	srl d              ; make HP in de fit into 1 byte by dividing by 4
+	rr e
+	srl d
+	rr e
+	ld a, [H_MULTIPLICAND+1]
+	ld b, a
+	ld a, [H_MULTIPLICAND+2]
+	srl b              ; divide multiplication result as well
+	rr a
+	srl b
+	rr a
+	ld [H_MULTIPLICAND+2], a
+	ld a, b
+	ld [H_MULTIPLICAND+1], a
+.maxHPSmaller256
+	ld a, e
+	ld [H_DIVISOR], a
+	ld b, $4
+	call Divide
+	ld a, [H_MULTIPLICAND+2]
+	ld e, a            ; e = bc * 48 / de (num of pixels of HP bar)
+	pop hl
+	and a
+	ret nz
+	ld e, $1           ; make result at least 1
+	ret
+
+; predef $48
+UpdateHPBar:
+UpdateHPBar2:
+	push hl
+	ld hl, wHPBarOldHP
+	ld a, [hli]
+	ld c, a      ; old HP into bc
+	ld a, [hli]
+	ld b, a
+	ld a, [hli]
+	ld e, a      ; new HP into de
+	ld d, [hl]
+	pop hl
+	push de
+	push bc
+	call UpdateHPBar_CalcHPDifference
+	ld a, e
+	ld [wHPBarHPDifference+1], a
+	ld a, d
+	ld [wHPBarHPDifference], a
+	pop bc
+	pop de
+	call UpdateHPBar_CompareNewHPToOldHP
+	ret z
+	ld a, $ff
+	jr c, .HPdecrease
+	ld a, $1
+.HPdecrease
+	ld [wHPBarDelta], a
+	call GetPredefRegisters
+	ld a, [wHPBarNewHP]
+	ld e, a
+	ld a, [wHPBarNewHP+1]
+	ld d, a
+.animateHPBarLoop
+	push de
+	ld a, [wHPBarOldHP]
+	ld c, a
+	ld a, [wHPBarOldHP+1]
+	ld b, a
+	call UpdateHPBar_CompareNewHPToOldHP
+	jr z, .animateHPBarDone
+	jr nc, .HPIncrease
+; HP decrease
+	dec bc        ; subtract 1 HP
+	ld a, c
+	ld [wHPBarNewHP], a
+	ld a, b
+	ld [wHPBarNewHP+1], a
+	call UpdateHPBar_CalcOldNewHPBarPixels
+	ld a, e
+	sub d         ; calc pixel difference
+	jr .ok
+.HPIncrease
+	inc bc        ; add 1 HP
+	ld a, c
+	ld [wHPBarNewHP], a
+	ld a, b
+	ld [wHPBarNewHP+1], a
+	call UpdateHPBar_CalcOldNewHPBarPixels
+	ld a, d
+	sub e         ; calc pixel difference
+.ok
+	call UpdateHPBar_PrintHPNumber
+	and a
+	jr z, .noPixelDifference
+	call UpdateHPBar_AnimateHPBar
+.noPixelDifference
+	ld a, [wHPBarNewHP]
+	ld [wHPBarOldHP], a
+	ld a, [wHPBarNewHP+1]
+	ld [wHPBarOldHP+1], a
+	pop de
+	jr .animateHPBarLoop
+.animateHPBarDone
+	pop de
+	ld a, e
+	ld [wHPBarOldHP], a
+	ld a, d
+	ld [wHPBarOldHP+1], a
+	or e
+	jr z, .monFainted
+	call UpdateHPBar_CalcOldNewHPBarPixels
+	ld d, e
+.monFainted
+	call UpdateHPBar_PrintHPNumber
+	ld a, $1
+	call UpdateHPBar_AnimateHPBar
+	jp Delay3
+
+; animates the HP bar going up or down for (a) ticks (two waiting frames each)
+; stops prematurely if bar is filled up
+; e: current health (in pixels) to start with
+UpdateHPBar_AnimateHPBar:
+	push hl
+.barAnimationLoop
+	push af
+	push de
+	ld d, $6
+	call DrawHPBar
+	ld c, 2
+	call DelayFrames
+	pop de
+	ld a, [wHPBarDelta] ; +1 or -1
+	add e
+	cp $31
+	jr nc, .barFilledUp
+	ld e, a
+	pop af
+	dec a
+	jr nz, .barAnimationLoop
+	pop hl
+	ret
+.barFilledUp
+	pop af
+	pop hl
+	ret
+
+; compares old HP and new HP and sets c and z flags accordingly
+UpdateHPBar_CompareNewHPToOldHP:
+	ld a, d
+	sub b
+	ret nz
+	ld a, e
+	sub c
+	ret
+
+; calcs HP difference between bc and de (into de)
+UpdateHPBar_CalcHPDifference:
+	ld a, d
+	sub b
+	jr c, .oldHPGreater
+	jr z, .testLowerByte
+.newHPGreater
+	ld a, e
+	sub c
+	ld e, a
+	ld a, d
+	sbc b
+	ld d, a
+	ret
+.oldHPGreater
+	ld a, c
+	sub e
+	ld e, a
+	ld a, b
+	sbc d
+	ld d, a
+	ret
+.testLowerByte
+	ld a, e
+	sub c
+	jr c, .oldHPGreater
+	jr nz, .newHPGreater
+	ld de, $0
+	ret
+
+UpdateHPBar_PrintHPNumber:
+	push af
+	push de
+	ld a, [wHPBarType]
+	and a
+	jr z, .done ; don't print number in enemy HUD
+; convert from little-endian to big-endian for PrintNumber
+	ld a, [wHPBarOldHP]
+	ld [wHPBarTempHP + 1], a
+	ld a, [wHPBarOldHP + 1]
+	ld [wHPBarTempHP], a
+	push hl
+	ld a, [hFlags_0xFFF6]
+	bit 0, a
+	jr z, .asm_fb15
+	ld de, $9
+	jr .next
+.asm_fb15
+	ld de, $15
+.next
+	add hl, de
+	push hl
+	ld a, " "
+	ld [hli], a
+	ld [hli], a
+	ld [hli], a
+	pop hl
+	ld de, wHPBarTempHP
+	lb bc, 2, 3
+	call PrintNumber
+	call DelayFrame
+	pop hl
+.done
+	pop de
+	pop af
+	ret
+
+; calcs number of HP bar pixels for old and new HP value
+; d: new pixels
+; e: old pixels
+UpdateHPBar_CalcOldNewHPBarPixels:
+	push hl
+	ld hl, wHPBarMaxHP
+	ld a, [hli]  ; max HP into de
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	ld a, [hli]  ; old HP into bc
+	ld c, a
+	ld a, [hli]
+	ld b, a
+	ld a, [hli]  ; new HP into hl
+	ld h, [hl]
+	ld l, a
+	push hl
+	push de
+	call GetHPBarLength ; calc num pixels for old HP
+	ld a, e
+	pop de
+	pop bc
+	push af
+	call GetHPBarLength ; calc num pixels for new HP
+	pop af
+	ld d, e
+	ld e, a
+	pop hl
+	ret
--- /dev/null
+++ b/engine/gfx/load_pokedex_tiles.asm
@@ -1,0 +1,11 @@
+; Loads tile patterns for tiles used in the pokedex.
+LoadPokedexTilePatterns:
+	call LoadHpBarAndStatusTilePatterns
+	ld de, PokedexTileGraphics
+	ld hl, vChars2 + $600
+	lb bc, BANK(PokedexTileGraphics), (PokedexTileGraphicsEnd - PokedexTileGraphics) / $10
+	call CopyVideoData
+	ld de, PokeballTileGraphics
+	ld hl, vChars2 + $720
+	lb bc, BANK(PokeballTileGraphics), $01
+	jp CopyVideoData ; load pokeball tile for marking caught mons
--- /dev/null
+++ b/engine/gfx/mon_icons.asm
@@ -1,0 +1,295 @@
+AnimatePartyMon_ForceSpeed1:
+	xor a
+	ld [wCurrentMenuItem], a
+	ld b, a
+	inc a
+	jr GetAnimationSpeed
+
+; wPartyMenuHPBarColors contains the party mon's health bar colors
+; 0: green
+; 1: yellow
+; 2: red
+AnimatePartyMon::
+	ld hl, wPartyMenuHPBarColors
+	ld a, [wCurrentMenuItem]
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hl]
+
+GetAnimationSpeed:
+	ld c, a
+	ld hl, PartyMonSpeeds
+	add hl, bc
+	ld a, [wOnSGB]
+	xor $1
+	add [hl]
+	ld c, a
+	add a
+	ld b, a
+	ld a, [wAnimCounter]
+	and a
+	jr z, .resetSprites
+	cp c
+	jr z, .animateSprite
+.incTimer
+	inc a
+	cp b
+	jr nz, .skipResetTimer
+	xor a ; reset timer
+.skipResetTimer
+	ld [wAnimCounter], a
+	jp DelayFrame
+.resetSprites
+	push bc
+	ld hl, wMonPartySpritesSavedOAM
+	ld de, wOAMBuffer
+	ld bc, $60
+	call CopyData
+	pop bc
+	xor a
+	jr .incTimer
+.animateSprite
+	push bc
+	ld hl, wOAMBuffer + $02 ; OAM tile id
+	ld bc, $10
+	ld a, [wCurrentMenuItem]
+	call AddNTimes
+	ld c, $40 ; amount to increase the tile id by
+	ld a, [hl]
+	cp $4 ; tile ID for ICON_BALL
+	jr z, .editCoords
+	cp $8 ; tile ID for ICON_HELIX
+	jr nz, .editTileIDS
+; ICON_BALL and ICON_HELIX only shake up and down
+.editCoords
+	dec hl
+	dec hl ; dec hl to the OAM y coord
+	ld c, $1 ; amount to increase the y coord by
+; otherwise, load a second sprite frame
+.editTileIDS
+	ld b, $4
+	ld de, $4
+.loop
+	ld a, [hl]
+	add c
+	ld [hl], a
+	add hl, de
+	dec b
+	jr nz, .loop
+	pop bc
+	ld a, c
+	jr .incTimer
+
+; Party mon animations cycle between 2 frames.
+; The members of the PartyMonSpeeds array specify the number of V-blanks
+; that each frame lasts for green HP, yellow HP, and red HP in order.
+; On the naming screen, the yellow HP speed is always used.
+PartyMonSpeeds:
+	db 5, 16, 32
+
+LoadMonPartySpriteGfx:
+; Load mon party sprite tile patterns into VRAM during V-blank.
+	ld hl, MonPartySpritePointers
+	ld a, $1c
+
+LoadAnimSpriteGfx:
+; Load animated sprite tile patterns into VRAM during V-blank. hl is the address
+; of an array of structures that contain arguments for CopyVideoData and a is
+; the number of structures in the array.
+	ld bc, $0
+.loop
+	push af
+	push bc
+	push hl
+	add hl, bc
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	ld a, [hli]
+	ld c, a
+	ld a, [hli]
+	ld b, a
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	call CopyVideoData
+	pop hl
+	pop bc
+	ld a, $6
+	add c
+	ld c, a
+	pop af
+	dec a
+	jr nz, .loop
+	ret
+
+LoadMonPartySpriteGfxWithLCDDisabled:
+; Load mon party sprite tile patterns into VRAM immediately by disabling the
+; LCD.
+	call DisableLCD
+	ld hl, MonPartySpritePointers
+	ld a, $1c
+	ld bc, $0
+.loop
+	push af
+	push bc
+	push hl
+	add hl, bc
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	push de
+	ld a, [hli]
+	ld c, a
+	swap c
+	ld b, $0
+	ld a, [hli]
+	ld e, [hl]
+	inc hl
+	ld d, [hl]
+	pop hl
+	call FarCopyData2
+	pop hl
+	pop bc
+	ld a, $6
+	add c
+	ld c, a
+	pop af
+	dec a
+	jr nz, .loop
+	jp EnableLCD
+
+INCLUDE "data/mon_party_sprite_pointers.asm"
+
+WriteMonPartySpriteOAMByPartyIndex:
+; Write OAM blocks for the party mon in [hPartyMonIndex].
+	push hl
+	push de
+	push bc
+	ld a, [hPartyMonIndex]
+	ld hl, wPartySpecies
+	ld e, a
+	ld d, 0
+	add hl, de
+	ld a, [hl]
+	call GetPartyMonSpriteID
+	ld [wOAMBaseTile], a
+	call WriteMonPartySpriteOAM
+	pop bc
+	pop de
+	pop hl
+	ret
+
+WriteMonPartySpriteOAMBySpecies:
+; Write OAM blocks for the party sprite of the species in
+; [wMonPartySpriteSpecies].
+	xor a
+	ld [hPartyMonIndex], a
+	ld a, [wMonPartySpriteSpecies]
+	call GetPartyMonSpriteID
+	ld [wOAMBaseTile], a
+	jr WriteMonPartySpriteOAM
+
+UnusedPartyMonSpriteFunction:
+; This function is unused and doesn't appear to do anything useful. It looks
+; like it may have been intended to load the tile patterns and OAM data for
+; the mon party sprite associated with the species in [wcf91].
+; However, its calculations are off and it loads garbage data.
+	ld a, [wcf91]
+	call GetPartyMonSpriteID
+	push af
+	ld hl, vSprites
+	call .LoadTilePatterns
+	pop af
+	add $54
+	ld hl, vSprites + $40
+	call .LoadTilePatterns
+	xor a
+	ld [wMonPartySpriteSpecies], a
+	jr WriteMonPartySpriteOAMBySpecies
+
+.LoadTilePatterns
+	push hl
+	add a
+	ld c, a
+	ld b, 0
+	ld hl, MonPartySpritePointers
+	add hl, bc
+	add hl, bc
+	add hl, bc
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	ld a, [hli]
+	ld c, a
+	ld a, [hli]
+	ld b, a
+	pop hl
+	jp CopyVideoData
+
+WriteMonPartySpriteOAM:
+; Write the OAM blocks for the first animation frame into the OAM buffer and
+; make a copy at wMonPartySpritesSavedOAM.
+	push af
+	ld c, $10
+	ld h, wOAMBuffer / $100
+	ld a, [hPartyMonIndex]
+	swap a
+	ld l, a
+	add $10
+	ld b, a
+	pop af
+	cp ICON_HELIX << 2
+	jr z, .helix
+	call WriteSymmetricMonPartySpriteOAM
+	jr .makeCopy
+.helix
+	call WriteAsymmetricMonPartySpriteOAM
+; Make a copy of the OAM buffer with the first animation frame written so that
+; we can flip back to it from the second frame by copying it back.
+.makeCopy
+	ld hl, wOAMBuffer
+	ld de, wMonPartySpritesSavedOAM
+	ld bc, $60
+	jp CopyData
+
+GetPartyMonSpriteID:
+	ld [wd11e], a
+	predef IndexToPokedex
+	ld a, [wd11e]
+	ld c, a
+	dec a
+	srl a
+	ld hl, MonPartyData
+	ld e, a
+	ld d, 0
+	add hl, de
+	ld a, [hl]
+	bit 0, c
+	jr nz, .skipSwap
+	swap a ; use lower nybble if pokedex num is even
+.skipSwap
+	and $f0
+	srl a
+	srl a
+	ret
+
+INCLUDE "data/mon_party_sprites.asm"
+
+INC_FRAME_1 EQUS "0, $20"
+INC_FRAME_2 EQUS "$20, $20"
+
+BugIconFrame1:       INCBIN "gfx/icons/bug.2bpp", INC_FRAME_1
+PlantIconFrame1:     INCBIN "gfx/icons/plant.2bpp", INC_FRAME_1
+BugIconFrame2:       INCBIN "gfx/icons/bug.2bpp", INC_FRAME_2
+PlantIconFrame2:     INCBIN "gfx/icons/plant.2bpp", INC_FRAME_2
+SnakeIconFrame1:     INCBIN "gfx/icons/snake.2bpp", INC_FRAME_1
+QuadrupedIconFrame1: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_1
+SnakeIconFrame2:     INCBIN "gfx/icons/snake.2bpp", INC_FRAME_2
+QuadrupedIconFrame2: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_2
+
+TradeBubbleIconGFX:  INCBIN "gfx/trade/bubble.2bpp"
--- /dev/null
+++ b/engine/gfx/oam_dma.asm
@@ -1,0 +1,26 @@
+WriteDMACodeToHRAM::
+; Since no other memory is available during OAM DMA,
+; DMARoutine is copied to HRAM and executed there.
+	ld c, $ff80 % $100
+	ld b, DMARoutineEnd - DMARoutine
+	ld hl, DMARoutine
+.copy
+	ld a, [hli]
+	ld [$ff00+c], a
+	inc c
+	dec b
+	jr nz, .copy
+	ret
+
+DMARoutine:
+	; initiate DMA
+	ld a, wOAMBuffer / $100
+	ld [rDMA], a
+
+	; wait for DMA to finish
+	ld a, $28
+.wait
+	dec a
+	jr nz, .wait
+	ret
+DMARoutineEnd:
--- /dev/null
+++ b/engine/gfx/palettes.asm
@@ -1,0 +1,641 @@
+_RunPaletteCommand:
+	call GetPredefRegisters
+	ld a, b
+	cp $ff
+	jr nz, .next
+	ld a, [wDefaultPaletteCommand] ; use default command if command ID is $ff
+.next
+	cp UPDATE_PARTY_MENU_BLK_PACKET
+	jp z, UpdatePartyMenuBlkPacket
+	ld l, a
+	ld h, 0
+	add hl, hl
+	ld de, SetPalFunctions
+	add hl, de
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld de, SendSGBPackets
+	push de
+	jp hl
+
+SetPal_BattleBlack:
+	ld hl, PalPacket_Black
+	ld de, BlkPacket_Battle
+	ret
+
+; uses PalPacket_Empty to build a packet based on mon IDs and health color
+SetPal_Battle:
+	ld hl, PalPacket_Empty
+	ld de, wPalPacket
+	ld bc, $10
+	call CopyData
+	ld a, [wPlayerBattleStatus3]
+	ld hl, wBattleMonSpecies
+	call DeterminePaletteID
+	ld b, a
+	ld a, [wEnemyBattleStatus3]
+	ld hl, wEnemyMonSpecies2
+	call DeterminePaletteID
+	ld c, a
+	ld hl, wPalPacket + 1
+	ld a, [wPlayerHPBarColor]
+	add PAL_GREENBAR
+	ld [hli], a
+	inc hl
+	ld a, [wEnemyHPBarColor]
+	add PAL_GREENBAR
+	ld [hli], a
+	inc hl
+	ld a, b
+	ld [hli], a
+	inc hl
+	ld a, c
+	ld [hl], a
+	ld hl, wPalPacket
+	ld de, BlkPacket_Battle
+	ld a, SET_PAL_BATTLE
+	ld [wDefaultPaletteCommand], a
+	ret
+
+SetPal_TownMap:
+	ld hl, PalPacket_TownMap
+	ld de, BlkPacket_WholeScreen
+	ret
+
+; uses PalPacket_Empty to build a packet based the mon ID
+SetPal_StatusScreen:
+	ld hl, PalPacket_Empty
+	ld de, wPalPacket
+	ld bc, $10
+	call CopyData
+	ld a, [wcf91]
+	cp NUM_POKEMON_INDEXES + 1
+	jr c, .pokemon
+	ld a, $1 ; not pokemon
+.pokemon
+	call DeterminePaletteIDOutOfBattle
+	push af
+	ld hl, wPalPacket + 1
+	ld a, [wStatusScreenHPBarColor]
+	add PAL_GREENBAR
+	ld [hli], a
+	inc hl
+	pop af
+	ld [hl], a
+	ld hl, wPalPacket
+	ld de, BlkPacket_StatusScreen
+	ret
+
+SetPal_PartyMenu:
+	ld hl, PalPacket_PartyMenu
+	ld de, wPartyMenuBlkPacket
+	ret
+
+SetPal_Pokedex:
+	ld hl, PalPacket_Pokedex
+	ld de, wPalPacket
+	ld bc, $10
+	call CopyData
+	ld a, [wcf91]
+	call DeterminePaletteIDOutOfBattle
+	ld hl, wPalPacket + 3
+	ld [hl], a
+	ld hl, wPalPacket
+	ld de, BlkPacket_Pokedex
+	ret
+
+SetPal_Slots:
+	ld hl, PalPacket_Slots
+	ld de, BlkPacket_Slots
+	ret
+
+SetPal_TitleScreen:
+	ld hl, PalPacket_Titlescreen
+	ld de, BlkPacket_Titlescreen
+	ret
+
+; used mostly for menus and the Oak intro
+SetPal_Generic:
+	ld hl, PalPacket_Generic
+	ld de, BlkPacket_WholeScreen
+	ret
+
+SetPal_NidorinoIntro:
+	ld hl, PalPacket_NidorinoIntro
+	ld de, BlkPacket_NidorinoIntro
+	ret
+
+SetPal_GameFreakIntro:
+	ld hl, PalPacket_GameFreakIntro
+	ld de, BlkPacket_GameFreakIntro
+	ld a, SET_PAL_GENERIC
+	ld [wDefaultPaletteCommand], a
+	ret
+
+; uses PalPacket_Empty to build a packet based on the current map
+SetPal_Overworld:
+	ld hl, PalPacket_Empty
+	ld de, wPalPacket
+	ld bc, $10
+	call CopyData
+	ld a, [wCurMapTileset]
+	cp CEMETERY
+	jr z, .PokemonTowerOrAgatha
+	cp CAVERN
+	jr z, .caveOrBruno
+	ld a, [wCurMap]
+	cp REDS_HOUSE_1F
+	jr c, .townOrRoute
+	cp CERULEAN_CAVE_2F
+	jr c, .normalDungeonOrBuilding
+	cp NAME_RATERS_HOUSE
+	jr c, .caveOrBruno
+	cp LORELEIS_ROOM
+	jr z, .Lorelei
+	cp BRUNOS_ROOM
+	jr z, .caveOrBruno
+.normalDungeonOrBuilding
+	ld a, [wLastMap] ; town or route that current dungeon or building is located
+.townOrRoute
+	cp SAFFRON_CITY + 1
+	jr c, .town
+	ld a, PAL_ROUTE - 1
+.town
+	inc a ; a town's palette ID is its map ID + 1
+	ld hl, wPalPacket + 1
+	ld [hld], a
+	ld de, BlkPacket_WholeScreen
+	ld a, SET_PAL_OVERWORLD
+	ld [wDefaultPaletteCommand], a
+	ret
+.PokemonTowerOrAgatha
+	ld a, PAL_GREYMON - 1
+	jr .town
+.caveOrBruno
+	ld a, PAL_CAVE - 1
+	jr .town
+.Lorelei
+	xor a
+	jr .town
+
+; used when a Pokemon is the only thing on the screen
+; such as evolution, trading and the Hall of Fame
+SetPal_PokemonWholeScreen:
+	push bc
+	ld hl, PalPacket_Empty
+	ld de, wPalPacket
+	ld bc, $10
+	call CopyData
+	pop bc
+	ld a, c
+	and a
+	ld a, PAL_BLACK
+	jr nz, .next
+	ld a, [wWholeScreenPaletteMonSpecies]
+	call DeterminePaletteIDOutOfBattle
+.next
+	ld [wPalPacket + 1], a
+	ld hl, wPalPacket
+	ld de, BlkPacket_WholeScreen
+	ret
+
+SetPal_TrainerCard:
+	ld hl, BlkPacket_TrainerCard
+	ld de, wTrainerCardBlkPacket
+	ld bc, $40
+	call CopyData
+	ld de, BadgeBlkDataLengths
+	ld hl, wTrainerCardBlkPacket + 2
+	ld a, [wObtainedBadges]
+	ld c, 8
+.badgeLoop
+	srl a
+	push af
+	jr c, .haveBadge
+; The player doens't have the badge, so zero the badge's blk data.
+	push bc
+	ld a, [de]
+	ld c, a
+	xor a
+.zeroBadgeDataLoop
+	ld [hli], a
+	dec c
+	jr nz, .zeroBadgeDataLoop
+	pop bc
+	jr .nextBadge
+.haveBadge
+; The player does have the badge, so skip past the badge's blk data.
+	ld a, [de]
+.skipBadgeDataLoop
+	inc hl
+	dec a
+	jr nz, .skipBadgeDataLoop
+.nextBadge
+	pop af
+	inc de
+	dec c
+	jr nz, .badgeLoop
+	ld hl, PalPacket_TrainerCard
+	ld de, wTrainerCardBlkPacket
+	ret
+
+SetPalFunctions:
+	dw SetPal_BattleBlack
+	dw SetPal_Battle
+	dw SetPal_TownMap
+	dw SetPal_StatusScreen
+	dw SetPal_Pokedex
+	dw SetPal_Slots
+	dw SetPal_TitleScreen
+	dw SetPal_NidorinoIntro
+	dw SetPal_Generic
+	dw SetPal_Overworld
+	dw SetPal_PartyMenu
+	dw SetPal_PokemonWholeScreen
+	dw SetPal_GameFreakIntro
+	dw SetPal_TrainerCard
+
+; The length of the blk data of each badge on the Trainer Card.
+; The Rainbow Badge has 3 entries because of its many colors.
+BadgeBlkDataLengths:
+	db 6     ; Boulder Badge
+	db 6     ; Cascade Badge
+	db 6     ; Thunder Badge
+	db 6 * 3 ; Rainbow Badge
+	db 6     ; Soul Badge
+	db 6     ; Marsh Badge
+	db 6     ; Volcano Badge
+	db 6     ; Earth Badge
+
+DeterminePaletteID:
+	bit TRANSFORMED, a ; a is battle status 3
+	ld a, PAL_GREYMON  ; if the mon has used Transform, use Ditto's palette
+	ret nz
+	ld a, [hl]
+DeterminePaletteIDOutOfBattle:
+	ld [wd11e], a
+	and a ; is the mon index 0?
+	jr z, .skipDexNumConversion
+	push bc
+	predef IndexToPokedex
+	pop bc
+	ld a, [wd11e]
+.skipDexNumConversion
+	ld e, a
+	ld d, 0
+	ld hl, MonsterPalettes ; not just for Pokemon, Trainers use it too
+	add hl, de
+	ld a, [hl]
+	ret
+
+InitPartyMenuBlkPacket:
+	ld hl, BlkPacket_PartyMenu
+	ld de, wPartyMenuBlkPacket
+	ld bc, $30
+	jp CopyData
+
+UpdatePartyMenuBlkPacket:
+; Update the blk packet with the palette of the HP bar that is
+; specified in [wWhichPartyMenuHPBar].
+	ld hl, wPartyMenuHPBarColors
+	ld a, [wWhichPartyMenuHPBar]
+	ld e, a
+	ld d, 0
+	add hl, de
+	ld e, l
+	ld d, h
+	ld a, [de]
+	and a
+	ld e, (1 << 2) | 1 ; green
+	jr z, .next
+	dec a
+	ld e, (2 << 2) | 2 ; yellow
+	jr z, .next
+	ld e, (3 << 2) | 3 ; red
+.next
+	push de
+	ld hl, wPartyMenuBlkPacket + 8 + 1
+	ld bc, 6
+	ld a, [wWhichPartyMenuHPBar]
+	call AddNTimes
+	pop de
+	ld [hl], e
+	ret
+
+SendSGBPacket:
+;check number of packets
+	ld a, [hl]
+	and $07
+	ret z
+; store number of packets in B
+	ld b, a
+.loop2
+; save B for later use
+	push bc
+; disable ReadJoypad to prevent it from interfering with sending the packet
+	ld a, 1
+	ld [hDisableJoypadPolling], a
+; send RESET signal (P14=LOW, P15=LOW)
+	xor a
+	ld [rJOYP], a
+; set P14=HIGH, P15=HIGH
+	ld a, $30
+	ld [rJOYP], a
+;load length of packets (16 bytes)
+	ld b, $10
+.nextByte
+;set bit counter (8 bits per byte)
+	ld e, $08
+; get next byte in the packet
+	ld a, [hli]
+	ld d, a
+.nextBit0
+	bit 0, d
+; if 0th bit is not zero set P14=HIGH,P15=LOW (send bit 1)
+	ld a, $10
+	jr nz, .next0
+; else (if 0th bit is zero) set P14=LOW,P15=HIGH (send bit 0)
+	ld a, $20
+.next0
+	ld [rJOYP], a
+; must set P14=HIGH,P15=HIGH between each "pulse"
+	ld a, $30
+	ld [rJOYP], a
+; rotation will put next bit in 0th position (so  we can always use command
+; "bit 0,d" to fetch the bit that has to be sent)
+	rr d
+; decrease bit counter so we know when we have sent all 8 bits of current byte
+	dec e
+	jr nz, .nextBit0
+	dec b
+	jr nz, .nextByte
+; send bit 1 as a "stop bit" (end of parameter data)
+	ld a, $20
+	ld [rJOYP], a
+; set P14=HIGH,P15=HIGH
+	ld a, $30
+	ld [rJOYP], a
+	xor a
+	ld [hDisableJoypadPolling], a
+; wait for about 70000 cycles
+	call Wait7000
+; restore (previously pushed) number of packets
+	pop bc
+	dec b
+; return if there are no more packets
+	ret z
+; else send 16 more bytes
+	jr .loop2
+
+LoadSGB:
+	xor a
+	ld [wOnSGB], a
+	call CheckSGB
+	ret nc
+	ld a, 1
+	ld [wOnSGB], a
+	ld a, [wGBC]
+	and a
+	jr z, .notGBC
+	ret
+.notGBC
+	di
+	call PrepareSuperNintendoVRAMTransfer
+	ei
+	ld a, 1
+	ld [wCopyingSGBTileData], a
+	ld de, ChrTrnPacket
+	ld hl, SGBBorderGraphics
+	call CopyGfxToSuperNintendoVRAM
+	xor a
+	ld [wCopyingSGBTileData], a
+	ld de, PctTrnPacket
+	ld hl, BorderPalettes
+	call CopyGfxToSuperNintendoVRAM
+	xor a
+	ld [wCopyingSGBTileData], a
+	ld de, PalTrnPacket
+	ld hl, SuperPalettes
+	call CopyGfxToSuperNintendoVRAM
+	call ClearVram
+	ld hl, MaskEnCancelPacket
+	jp SendSGBPacket
+
+PrepareSuperNintendoVRAMTransfer:
+	ld hl, .packetPointers
+	ld c, 9
+.loop
+	push bc
+	ld a, [hli]
+	push hl
+	ld h, [hl]
+	ld l, a
+	call SendSGBPacket
+	pop hl
+	inc hl
+	pop bc
+	dec c
+	jr nz, .loop
+	ret
+
+.packetPointers
+; Only the first packet is needed.
+	dw MaskEnFreezePacket
+	dw DataSnd_72548
+	dw DataSnd_72558
+	dw DataSnd_72568
+	dw DataSnd_72578
+	dw DataSnd_72588
+	dw DataSnd_72598
+	dw DataSnd_725a8
+	dw DataSnd_725b8
+
+CheckSGB:
+; Returns whether the game is running on an SGB in carry.
+	ld hl, MltReq2Packet
+	di
+	call SendSGBPacket
+	ld a, 1
+	ld [hDisableJoypadPolling], a
+	ei
+	call Wait7000
+	ld a, [rJOYP]
+	and $3
+	cp $3
+	jr nz, .isSGB
+	ld a, $20
+	ld [rJOYP], a
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	call Wait7000
+	call Wait7000
+	ld a, $30
+	ld [rJOYP], a
+	call Wait7000
+	call Wait7000
+	ld a, $10
+	ld [rJOYP], a
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	call Wait7000
+	call Wait7000
+	ld a, $30
+	ld [rJOYP], a
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	ld a, [rJOYP]
+	call Wait7000
+	call Wait7000
+	ld a, [rJOYP]
+	and $3
+	cp $3
+	jr nz, .isSGB
+	call SendMltReq1Packet
+	and a
+	ret
+.isSGB
+	call SendMltReq1Packet
+	scf
+	ret
+
+SendMltReq1Packet:
+	ld hl, MltReq1Packet
+	call SendSGBPacket
+	jp Wait7000
+
+CopyGfxToSuperNintendoVRAM:
+	di
+	push de
+	call DisableLCD
+	ld a, $e4
+	ld [rBGP], a
+	ld de, vChars1
+	ld a, [wCopyingSGBTileData]
+	and a
+	jr z, .notCopyingTileData
+	call CopySGBBorderTiles
+	jr .next
+.notCopyingTileData
+	ld bc, $1000
+	call CopyData
+.next
+	ld hl, vBGMap0
+	ld de, $c
+	ld a, $80
+	ld c, $d
+.loop
+	ld b, $14
+.innerLoop
+	ld [hli], a
+	inc a
+	dec b
+	jr nz, .innerLoop
+	add hl, de
+	dec c
+	jr nz, .loop
+	ld a, $e3
+	ld [rLCDC], a
+	pop hl
+	call SendSGBPacket
+	xor a
+	ld [rBGP], a
+	ei
+	ret
+
+Wait7000:
+; Each loop takes 9 cycles so this routine actually waits 63000 cycles.
+	ld de, 7000
+.loop
+	nop
+	nop
+	nop
+	dec de
+	ld a, d
+	or e
+	jr nz, .loop
+	ret
+
+SendSGBPackets:
+	ld a, [wGBC]
+	and a
+	jr z, .notGBC
+	push de
+	call InitGBCPalettes
+	pop hl
+	call EmptyFunc5
+	ret
+.notGBC
+	push de
+	call SendSGBPacket
+	pop hl
+	jp SendSGBPacket
+
+InitGBCPalettes:
+	ld a, $80 ; index 0 with auto-increment
+	ld [rBGPI], a
+	inc hl
+	ld c, $20
+.loop
+	ld a, [hli]
+	inc hl
+	add a
+	add a
+	add a
+	ld de, SuperPalettes
+	add e
+	jr nc, .noCarry
+	inc d
+.noCarry
+	ld a, [de]
+	ld [rBGPD], a
+	dec c
+	jr nz, .loop
+	ret
+
+EmptyFunc5:
+	ret
+
+CopySGBBorderTiles:
+; SGB tile data is stored in a 4BPP planar format.
+; Each tile is 32 bytes. The first 16 bytes contain bit planes 1 and 2, while
+; the second 16 bytes contain bit planes 3 and 4.
+; This function converts 2BPP planar data into this format by mapping
+; 2BPP colors 0-3 to 4BPP colors 0-3. 4BPP colors 4-15 are not used.
+	ld b, 128
+
+.tileLoop
+
+; Copy bit planes 1 and 2 of the tile data.
+	ld c, 16
+.copyLoop
+	ld a, [hli]
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .copyLoop
+
+; Zero bit planes 3 and 4.
+	ld c, 16
+	xor a
+.zeroLoop
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .zeroLoop
+
+	dec b
+	jr nz, .tileLoop
+	ret
+
+INCLUDE "data/sgb_packets.asm"
+
+INCLUDE "data/mon_palettes.asm"
+
+INCLUDE "data/super_palettes.asm"
+
+INCLUDE "data/sgb_border.asm"
--- /dev/null
+++ b/engine/gfx/screen_effects.asm
@@ -1,0 +1,71 @@
+; b = new colour for BG colour 0 (usually white) for 4 frames
+ChangeBGPalColor0_4Frames:
+	call GetPredefRegisters
+	ld a, [rBGP]
+	or b
+	ld [rBGP], a
+	ld c, 4
+	call DelayFrames
+	ld a, [rBGP]
+	and %11111100
+	ld [rBGP], a
+	ret
+
+PredefShakeScreenVertically:
+; Moves the window down and then back in a sequence of progressively smaller
+; numbers of pixels, starting at b.
+	call GetPredefRegisters
+	ld a, 1
+	ld [wDisableVBlankWYUpdate], a
+	xor a
+.loop
+	ld [$ff96], a
+	call .MutateWY
+	call .MutateWY
+	dec b
+	ld a, b
+	jr nz, .loop
+	xor a
+	ld [wDisableVBlankWYUpdate], a
+	ret
+
+.MutateWY
+	ld a, [$ff96]
+	xor b
+	ld [$ff96], a
+	ld [rWY], a
+	ld c, 3
+	jp DelayFrames
+
+PredefShakeScreenHorizontally:
+; Moves the window right and then back in a sequence of progressively smaller
+; numbers of pixels, starting at b.
+	call GetPredefRegisters
+	xor a
+.loop
+	ld [$ff97], a
+	call .MutateWX
+	ld c, 1
+	call DelayFrames
+	call .MutateWX
+	dec b
+	ld a, b
+	jr nz, .loop
+
+; restore normal WX
+	ld a, 7
+	ld [rWX], a
+	ret
+
+.MutateWX
+	ld a, [$ff97]
+	xor b
+	ld [$ff97], a
+	bit 7, a
+	jr z, .skipZeroing
+	xor a ; zero a if it's negative
+.skipZeroing
+	add 7
+	ld [rWX], a
+	ld c, 4
+	jp DelayFrames
--- /dev/null
+++ b/engine/gfx/sprite_oam.asm
@@ -1,0 +1,189 @@
+PrepareOAMData::
+; Determine OAM data for currently visible
+; sprites and write it to wOAMBuffer.
+
+	ld a, [wUpdateSpritesEnabled]
+	dec a
+	jr z, .updateEnabled
+
+	cp -1
+	ret nz
+	ld [wUpdateSpritesEnabled], a
+	jp HideSprites
+
+.updateEnabled
+	xor a
+	ld [hOAMBufferOffset], a
+
+.spriteLoop
+	ld [hSpriteOffset2], a
+
+	ld d, wSpriteStateData1 / $100
+	ld a, [hSpriteOffset2]
+	ld e, a
+	ld a, [de] ; c1x0
+	and a
+	jp z, .nextSprite
+
+	inc e
+	inc e
+	ld a, [de] ; c1x2 (facing/anim)
+	ld [wd5cd], a
+	cp $ff ; off-screen (don't draw)
+	jr nz, .visible
+
+	call GetSpriteScreenXY
+	jr .nextSprite
+
+.visible
+	cp $a0 ; is the sprite unchanging like an item ball or boulder?
+	jr c, .usefacing
+
+; unchanging
+	and $f
+	add $10 ; skip to the second half of the table which doesn't account for facing direction
+	jr .next
+
+.usefacing
+	and $f
+
+.next
+	ld l, a
+
+; get sprite priority
+	push de
+	inc d
+	ld a, e
+	add $5
+	ld e, a
+	ld a, [de] ; c2x7
+	and $80
+	ld [hSpritePriority], a ; temp store sprite priority
+	pop de
+
+; read the entry from the table
+	ld h, 0
+	ld bc, SpriteFacingAndAnimationTable
+	add hl, hl
+	add hl, hl
+	add hl, bc
+	ld a, [hli]
+	ld c, a
+	ld a, [hli]
+	ld b, a
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+
+	call GetSpriteScreenXY
+
+	ld a, [hOAMBufferOffset]
+	ld e, a
+	ld d, wOAMBuffer / $100
+
+.tileLoop
+	ld a, [hSpriteScreenY]   ; temp for sprite Y position
+	add $10                  ; Y=16 is top of screen (Y=0 is invisible)
+	add [hl]                 ; add Y offset from table
+	ld [de], a               ; write new sprite OAM Y position
+	inc hl
+	ld a, [hSpriteScreenX]   ; temp for sprite X position
+	add $8                   ; X=8 is left of screen (X=0 is invisible)
+	add [hl]                 ; add X offset from table
+	inc e
+	ld [de], a               ; write new sprite OAM X position
+	inc e
+	ld a, [bc]               ; read pattern number offset (accommodates orientation (offset 0,4 or 8) and animation (offset 0 or $80))
+	inc bc
+	push bc
+	ld b, a
+
+	ld a, [wd5cd]            ; temp copy of c1x2
+	swap a                   ; high nybble determines sprite used (0 is always player sprite, next are some npcs)
+	and $f
+
+	; Sprites $a and $b have one face (and therefore 4 tiles instead of 12).
+	; As a result, sprite $b's tile offset is less than normal.
+	cp $b
+	jr nz, .notFourTileSprite
+	ld a, $a * 12 + 4
+	jr .next2
+
+.notFourTileSprite
+	; a *= 12
+	sla a
+	sla a
+	ld c, a
+	sla a
+	add c
+
+.next2
+	add b ; add the tile offset from the table (based on frame and facing direction)
+	pop bc
+	ld [de], a ; tile id
+	inc hl
+	inc e
+	ld a, [hl]
+	bit 1, a ; is the tile allowed to set the sprite priority bit?
+	jr z, .skipPriority
+	ld a, [hSpritePriority]
+	or [hl]
+.skipPriority
+	inc hl
+	ld [de], a
+	inc e
+	bit 0, a ; OAMFLAG_ENDOFDATA
+	jr z, .tileLoop
+
+	ld a, e
+	ld [hOAMBufferOffset], a
+
+.nextSprite
+	ld a, [hSpriteOffset2]
+	add $10
+	cp $100 % $100
+	jp nz, .spriteLoop
+
+	; Clear unused OAM.
+	ld a, [hOAMBufferOffset]
+	ld l, a
+	ld h, wOAMBuffer / $100
+	ld de, $4
+	ld b, $a0
+	ld a, [wd736]
+	bit 6, a ; jumping down ledge or fishing animation?
+	ld a, $a0
+	jr z, .clear
+
+; Don't clear the last 4 entries because they are used for the shadow in the
+; jumping down ledge animation and the rod in the fishing animation.
+	ld a, $90
+
+.clear
+	cp l
+	ret z
+	ld [hl], b
+	add hl, de
+	jr .clear
+
+GetSpriteScreenXY:
+	inc e
+	inc e
+	ld a, [de] ; c1x4
+	ld [hSpriteScreenY], a
+	inc e
+	inc e
+	ld a, [de] ; c1x6
+	ld [hSpriteScreenX], a
+	ld a, 4
+	add e
+	ld e, a
+	ld a, [hSpriteScreenY]
+	add 4
+	and $f0
+	ld [de], a ; c1xa (y)
+	inc e
+	ld a, [hSpriteScreenX]
+	and $f0
+	ld [de], a  ; c1xb (x)
+	ret
--- a/engine/give_pokemon.asm
+++ /dev/null
@@ -1,82 +1,0 @@
-_GivePokemon::
-; returns success in carry
-; and whether the mon was added to the party in [wAddedToParty]
-	call EnableAutoTextBoxDrawing
-	xor a
-	ld [wAddedToParty], a
-	ld a, [wPartyCount]
-	cp PARTY_LENGTH
-	jr c, .addToParty
-	ld a, [wNumInBox]
-	cp MONS_PER_BOX
-	jr nc, .boxFull
-; add to box
-	xor a
-	ld [wEnemyBattleStatus3], a
-	ld a, [wcf91]
-	ld [wEnemyMonSpecies2], a
-	callab LoadEnemyMonData
-	call SetPokedexOwnedFlag
-	callab SendNewMonToBox
-	ld hl, wcf4b
-	ld a, [wCurrentBoxNum]
-	and $7f
-	cp 9
-	jr c, .singleDigitBoxNum
-	sub 9
-	ld [hl], "1"
-	inc hl
-	add "0"
-	jr .next
-.singleDigitBoxNum
-	add "1"
-.next
-	ld [hli], a
-	ld [hl], "@"
-	ld hl, SentToBoxText
-	call PrintText
-	scf
-	ret
-.boxFull
-	ld hl, BoxIsFullText
-	call PrintText
-	and a
-	ret
-.addToParty
-	call SetPokedexOwnedFlag
-	call AddPartyMon
-	ld a, 1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld [wAddedToParty], a
-	scf
-	ret
-
-SetPokedexOwnedFlag:
-	ld a, [wcf91]
-	push af
-	ld [wd11e], a
-	predef IndexToPokedex
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld hl, wPokedexOwned
-	ld b, FLAG_SET
-	predef FlagActionPredef
-	pop af
-	ld [wd11e], a
-	call GetMonName
-	ld hl, GotMonText
-	jp PrintText
-
-GotMonText:
-	TX_FAR _GotMonText
-	TX_SFX_ITEM_1
-	db "@"
-
-SentToBoxText:
-	TX_FAR _SentToBoxText
-	db "@"
-
-BoxIsFullText:
-	TX_FAR _BoxIsFullText
-	db "@"
--- a/engine/hall_of_fame.asm
+++ /dev/null
@@ -1,288 +1,0 @@
-AnimateHallOfFame:
-	call HoFFadeOutScreenAndMusic
-	call ClearScreen
-	ld c, 100
-	call DelayFrames
-	call LoadFontTilePatterns
-	call LoadTextBoxTilePatterns
-	call DisableLCD
-	ld hl, vBGMap0
-	ld bc, $800
-	ld a, " "
-	call FillMemory
-	call EnableLCD
-	ld hl, rLCDC
-	set 3, [hl]
-	xor a
-	ld hl, wHallOfFame
-	ld bc, HOF_TEAM
-	call FillMemory
-	xor a
-	ld [wUpdateSpritesEnabled], a
-	ld [hTilesetType], a
-	ld [wSpriteFlipped], a
-	ld [wLetterPrintingDelayFlags], a ; no delay
-	ld [wHoFMonOrPlayer], a ; mon
-	inc a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld hl, wNumHoFTeams
-	ld a, [hl]
-	inc a
-	jr z, .skipInc ; don't wrap around to 0
-	inc [hl]
-.skipInc
-	ld a, $90
-	ld [hWY], a
-	ld c, BANK(Music_HallOfFame)
-	ld a, MUSIC_HALL_OF_FAME
-	call PlayMusic
-	ld hl, wPartySpecies
-	ld c, $ff
-.partyMonLoop
-	ld a, [hli]
-	cp $ff
-	jr z, .doneShowingParty
-	inc c
-	push hl
-	push bc
-	ld [wHoFMonSpecies], a
-	ld a, c
-	ld [wHoFPartyMonIndex], a
-	ld hl, wPartyMon1Level
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld a, [hl]
-	ld [wHoFMonLevel], a
-	call HoFShowMonOrPlayer
-	call HoFDisplayAndRecordMonInfo
-	ld c, 80
-	call DelayFrames
-	coord hl, 2, 13
-	ld b, 3
-	ld c, 14
-	call TextBoxBorder
-	coord hl, 4, 15
-	ld de, HallOfFameText
-	call PlaceString
-	ld c, 180
-	call DelayFrames
-	call GBFadeOutToWhite
-	pop bc
-	pop hl
-	jr .partyMonLoop
-.doneShowingParty
-	ld a, c
-	inc a
-	ld hl, wHallOfFame
-	ld bc, HOF_MON
-	call AddNTimes
-	ld [hl], $ff
-	call SaveHallOfFameTeams
-	xor a
-	ld [wHoFMonSpecies], a
-	inc a
-	ld [wHoFMonOrPlayer], a ; player
-	call HoFShowMonOrPlayer
-	call HoFDisplayPlayerStats
-	call HoFFadeOutScreenAndMusic
-	xor a
-	ld [hWY], a
-	ld hl, rLCDC
-	res 3, [hl]
-	ret
-
-HallOfFameText:
-	db "HALL OF FAME@"
-
-HoFShowMonOrPlayer:
-	call ClearScreen
-	ld a, $d0
-	ld [hSCY], a
-	ld a, $c0
-	ld [hSCX], a
-	ld a, [wHoFMonSpecies]
-	ld [wcf91], a
-	ld [wd0b5], a
-	ld [wBattleMonSpecies2], a
-	ld [wWholeScreenPaletteMonSpecies], a
-	ld a, [wHoFMonOrPlayer]
-	and a
-	jr z, .showMon
-; show player
-	call HoFLoadPlayerPics
-	jr .next1
-.showMon
-	coord hl, 12, 5
-	call GetMonHeader
-	call LoadFrontSpriteByMonIndex
-	predef LoadMonBackPic
-.next1
-	ld b, SET_PAL_POKEMON_WHOLE_SCREEN
-	ld c, 0
-	call RunPaletteCommand
-	ld a, %11100100
-	ld [rBGP], a
-	ld c, $31 ; back pic
-	call HoFLoadMonPlayerPicTileIDs
-	ld d, $a0
-	ld e, 4
-	ld a, [wOnSGB]
-	and a
-	jr z, .next2
-	sla e ; scroll more slowly on SGB
-.next2
-	call .ScrollPic ; scroll back pic left
-	xor a
-	ld [hSCY], a
-	ld c, a ; front pic
-	call HoFLoadMonPlayerPicTileIDs
-	ld d, 0
-	ld e, -4
-; scroll front pic right
-
-.ScrollPic
-	call DelayFrame
-	ld a, [hSCX]
-	add e
-	ld [hSCX], a
-	cp d
-	jr nz, .ScrollPic
-	ret
-
-HoFDisplayAndRecordMonInfo:
-	ld a, [wHoFPartyMonIndex]
-	ld hl, wPartyMonNicks
-	call GetPartyMonName
-	call HoFDisplayMonInfo
-	jp HoFRecordMonInfo
-
-HoFDisplayMonInfo:
-	coord hl, 0, 2
-	ld b, 9
-	ld c, 10
-	call TextBoxBorder
-	coord hl, 2, 6
-	ld de, HoFMonInfoText
-	call PlaceString
-	coord hl, 1, 4
-	ld de, wcd6d
-	call PlaceString
-	ld a, [wHoFMonLevel]
-	coord hl, 8, 7
-	call PrintLevelCommon
-	ld a, [wHoFMonSpecies]
-	ld [wd0b5], a
-	coord hl, 3, 9
-	predef PrintMonType
-	ld a, [wHoFMonSpecies]
-	jp PlayCry
-
-HoFMonInfoText:
-	db   "LEVEL/"
-	next "TYPE1/"
-	next "TYPE2/@"
-
-HoFLoadPlayerPics:
-	ld de, RedPicFront
-	ld a, BANK(RedPicFront)
-	call UncompressSpriteFromDE
-	ld hl, sSpriteBuffer1
-	ld de, sSpriteBuffer0
-	ld bc, $310
-	call CopyData
-	ld de, vFrontPic
-	call InterlaceMergeSpriteBuffers
-	ld de, RedPicBack
-	ld a, BANK(RedPicBack)
-	call UncompressSpriteFromDE
-	predef ScaleSpriteByTwo
-	ld de, vBackPic
-	call InterlaceMergeSpriteBuffers
-	ld c, $1
-
-HoFLoadMonPlayerPicTileIDs:
-; c = base tile ID
-	ld b, 0
-	coord hl, 12, 5
-	predef_jump CopyTileIDsFromList
-
-HoFDisplayPlayerStats:
-	SetEvent EVENT_HALL_OF_FAME_DEX_RATING
-	predef DisplayDexRating
-	coord hl, 0, 4
-	ld b, 6
-	ld c, 10
-	call TextBoxBorder
-	coord hl, 5, 0
-	ld b, 2
-	ld c, 9
-	call TextBoxBorder
-	coord hl, 7, 2
-	ld de, wPlayerName
-	call PlaceString
-	coord hl, 1, 6
-	ld de, HoFPlayTimeText
-	call PlaceString
-	coord hl, 5, 7
-	ld de, wPlayTimeHours
-	lb bc, 1, 3
-	call PrintNumber
-	ld [hl], $6d
-	inc hl
-	ld de, wPlayTimeMinutes
-	lb bc, LEADING_ZEROES | 1, 2
-	call PrintNumber
-	coord hl, 1, 9
-	ld de, HoFMoneyText
-	call PlaceString
-	coord hl, 4, 10
-	ld de, wPlayerMoney
-	ld c, $a3
-	call PrintBCDNumber
-	ld hl, DexSeenOwnedText
-	call HoFPrintTextAndDelay
-	ld hl, DexRatingText
-	call HoFPrintTextAndDelay
-	ld hl, wDexRatingText
-
-HoFPrintTextAndDelay:
-	call PrintText
-	ld c, 120
-	jp DelayFrames
-
-HoFPlayTimeText:
-	db "PLAY TIME@"
-
-HoFMoneyText:
-	db "MONEY@"
-
-DexSeenOwnedText:
-	TX_FAR _DexSeenOwnedText
-	db "@"
-
-DexRatingText:
-	TX_FAR _DexRatingText
-	db "@"
-
-HoFRecordMonInfo:
-	ld hl, wHallOfFame
-	ld bc, HOF_MON
-	ld a, [wHoFPartyMonIndex]
-	call AddNTimes
-	ld a, [wHoFMonSpecies]
-	ld [hli], a
-	ld a, [wHoFMonLevel]
-	ld [hli], a
-	ld e, l
-	ld d, h
-	ld hl, wcd6d
-	ld bc, NAME_LENGTH
-	jp CopyData
-
-HoFFadeOutScreenAndMusic:
-	ld a, 10
-	ld [wAudioFadeOutCounterReloadValue], a
-	ld [wAudioFadeOutCounter], a
-	ld a, $ff
-	ld [wAudioFadeOutControl], a
-	jp GBFadeOutToWhite
--- a/engine/heal_party.asm
+++ /dev/null
@@ -1,99 +1,0 @@
-HealParty:
-; Restore HP and PP.
-
-	ld hl, wPartySpecies
-	ld de, wPartyMon1HP
-.healmon
-	ld a, [hli]
-	cp $ff
-	jr z, .done
-
-	push hl
-	push de
-
-	ld hl, wPartyMon1Status - wPartyMon1HP
-	add hl, de
-	xor a
-	ld [hl], a
-
-	push de
-	ld b, NUM_MOVES ; A Pokémon has 4 moves
-.pp
-	ld hl, wPartyMon1Moves - wPartyMon1HP
-	add hl, de
-
-	ld a, [hl]
-	and a
-	jr z, .nextmove
-
-	dec a
-	ld hl, wPartyMon1PP - wPartyMon1HP
-	add hl, de
-
-	push hl
-	push de
-	push bc
-
-	ld hl, Moves
-	ld bc, MoveEnd - Moves
-	call AddNTimes
-	ld de, wcd6d
-	ld a, BANK(Moves)
-	call FarCopyData
-	ld a, [wcd6d + 5] ; PP is byte 5 of move data
-
-	pop bc
-	pop de
-	pop hl
-
-	inc de
-	push bc
-	ld b, a
-	ld a, [hl]
-	and $c0
-	add b
-	ld [hl], a
-	pop bc
-
-.nextmove
-	dec b
-	jr nz, .pp
-	pop de
-
-	ld hl, wPartyMon1MaxHP - wPartyMon1HP
-	add hl, de
-	ld a, [hli]
-	ld [de], a
-	inc de
-	ld a, [hl]
-	ld [de], a
-
-	pop de
-	pop hl
-
-	push hl
-	ld bc, wPartyMon2 - wPartyMon1
-	ld h, d
-	ld l, e
-	add hl, bc
-	ld d, h
-	ld e, l
-	pop hl
-	jr .healmon
-
-.done
-	xor a
-	ld [wWhichPokemon], a
-	ld [wd11e], a
-
-	ld a, [wPartyCount]
-	ld b, a
-.ppup
-	push bc
-	call RestoreBonusPP
-	pop bc
-	ld hl, wWhichPokemon
-	inc [hl]
-	dec b
-	jr nz, .ppup
-	ret
--- a/engine/hidden_object_functions14.asm
+++ /dev/null
@@ -1,100 +1,0 @@
-PrintNotebookText:
-	call EnableAutoTextBoxDrawing
-	ld a, $1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld a, [wHiddenObjectFunctionArgument]
-	jp PrintPredefTextID
-
-TMNotebook::
-	TX_FAR TMNotebookText
-	TX_WAIT
-	db "@"
-
-ViridianSchoolNotebook::
-	TX_ASM
-	ld hl, ViridianSchoolNotebookText1
-	call PrintText
-	call TurnPageSchoolNotebook
-	jr nz, .doneReading
-	ld hl, ViridianSchoolNotebookText2
-	call PrintText
-	call TurnPageSchoolNotebook
-	jr nz, .doneReading
-	ld hl, ViridianSchoolNotebookText3
-	call PrintText
-	call TurnPageSchoolNotebook
-	jr nz, .doneReading
-	ld hl, ViridianSchoolNotebookText4
-	call PrintText
-	ld hl, ViridianSchoolNotebookText5
-	call PrintText
-.doneReading
-	jp TextScriptEnd
-
-TurnPageSchoolNotebook:
-	ld hl, TurnPageText
-	call PrintText
-	call YesNoChoice
-	ld a, [wCurrentMenuItem]
-	and a
-	ret
-
-TurnPageText:
-	TX_FAR _TurnPageText
-	db "@"
-
-ViridianSchoolNotebookText5:
-	TX_FAR _ViridianSchoolNotebookText5
-	TX_WAIT
-	db "@"
-
-ViridianSchoolNotebookText1:
-	TX_FAR _ViridianSchoolNotebookText1
-	db "@"
-
-ViridianSchoolNotebookText2:
-	TX_FAR _ViridianSchoolNotebookText2
-	db "@"
-
-ViridianSchoolNotebookText3:
-	TX_FAR _ViridianSchoolNotebookText3
-	db "@"
-
-ViridianSchoolNotebookText4:
-	TX_FAR _ViridianSchoolNotebookText4
-	db "@"
-
-PrintFightingDojoText2:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump EnemiesOnEverySideText
-
-EnemiesOnEverySideText::
-	TX_FAR _EnemiesOnEverySideText
-	db "@"
-
-PrintFightingDojoText3:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump WhatGoesAroundComesAroundText
-
-WhatGoesAroundComesAroundText::
-	TX_FAR _WhatGoesAroundComesAroundText
-	db "@"
-
-PrintFightingDojoText:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump FightingDojoText
-
-FightingDojoText::
-	TX_FAR _FightingDojoText
-	db "@"
-
-PrintIndigoPlateauHQText:
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP
-	ret nz
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump IndigoPlateauHQText
-
-IndigoPlateauHQText::
-	TX_FAR _IndigoPlateauHQText
-	db "@"
--- a/engine/hidden_object_functions17.asm
+++ /dev/null
@@ -1,475 +1,0 @@
-PrintRedSNESText:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump RedBedroomSNESText
-
-RedBedroomSNESText::
-	TX_FAR _RedBedroomSNESText
-	db "@"
-
-OpenRedsPC:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump RedBedroomPCText
-
-RedBedroomPCText::
-	TX_PLAYERS_PC
-
-Route15GateLeftBinoculars:
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP
-	ret nz
-	call EnableAutoTextBoxDrawing
-	tx_pre Route15UpstairsBinocularsText
-	ld a, ARTICUNO
-	ld [wcf91], a
-	call PlayCry
-	jp DisplayMonFrontSpriteInBox
-
-Route15UpstairsBinocularsText::
-	TX_FAR _Route15UpstairsBinocularsText
-	db "@"
-
-AerodactylFossil:
-	ld a, FOSSIL_AERODACTYL
-	ld [wcf91], a
-	call DisplayMonFrontSpriteInBox
-	call EnableAutoTextBoxDrawing
-	tx_pre AerodactylFossilText
-	ret
-
-AerodactylFossilText::
-	TX_FAR _AerodactylFossilText
-	db "@"
-
-KabutopsFossil:
-	ld a, FOSSIL_KABUTOPS
-	ld [wcf91], a
-	call DisplayMonFrontSpriteInBox
-	call EnableAutoTextBoxDrawing
-	tx_pre KabutopsFossilText
-	ret
-
-KabutopsFossilText::
-	TX_FAR _KabutopsFossilText
-	db "@"
-
-DisplayMonFrontSpriteInBox:
-; Displays a pokemon's front sprite in a pop-up window.
-; [wcf91] = pokemon internal id number
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Delay3
-	xor a
-	ld [hWY], a
-	call SaveScreenTilesToBuffer1
-	ld a, MON_SPRITE_POPUP
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	call UpdateSprites
-	ld a, [wcf91]
-	ld [wd0b5], a
-	call GetMonHeader
-	ld de, vChars1 + $310
-	call LoadMonFrontSprite
-	ld a, $80
-	ld [hStartTileID], a
-	coord hl, 10, 11
-	predef AnimateSendingOutMon
-	call WaitForTextScrollButtonPress
-	call LoadScreenTilesFromBuffer1
-	call Delay3
-	ld a, $90
-	ld [hWY], a
-	ret
-
-PrintBlackboardLinkCableText:
-	call EnableAutoTextBoxDrawing
-	ld a, $1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld a, [wHiddenObjectFunctionArgument]
-	call PrintPredefTextID
-	ret
-
-LinkCableHelp::
-	TX_ASM
-	call SaveScreenTilesToBuffer1
-	ld hl, LinkCableHelpText1
-	call PrintText
-	xor a
-	ld [wMenuItemOffset], a ; not used
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 3
-	ld [wMaxMenuItem], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-.linkHelpLoop
-	ld hl, wd730
-	set 6, [hl]
-	coord hl, 0, 0
-	ld b, 8
-	ld c, 13
-	call TextBoxBorder
-	coord hl, 2, 2
-	ld de, HowToLinkText
-	call PlaceString
-	ld hl, LinkCableHelpText2
-	call PrintText
-	call HandleMenuInput
-	bit 1, a ; pressed b
-	jr nz, .exit
-	ld a, [wCurrentMenuItem]
-	cp 3 ; pressed a on "STOP READING"
-	jr z, .exit
-	ld hl, wd730
-	res 6, [hl]
-	ld hl, LinkCableInfoTexts
-	add a
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	call PrintText
-	jp .linkHelpLoop
-.exit
-	ld hl, wd730
-	res 6, [hl]
-	call LoadScreenTilesFromBuffer1
-	jp TextScriptEnd
-
-LinkCableHelpText1:
-	TX_FAR _LinkCableHelpText1
-	db "@"
-
-LinkCableHelpText2:
-	TX_FAR _LinkCableHelpText2
-	db "@"
-
-HowToLinkText:
-	db   "HOW TO LINK"
-	next "COLOSSEUM"
-	next "TRADE CENTER"
-	next "STOP READING@"
-
-LinkCableInfoTexts:
-	dw LinkCableInfoText1
-	dw LinkCableInfoText2
-	dw LinkCableInfoText3
-
-LinkCableInfoText1:
-	TX_FAR _LinkCableInfoText1
-	db "@"
-
-LinkCableInfoText2:
-	TX_FAR _LinkCableInfoText2
-	db "@"
-
-LinkCableInfoText3:
-	TX_FAR _LinkCableInfoText3
-	db "@"
-
-ViridianSchoolBlackboard::
-	TX_ASM
-	call SaveScreenTilesToBuffer1
-	ld hl, ViridianSchoolBlackboardText1
-	call PrintText
-	xor a
-	ld [wMenuItemOffset], a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld a, D_LEFT | D_RIGHT | A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 2
-	ld [wMaxMenuItem], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-.blackboardLoop
-	ld hl, wd730
-	set 6, [hl]
-	coord hl, 0, 0
-	lb bc, 6, 10
-	call TextBoxBorder
-	coord hl, 1, 2
-	ld de, StatusAilmentText1
-	call PlaceString
-	coord hl, 6, 2
-	ld de, StatusAilmentText2
-	call PlaceString
-	ld hl, ViridianSchoolBlackboardText2
-	call PrintText
-	call HandleMenuInput ; pressing up and down is handled in here
-	bit 1, a ; pressed b
-	jr nz, .exitBlackboard
-	bit 4, a ; pressed right
-	jr z, .didNotPressRight
-	; move cursor to right column
-	ld a, 2
-	ld [wMaxMenuItem], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 6
-	ld [wTopMenuItemX], a
-	ld a, 3 ; in the the right column, use an offset to prevent overlap
-	ld [wMenuItemOffset], a
-	jr .blackboardLoop
-.didNotPressRight
-	bit 5, a ; pressed left
-	jr z, .didNotPressLeftOrRight
-	; move cursor to left column
-	ld a, 2
-	ld [wMaxMenuItem], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-	xor a
-	ld [wMenuItemOffset], a
-	jr .blackboardLoop
-.didNotPressLeftOrRight
-	ld a, [wCurrentMenuItem]
-	ld b, a
-	ld a, [wMenuItemOffset]
-	add b
-	cp 5 ; cursor is pointing to "QUIT"
-	jr z, .exitBlackboard
-	; we must have pressed a on a status condition
-	; so print the text
-	ld hl, wd730
-	res 6, [hl]
-	ld hl, ViridianBlackboardStatusPointers
-	add a
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	call PrintText
-	jp .blackboardLoop
-.exitBlackboard
-	ld hl, wd730
-	res 6, [hl]
-	call LoadScreenTilesFromBuffer1
-	jp TextScriptEnd
-
-ViridianSchoolBlackboardText1:
-	TX_FAR _ViridianSchoolBlackboardText1
-	db "@"
-
-ViridianSchoolBlackboardText2:
-	TX_FAR _ViridianSchoolBlackboardText2
-	db "@"
-
-StatusAilmentText1:
-	db   " SLP"
-	next " PSN"
-	next " PAR@"
-
-StatusAilmentText2:
-	db   " BRN"
-	next " FRZ"
-	next " QUIT@@"
-
-ViridianBlackboardStatusPointers:
-	dw ViridianBlackboardSleepText
-	dw ViridianBlackboardPoisonText
-	dw ViridianBlackboardPrlzText
-	dw ViridianBlackboardBurnText
-	dw ViridianBlackboardFrozenText
-
-ViridianBlackboardSleepText:
-	TX_FAR _ViridianBlackboardSleepText
-	db "@"
-
-ViridianBlackboardPoisonText:
-	TX_FAR _ViridianBlackboardPoisonText
-	db "@"
-
-ViridianBlackboardPrlzText:
-	TX_FAR _ViridianBlackboardPrlzText
-	db "@"
-
-ViridianBlackboardBurnText:
-	TX_FAR _ViridianBlackboardBurnText
-	db "@"
-
-ViridianBlackboardFrozenText:
-	TX_FAR _ViridianBlackboardFrozenText
-	db "@"
-
-PrintTrashText:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump VermilionGymTrashText
-
-VermilionGymTrashText::
-	TX_FAR _VermilionGymTrashText
-	db "@"
-
-GymTrashScript:
-	call EnableAutoTextBoxDrawing
-	ld a, [wHiddenObjectFunctionArgument]
-	ld [wGymTrashCanIndex], a
-
-; Don't do the trash can puzzle if it's already been done.
-	CheckEvent EVENT_2ND_LOCK_OPENED
-	jr z, .ok
-
-	tx_pre_jump VermilionGymTrashText
-
-.ok
-	CheckEventReuseA EVENT_1ST_LOCK_OPENED
-	jr nz, .trySecondLock
-
-	ld a, [wFirstLockTrashCanIndex]
-	ld b, a
-	ld a, [wGymTrashCanIndex]
-	cp b
-	jr z, .openFirstLock
-
-	tx_pre_id VermilionGymTrashText
-	jr .done
-
-.openFirstLock
-; Next can is trying for the second switch.
-	SetEvent EVENT_1ST_LOCK_OPENED
-
-	ld hl, GymTrashCans
-	ld a, [wGymTrashCanIndex]
-	; * 5
-	ld b, a
-	add a
-	add a
-	add b
-
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hli]
-
-; There is a bug in this code. It should calculate a value in the range [0, 3]
-; but if the mask and random number don't have any 1 bits in common, then
-; the result of the AND will be 0. When 1 is subtracted from that, the value
-; will become $ff. This will result in 255 being added to hl, which will cause
-; hl to point to one of the zero bytes that pad the end of the ROM bank.
-; Trash can 0 was intended to be able to have the second lock only when the
-; first lock was in trash can 1 or 3. However, due to this bug, trash can 0 can
-; have the second lock regardless of which trash can had the first lock.
-
-	ld [hGymTrashCanRandNumMask], a
-	push hl
-	call Random
-	swap a
-	ld b, a
-	ld a, [hGymTrashCanRandNumMask]
-	and b
-	dec a
-	pop hl
-
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hl]
-	and $f
-	ld [wSecondLockTrashCanIndex], a
-
-	tx_pre_id VermilionGymTrashSuccessText1
-	jr .done
-
-.trySecondLock
-	ld a, [wSecondLockTrashCanIndex]
-	ld b, a
-	ld a, [wGymTrashCanIndex]
-	cp b
-	jr z, .openSecondLock
-
-; Reset the cans.
-	ResetEvent EVENT_1ST_LOCK_OPENED
-	call Random
-
-	and $e
-	ld [wFirstLockTrashCanIndex], a
-
-	tx_pre_id VermilionGymTrashFailText
-	jr .done
-
-.openSecondLock
-; Completed the trash can puzzle.
-	SetEvent EVENT_2ND_LOCK_OPENED
-	ld hl, wCurrentMapScriptFlags
-	set 6, [hl]
-
-	tx_pre_id VermilionGymTrashSuccessText3
-
-.done
-	jp PrintPredefTextID
-
-GymTrashCans:
-; byte 0: mask for random number
-; bytes 1-4: indices of the trash cans that can have the second lock
-;            (but see the comment above explaining a bug regarding this)
-; Note that the mask is simply the number of valid trash can indices that
-; follow. The remaining bytes are filled with 0 to pad the length of each entry
-; to 5 bytes.
-	db 2,  1,  3,  0,  0 ; 0
-	db 3,  0,  2,  4,  0 ; 1
-	db 2,  1,  5,  0,  0 ; 2
-	db 3,  0,  4,  6,  0 ; 3
-	db 4,  1,  3,  5,  7 ; 4
-	db 3,  2,  4,  8,  0 ; 5
-	db 3,  3,  7,  9,  0 ; 6
-	db 4,  4,  6,  8, 10 ; 7
-	db 3,  5,  7, 11,  0 ; 8
-	db 3,  6, 10, 12,  0 ; 9
-	db 4,  7,  9, 11, 13 ; 10
-	db 3,  8, 10, 14,  0 ; 11
-	db 2,  9, 13,  0,  0 ; 12
-	db 3, 10, 12, 14,  0 ; 13
-	db 2, 11, 13,  0,  0 ; 14
-
-VermilionGymTrashSuccessText1::
-	TX_FAR _VermilionGymTrashSuccessText1
-	TX_ASM
-	call WaitForSoundToFinish
-	ld a, SFX_SWITCH
-	call PlaySound
-	call WaitForSoundToFinish
-	jp TextScriptEnd
-
-; unused
-VermilionGymTrashSuccessText2::
-	TX_FAR _VermilionGymTrashSuccessText2
-	db "@"
-
-; unused
-VermilionGymTrashSuccesPlaySfx:
-	TX_ASM
-	call WaitForSoundToFinish
-	ld a, SFX_SWITCH
-	call PlaySound
-	call WaitForSoundToFinish
-	jp TextScriptEnd
-
-VermilionGymTrashSuccessText3::
-	TX_FAR _VermilionGymTrashSuccessText3
-	TX_ASM
-	call WaitForSoundToFinish
-	ld a, SFX_GO_INSIDE
-	call PlaySound
-	call WaitForSoundToFinish
-	jp TextScriptEnd
-
-VermilionGymTrashFailText::
-	TX_FAR _VermilionGymTrashFailText
-	TX_ASM
-	call WaitForSoundToFinish
-	ld a, SFX_DENIED
-	call PlaySound
-	call WaitForSoundToFinish
-	jp TextScriptEnd
--- a/engine/hidden_object_functions18.asm
+++ /dev/null
@@ -1,198 +1,0 @@
-GymStatues:
-; if in a gym and have the corresponding badge, a = GymStatueText2_id and jp PrintPredefTextID
-; if in a gym and don’t have the corresponding badge, a = GymStatueText1_id and jp PrintPredefTextID
-; else ret
-	call EnableAutoTextBoxDrawing
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP
-	ret nz
-	ld hl, .BadgeFlags
-	ld a, [wCurMap]
-	ld b, a
-.loop
-	ld a, [hli]
-	cp $ff
-	ret z
-	cp b
-	jr z, .match
-	inc hl
-	jr .loop
-.match
-	ld b, [hl]
-	ld a, [wBeatGymFlags]
-	and b
-	cp b
-	tx_pre_id GymStatueText2
-	jr z, .haveBadge
-	tx_pre_id GymStatueText1
-.haveBadge
-	jp PrintPredefTextID
-
-.BadgeFlags:
-	db PEWTER_GYM,   %00000001
-	db CERULEAN_GYM, %00000010
-	db VERMILION_GYM,%00000100
-	db CELADON_GYM,  %00001000
-	db FUCHSIA_GYM,  %00010000
-	db SAFFRON_GYM,  %00100000
-	db CINNABAR_GYM, %01000000
-	db VIRIDIAN_GYM, %10000000
-	db $ff
-
-GymStatueText1::
-	TX_FAR _GymStatueText1
-	db "@"
-
-GymStatueText2::
-	TX_FAR _GymStatueText2
-	db "@"
-
-PrintBenchGuyText:
-	call EnableAutoTextBoxDrawing
-	ld hl, BenchGuyTextPointers
-	ld a, [wCurMap]
-	ld b, a
-.loop
-	ld a, [hli]
-	cp $ff
-	ret z
-	cp b
-	jr z, .match
-	inc hl
-	inc hl
-	jr .loop
-.match
-	ld a, [hli]
-	ld b, a
-	ld a, [wSpriteStateData1 + 9]
-	cp b
-	jr nz, .loop ; player isn't facing left at the bench guy
-	ld a, [hl]
-	jp PrintPredefTextID
-
-; format: db map id, player sprite facing direction, text id of PredefTextIDPointerTable
-BenchGuyTextPointers:
-	db VIRIDIAN_POKECENTER,   SPRITE_FACING_LEFT
-	db_tx_pre ViridianCityPokecenterBenchGuyText
-	db PEWTER_POKECENTER,     SPRITE_FACING_LEFT
-	db_tx_pre PewterCityPokecenterBenchGuyText
-	db CERULEAN_POKECENTER,   SPRITE_FACING_LEFT
-	db_tx_pre CeruleanCityPokecenterBenchGuyText
-	db LAVENDER_POKECENTER,   SPRITE_FACING_LEFT
-	db_tx_pre LavenderCityPokecenterBenchGuyText
-	db VERMILION_POKECENTER,  SPRITE_FACING_LEFT
-	db_tx_pre VermilionCityPokecenterBenchGuyText
-	db CELADON_POKECENTER,    SPRITE_FACING_LEFT
-	db_tx_pre CeladonCityPokecenterBenchGuyText
-	db CELADON_HOTEL,         SPRITE_FACING_LEFT
-	db_tx_pre CeladonCityHotelText
-	db FUCHSIA_POKECENTER,    SPRITE_FACING_LEFT
-	db_tx_pre FuchsiaCityPokecenterBenchGuyText
-	db CINNABAR_POKECENTER,   SPRITE_FACING_LEFT
-	db_tx_pre CinnabarIslandPokecenterBenchGuyText
-	db SAFFRON_POKECENTER,    SPRITE_FACING_LEFT
-	db_tx_pre SaffronCityPokecenterBenchGuyText
-	db MT_MOON_POKECENTER,    SPRITE_FACING_LEFT
-	db_tx_pre MtMoonPokecenterBenchGuyText
-	db ROCK_TUNNEL_POKECENTER,SPRITE_FACING_LEFT
-	db_tx_pre RockTunnelPokecenterBenchGuyText
-	db $FF
-
-ViridianCityPokecenterBenchGuyText::
-	TX_FAR _ViridianCityPokecenterGuyText
-	db "@"
-
-PewterCityPokecenterBenchGuyText::
-	TX_FAR _PewterCityPokecenterGuyText
-	db "@"
-
-CeruleanCityPokecenterBenchGuyText::
-	TX_FAR _CeruleanPokecenterGuyText
-	db "@"
-
-LavenderCityPokecenterBenchGuyText::
-	TX_FAR _LavenderPokecenterGuyText
-	db "@"
-
-MtMoonPokecenterBenchGuyText::
-	TX_FAR _MtMoonPokecenterBenchGuyText
-	db "@"
-
-RockTunnelPokecenterBenchGuyText::
-	TX_FAR _RockTunnelPokecenterGuyText
-	db "@"
-
-UnusedBenchGuyText1::
-	TX_FAR _UnusedBenchGuyText1
-	db "@"
-
-UnusedBenchGuyText2::
-	TX_FAR _UnusedBenchGuyText2
-	db "@"
-
-UnusedBenchGuyText3::
-	TX_FAR _UnusedBenchGuyText3
-	db "@"
-
-VermilionCityPokecenterBenchGuyText::
-	TX_FAR _VermilionPokecenterGuyText
-	db "@"
-
-CeladonCityPokecenterBenchGuyText::
-	TX_FAR _CeladonCityPokecenterGuyText
-	db "@"
-
-FuchsiaCityPokecenterBenchGuyText::
-	TX_FAR _FuchsiaCityPokecenterGuyText
-	db "@"
-
-CinnabarIslandPokecenterBenchGuyText::
-	TX_FAR _CinnabarPokecenterGuyText
-	db "@"
-
-SaffronCityPokecenterBenchGuyText::
-	TX_ASM
-	CheckEvent EVENT_BEAT_SILPH_CO_GIOVANNI
-	ld hl, SaffronCityPokecenterBenchGuyText2
-	jr nz, .asm_624f2
-	ld hl, SaffronCityPokecenterBenchGuyText1
-.asm_624f2
-	call PrintText
-	jp TextScriptEnd
-
-SaffronCityPokecenterBenchGuyText1:
-	TX_FAR _SaffronCityPokecenterGuyText1
-	db "@"
-
-SaffronCityPokecenterBenchGuyText2:
-	TX_FAR _SaffronCityPokecenterGuyText2
-	db "@"
-
-CeladonCityHotelText::
-	TX_FAR _CeladonCityHotelText
-	db "@"
-
-	ret
-
-UnusedPredefText::
-	db "@"
-
-PrintBookcaseText:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump BookcaseText
-
-BookcaseText::
-	TX_FAR _BookcaseText
-	db "@"
-
-OpenPokemonCenterPC:
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP ; check to see if player is facing up
-	ret nz
-	call EnableAutoTextBoxDrawing
-	ld a, $1
-	ld [wAutoTextBoxDrawingControl], a
-	tx_pre_jump PokemonCenterPCText
-
-PokemonCenterPCText::
-	TX_POKECENTER_PC
--- a/engine/hidden_object_functions3.asm
+++ /dev/null
@@ -1,117 +1,0 @@
-; prints text for bookshelves in buildings without sign events
-PrintBookshelfText::
-	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
-	cp SPRITE_FACING_UP
-	jr nz, .noMatch
-; facing up
-	ld a, [wCurMapTileset]
-	ld b, a
-	aCoord 8, 7
-	ld c, a
-	ld hl, BookshelfTileIDs
-.loop
-	ld a, [hli]
-	cp $ff
-	jr z, .noMatch
-	cp b
-	jr nz, .nextBookshelfEntry1
-	ld a, [hli]
-	cp c
-	jr nz, .nextBookshelfEntry2
-	ld a, [hl]
-	push af
-	call EnableAutoTextBoxDrawing
-	pop af
-	call PrintPredefTextID
-	xor a
-	ld [$ffdb], a
-	ret
-.nextBookshelfEntry1
-	inc hl
-.nextBookshelfEntry2
-	inc hl
-	jr .loop
-.noMatch
-	ld a, $ff
-	ld [$ffdb], a
-	jpba PrintCardKeyText
-
-INCLUDE "data/bookshelf_tile_ids.asm"
-
-IndigoPlateauStatues::
-	TX_ASM
-	ld hl, IndigoPlateauStatuesText1
-	call PrintText
-	ld a, [wXCoord]
-	bit 0, a
-	ld hl, IndigoPlateauStatuesText2
-	jr nz, .ok
-	ld hl, IndigoPlateauStatuesText3
-.ok
-	call PrintText
-	jp TextScriptEnd
-
-IndigoPlateauStatuesText1:
-	TX_FAR _IndigoPlateauStatuesText1
-	db "@"
-
-IndigoPlateauStatuesText2:
-	TX_FAR _IndigoPlateauStatuesText2
-	db "@"
-
-IndigoPlateauStatuesText3:
-	TX_FAR _IndigoPlateauStatuesText3
-	db "@"
-
-BookOrSculptureText::
-	TX_ASM
-	ld hl, PokemonBooksText
-	ld a, [wCurMapTileset]
-	cp MANSION ; Celadon Mansion tileset
-	jr nz, .ok
-	aCoord 8, 6
-	cp $38
-	jr nz, .ok
-	ld hl, DiglettSculptureText
-.ok
-	call PrintText
-	jp TextScriptEnd
-
-PokemonBooksText:
-	TX_FAR _PokemonBooksText
-	db "@"
-
-DiglettSculptureText:
-	TX_FAR _DiglettSculptureText
-	db "@"
-
-ElevatorText::
-	TX_FAR _ElevatorText
-	db "@"
-
-TownMapText::
-	TX_FAR _TownMapText
-	TX_BLINK
-	TX_ASM
-	ld a, $1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld hl, wd730
-	set 6, [hl]
-	call GBPalWhiteOutWithDelay3
-	xor a
-	ld [hWY], a
-	inc a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call LoadFontTilePatterns
-	callba DisplayTownMap
-	ld hl, wd730
-	res 6, [hl]
-	ld de, TextScriptEnd
-	push de
-	ld a, [H_LOADEDROMBANK]
-	push af
-	jp CloseTextDisplay
-
-PokemonStuffText::
-	TX_FAR _PokemonStuffText
-	db "@"
--- a/engine/hidden_object_functions7.asm
+++ /dev/null
@@ -1,467 +1,0 @@
-PrintNewBikeText:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump NewBicycleText
-
-NewBicycleText::
-	TX_FAR _NewBicycleText
-	db "@"
-
-DisplayOakLabLeftPoster:
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump PushStartText
-
-PushStartText::
-	TX_FAR _PushStartText
-	db "@"
-
-DisplayOakLabRightPoster:
-	call EnableAutoTextBoxDrawing
-	ld hl, wPokedexOwned
-	ld b, wPokedexOwnedEnd - wPokedexOwned
-	call CountSetBits
-	ld a, [wNumSetBits]
-	cp 2
-	tx_pre_id SaveOptionText
-	jr c, .ownLessThanTwo
-	; own two or more mon
-	tx_pre_id StrengthsAndWeaknessesText
-.ownLessThanTwo
-	jp PrintPredefTextID
-
-SaveOptionText::
-	TX_FAR _SaveOptionText
-	db "@"
-
-StrengthsAndWeaknessesText::
-	TX_FAR _StrengthsAndWeaknessesText
-	db "@"
-
-SafariZoneCheck::
-	CheckEventHL EVENT_IN_SAFARI_ZONE ; if we are not in the Safari Zone,
-	jr z, SafariZoneGameStillGoing ; don't bother printing game over text
-	ld a, [wNumSafariBalls]
-	and a
-	jr z, SafariZoneGameOver
-	jr SafariZoneGameStillGoing
-
-SafariZoneCheckSteps::
-	ld a, [wSafariSteps]
-	ld b, a
-	ld a, [wSafariSteps + 1]
-	ld c, a
-	or b
-	jr z, SafariZoneGameOver
-	dec bc
-	ld a, b
-	ld [wSafariSteps], a
-	ld a, c
-	ld [wSafariSteps + 1], a
-SafariZoneGameStillGoing:
-	xor a
-	ld [wSafariZoneGameOver], a
-	ret
-
-SafariZoneGameOver:
-	call EnableAutoTextBoxDrawing
-	xor a
-	ld [wAudioFadeOutControl], a
-	dec a
-	call PlaySound
-	ld c, BANK(SFX_Safari_Zone_PA)
-	ld a, SFX_SAFARI_ZONE_PA
-	call PlayMusic
-.waitForMusicToPlay
-	ld a, [wChannelSoundIDs + Ch5]
-	cp SFX_SAFARI_ZONE_PA
-	jr nz, .waitForMusicToPlay
-	ld a, TEXT_SAFARI_GAME_OVER
-	ld [hSpriteIndexOrTextID], a
-	call DisplayTextID
-	xor a
-	ld [wPlayerMovingDirection], a
-	ld a, SAFARI_ZONE_GATE
-	ld [hWarpDestinationMap], a
-	ld a, $3
-	ld [wDestinationWarpID], a
-	ld a, $5
-	ld [wSafariZoneGateCurScript], a
-	SetEvent EVENT_SAFARI_GAME_OVER
-	ld a, 1
-	ld [wSafariZoneGameOver], a
-	ret
-
-PrintSafariGameOverText::
-	xor a
-	ld [wJoyIgnore], a
-	ld hl, SafariGameOverText
-	jp PrintText
-
-SafariGameOverText:
-	TX_ASM
-	ld a, [wNumSafariBalls]
-	and a
-	jr z, .noMoreSafariBalls
-	ld hl, TimesUpText
-	call PrintText
-.noMoreSafariBalls
-	ld hl, GameOverText
-	call PrintText
-	jp TextScriptEnd
-
-TimesUpText:
-	TX_FAR _TimesUpText
-	db "@"
-
-GameOverText:
-	TX_FAR _GameOverText
-	db "@"
-
-PrintCinnabarQuiz:
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP
-	ret nz
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump CinnabarGymQuiz
-
-CinnabarGymQuiz::
-	TX_ASM
-	xor a
-	ld [wOpponentAfterWrongAnswer], a
-	ld a, [wHiddenObjectFunctionArgument]
-	push af
-	and $f
-	ld [hGymGateIndex], a
-	pop af
-	and $f0
-	swap a
-	ld [$ffdc], a
-	ld hl, CinnabarGymQuizIntroText
-	call PrintText
-	ld a, [hGymGateIndex]
-	dec a
-	add a
-	ld d, 0
-	ld e, a
-	ld hl, CinnabarQuizQuestions
-	add hl, de
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	call PrintText
-	ld a, 1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	call CinnabarGymQuiz_1ea92
-	jp TextScriptEnd
-
-CinnabarGymQuizIntroText:
-	TX_FAR _CinnabarGymQuizIntroText
-	db "@"
-
-CinnabarQuizQuestions:
-	dw CinnabarQuizQuestionsText1
-	dw CinnabarQuizQuestionsText2
-	dw CinnabarQuizQuestionsText3
-	dw CinnabarQuizQuestionsText4
-	dw CinnabarQuizQuestionsText5
-	dw CinnabarQuizQuestionsText6
-
-CinnabarQuizQuestionsText1:
-	TX_FAR _CinnabarQuizQuestionsText1
-	db "@"
-
-CinnabarQuizQuestionsText2:
-	TX_FAR _CinnabarQuizQuestionsText2
-	db "@"
-
-CinnabarQuizQuestionsText3:
-	TX_FAR _CinnabarQuizQuestionsText3
-	db "@"
-
-CinnabarQuizQuestionsText4:
-	TX_FAR _CinnabarQuizQuestionsText4
-	db "@"
-
-CinnabarQuizQuestionsText5:
-	TX_FAR _CinnabarQuizQuestionsText5
-	db "@"
-
-CinnabarQuizQuestionsText6:
-	TX_FAR _CinnabarQuizQuestionsText6
-	db "@"
-
-CinnabarGymGateFlagAction:
-	EventFlagAddress hl, EVENT_CINNABAR_GYM_GATE0_UNLOCKED
-	predef_jump FlagActionPredef
-
-CinnabarGymQuiz_1ea92:
-	call YesNoChoice
-	ld a, [$ffdc]
-	ld c, a
-	ld a, [wCurrentMenuItem]
-	cp c
-	jr nz, .wrongAnswer
-	ld hl, wCurrentMapScriptFlags
-	set 5, [hl]
-	ld a, [hGymGateIndex]
-	ld [$ffe0], a
-	ld hl, CinnabarGymQuizCorrectText
-	call PrintText
-	ld a, [$ffe0]
-	AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
-	ld c, a
-	ld b, FLAG_SET
-	call CinnabarGymGateFlagAction
-	jp UpdateCinnabarGymGateTileBlocks_
-.wrongAnswer
-	call WaitForSoundToFinish
-	ld a, SFX_DENIED
-	call PlaySound
-	call WaitForSoundToFinish
-	ld hl, CinnabarGymQuizIncorrectText
-	call PrintText
-	ld a, [hGymGateIndex]
-	add $2
-	AdjustEventBit EVENT_BEAT_CINNABAR_GYM_TRAINER_0, 2
-	ld c, a
-	ld b, FLAG_TEST
-	EventFlagAddress hl, EVENT_BEAT_CINNABAR_GYM_TRAINER_0
-	predef FlagActionPredef
-	ld a, c
-	and a
-	ret nz
-	ld a, [hGymGateIndex]
-	add $2
-	ld [wOpponentAfterWrongAnswer], a
-	ret
-
-CinnabarGymQuizCorrectText:
-	TX_SFX_ITEM_1
-	TX_FAR _CinnabarGymQuizCorrectText
-	TX_BLINK
-	TX_ASM
-
-	ld a, [$ffe0]
-	AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
-	ld c, a
-	ld b, FLAG_TEST
-	call CinnabarGymGateFlagAction
-	ld a, c
-	and a
-	jp nz, TextScriptEnd
-	call WaitForSoundToFinish
-	ld a, SFX_GO_INSIDE
-	call PlaySound
-	call WaitForSoundToFinish
-	jp TextScriptEnd
-
-CinnabarGymQuizIncorrectText:
-	TX_FAR _CinnabarGymQuizIncorrectText
-	db "@"
-
-UpdateCinnabarGymGateTileBlocks_::
-; Update the overworld map with open floor blocks or locked gate blocks
-; depending on event flags.
-	ld a, 6
-	ld [hGymGateIndex], a
-.loop
-	ld a, [hGymGateIndex]
-	dec a
-	add a
-	add a
-	ld d, 0
-	ld e, a
-	ld hl, CinnabarGymGateCoords
-	add hl, de
-	ld a, [hli]
-	ld b, [hl]
-	ld c, a
-	inc hl
-	ld a, [hl]
-	ld [wGymGateTileBlock], a
-	push bc
-	ld a, [hGymGateIndex]
-	ld [$ffe0], a
-	AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
-	ld c, a
-	ld b, FLAG_TEST
-	call CinnabarGymGateFlagAction
-	ld a, c
-	and a
-	jr nz, .unlocked
-	ld a, [wGymGateTileBlock]
-	jr .next
-.unlocked
-	ld a, $e
-.next
-	pop bc
-	ld [wNewTileBlockID], a
-	predef ReplaceTileBlock
-	ld hl, hGymGateIndex
-	dec [hl]
-	jr nz, .loop
-	ret
-
-CinnabarGymGateCoords:
-	; format: x-coord, y-coord, direction, padding
-	; direction: $54 = horizontal gate, $5f = vertical gate
-	db $09,$03,$54,$00
-	db $06,$03,$54,$00
-	db $06,$06,$54,$00
-	db $03,$08,$5f,$00
-	db $02,$06,$54,$00
-	db $02,$03,$54,$00
-
-PrintMagazinesText:
-	call EnableAutoTextBoxDrawing
-	tx_pre MagazinesText
-	ret
-
-MagazinesText::
-	TX_FAR _MagazinesText
-	db "@"
-
-BillsHousePC:
-	call EnableAutoTextBoxDrawing
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP
-	ret nz
-	CheckEvent EVENT_LEFT_BILLS_HOUSE_AFTER_HELPING
-	jr nz, .displayBillsHousePokemonList
-	CheckEventReuseA EVENT_USED_CELL_SEPARATOR_ON_BILL
-	jr nz, .displayBillsHouseMonitorText
-	CheckEventReuseA EVENT_BILL_SAID_USE_CELL_SEPARATOR
-	jr nz, .doCellSeparator
-.displayBillsHouseMonitorText
-	tx_pre_jump BillsHouseMonitorText
-.doCellSeparator
-	ld a, $1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	tx_pre BillsHouseInitiatedText
-	ld c, 32
-	call DelayFrames
-	ld a, SFX_TINK
-	call PlaySound
-	call WaitForSoundToFinish
-	ld c, 80
-	call DelayFrames
-	ld a, SFX_SHRINK
-	call PlaySound
-	call WaitForSoundToFinish
-	ld c, 48
-	call DelayFrames
-	ld a, SFX_TINK
-	call PlaySound
-	call WaitForSoundToFinish
-	ld c, 32
-	call DelayFrames
-	ld a, SFX_GET_ITEM_1
-	call PlaySound
-	call WaitForSoundToFinish
-	call PlayDefaultMusic
-	SetEvent EVENT_USED_CELL_SEPARATOR_ON_BILL
-	ret
-.displayBillsHousePokemonList
-	ld a, $1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	tx_pre BillsHousePokemonList
-	ret
-
-BillsHouseMonitorText::
-	TX_FAR _BillsHouseMonitorText
-	db "@"
-
-BillsHouseInitiatedText::
-	TX_FAR _BillsHouseInitiatedText
-	TX_BLINK
-	TX_ASM
-	ld a, $ff
-	ld [wNewSoundID], a
-	call PlaySound
-	ld c, 16
-	call DelayFrames
-	ld a, SFX_SWITCH
-	call PlaySound
-	call WaitForSoundToFinish
-	ld c, 60
-	call DelayFrames
-	jp TextScriptEnd
-
-BillsHousePokemonList::
-	TX_ASM
-	call SaveScreenTilesToBuffer1
-	ld hl, BillsHousePokemonListText1
-	call PrintText
-	xor a
-	ld [wMenuItemOffset], a ; not used
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 4
-	ld [wMaxMenuItem], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-.billsPokemonLoop
-	ld hl, wd730
-	set 6, [hl]
-	coord hl, 0, 0
-	ld b, 10
-	ld c, 9
-	call TextBoxBorder
-	coord hl, 2, 2
-	ld de, BillsMonListText
-	call PlaceString
-	ld hl, BillsHousePokemonListText2
-	call PrintText
-	call SaveScreenTilesToBuffer2
-	call HandleMenuInput
-	bit 1, a ; pressed b
-	jr nz, .cancel
-	ld a, [wCurrentMenuItem]
-	add EEVEE
-	cp EEVEE
-	jr z, .displayPokedex
-	cp FLAREON
-	jr z, .displayPokedex
-	cp JOLTEON
-	jr z, .displayPokedex
-	cp VAPOREON
-	jr z, .displayPokedex
-	jr .cancel
-.displayPokedex
-	call DisplayPokedex
-	call LoadScreenTilesFromBuffer2
-	jr .billsPokemonLoop
-.cancel
-	ld hl, wd730
-	res 6, [hl]
-	call LoadScreenTilesFromBuffer2
-	jp TextScriptEnd
-
-BillsHousePokemonListText1:
-	TX_FAR _BillsHousePokemonListText1
-	db "@"
-
-BillsMonListText:
-	db   "EEVEE"
-	next "FLAREON"
-	next "JOLTEON"
-	next "VAPOREON"
-	next "CANCEL@"
-
-BillsHousePokemonListText2:
-	TX_FAR _BillsHousePokemonListText2
-	db "@"
-
-DisplayOakLabEmailText:
-	ld a, [wSpriteStateData1 + 9]
-	cp SPRITE_FACING_UP
-	ret nz
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump OakLabEmailText
-
-OakLabEmailText::
-	TX_FAR _OakLabEmailText
-	db "@"
--- a/engine/hp_bar.asm
+++ /dev/null
@@ -1,270 +1,0 @@
-HPBarLength:
-	call GetPredefRegisters
-
-; calculates bc * 48 / de, the number of pixels the HP bar has
-; the result is always at least 1
-GetHPBarLength:
-	push hl
-	xor a
-	ld hl, H_MULTIPLICAND
-	ld [hli], a
-	ld a, b
-	ld [hli], a
-	ld a, c
-	ld [hli], a
-	ld [hl], $30
-	call Multiply      ; 48 * bc (hp bar is 48 pixels long)
-	ld a, d
-	and a
-	jr z, .maxHPSmaller256
-	srl d              ; make HP in de fit into 1 byte by dividing by 4
-	rr e
-	srl d
-	rr e
-	ld a, [H_MULTIPLICAND+1]
-	ld b, a
-	ld a, [H_MULTIPLICAND+2]
-	srl b              ; divide multiplication result as well
-	rr a
-	srl b
-	rr a
-	ld [H_MULTIPLICAND+2], a
-	ld a, b
-	ld [H_MULTIPLICAND+1], a
-.maxHPSmaller256
-	ld a, e
-	ld [H_DIVISOR], a
-	ld b, $4
-	call Divide
-	ld a, [H_MULTIPLICAND+2]
-	ld e, a            ; e = bc * 48 / de (num of pixels of HP bar)
-	pop hl
-	and a
-	ret nz
-	ld e, $1           ; make result at least 1
-	ret
-
-; predef $48
-UpdateHPBar:
-UpdateHPBar2:
-	push hl
-	ld hl, wHPBarOldHP
-	ld a, [hli]
-	ld c, a      ; old HP into bc
-	ld a, [hli]
-	ld b, a
-	ld a, [hli]
-	ld e, a      ; new HP into de
-	ld d, [hl]
-	pop hl
-	push de
-	push bc
-	call UpdateHPBar_CalcHPDifference
-	ld a, e
-	ld [wHPBarHPDifference+1], a
-	ld a, d
-	ld [wHPBarHPDifference], a
-	pop bc
-	pop de
-	call UpdateHPBar_CompareNewHPToOldHP
-	ret z
-	ld a, $ff
-	jr c, .HPdecrease
-	ld a, $1
-.HPdecrease
-	ld [wHPBarDelta], a
-	call GetPredefRegisters
-	ld a, [wHPBarNewHP]
-	ld e, a
-	ld a, [wHPBarNewHP+1]
-	ld d, a
-.animateHPBarLoop
-	push de
-	ld a, [wHPBarOldHP]
-	ld c, a
-	ld a, [wHPBarOldHP+1]
-	ld b, a
-	call UpdateHPBar_CompareNewHPToOldHP
-	jr z, .animateHPBarDone
-	jr nc, .HPIncrease
-; HP decrease
-	dec bc        ; subtract 1 HP
-	ld a, c
-	ld [wHPBarNewHP], a
-	ld a, b
-	ld [wHPBarNewHP+1], a
-	call UpdateHPBar_CalcOldNewHPBarPixels
-	ld a, e
-	sub d         ; calc pixel difference
-	jr .ok
-.HPIncrease
-	inc bc        ; add 1 HP
-	ld a, c
-	ld [wHPBarNewHP], a
-	ld a, b
-	ld [wHPBarNewHP+1], a
-	call UpdateHPBar_CalcOldNewHPBarPixels
-	ld a, d
-	sub e         ; calc pixel difference
-.ok
-	call UpdateHPBar_PrintHPNumber
-	and a
-	jr z, .noPixelDifference
-	call UpdateHPBar_AnimateHPBar
-.noPixelDifference
-	ld a, [wHPBarNewHP]
-	ld [wHPBarOldHP], a
-	ld a, [wHPBarNewHP+1]
-	ld [wHPBarOldHP+1], a
-	pop de
-	jr .animateHPBarLoop
-.animateHPBarDone
-	pop de
-	ld a, e
-	ld [wHPBarOldHP], a
-	ld a, d
-	ld [wHPBarOldHP+1], a
-	or e
-	jr z, .monFainted
-	call UpdateHPBar_CalcOldNewHPBarPixels
-	ld d, e
-.monFainted
-	call UpdateHPBar_PrintHPNumber
-	ld a, $1
-	call UpdateHPBar_AnimateHPBar
-	jp Delay3
-
-; animates the HP bar going up or down for (a) ticks (two waiting frames each)
-; stops prematurely if bar is filled up
-; e: current health (in pixels) to start with
-UpdateHPBar_AnimateHPBar:
-	push hl
-.barAnimationLoop
-	push af
-	push de
-	ld d, $6
-	call DrawHPBar
-	ld c, 2
-	call DelayFrames
-	pop de
-	ld a, [wHPBarDelta] ; +1 or -1
-	add e
-	cp $31
-	jr nc, .barFilledUp
-	ld e, a
-	pop af
-	dec a
-	jr nz, .barAnimationLoop
-	pop hl
-	ret
-.barFilledUp
-	pop af
-	pop hl
-	ret
-
-; compares old HP and new HP and sets c and z flags accordingly
-UpdateHPBar_CompareNewHPToOldHP:
-	ld a, d
-	sub b
-	ret nz
-	ld a, e
-	sub c
-	ret
-
-; calcs HP difference between bc and de (into de)
-UpdateHPBar_CalcHPDifference:
-	ld a, d
-	sub b
-	jr c, .oldHPGreater
-	jr z, .testLowerByte
-.newHPGreater
-	ld a, e
-	sub c
-	ld e, a
-	ld a, d
-	sbc b
-	ld d, a
-	ret
-.oldHPGreater
-	ld a, c
-	sub e
-	ld e, a
-	ld a, b
-	sbc d
-	ld d, a
-	ret
-.testLowerByte
-	ld a, e
-	sub c
-	jr c, .oldHPGreater
-	jr nz, .newHPGreater
-	ld de, $0
-	ret
-
-UpdateHPBar_PrintHPNumber:
-	push af
-	push de
-	ld a, [wHPBarType]
-	and a
-	jr z, .done ; don't print number in enemy HUD
-; convert from little-endian to big-endian for PrintNumber
-	ld a, [wHPBarOldHP]
-	ld [wHPBarTempHP + 1], a
-	ld a, [wHPBarOldHP + 1]
-	ld [wHPBarTempHP], a
-	push hl
-	ld a, [hFlags_0xFFF6]
-	bit 0, a
-	jr z, .asm_fb15
-	ld de, $9
-	jr .next
-.asm_fb15
-	ld de, $15
-.next
-	add hl, de
-	push hl
-	ld a, " "
-	ld [hli], a
-	ld [hli], a
-	ld [hli], a
-	pop hl
-	ld de, wHPBarTempHP
-	lb bc, 2, 3
-	call PrintNumber
-	call DelayFrame
-	pop hl
-.done
-	pop de
-	pop af
-	ret
-
-; calcs number of HP bar pixels for old and new HP value
-; d: new pixels
-; e: old pixels
-UpdateHPBar_CalcOldNewHPBarPixels:
-	push hl
-	ld hl, wHPBarMaxHP
-	ld a, [hli]  ; max HP into de
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	ld a, [hli]  ; old HP into bc
-	ld c, a
-	ld a, [hli]
-	ld b, a
-	ld a, [hli]  ; new HP into hl
-	ld h, [hl]
-	ld l, a
-	push hl
-	push de
-	call GetHPBarLength ; calc num pixels for old HP
-	ld a, e
-	pop de
-	pop bc
-	push af
-	call GetHPBarLength ; calc num pixels for new HP
-	pop af
-	ld d, e
-	ld e, a
-	pop hl
-	ret
--- a/engine/in_game_trades.asm
+++ /dev/null
@@ -1,330 +1,0 @@
-DoInGameTradeDialogue:
-; trigger the trade offer/action specified by wWhichTrade
-	call SaveScreenTilesToBuffer2
-	ld hl, TradeMons
-	ld a, [wWhichTrade]
-	ld b, a
-	swap a
-	sub b
-	sub b
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hli]
-	ld [wInGameTradeGiveMonSpecies], a
-	ld a, [hli]
-	ld [wInGameTradeReceiveMonSpecies], a
-	ld a, [hli]
-	push af
-	ld de, wInGameTradeMonNick
-	ld bc, NAME_LENGTH
-	call CopyData
-	pop af
-	ld l, a
-	ld h, 0
-	ld de, InGameTradeTextPointers
-	add hl, hl
-	add hl, de
-	ld a, [hli]
-	ld [wInGameTradeTextPointerTablePointer], a
-	ld a, [hl]
-	ld [wInGameTradeTextPointerTablePointer + 1], a
-	ld a, [wInGameTradeGiveMonSpecies]
-	ld de, wInGameTradeGiveMonName
-	call InGameTrade_GetMonName
-	ld a, [wInGameTradeReceiveMonSpecies]
-	ld de, wInGameTradeReceiveMonName
-	call InGameTrade_GetMonName
-	ld hl, wCompletedInGameTradeFlags
-	ld a, [wWhichTrade]
-	ld c, a
-	ld b, FLAG_TEST
-	predef FlagActionPredef
-	ld a, c
-	and a
-	ld a, $4
-	ld [wInGameTradeTextPointerTableIndex], a
-	jr nz, .printText
-; if the trade hasn't been done yet
-	xor a
-	ld [wInGameTradeTextPointerTableIndex], a
-	call .printText
-	ld a, $1
-	ld [wInGameTradeTextPointerTableIndex], a
-	call YesNoChoice
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .printText
-	call InGameTrade_DoTrade
-	jr c, .printText
-	ld hl, TradedForText
-	call PrintText
-.printText
-	ld hl, wInGameTradeTextPointerTableIndex
-	ld a, [hld] ; wInGameTradeTextPointerTableIndex
-	ld e, a
-	ld d, 0
-	ld a, [hld] ; wInGameTradeTextPointerTablePointer + 1
-	ld l, [hl] ; wInGameTradeTextPointerTablePointer
-	ld h, a
-	add hl, de
-	add hl, de
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	jp PrintText
-
-; copies name of species a to hl
-InGameTrade_GetMonName:
-	push de
-	ld [wd11e], a
-	call GetMonName
-	ld hl, wcd6d
-	pop de
-	ld bc, NAME_LENGTH
-	jp CopyData
-
-INCLUDE "data/trades.asm"
-
-InGameTrade_DoTrade:
-	xor a ; NORMAL_PARTY_MENU
-	ld [wPartyMenuTypeOrMessageID], a
-	dec a
-	ld [wUpdateSpritesEnabled], a
-	call DisplayPartyMenu
-	push af
-	call InGameTrade_RestoreScreen
-	pop af
-	ld a, $1
-	jp c, .tradeFailed ; jump if the player didn't select a pokemon
-	ld a, [wInGameTradeGiveMonSpecies]
-	ld b, a
-	ld a, [wcf91]
-	cp b
-	ld a, $2
-	jr nz, .tradeFailed ; jump if the selected mon's species is not the required one
-	ld a, [wWhichPokemon]
-	ld hl, wPartyMon1Level
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld a, [hl]
-	ld [wCurEnemyLVL], a
-	ld hl, wCompletedInGameTradeFlags
-	ld a, [wWhichTrade]
-	ld c, a
-	ld b, FLAG_SET
-	predef FlagActionPredef
-	ld hl, ConnectCableText
-	call PrintText
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wCurEnemyLVL]
-	push af
-	call LoadHpBarAndStatusTilePatterns
-	call InGameTrade_PrepareTradeData
-	predef InternalClockTradeAnim
-	pop af
-	ld [wCurEnemyLVL], a
-	pop af
-	ld [wWhichPokemon], a
-	ld a, [wInGameTradeReceiveMonSpecies]
-	ld [wcf91], a
-	xor a
-	ld [wMonDataLocation], a ; not used
-	ld [wRemoveMonFromBox], a
-	call RemovePokemon
-	ld a, $80 ; prevent the player from naming the mon
-	ld [wMonDataLocation], a
-	call AddPartyMon
-	call InGameTrade_CopyDataToReceivedMon
-	callab EvolveTradeMon
-	call ClearScreen
-	call InGameTrade_RestoreScreen
-	callba RedrawMapView
-	and a
-	ld a, $3
-	jr .tradeSucceeded
-.tradeFailed
-	scf
-.tradeSucceeded
-	ld [wInGameTradeTextPointerTableIndex], a
-	ret
-
-InGameTrade_RestoreScreen:
-	call GBPalWhiteOutWithDelay3
-	call RestoreScreenTilesAndReloadTilePatterns
-	call ReloadTilesetTilePatterns
-	call LoadScreenTilesFromBuffer2
-	call Delay3
-	call LoadGBPal
-	ld c, 10
-	call DelayFrames
-	jpba LoadWildData
-
-InGameTrade_PrepareTradeData:
-	ld hl, wTradedPlayerMonSpecies
-	ld a, [wInGameTradeGiveMonSpecies]
-	ld [hli], a ; wTradedPlayerMonSpecies
-	ld a, [wInGameTradeReceiveMonSpecies]
-	ld [hl], a ; wTradedEnemyMonSpecies
-	ld hl, wPartyMonOT
-	ld bc, NAME_LENGTH
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld de, wTradedPlayerMonOT
-	ld bc, NAME_LENGTH
-	call InGameTrade_CopyData
-	ld hl, InGameTrade_TrainerString
-	ld de, wTradedEnemyMonOT
-	call InGameTrade_CopyData
-	ld de, wLinkEnemyTrainerName
-	call InGameTrade_CopyData
-	ld hl, wPartyMon1OTID
-	ld bc, wPartyMon2 - wPartyMon1
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld de, wTradedPlayerMonOTID
-	ld bc, $2
-	call InGameTrade_CopyData
-	call Random
-	ld hl, hRandomAdd
-	ld de, wTradedEnemyMonOTID
-	jp CopyData
-
-InGameTrade_CopyData:
-	push hl
-	push bc
-	call CopyData
-	pop bc
-	pop hl
-	ret
-
-InGameTrade_CopyDataToReceivedMon:
-	ld hl, wPartyMonNicks
-	ld bc, NAME_LENGTH
-	call InGameTrade_GetReceivedMonPointer
-	ld hl, wInGameTradeMonNick
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, wPartyMonOT
-	ld bc, NAME_LENGTH
-	call InGameTrade_GetReceivedMonPointer
-	ld hl, InGameTrade_TrainerString
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, wPartyMon1OTID
-	ld bc, wPartyMon2 - wPartyMon1
-	call InGameTrade_GetReceivedMonPointer
-	ld hl, wTradedEnemyMonOTID
-	ld bc, $2
-	jp CopyData
-
-; the received mon's index is (partyCount - 1),
-; so this adds bc to hl (partyCount - 1) times and moves the result to de
-InGameTrade_GetReceivedMonPointer:
-	ld a, [wPartyCount]
-	dec a
-	call AddNTimes
-	ld e, l
-	ld d, h
-	ret
-
-InGameTrade_TrainerString:
-	; "TRAINER@@@@@@@@@@"
-	db $5d, "@@@@@@@@@@"
-
-InGameTradeTextPointers:
-	dw TradeTextPointers1
-	dw TradeTextPointers2
-	dw TradeTextPointers3
-
-TradeTextPointers1:
-	dw WannaTrade1Text
-	dw NoTrade1Text
-	dw WrongMon1Text
-	dw Thanks1Text
-	dw AfterTrade1Text
-
-TradeTextPointers2:
-	dw WannaTrade2Text
-	dw NoTrade2Text
-	dw WrongMon2Text
-	dw Thanks2Text
-	dw AfterTrade2Text
-
-TradeTextPointers3:
-	dw WannaTrade3Text
-	dw NoTrade3Text
-	dw WrongMon3Text
-	dw Thanks3Text
-	dw AfterTrade3Text
-
-ConnectCableText:
-	TX_FAR _ConnectCableText
-	db "@"
-
-TradedForText:
-	TX_FAR _TradedForText
-	TX_SFX_KEY_ITEM
-	TX_DELAY
-	db "@"
-
-WannaTrade1Text:
-	TX_FAR _WannaTrade1Text
-	db "@"
-
-NoTrade1Text:
-	TX_FAR _NoTrade1Text
-	db "@"
-
-WrongMon1Text:
-	TX_FAR _WrongMon1Text
-	db "@"
-
-Thanks1Text:
-	TX_FAR _Thanks1Text
-	db "@"
-
-AfterTrade1Text:
-	TX_FAR _AfterTrade1Text
-	db "@"
-
-WannaTrade2Text:
-	TX_FAR _WannaTrade2Text
-	db "@"
-
-NoTrade2Text:
-	TX_FAR _NoTrade2Text
-	db "@"
-
-WrongMon2Text:
-	TX_FAR _WrongMon2Text
-	db "@"
-
-Thanks2Text:
-	TX_FAR _Thanks2Text
-	db "@"
-
-AfterTrade2Text:
-	TX_FAR _AfterTrade2Text
-	db "@"
-
-WannaTrade3Text:
-	TX_FAR _WannaTrade3Text
-	db "@"
-
-NoTrade3Text:
-	TX_FAR _NoTrade3Text
-	db "@"
-
-WrongMon3Text:
-	TX_FAR _WrongMon3Text
-	db "@"
-
-Thanks3Text:
-	TX_FAR _Thanks3Text
-	db "@"
-
-AfterTrade3Text:
-	TX_FAR _AfterTrade3Text
-	db "@"
--- a/engine/init_player_data.asm
+++ /dev/null
@@ -1,55 +1,0 @@
-InitPlayerData:
-InitPlayerData2:
-
-	call Random
-	ld a, [hRandomSub]
-	ld [wPlayerID], a
-
-	call Random
-	ld a, [hRandomAdd]
-	ld [wPlayerID + 1], a
-
-	ld a, $ff
-	ld [wUnusedD71B], a
-
-	ld hl, wPartyCount
-	call InitializeEmptyList
-	ld hl, wNumInBox
-	call InitializeEmptyList
-	ld hl, wNumBagItems
-	call InitializeEmptyList
-	ld hl, wNumBoxItems
-	call InitializeEmptyList
-
-START_MONEY EQU $3000
-	ld hl, wPlayerMoney + 1
-	ld a, START_MONEY / $100
-	ld [hld], a
-	xor a
-	ld [hli], a
-	inc hl
-	ld [hl], a
-
-	ld [wMonDataLocation], a
-
-	ld hl, wObtainedBadges
-	ld [hli], a
-
-	ld [hl], a
-
-	ld hl, wPlayerCoins
-	ld [hli], a
-	ld [hl], a
-
-	ld hl, wGameProgressFlags
-	ld bc, wGameProgressFlagsEnd - wGameProgressFlags
-	call FillMemory ; clear all game progress flags
-
-	jp InitializeMissableObjectsFlags
-
-InitializeEmptyList:
-	xor a ; count
-	ld [hli], a
-	dec a ; terminator
-	ld [hl], a
-	ret
--- a/engine/intro.asm
+++ /dev/null
@@ -1,470 +1,0 @@
-const_value = -1
-	const MOVE_NIDORINO_RIGHT
-	const MOVE_GENGAR_RIGHT
-	const MOVE_GENGAR_LEFT
-
-ANIMATION_END EQU 80
-
-const_value = 3
-	const GENGAR_INTRO_TILES1
-	const GENGAR_INTRO_TILES2
-	const GENGAR_INTRO_TILES3
-
-PlayIntro:
-	xor a
-	ld [hJoyHeld], a
-	inc a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call PlayShootingStar
-	call PlayIntroScene
-	call GBFadeOutToWhite
-	xor a
-	ld [hSCX], a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call ClearSprites
-	call DelayFrame
-	ret
-
-PlayIntroScene:
-	ld b, SET_PAL_NIDORINO_INTRO
-	call RunPaletteCommand
-	ldPal a, BLACK, DARK_GRAY, LIGHT_GRAY, WHITE
-	ld [rBGP], a
-	ld [rOBP0], a
-	ld [rOBP1], a
-	xor a
-	ld [hSCX], a
-	ld b, GENGAR_INTRO_TILES1
-	call IntroCopyTiles
-	ld a, 0
-	ld [wBaseCoordX], a
-	ld a, 80
-	ld [wBaseCoordY], a
-	lb bc, 6, 6
-	call InitIntroNidorinoOAM
-	lb de, 80 / 2, MOVE_NIDORINO_RIGHT
-	call IntroMoveMon
-	ret c
-
-; hip
-	ld a, SFX_INTRO_HIP
-	call PlaySound
-	xor a
-	ld [wIntroNidorinoBaseTile], a
-	ld de, IntroNidorinoAnimation1
-	call AnimateIntroNidorino
-; hop
-	ld a, SFX_INTRO_HOP
-	call PlaySound
-	ld de, IntroNidorinoAnimation2
-	call AnimateIntroNidorino
-	ld c, 10
-	call CheckForUserInterruption
-	ret c
-
-; hip
-	ld a, SFX_INTRO_HIP
-	call PlaySound
-	ld de, IntroNidorinoAnimation1
-	call AnimateIntroNidorino
-; hop
-	ld a, SFX_INTRO_HOP
-	call PlaySound
-	ld de, IntroNidorinoAnimation2
-	call AnimateIntroNidorino
-	ld c, 30
-	call CheckForUserInterruption
-	ret c
-
-; raise
-	ld b, GENGAR_INTRO_TILES2
-	call IntroCopyTiles
-	ld a, SFX_INTRO_RAISE
-	call PlaySound
-	lb de, 8 / 2, MOVE_GENGAR_LEFT
-	call IntroMoveMon
-	ld c, 30
-	call CheckForUserInterruption
-	ret c
-
-; slash
-	ld b, GENGAR_INTRO_TILES3
-	call IntroCopyTiles
-	ld a, SFX_INTRO_CRASH
-	call PlaySound
-	lb de, 16 / 2, MOVE_GENGAR_RIGHT
-	call IntroMoveMon
-; hip
-	ld a, SFX_INTRO_HIP
-	call PlaySound
-	ld a, (FightIntroFrontMon2 - FightIntroFrontMon) / BYTES_PER_TILE
-	ld [wIntroNidorinoBaseTile], a
-	ld de, IntroNidorinoAnimation3
-	call AnimateIntroNidorino
-	ld c, 30
-	call CheckForUserInterruption
-	ret c
-
-	lb de, 8 / 2, MOVE_GENGAR_LEFT
-	call IntroMoveMon
-	ld b, GENGAR_INTRO_TILES1
-	call IntroCopyTiles
-	ld c, 60
-	call CheckForUserInterruption
-	ret c
-
-; hip
-	ld a, SFX_INTRO_HIP
-	call PlaySound
-	xor a
-	ld [wIntroNidorinoBaseTile], a
-	ld de, IntroNidorinoAnimation4
-	call AnimateIntroNidorino
-; hop
-	ld a, SFX_INTRO_HOP
-	call PlaySound
-	ld de, IntroNidorinoAnimation5
-	call AnimateIntroNidorino
-	ld c, 20
-	call CheckForUserInterruption
-	ret c
-
-	ld a, (FightIntroFrontMon2 - FightIntroFrontMon) / BYTES_PER_TILE
-	ld [wIntroNidorinoBaseTile], a
-	ld de, IntroNidorinoAnimation6
-	call AnimateIntroNidorino
-	ld c, 30
-	call CheckForUserInterruption
-	ret c
-
-; lunge
-	ld a, SFX_INTRO_LUNGE
-	call PlaySound
-	ld a, (FightIntroFrontMon3 - FightIntroFrontMon) / BYTES_PER_TILE
-	ld [wIntroNidorinoBaseTile], a
-	ld de, IntroNidorinoAnimation7
-	jp AnimateIntroNidorino
-
-AnimateIntroNidorino:
-	ld a, [de]
-	cp ANIMATION_END
-	ret z
-	ld [wBaseCoordY], a
-	inc de
-	ld a, [de]
-	ld [wBaseCoordX], a
-	push de
-	ld c, 6 * 6
-	call UpdateIntroNidorinoOAM
-	ld c, 5
-	call DelayFrames
-	pop de
-	inc de
-	jr AnimateIntroNidorino
-
-UpdateIntroNidorinoOAM:
-	ld hl, wOAMBuffer
-	ld a, [wIntroNidorinoBaseTile]
-	ld d, a
-.loop
-	ld a, [wBaseCoordY]
-	add [hl]
-	ld [hli], a ; Y
-	ld a, [wBaseCoordX]
-	add [hl]
-	ld [hli], a ; X
-	ld a, d
-	ld [hli], a ; tile
-	inc hl
-	inc d
-	dec c
-	jr nz, .loop
-	ret
-
-InitIntroNidorinoOAM:
-	ld hl, wOAMBuffer
-	ld d, 0
-.loop
-	push bc
-	ld a, [wBaseCoordY]
-	ld e, a
-.innerLoop
-	ld a, e
-	add 8
-	ld e, a
-	ld [hli], a ; Y
-	ld a, [wBaseCoordX]
-	ld [hli], a ; X
-	ld a, d
-	ld [hli], a ; tile
-	ld a, OAM_BEHIND_BG
-	ld [hli], a ; attributes
-	inc d
-	dec c
-	jr nz, .innerLoop
-	ld a, [wBaseCoordX]
-	add 8
-	ld [wBaseCoordX], a
-	pop bc
-	dec b
-	jr nz, .loop
-	ret
-
-IntroClearScreen:
-	ld hl, vBGMap1
-	ld bc, BG_MAP_WIDTH * SCREEN_HEIGHT
-	jr IntroClearCommon
-
-IntroClearMiddleOfScreen:
-; clear the area of the tile map between the black bars on the top and bottom
-	coord hl, 0, 4
-	ld bc, SCREEN_WIDTH * 10
-
-IntroClearCommon:
-	ld [hl], 0
-	inc hl
-	dec bc
-	ld a, b
-	or c
-	jr nz, IntroClearCommon
-	ret
-
-IntroPlaceBlackTiles:
-	ld a, 1
-.loop
-	ld [hli], a
-	dec c
-	jr nz, .loop
-	ret
-
-IntroMoveMon:
-; d = number of times to move the mon (2 pixels each time)
-	ld a, e
-	cp MOVE_NIDORINO_RIGHT
-	jr z, .moveNidorinoRight
-	cp MOVE_GENGAR_LEFT
-	jr z, .moveGengarLeft
-; move Gengar right
-	ld a, [hSCX]
-	dec a
-	dec a
-	jr .next
-.moveNidorinoRight
-	push de
-	ld a, 2
-	ld [wBaseCoordX], a
-	xor a
-	ld [wBaseCoordY], a
-	ld c, 6 * 6
-	call UpdateIntroNidorinoOAM
-	pop de
-.moveGengarLeft
-	ld a, [hSCX]
-	inc a
-	inc a
-.next
-	ld [hSCX], a
-	push de
-	ld c, 2
-	call CheckForUserInterruption
-	pop de
-	ret c
-	dec d
-	jr nz, IntroMoveMon
-	ret
-
-IntroCopyTiles:
-	coord hl, 13, 7
-
-CopyTileIDsFromList_ZeroBaseTileID:
-	ld c, 0
-	predef_jump CopyTileIDsFromList
-
-PlayMoveSoundB:
-; unused
-	predef GetMoveSoundB
-	ld a, b
-	jp PlaySound
-
-LoadIntroGraphics:
-	ld hl, FightIntroBackMon
-	ld de, vChars2
-	ld bc, FightIntroBackMonEnd - FightIntroBackMon
-	ld a, BANK(FightIntroBackMon)
-	call FarCopyData2
-	ld hl, GameFreakIntro
-	ld de, vChars2 + (FightIntroBackMonEnd - FightIntroBackMon)
-	ld bc, GameFreakIntroEnd - GameFreakIntro
-	ld a, BANK(GameFreakIntro)
-	call FarCopyData2
-	ld hl, GameFreakIntro
-	ld de, vChars1
-	ld bc, GameFreakIntroEnd - GameFreakIntro
-	ld a, BANK(GameFreakIntro)
-	call FarCopyData2
-	ld hl, FightIntroFrontMon
-	ld de, vChars0
-	ld bc, FightIntroFrontMonEnd - FightIntroFrontMon
-	ld a, BANK(FightIntroFrontMon)
-	jp FarCopyData2
-
-PlayShootingStar:
-	ld b, SET_PAL_GAME_FREAK_INTRO
-	call RunPaletteCommand
-	callba LoadCopyrightAndTextBoxTiles
-	ldPal a, BLACK, DARK_GRAY, LIGHT_GRAY, WHITE
-	ld [rBGP], a
-	ld c, 180
-	call DelayFrames
-	call ClearScreen
-	call DisableLCD
-	xor a
-	ld [wCurOpponent], a
-	call IntroDrawBlackBars
-	call LoadIntroGraphics
-	call EnableLCD
-	ld hl, rLCDC
-	res 5, [hl]
-	set 3, [hl]
-	ld c, 64
-	call DelayFrames
-	callba AnimateShootingStar
-	push af
-	pop af
-	jr c, .next ; skip the delay if the user interrupted the animation
-	ld c, 40
-	call DelayFrames
-.next
-	ld a, BANK(Music_IntroBattle)
-	ld [wAudioROMBank], a
-	ld [wAudioSavedROMBank], a
-	ld a, MUSIC_INTRO_BATTLE
-	ld [wNewSoundID], a
-	call PlaySound
-	call IntroClearMiddleOfScreen
-	call ClearSprites
-	jp Delay3
-
-IntroDrawBlackBars:
-; clear the screen and draw black bars on the top and bottom
-	call IntroClearScreen
-	coord hl, 0, 0
-	ld c, SCREEN_WIDTH * 4
-	call IntroPlaceBlackTiles
-	coord hl, 0, 14
-	ld c, SCREEN_WIDTH * 4
-	call IntroPlaceBlackTiles
-	ld hl, vBGMap1
-	ld c,  BG_MAP_WIDTH * 4
-	call IntroPlaceBlackTiles
-	ld hl, vBGMap1 + BG_MAP_WIDTH * 14
-	ld c,  BG_MAP_WIDTH * 4
-	jp IntroPlaceBlackTiles
-
-EmptyFunc4:
-	ret
-
-IntroNidorinoAnimation0:
-	db 0, 0
-	db ANIMATION_END
-
-IntroNidorinoAnimation1:
-; This is a sequence of pixel movements for part of the Nidorino animation. This
-; list describes how Nidorino should hop.
-; First byte is y movement, second byte is x movement
-	db  0, 0
-	db -2, 2
-	db -1, 2
-	db  1, 2
-	db  2, 2
-	db ANIMATION_END
-
-IntroNidorinoAnimation2:
-; This is a sequence of pixel movements for part of the Nidorino animation.
-; First byte is y movement, second byte is x movement
-	db  0,  0
-	db -2, -2
-	db -1, -2
-	db  1, -2
-	db  2, -2
-	db ANIMATION_END
-
-IntroNidorinoAnimation3:
-; This is a sequence of pixel movements for part of the Nidorino animation.
-; First byte is y movement, second byte is x movement
-	db   0, 0
-	db -12, 6
-	db  -8, 6
-	db   8, 6
-	db  12, 6
-	db ANIMATION_END
-
-IntroNidorinoAnimation4:
-; This is a sequence of pixel movements for part of the Nidorino animation.
-; First byte is y movement, second byte is x movement
-	db  0,  0
-	db -8, -4
-	db -4, -4
-	db  4, -4
-	db  8, -4
-	db ANIMATION_END
-
-IntroNidorinoAnimation5:
-; This is a sequence of pixel movements for part of the Nidorino animation.
-; First byte is y movement, second byte is x movement
-	db  0, 0
-	db -8, 4
-	db -4, 4
-	db  4, 4
-	db  8, 4
-	db ANIMATION_END
-
-IntroNidorinoAnimation6:
-; This is a sequence of pixel movements for part of the Nidorino animation.
-; First byte is y movement, second byte is x movement
-	db 0, 0
-	db 2, 0
-	db 2, 0
-	db 0, 0
-	db ANIMATION_END
-
-IntroNidorinoAnimation7:
-; This is a sequence of pixel movements for part of the Nidorino animation.
-; First byte is y movement, second byte is x movement
-	db -8, -16
-	db -7, -14
-	db -6, -12
-	db -4, -10
-	db ANIMATION_END
-
-GameFreakIntro:
-	INCBIN "gfx/intro_credits/gamefreak_presents.2bpp"
-	INCBIN "gfx/intro_credits/gamefreak_logo.2bpp"
-	ds 16, $00 ; blank tile
-GameFreakIntroEnd:
-
-FightIntroBackMon:
-	INCBIN "gfx/intro_credits/gengar.2bpp"
-FightIntroBackMonEnd:
-
-FightIntroFrontMon:
-
-IF DEF(_RED)
-	INCBIN "gfx/intro_credits/red_nidorino_1.2bpp"
-FightIntroFrontMon2:
-	INCBIN "gfx/intro_credits/red_nidorino_2.2bpp"
-FightIntroFrontMon3:
-	INCBIN "gfx/intro_credits/red_nidorino_3.2bpp"
-ENDC
-
-IF DEF(_BLUE)
-	INCBIN "gfx/intro_credits/blue_jigglypuff_1.2bpp"
-FightIntroFrontMon2:
-	INCBIN "gfx/intro_credits/blue_jigglypuff_2.2bpp"
-FightIntroFrontMon3:
-	INCBIN "gfx/intro_credits/blue_jigglypuff_3.2bpp"
-ENDC
-
-FightIntroFrontMonEnd:
-
-	ds 16, $00 ; blank tile
--- /dev/null
+++ b/engine/items/get_bag_item_quantity.asm
@@ -1,0 +1,18 @@
+GetQuantityOfItemInBag:
+; In: b = item ID
+; Out: b = how many of that item are in the bag
+	call GetPredefRegisters
+	ld hl, wNumBagItems
+.loop
+	inc hl
+	ld a, [hli]
+	cp $ff
+	jr z, .notInBag
+	cp b
+	jr nz, .loop
+	ld a, [hl]
+	ld b, a
+	ret
+.notInBag
+	ld b, 0
+	ret
--- /dev/null
+++ b/engine/items/item_effects.asm
@@ -1,0 +1,2986 @@
+UseItem_::
+	ld a, 1
+	ld [wActionResultOrTookBattleTurn], a ; initialise to success value
+	ld a, [wcf91] ;contains item_ID
+	cp HM_01
+	jp nc, ItemUseTMHM
+	ld hl, ItemUsePtrTable
+	dec a
+	add a
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	jp hl
+
+ItemUsePtrTable:
+	dw ItemUseBall       ; MASTER_BALL
+	dw ItemUseBall       ; ULTRA_BALL
+	dw ItemUseBall       ; GREAT_BALL
+	dw ItemUseBall       ; POKE_BALL
+	dw ItemUseTownMap    ; TOWN_MAP
+	dw ItemUseBicycle    ; BICYCLE
+	dw ItemUseSurfboard  ; out-of-battle Surf effect
+	dw ItemUseBall       ; SAFARI_BALL
+	dw ItemUsePokedex    ; POKEDEX
+	dw ItemUseEvoStone   ; MOON_STONE
+	dw ItemUseMedicine   ; ANTIDOTE
+	dw ItemUseMedicine   ; BURN_HEAL
+	dw ItemUseMedicine   ; ICE_HEAL
+	dw ItemUseMedicine   ; AWAKENING
+	dw ItemUseMedicine   ; PARLYZ_HEAL
+	dw ItemUseMedicine   ; FULL_RESTORE
+	dw ItemUseMedicine   ; MAX_POTION
+	dw ItemUseMedicine   ; HYPER_POTION
+	dw ItemUseMedicine   ; SUPER_POTION
+	dw ItemUseMedicine   ; POTION
+	dw ItemUseBait       ; BOULDERBADGE
+	dw ItemUseRock       ; CASCADEBADGE
+	dw UnusableItem      ; THUNDERBADGE
+	dw UnusableItem      ; RAINBOWBADGE
+	dw UnusableItem      ; SOULBADGE
+	dw UnusableItem      ; MARSHBADGE
+	dw UnusableItem      ; VOLCANOBADGE
+	dw UnusableItem      ; EARTHBADGE
+	dw ItemUseEscapeRope ; ESCAPE_ROPE
+	dw ItemUseRepel      ; REPEL
+	dw UnusableItem      ; OLD_AMBER
+	dw ItemUseEvoStone   ; FIRE_STONE
+	dw ItemUseEvoStone   ; THUNDER_STONE
+	dw ItemUseEvoStone   ; WATER_STONE
+	dw ItemUseVitamin    ; HP_UP
+	dw ItemUseVitamin    ; PROTEIN
+	dw ItemUseVitamin    ; IRON
+	dw ItemUseVitamin    ; CARBOS
+	dw ItemUseVitamin    ; CALCIUM
+	dw ItemUseVitamin    ; RARE_CANDY
+	dw UnusableItem      ; DOME_FOSSIL
+	dw UnusableItem      ; HELIX_FOSSIL
+	dw UnusableItem      ; SECRET_KEY
+	dw UnusableItem
+	dw UnusableItem      ; BIKE_VOUCHER
+	dw ItemUseXAccuracy  ; X_ACCURACY
+	dw ItemUseEvoStone   ; LEAF_STONE
+	dw ItemUseCardKey    ; CARD_KEY
+	dw UnusableItem      ; NUGGET
+	dw UnusableItem      ; ??? PP_UP
+	dw ItemUsePokedoll   ; POKE_DOLL
+	dw ItemUseMedicine   ; FULL_HEAL
+	dw ItemUseMedicine   ; REVIVE
+	dw ItemUseMedicine   ; MAX_REVIVE
+	dw ItemUseGuardSpec  ; GUARD_SPEC
+	dw ItemUseSuperRepel ; SUPER_REPL
+	dw ItemUseMaxRepel   ; MAX_REPEL
+	dw ItemUseDireHit    ; DIRE_HIT
+	dw UnusableItem      ; COIN
+	dw ItemUseMedicine   ; FRESH_WATER
+	dw ItemUseMedicine   ; SODA_POP
+	dw ItemUseMedicine   ; LEMONADE
+	dw UnusableItem      ; S_S_TICKET
+	dw UnusableItem      ; GOLD_TEETH
+	dw ItemUseXStat      ; X_ATTACK
+	dw ItemUseXStat      ; X_DEFEND
+	dw ItemUseXStat      ; X_SPEED
+	dw ItemUseXStat      ; X_SPECIAL
+	dw ItemUseCoinCase   ; COIN_CASE
+	dw ItemUseOaksParcel ; OAKS_PARCEL
+	dw ItemUseItemfinder ; ITEMFINDER
+	dw UnusableItem      ; SILPH_SCOPE
+	dw ItemUsePokeflute  ; POKE_FLUTE
+	dw UnusableItem      ; LIFT_KEY
+	dw UnusableItem      ; EXP_ALL
+	dw ItemUseOldRod     ; OLD_ROD
+	dw ItemUseGoodRod    ; GOOD_ROD
+	dw ItemUseSuperRod   ; SUPER_ROD
+	dw ItemUsePPUp       ; PP_UP (real one)
+	dw ItemUsePPRestore  ; ETHER
+	dw ItemUsePPRestore  ; MAX_ETHER
+	dw ItemUsePPRestore  ; ELIXER
+	dw ItemUsePPRestore  ; MAX_ELIXER
+
+ItemUseBall:
+
+; Balls can't be used out of battle.
+	ld a, [wIsInBattle]
+	and a
+	jp z, ItemUseNotTime
+
+; Balls can't catch trainers' Pokémon.
+	dec a
+	jp nz, ThrowBallAtTrainerMon
+
+; If this is for the old man battle, skip checking if the party & box are full.
+	ld a, [wBattleType]
+	dec a
+	jr z, .canUseBall
+
+	ld a, [wPartyCount] ; is party full?
+	cp PARTY_LENGTH
+	jr nz, .canUseBall
+	ld a, [wNumInBox] ; is box full?
+	cp MONS_PER_BOX
+	jp z, BoxFullCannotThrowBall
+
+.canUseBall
+	xor a
+	ld [wCapturedMonSpecies], a
+
+	ld a, [wBattleType]
+	cp BATTLE_TYPE_SAFARI
+	jr nz, .skipSafariZoneCode
+
+.safariZone
+	ld hl, wNumSafariBalls
+	dec [hl] ; remove a Safari Ball
+
+.skipSafariZoneCode
+	call RunDefaultPaletteCommand
+
+	ld a, $43 ; successful capture value
+	ld [wPokeBallAnimData], a
+
+	call LoadScreenTilesFromBuffer1
+	ld hl, ItemUseText00
+	call PrintText
+
+; If the player is fighting an unidentified ghost, set the value that indicates
+; the Pokémon can't be caught and skip the capture calculations.
+	callab IsGhostBattle
+	ld b, $10 ; can't be caught value
+	jp z, .setAnimData
+
+	ld a, [wBattleType]
+	dec a
+	jr nz, .notOldManBattle
+
+.oldManBattle
+	ld hl, wGrassRate
+	ld de, wPlayerName
+	ld bc, NAME_LENGTH
+	call CopyData ; save the player's name in the Wild Monster data (part of the Cinnabar Island Missingno. glitch)
+	jp .captured
+
+.notOldManBattle
+; If the player is fighting the ghost Marowak, set the value that indicates the
+; Pokémon can't be caught and skip the capture calculations.
+	ld a, [wCurMap]
+	cp POKEMON_TOWER_6F
+	jr nz, .loop
+	ld a, [wEnemyMonSpecies2]
+	cp MAROWAK
+	ld b, $10 ; can't be caught value
+	jp z, .setAnimData
+
+; Get the first random number. Let it be called Rand1.
+; Rand1 must be within a certain range according the kind of ball being thrown.
+; The ranges are as follows.
+; Poké Ball:         [0, 255]
+; Great Ball:        [0, 200]
+; Ultra/Safari Ball: [0, 150]
+; Loop until an acceptable number is found.
+
+.loop
+	call Random
+	ld b, a
+
+; Get the item ID.
+	ld hl, wcf91
+	ld a, [hl]
+
+; The Master Ball always succeeds.
+	cp MASTER_BALL
+	jp z, .captured
+
+; Anything will do for the basic Poké Ball.
+	cp POKE_BALL
+	jr z, .checkForAilments
+
+; If it's a Great/Ultra/Safari Ball and Rand1 is greater than 200, try again.
+	ld a, 200
+	cp b
+	jr c, .loop
+
+; Less than or equal to 200 is good enough for a Great Ball.
+	ld a, [hl]
+	cp GREAT_BALL
+	jr z, .checkForAilments
+
+; If it's an Ultra/Safari Ball and Rand1 is greater than 150, try again.
+	ld a, 150
+	cp b
+	jr c, .loop
+
+.checkForAilments
+; Pokémon can be caught more easily with a status ailment.
+; Depending on the status ailment, a certain value will be subtracted from
+; Rand1. Let this value be called Status.
+; The larger Status is, the more easily the Pokémon can be caught.
+; no status ailment:     Status = 0
+; Burn/Paralysis/Poison: Status = 12
+; Freeze/Sleep:          Status = 25
+; If Status is greater than Rand1, the Pokémon will be caught for sure.
+	ld a, [wEnemyMonStatus]
+	and a
+	jr z, .skipAilmentValueSubtraction ; no ailments
+	and 1 << FRZ | SLP
+	ld c, 12
+	jr z, .notFrozenOrAsleep
+	ld c, 25
+.notFrozenOrAsleep
+	ld a, b
+	sub c
+	jp c, .captured
+	ld b, a
+
+.skipAilmentValueSubtraction
+	push bc ; save (Rand1 - Status)
+
+; Calculate MaxHP * 255.
+	xor a
+	ld [H_MULTIPLICAND], a
+	ld hl, wEnemyMonMaxHP
+	ld a, [hli]
+	ld [H_MULTIPLICAND + 1], a
+	ld a, [hl]
+	ld [H_MULTIPLICAND + 2], a
+	ld a, 255
+	ld [H_MULTIPLIER], a
+	call Multiply
+
+; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
+	ld a, [wcf91]
+	cp GREAT_BALL
+	ld a, 12
+	jr nz, .skip1
+	ld a, 8
+
+.skip1
+; Note that the results of all division operations are floored.
+
+; Calculate (MaxHP * 255) / BallFactor.
+	ld [H_DIVISOR], a
+	ld b, 4 ; number of bytes in dividend
+	call Divide
+
+; Divide the enemy's current HP by 4. HP is not supposed to exceed 999 so
+; the result should fit in a. If the division results in a quotient of 0,
+; change it to 1.
+	ld hl, wEnemyMonHP
+	ld a, [hli]
+	ld b, a
+	ld a, [hl]
+	srl b
+	rr a
+	srl b
+	rr a
+	and a
+	jr nz, .skip2
+	inc a
+
+.skip2
+; Let W = ((MaxHP * 255) / BallFactor) / max(HP / 4, 1). Calculate W.
+	ld [H_DIVISOR], a
+	ld b, 4
+	call Divide
+
+; If W > 255, store 255 in [H_QUOTIENT + 3].
+; Let X = min(W, 255) = [H_QUOTIENT + 3].
+	ld a, [H_QUOTIENT + 2]
+	and a
+	jr z, .skip3
+	ld a, 255
+	ld [H_QUOTIENT + 3], a
+
+.skip3
+	pop bc ; b = Rand1 - Status
+
+; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon.
+	ld a, [wEnemyMonActualCatchRate]
+	cp b
+	jr c, .failedToCapture
+
+; If W > 255, the ball captures the Pokémon.
+	ld a, [H_QUOTIENT + 2]
+	and a
+	jr nz, .captured
+
+	call Random ; Let this random number be called Rand2.
+
+; If Rand2 > X, the ball fails to capture the Pokémon.
+	ld b, a
+	ld a, [H_QUOTIENT + 3]
+	cp b
+	jr c, .failedToCapture
+
+.captured
+	jr .skipShakeCalculations
+
+.failedToCapture
+	ld a, [H_QUOTIENT + 3]
+	ld [wPokeBallCaptureCalcTemp], a ; Save X.
+
+; Calculate CatchRate * 100.
+	xor a
+	ld [H_MULTIPLICAND], a
+	ld [H_MULTIPLICAND + 1], a
+	ld a, [wEnemyMonActualCatchRate]
+	ld [H_MULTIPLICAND + 2], a
+	ld a, 100
+	ld [H_MULTIPLIER], a
+	call Multiply
+
+; Determine BallFactor2.
+; Poké Ball:         BallFactor2 = 255
+; Great Ball:        BallFactor2 = 200
+; Ultra/Safari Ball: BallFactor2 = 150
+	ld a, [wcf91]
+	ld b, 255
+	cp POKE_BALL
+	jr z, .skip4
+	ld b, 200
+	cp GREAT_BALL
+	jr z, .skip4
+	ld b, 150
+	cp ULTRA_BALL
+	jr z, .skip4
+
+.skip4
+; Let Y = (CatchRate * 100) / BallFactor2. Calculate Y.
+	ld a, b
+	ld [H_DIVISOR], a
+	ld b, 4
+	call Divide
+
+; If Y > 255, there are 3 shakes.
+; Note that this shouldn't be possible.
+; The maximum value of Y is (255 * 100) / 150 = 170.
+	ld a, [H_QUOTIENT + 2]
+	and a
+	ld b, $63 ; 3 shakes
+	jr nz, .setAnimData
+
+; Calculate X * Y.
+	ld a, [wPokeBallCaptureCalcTemp]
+	ld [H_MULTIPLIER], a
+	call Multiply
+
+; Calculate (X * Y) / 255.
+	ld a, 255
+	ld [H_DIVISOR], a
+	ld b, 4
+	call Divide
+
+; Determine Status2.
+; no status ailment:     Status2 = 0
+; Burn/Paralysis/Poison: Status2 = 5
+; Freeze/Sleep:          Status2 = 10
+	ld a, [wEnemyMonStatus]
+	and a
+	jr z, .skip5
+	and 1 << FRZ | SLP
+	ld b, 5
+	jr z, .addAilmentValue
+	ld b, 10
+
+.addAilmentValue
+; If the Pokémon has a status ailment, add Status2.
+	ld a, [H_QUOTIENT + 3]
+	add b
+	ld [H_QUOTIENT + 3], a
+
+.skip5
+; Finally determine the number of shakes.
+; Let Z = ((X * Y) / 255) + Status2 = [H_QUOTIENT + 3].
+; The number of shakes depend on the range Z is in.
+; 0  ≤ Z < 10: 0 shakes (the ball misses)
+; 10 ≤ Z < 30: 1 shake
+; 30 ≤ Z < 70: 2 shakes
+; 70 ≤ Z:      3 shakes
+	ld a, [H_QUOTIENT + 3]
+	cp 10
+	ld b, $20
+	jr c, .setAnimData
+	cp 30
+	ld b, $61
+	jr c, .setAnimData
+	cp 70
+	ld b, $62
+	jr c, .setAnimData
+	ld b, $63
+
+.setAnimData
+	ld a, b
+	ld [wPokeBallAnimData], a
+
+.skipShakeCalculations
+	ld c, 20
+	call DelayFrames
+
+; Do the animation.
+	ld a, TOSS_ANIM
+	ld [wAnimationID], a
+	xor a
+	ld [H_WHOSETURN], a
+	ld [wAnimationType], a
+	ld [wDamageMultipliers], a
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wcf91]
+	push af
+	predef MoveAnimation
+	pop af
+	ld [wcf91], a
+	pop af
+	ld [wWhichPokemon], a
+
+; Determine the message to display from the animation.
+	ld a, [wPokeBallAnimData]
+	cp $10
+	ld hl, ItemUseBallText00
+	jp z, .printMessage
+	cp $20
+	ld hl, ItemUseBallText01
+	jp z, .printMessage
+	cp $61
+	ld hl, ItemUseBallText02
+	jp z, .printMessage
+	cp $62
+	ld hl, ItemUseBallText03
+	jp z, .printMessage
+	cp $63
+	ld hl, ItemUseBallText04
+	jp z, .printMessage
+
+; Save current HP.
+	ld hl, wEnemyMonHP
+	ld a, [hli]
+	push af
+	ld a, [hli]
+	push af
+
+; Save status ailment.
+	inc hl
+	ld a, [hl]
+	push af
+
+	push hl
+
+; If the Pokémon is transformed, the Pokémon is assumed to be a Ditto.
+; This is a bug because a wild Pokémon could have used Transform via
+; Mirror Move even though the only wild Pokémon that knows Transform is Ditto.
+	ld hl, wEnemyBattleStatus3
+	bit TRANSFORMED, [hl]
+	jr z, .notTransformed
+	ld a, DITTO
+	ld [wEnemyMonSpecies2], a
+	jr .skip6
+
+.notTransformed
+; If the Pokémon is not transformed, set the transformed bit and copy the
+; DVs to wTransformedEnemyMonOriginalDVs so that LoadEnemyMonData won't generate
+; new DVs.
+	set TRANSFORMED, [hl]
+	ld hl, wTransformedEnemyMonOriginalDVs
+	ld a, [wEnemyMonDVs]
+	ld [hli], a
+	ld a, [wEnemyMonDVs + 1]
+	ld [hl], a
+
+.skip6
+	ld a, [wcf91]
+	push af
+	ld a, [wEnemyMonSpecies2]
+	ld [wcf91], a
+	ld a, [wEnemyMonLevel]
+	ld [wCurEnemyLVL], a
+	callab LoadEnemyMonData
+	pop af
+	ld [wcf91], a
+	pop hl
+	pop af
+	ld [hld], a
+	dec hl
+	pop af
+	ld [hld], a
+	pop af
+	ld [hl], a
+	ld a, [wEnemyMonSpecies]
+	ld [wCapturedMonSpecies], a
+	ld [wcf91], a
+	ld [wd11e], a
+	ld a, [wBattleType]
+	dec a ; is this the old man battle?
+	jr z, .oldManCaughtMon ; if so, don't give the player the caught Pokémon
+
+	ld hl, ItemUseBallText05
+	call PrintText
+
+; Add the caught Pokémon to the Pokédex.
+	predef IndexToPokedex
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_TEST
+	ld hl, wPokedexOwned
+	predef FlagActionPredef
+	ld a, c
+	push af
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_SET
+	predef FlagActionPredef
+	pop af
+
+	and a ; was the Pokémon already in the Pokédex?
+	jr nz, .skipShowingPokedexData ; if so, don't show the Pokédex data
+
+	ld hl, ItemUseBallText06
+	call PrintText
+	call ClearSprites
+	ld a, [wEnemyMonSpecies]
+	ld [wd11e], a
+	predef ShowPokedexData
+
+.skipShowingPokedexData
+	ld a, [wPartyCount]
+	cp PARTY_LENGTH ; is party full?
+	jr z, .sendToBox
+	xor a ; PLAYER_PARTY_DATA
+	ld [wMonDataLocation], a
+	call ClearSprites
+	call AddPartyMon
+	jr .done
+
+.sendToBox
+	call ClearSprites
+	call SendNewMonToBox
+	ld hl, ItemUseBallText07
+	CheckEvent EVENT_MET_BILL
+	jr nz, .printTransferredToPCText
+	ld hl, ItemUseBallText08
+.printTransferredToPCText
+	call PrintText
+	jr .done
+
+.oldManCaughtMon
+	ld hl, ItemUseBallText05
+
+.printMessage
+	call PrintText
+	call ClearSprites
+
+.done
+	ld a, [wBattleType]
+	and a ; is this the old man battle?
+	ret nz ; if so, don't remove a ball from the bag
+
+; Remove a ball from the bag.
+	ld hl, wNumBagItems
+	inc a
+	ld [wItemQuantity], a
+	jp RemoveItemFromInventory
+
+ItemUseBallText00:
+;"It dodged the thrown ball!"
+;"This pokemon can't be caught"
+	TX_FAR _ItemUseBallText00
+	db "@"
+ItemUseBallText01:
+;"You missed the pokemon!"
+	TX_FAR _ItemUseBallText01
+	db "@"
+ItemUseBallText02:
+;"Darn! The pokemon broke free!"
+	TX_FAR _ItemUseBallText02
+	db "@"
+ItemUseBallText03:
+;"Aww! It appeared to be caught!"
+	TX_FAR _ItemUseBallText03
+	db "@"
+ItemUseBallText04:
+;"Shoot! It was so close too!"
+	TX_FAR _ItemUseBallText04
+	db "@"
+ItemUseBallText05:
+;"All right! {MonName} was caught!"
+;play sound
+	TX_FAR _ItemUseBallText05
+	TX_SFX_CAUGHT_MON
+	TX_BLINK
+	db "@"
+ItemUseBallText07:
+;"X was transferred to Bill's PC"
+	TX_FAR _ItemUseBallText07
+	db "@"
+ItemUseBallText08:
+;"X was transferred to someone's PC"
+	TX_FAR _ItemUseBallText08
+	db "@"
+
+ItemUseBallText06:
+;"New DEX data will be added..."
+;play sound
+	TX_FAR _ItemUseBallText06
+	TX_SFX_DEX_PAGE_ADDED
+	TX_BLINK
+	db "@"
+
+ItemUseTownMap:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	jpba DisplayTownMap
+
+ItemUseBicycle:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	ld a, [wWalkBikeSurfState]
+	ld [wWalkBikeSurfStateCopy], a
+	cp 2 ; is the player surfing?
+	jp z, ItemUseNotTime
+	dec a ; is player already bicycling?
+	jr nz, .tryToGetOnBike
+.getOffBike
+	call ItemUseReloadOverworldData
+	xor a
+	ld [wWalkBikeSurfState], a ; change player state to walking
+	call PlayDefaultMusic ; play walking music
+	ld hl, GotOffBicycleText
+	jr .printText
+.tryToGetOnBike
+	call IsBikeRidingAllowed
+	jp nc, NoCyclingAllowedHere
+	call ItemUseReloadOverworldData
+	xor a ; no keys pressed
+	ld [hJoyHeld], a ; current joypad state
+	inc a
+	ld [wWalkBikeSurfState], a ; change player state to bicycling
+	ld hl, GotOnBicycleText
+	call PlayDefaultMusic ; play bike riding music
+.printText
+	jp PrintText
+
+; used for Surf out-of-battle effect
+ItemUseSurfboard:
+	ld a, [wWalkBikeSurfState]
+	ld [wWalkBikeSurfStateCopy], a
+	cp 2 ; is the player already surfing?
+	jr z, .tryToStopSurfing
+.tryToSurf
+	call IsNextTileShoreOrWater
+	jp c, SurfingAttemptFailed
+	ld hl, TilePairCollisionsWater
+	call CheckForTilePairCollisions
+	jp c, SurfingAttemptFailed
+.surf
+	call .makePlayerMoveForward
+	ld hl, wd730
+	set 7, [hl]
+	ld a, 2
+	ld [wWalkBikeSurfState], a ; change player state to surfing
+	call PlayDefaultMusic ; play surfing music
+	ld hl, SurfingGotOnText
+	jp PrintText
+.tryToStopSurfing
+	xor a
+	ld [hSpriteIndexOrTextID], a
+	ld d, 16 ; talking range in pixels (normal range)
+	call IsSpriteInFrontOfPlayer2
+	res 7, [hl]
+	ld a, [hSpriteIndexOrTextID]
+	and a ; is there a sprite in the way?
+	jr nz, .cannotStopSurfing
+	ld hl, TilePairCollisionsWater
+	call CheckForTilePairCollisions
+	jr c, .cannotStopSurfing
+	ld hl, wTilesetCollisionPtr ; pointer to list of passable tiles
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a ; hl now points to passable tiles
+	ld a, [wTileInFrontOfPlayer] ; tile in front of the player
+	ld b, a
+.passableTileLoop
+	ld a, [hli]
+	cp b
+	jr z, .stopSurfing
+	cp $ff
+	jr nz, .passableTileLoop
+.cannotStopSurfing
+	ld hl, SurfingNoPlaceToGetOffText
+	jp PrintText
+.stopSurfing
+	call .makePlayerMoveForward
+	ld hl, wd730
+	set 7, [hl]
+	xor a
+	ld [wWalkBikeSurfState], a ; change player state to walking
+	dec a
+	ld [wJoyIgnore], a
+	call PlayDefaultMusic ; play walking music
+	jp LoadWalkingPlayerSpriteGraphics
+; uses a simulated button press to make the player move forward
+.makePlayerMoveForward
+	ld a, [wPlayerDirection] ; direction the player is going
+	bit PLAYER_DIR_BIT_UP, a
+	ld b, D_UP
+	jr nz, .storeSimulatedButtonPress
+	bit PLAYER_DIR_BIT_DOWN, a
+	ld b, D_DOWN
+	jr nz, .storeSimulatedButtonPress
+	bit PLAYER_DIR_BIT_LEFT, a
+	ld b, D_LEFT
+	jr nz, .storeSimulatedButtonPress
+	ld b, D_RIGHT
+.storeSimulatedButtonPress
+	ld a, b
+	ld [wSimulatedJoypadStatesEnd], a
+	xor a
+	ld [wWastedByteCD39], a
+	inc a
+	ld [wSimulatedJoypadStatesIndex], a
+	ret
+
+SurfingGotOnText:
+	TX_FAR _SurfingGotOnText
+	db "@"
+
+SurfingNoPlaceToGetOffText:
+	TX_FAR _SurfingNoPlaceToGetOffText
+	db "@"
+
+ItemUsePokedex:
+	predef_jump ShowPokedexMenu
+
+ItemUseEvoStone:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wcf91]
+	ld [wEvoStoneItemID], a
+	push af
+	ld a, EVO_STONE_PARTY_MENU
+	ld [wPartyMenuTypeOrMessageID], a
+	ld a, $ff
+	ld [wUpdateSpritesEnabled], a
+	call DisplayPartyMenu
+	pop bc
+	jr c, .canceledItemUse
+	ld a, b
+	ld [wcf91], a
+	ld a, $01
+	ld [wForceEvolution], a
+	ld a, SFX_HEAL_AILMENT
+	call PlaySoundWaitForCurrent
+	call WaitForSoundToFinish
+	callab TryEvolvingMon ; try to evolve pokemon
+	ld a, [wEvolutionOccurred]
+	and a
+	jr z, .noEffect
+	pop af
+	ld [wWhichPokemon], a
+	ld hl, wNumBagItems
+	ld a, 1 ; remove 1 stone
+	ld [wItemQuantity], a
+	jp RemoveItemFromInventory
+.noEffect
+	call ItemUseNoEffect
+.canceledItemUse
+	xor a
+	ld [wActionResultOrTookBattleTurn], a ; item not used
+	pop af
+	ret
+
+ItemUseVitamin:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+
+ItemUseMedicine:
+	ld a, [wPartyCount]
+	and a
+	jp z, .emptyParty
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wcf91]
+	push af
+	ld a, USE_ITEM_PARTY_MENU
+	ld [wPartyMenuTypeOrMessageID], a
+	ld a, $ff
+	ld [wUpdateSpritesEnabled], a
+	ld a, [wPseudoItemID]
+	and a ; using Softboiled?
+	jr z, .notUsingSoftboiled
+; if using softboiled
+	call GoBackToPartyMenu
+	jr .getPartyMonDataAddress
+.emptyParty
+	ld hl, .emptyPartyText
+	xor a
+	ld [wActionResultOrTookBattleTurn], a ; item use failed
+	jp PrintText
+.emptyPartyText
+	text "You don't have"
+	line "any #MON!"
+	prompt
+.notUsingSoftboiled
+	call DisplayPartyMenu
+.getPartyMonDataAddress
+	jp c, .canceledItemUse
+	ld hl, wPartyMons
+	ld bc, wPartyMon2 - wPartyMon1
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld a, [wWhichPokemon]
+	ld [wUsedItemOnWhichPokemon], a
+	ld d, a
+	ld a, [wcf91]
+	ld e, a
+	ld [wd0b5], a
+	pop af
+	ld [wcf91], a
+	pop af
+	ld [wWhichPokemon], a
+	ld a, [wPseudoItemID]
+	and a ; using Softboiled?
+	jr z, .checkItemType
+; if using softboiled
+	ld a, [wWhichPokemon]
+	cp d ; is the pokemon trying to use softboiled on itself?
+	jr z, ItemUseMedicine ; if so, force another choice
+.checkItemType
+	ld a, [wcf91]
+	cp REVIVE
+	jr nc, .healHP ; if it's a Revive or Max Revive
+	cp FULL_HEAL
+	jr z, .cureStatusAilment ; if it's a Full Heal
+	cp HP_UP
+	jp nc, .useVitamin ; if it's a vitamin or Rare Candy
+	cp FULL_RESTORE
+	jr nc, .healHP ; if it's a Full Restore or one of the potions
+; fall through if it's one of the status-specific healing items
+.cureStatusAilment
+	ld bc, wPartyMon1Status - wPartyMon1
+	add hl, bc ; hl now points to status
+	ld a, [wcf91]
+	lb bc, ANTIDOTE_MSG, 1 << PSN
+	cp ANTIDOTE
+	jr z, .checkMonStatus
+	lb bc, BURN_HEAL_MSG, 1 << BRN
+	cp BURN_HEAL
+	jr z, .checkMonStatus
+	lb bc, ICE_HEAL_MSG, 1 << FRZ
+	cp ICE_HEAL
+	jr z, .checkMonStatus
+	lb bc, AWAKENING_MSG, SLP
+	cp AWAKENING
+	jr z, .checkMonStatus
+	lb bc, PARALYZ_HEAL_MSG, 1 << PAR
+	cp PARLYZ_HEAL
+	jr z, .checkMonStatus
+	lb bc, FULL_HEAL_MSG, $ff ; Full Heal
+.checkMonStatus
+	ld a, [hl] ; pokemon's status
+	and c ; does the pokemon have a status ailment the item can cure?
+	jp z, .healingItemNoEffect
+; if the pokemon has a status the item can heal
+	xor a
+	ld [hl], a ; remove the status ailment in the party data
+	ld a, b
+	ld [wPartyMenuTypeOrMessageID], a ; the message to display for the item used
+	ld a, [wPlayerMonNumber]
+	cp d ; is pokemon the item was used on active in battle?
+	jp nz, .doneHealing
+; if it is active in battle
+	xor a
+	ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data
+	push hl
+	ld hl, wPlayerBattleStatus3
+	res BADLY_POISONED, [hl] ; heal Toxic status
+	pop hl
+	ld bc, wPartyMon1Stats - wPartyMon1Status
+	add hl, bc ; hl now points to party stats
+	ld de, wBattleMonStats
+	ld bc, NUM_STATS * 2
+	call CopyData ; copy party stats to in-battle stat data
+	predef DoubleOrHalveSelectedStats
+	jp .doneHealing
+.healHP
+	inc hl ; hl = address of current HP
+	ld a, [hli]
+	ld b, a
+	ld [wHPBarOldHP+1], a
+	ld a, [hl]
+	ld c, a
+	ld [wHPBarOldHP], a ; current HP stored at wHPBarOldHP (2 bytes, big-endian)
+	or b
+	jr nz, .notFainted
+.fainted
+	ld a, [wcf91]
+	cp REVIVE
+	jr z, .updateInBattleFaintedData
+	cp MAX_REVIVE
+	jr z, .updateInBattleFaintedData
+	jp .healingItemNoEffect
+.updateInBattleFaintedData
+	ld a, [wIsInBattle]
+	and a
+	jr z, .compareCurrentHPToMaxHP
+	push hl
+	push de
+	push bc
+	ld a, [wUsedItemOnWhichPokemon]
+	ld c, a
+	ld hl, wPartyFoughtCurrentEnemyFlags
+	ld b, FLAG_TEST
+	predef FlagActionPredef
+	ld a, c
+	and a
+	jr z, .next
+	ld a, [wUsedItemOnWhichPokemon]
+	ld c, a
+	ld hl, wPartyGainExpFlags
+	ld b, FLAG_SET
+	predef FlagActionPredef
+.next
+	pop bc
+	pop de
+	pop hl
+	jr .compareCurrentHPToMaxHP
+.notFainted
+	ld a, [wcf91]
+	cp REVIVE
+	jp z, .healingItemNoEffect
+	cp MAX_REVIVE
+	jp z, .healingItemNoEffect
+.compareCurrentHPToMaxHP
+	push hl
+	push bc
+	ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1)
+	add hl, bc ; hl now points to max HP
+	pop bc
+	ld a, [hli]
+	cp b
+	jr nz, .skipComparingLSB ; no need to compare the LSB's if the MSB's don't match
+	ld a, [hl]
+	cp c
+.skipComparingLSB
+	pop hl
+	jr nz, .notFullHP
+.fullHP ; if the pokemon's current HP equals its max HP
+	ld a, [wcf91]
+	cp FULL_RESTORE
+	jp nz, .healingItemNoEffect
+	inc hl
+	inc hl
+	ld a, [hld] ; status ailment
+	and a ; does the pokemon have a status ailment?
+	jp z, .healingItemNoEffect
+	ld a, FULL_HEAL
+	ld [wcf91], a
+	dec hl
+	dec hl
+	dec hl
+	jp .cureStatusAilment
+.notFullHP ; if the pokemon's current HP doesn't equal its max HP
+	xor a
+	ld [wLowHealthAlarm], a ;disable low health alarm
+	ld [wChannelSoundIDs + Ch5], a
+	push hl
+	push de
+	ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1)
+	add hl, bc ; hl now points to max HP
+	ld a, [hli]
+	ld [wHPBarMaxHP+1], a
+	ld a, [hl]
+	ld [wHPBarMaxHP], a ; max HP stored at wHPBarMaxHP (2 bytes, big-endian)
+	ld a, [wPseudoItemID]
+	and a ; using Softboiled?
+	jp z, .notUsingSoftboiled2
+; if using softboiled
+	ld hl, wHPBarMaxHP
+	ld a, [hli]
+	push af
+	ld a, [hli]
+	push af
+	ld a, [hli]
+	push af
+	ld a, [hl]
+	push af
+	ld hl, wPartyMon1MaxHP
+	ld a, [wWhichPokemon]
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld a, [hli]
+	ld [wHPBarMaxHP + 1], a
+	ld [H_DIVIDEND], a
+	ld a, [hl]
+	ld [wHPBarMaxHP], a
+	ld [H_DIVIDEND + 1], a
+	ld a, 5
+	ld [H_DIVISOR], a
+	ld b, 2 ; number of bytes
+	call Divide ; get 1/5 of max HP of pokemon that used Softboiled
+	ld bc, (wPartyMon1HP + 1) - (wPartyMon1MaxHP + 1)
+	add hl, bc ; hl now points to LSB of current HP of pokemon that used Softboiled
+; subtract 1/5 of max HP from current HP of pokemon that used Softboiled
+	ld a, [H_QUOTIENT + 3]
+	push af
+	ld b, a
+	ld a, [hl]
+	ld [wHPBarOldHP], a
+	sub b
+	ld [hld], a
+	ld [wHPBarNewHP], a
+	ld a, [H_QUOTIENT + 2]
+	ld b, a
+	ld a, [hl]
+	ld [wHPBarOldHP+1], a
+	sbc b
+	ld [hl], a
+	ld [wHPBarNewHP+1], a
+	coord hl, 4, 1
+	ld a, [wWhichPokemon]
+	ld bc, 2 * SCREEN_WIDTH
+	call AddNTimes ; calculate coordinates of HP bar of pokemon that used Softboiled
+	ld a, SFX_HEAL_HP
+	call PlaySoundWaitForCurrent
+	ld a, [hFlags_0xFFF6]
+	set 0, a
+	ld [hFlags_0xFFF6], a
+	ld a, $02
+	ld [wHPBarType], a
+	predef UpdateHPBar2 ; animate HP bar decrease of pokemon that used Softboiled
+	ld a, [hFlags_0xFFF6]
+	res 0, a
+	ld [hFlags_0xFFF6], a
+	pop af
+	ld b, a ; store heal amount (1/5 of max HP)
+	ld hl, wHPBarOldHP + 1
+	pop af
+	ld [hld], a
+	pop af
+	ld [hld], a
+	pop af
+	ld [hld], a
+	pop af
+	ld [hl], a
+	jr .addHealAmount
+.notUsingSoftboiled2
+	ld a, [wcf91]
+	cp SODA_POP
+	ld b, 60 ; Soda Pop heal amount
+	jr z, .addHealAmount
+	ld b, 80 ; Lemonade heal amount
+	jr nc, .addHealAmount
+	cp FRESH_WATER
+	ld b, 50 ; Fresh Water heal amount
+	jr z, .addHealAmount
+	cp SUPER_POTION
+	ld b, 200 ; Hyper Potion heal amount
+	jr c, .addHealAmount
+	ld b, 50 ; Super Potion heal amount
+	jr z, .addHealAmount
+	ld b, 20 ; Potion heal amount
+.addHealAmount
+	pop de
+	pop hl
+	ld a, [hl]
+	add b
+	ld [hld], a
+	ld [wHPBarNewHP], a
+	ld a, [hl]
+	ld [wHPBarNewHP+1], a
+	jr nc, .noCarry
+	inc [hl]
+	ld a, [hl]
+	ld [wHPBarNewHP + 1], a
+.noCarry
+	push de
+	inc hl
+	ld d, h
+	ld e, l ; de now points to current HP
+	ld hl, (wPartyMon1MaxHP + 1) - (wPartyMon1HP + 1)
+	add hl, de ; hl now points to max HP
+	ld a, [wcf91]
+	cp REVIVE
+	jr z, .setCurrentHPToHalfMaxHP
+	ld a, [hld]
+	ld b, a
+	ld a, [de]
+	sub b
+	dec de
+	ld b, [hl]
+	ld a, [de]
+	sbc b
+	jr nc, .setCurrentHPToMaxHp ; if current HP exceeds max HP after healing
+	ld a, [wcf91]
+	cp HYPER_POTION
+	jr c, .setCurrentHPToMaxHp ; if using a Full Restore or Max Potion
+	cp MAX_REVIVE
+	jr z, .setCurrentHPToMaxHp ; if using a Max Revive
+	jr .updateInBattleData
+.setCurrentHPToHalfMaxHP
+	dec hl
+	dec de
+	ld a, [hli]
+	srl a
+	ld [de], a
+	ld [wHPBarNewHP+1], a
+	ld a, [hl]
+	rr a
+	inc de
+	ld [de], a
+	ld [wHPBarNewHP], a
+	dec de
+	jr .doneHealingPartyHP
+.setCurrentHPToMaxHp
+	ld a, [hli]
+	ld [de], a
+	ld [wHPBarNewHP+1], a
+	inc de
+	ld a, [hl]
+	ld [de], a
+	ld [wHPBarNewHP], a
+	dec de
+.doneHealingPartyHP ; done updating the pokemon's current HP in the party data structure
+	ld a, [wcf91]
+	cp FULL_RESTORE
+	jr nz, .updateInBattleData
+	ld bc, wPartyMon1Status - (wPartyMon1MaxHP + 1)
+	add hl, bc
+	xor a
+	ld [hl], a ; remove the status ailment in the party data
+.updateInBattleData
+	ld h, d
+	ld l, e
+	pop de
+	ld a, [wPlayerMonNumber]
+	cp d ; is pokemon the item was used on active in battle?
+	jr nz, .calculateHPBarCoords
+; copy party HP to in-battle HP
+	ld a, [hli]
+	ld [wBattleMonHP], a
+	ld a, [hld]
+	ld [wBattleMonHP + 1], a
+	ld a, [wcf91]
+	cp FULL_RESTORE
+	jr nz, .calculateHPBarCoords
+	xor a
+	ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data
+.calculateHPBarCoords
+	ld hl, wOAMBuffer + $90
+	ld bc, 2 * SCREEN_WIDTH
+	inc d
+.calculateHPBarCoordsLoop
+	add hl, bc
+	dec d
+	jr nz, .calculateHPBarCoordsLoop
+	jr .doneHealing
+.healingItemNoEffect
+	call ItemUseNoEffect
+	jp .done
+.doneHealing
+	ld a, [wPseudoItemID]
+	and a ; using Softboiled?
+	jr nz, .skipRemovingItem ; no item to remove if using Softboiled
+	push hl
+	call RemoveUsedItem
+	pop hl
+.skipRemovingItem
+	ld a, [wcf91]
+	cp FULL_RESTORE
+	jr c, .playStatusAilmentCuringSound
+	cp FULL_HEAL
+	jr z, .playStatusAilmentCuringSound
+	ld a, SFX_HEAL_HP
+	call PlaySoundWaitForCurrent
+	ld a, [hFlags_0xFFF6]
+	set 0, a
+	ld [hFlags_0xFFF6], a
+	ld a, $02
+	ld [wHPBarType], a
+	predef UpdateHPBar2 ; animate the HP bar lengthening
+	ld a, [hFlags_0xFFF6]
+	res 0, a
+	ld [hFlags_0xFFF6], a
+	ld a, REVIVE_MSG
+	ld [wPartyMenuTypeOrMessageID], a
+	ld a, [wcf91]
+	cp REVIVE
+	jr z, .showHealingItemMessage
+	cp MAX_REVIVE
+	jr z, .showHealingItemMessage
+	ld a, POTION_MSG
+	ld [wPartyMenuTypeOrMessageID], a
+	jr .showHealingItemMessage
+.playStatusAilmentCuringSound
+	ld a, SFX_HEAL_AILMENT
+	call PlaySoundWaitForCurrent
+.showHealingItemMessage
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call ClearScreen
+	dec a
+	ld [wUpdateSpritesEnabled], a
+	call RedrawPartyMenu ; redraws the party menu and displays the message
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld c, 50
+	call DelayFrames
+	call WaitForTextScrollButtonPress
+	jr .done
+.canceledItemUse
+	xor a
+	ld [wActionResultOrTookBattleTurn], a ; item use failed
+	pop af
+	pop af
+.done
+	ld a, [wPseudoItemID]
+	and a ; using Softboiled?
+	ret nz ; if so, return
+	call GBPalWhiteOut
+	call z, RunDefaultPaletteCommand
+	ld a, [wIsInBattle]
+	and a
+	ret nz
+	jp ReloadMapData
+.useVitamin
+	push hl
+	ld a, [hl]
+	ld [wd0b5], a
+	ld [wd11e], a
+	ld bc, wPartyMon1Level - wPartyMon1
+	add hl, bc ; hl now points to level
+	ld a, [hl] ; a = level
+	ld [wCurEnemyLVL], a ; store level
+	call GetMonHeader
+	push de
+	ld a, d
+	ld hl, wPartyMonNicks
+	call GetPartyMonName
+	pop de
+	pop hl
+	ld a, [wcf91]
+	cp RARE_CANDY
+	jp z, .useRareCandy
+	push hl
+	sub HP_UP
+	add a
+	ld bc, wPartyMon1HPExp - wPartyMon1
+	add hl, bc
+	add l
+	ld l, a
+	jr nc, .noCarry2
+	inc h
+.noCarry2
+	ld a, 10
+	ld b, a
+	ld a, [hl] ; a = MSB of stat experience of the appropriate stat
+	cp 100 ; is there already at least 25600 (256 * 100) stat experience?
+	jr nc, .vitaminNoEffect ; if so, vitamins can't add any more
+	add b ; add 2560 (256 * 10) stat experience
+	jr nc, .noCarry3 ; a carry should be impossible here, so this will always jump
+	ld a, 255
+.noCarry3
+	ld [hl], a
+	pop hl
+	call .recalculateStats
+	ld hl, VitaminText
+	ld a, [wcf91]
+	sub HP_UP - 1
+	ld c, a
+.statNameLoop ; loop to get the address of the name of the stat the vitamin increases
+	dec c
+	jr z, .gotStatName
+.statNameInnerLoop
+	ld a, [hli]
+	ld b, a
+	ld a, $50
+	cp b
+	jr nz, .statNameInnerLoop
+	jr .statNameLoop
+.gotStatName
+	ld de, wcf4b
+	ld bc, 10
+	call CopyData ; copy the stat's name to wcf4b
+	ld a, SFX_HEAL_AILMENT
+	call PlaySound
+	ld hl, VitaminStatRoseText
+	call PrintText
+	jp RemoveUsedItem
+.vitaminNoEffect
+	pop hl
+	ld hl, VitaminNoEffectText
+	call PrintText
+	jp GBPalWhiteOut
+.recalculateStats
+	ld bc, wPartyMon1Stats - wPartyMon1
+	add hl, bc
+	ld d, h
+	ld e, l ; de now points to stats
+	ld bc, (wPartyMon1Exp + 2) - wPartyMon1Stats
+	add hl, bc ; hl now points to LSB of experience
+	ld b, 1
+	jp CalcStats ; recalculate stats
+.useRareCandy
+	push hl
+	ld bc, wPartyMon1Level - wPartyMon1
+	add hl, bc ; hl now points to level
+	ld a, [hl] ; a = level
+	cp MAX_LEVEL
+	jr z, .vitaminNoEffect ; can't raise level above 100
+	inc a
+	ld [hl], a ; store incremented level
+	ld [wCurEnemyLVL], a
+	push hl
+	push de
+	ld d, a
+	callab CalcExperience ; calculate experience for next level and store it at $ff96
+	pop de
+	pop hl
+	ld bc, wPartyMon1Exp - wPartyMon1Level
+	add hl, bc ; hl now points to MSB of experience
+; update experience to minimum for new level
+	ld a, [hExperience]
+	ld [hli], a
+	ld a, [hExperience + 1]
+	ld [hli], a
+	ld a, [hExperience + 2]
+	ld [hl], a
+	pop hl
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wcf91]
+	push af
+	push de
+	push hl
+	ld bc, wPartyMon1MaxHP - wPartyMon1
+	add hl, bc ; hl now points to MSB of max HP
+	ld a, [hli]
+	ld b, a
+	ld c, [hl]
+	pop hl
+	push bc
+	push hl
+	call .recalculateStats
+	pop hl
+	ld bc, (wPartyMon1MaxHP + 1) - wPartyMon1
+	add hl, bc ; hl now points to LSB of max HP
+	pop bc
+	ld a, [hld]
+	sub c
+	ld c, a
+	ld a, [hl]
+	sbc b
+	ld b, a ; bc = the amount of max HP gained from leveling up
+; add the amount gained to the current HP
+	ld de, (wPartyMon1HP + 1) - wPartyMon1MaxHP
+	add hl, de ; hl now points to LSB of current HP
+	ld a, [hl]
+	add c
+	ld [hld], a
+	ld a, [hl]
+	adc b
+	ld [hl], a
+	ld a, RARE_CANDY_MSG
+	ld [wPartyMenuTypeOrMessageID], a
+	call RedrawPartyMenu
+	pop de
+	ld a, d
+	ld [wWhichPokemon], a
+	ld a, e
+	ld [wd11e], a
+	xor a ; PLAYER_PARTY_DATA
+	ld [wMonDataLocation], a
+	call LoadMonData
+	ld d, $01
+	callab PrintStatsBox ; display new stats text box
+	call WaitForTextScrollButtonPress ; wait for button press
+	xor a ; PLAYER_PARTY_DATA
+	ld [wMonDataLocation], a
+	predef LearnMoveFromLevelUp ; learn level up move, if any
+	xor a
+	ld [wForceEvolution], a
+	callab TryEvolvingMon ; evolve pokemon, if appropriate
+	ld a, $01
+	ld [wUpdateSpritesEnabled], a
+	pop af
+	ld [wcf91], a
+	pop af
+	ld [wWhichPokemon], a
+	jp RemoveUsedItem
+
+VitaminStatRoseText:
+	TX_FAR _VitaminStatRoseText
+	db "@"
+
+VitaminNoEffectText:
+	TX_FAR _VitaminNoEffectText
+	db "@"
+
+VitaminText:
+	db "HEALTH@"
+	db "ATTACK@"
+	db "DEFENSE@"
+	db "SPEED@"
+	db "SPECIAL@"
+
+ItemUseBait:
+	ld hl, ThrewBaitText
+	call PrintText
+	ld hl, wEnemyMonActualCatchRate ; catch rate
+	srl [hl] ; halve catch rate
+	ld a, BAIT_ANIM
+	ld hl, wSafariBaitFactor ; bait factor
+	ld de, wSafariEscapeFactor ; escape factor
+	jr BaitRockCommon
+
+ItemUseRock:
+	ld hl, ThrewRockText
+	call PrintText
+	ld hl, wEnemyMonActualCatchRate ; catch rate
+	ld a, [hl]
+	add a ; double catch rate
+	jr nc, .noCarry
+	ld a, $ff
+.noCarry
+	ld [hl], a
+	ld a, ROCK_ANIM
+	ld hl, wSafariEscapeFactor ; escape factor
+	ld de, wSafariBaitFactor ; bait factor
+
+BaitRockCommon:
+	ld [wAnimationID], a
+	xor a
+	ld [wAnimationType], a
+	ld [H_WHOSETURN], a
+	ld [de], a ; zero escape factor (for bait), zero bait factor (for rock)
+.randomLoop ; loop until a random number less than 5 is generated
+	call Random
+	and 7
+	cp 5
+	jr nc, .randomLoop
+	inc a ; increment the random number, giving a range from 1 to 5 inclusive
+	ld b, a
+	ld a, [hl]
+	add b ; increase bait factor (for bait), increase escape factor (for rock)
+	jr nc, .noCarry
+	ld a, $ff
+.noCarry
+	ld [hl], a
+	predef MoveAnimation ; do animation
+	ld c, 70
+	jp DelayFrames
+
+ThrewBaitText:
+	TX_FAR _ThrewBaitText
+	db "@"
+
+ThrewRockText:
+	TX_FAR _ThrewRockText
+	db "@"
+
+; also used for Dig out-of-battle effect
+ItemUseEscapeRope:
+	ld a, [wIsInBattle]
+	and a
+	jr nz, .notUsable
+	ld a, [wCurMap]
+	cp AGATHAS_ROOM
+	jr z, .notUsable
+	ld a, [wCurMapTileset]
+	ld b, a
+	ld hl, EscapeRopeTilesets
+.loop
+	ld a, [hli]
+	cp $ff
+	jr z, .notUsable
+	cp b
+	jr nz, .loop
+	ld hl, wd732
+	set 3, [hl]
+	set 6, [hl]
+	ld hl, wd72e
+	res 4, [hl]
+	ResetEvent EVENT_IN_SAFARI_ZONE
+	xor a
+	ld [wNumSafariBalls], a
+	ld [wSafariZoneGateCurScript], a
+	inc a
+	ld [wEscapedFromBattle], a
+	ld [wActionResultOrTookBattleTurn], a ; item used
+	ld a, [wPseudoItemID]
+	and a ; using Dig?
+	ret nz ; if so, return
+	call ItemUseReloadOverworldData
+	ld c, 30
+	call DelayFrames
+	jp RemoveUsedItem
+.notUsable
+	jp ItemUseNotTime
+
+EscapeRopeTilesets:
+	db FOREST, CEMETERY, CAVERN, FACILITY, INTERIOR
+	db $ff ; terminator
+
+ItemUseRepel:
+	ld b, 100
+
+ItemUseRepelCommon:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	ld a, b
+	ld [wRepelRemainingSteps], a
+	jp PrintItemUseTextAndRemoveItem
+
+; handles X Accuracy item
+ItemUseXAccuracy:
+	ld a, [wIsInBattle]
+	and a
+	jp z, ItemUseNotTime
+	ld hl, wPlayerBattleStatus2
+	set USING_X_ACCURACY, [hl] ; X Accuracy bit
+	jp PrintItemUseTextAndRemoveItem
+
+; This function is bugged and never works. It always jumps to ItemUseNotTime.
+; The Card Key is handled in a different way.
+ItemUseCardKey:
+	xor a
+	ld [wUnusedD71F], a
+	call GetTileAndCoordsInFrontOfPlayer
+	ld a, [GetTileAndCoordsInFrontOfPlayer]
+	cp $18
+	jr nz, .next0
+	ld hl, CardKeyTable1
+	jr .next1
+.next0
+	cp $24
+	jr nz, .next2
+	ld hl, CardKeyTable2
+	jr .next1
+.next2
+	cp $5e
+	jp nz, ItemUseNotTime
+	ld hl, CardKeyTable3
+.next1
+	ld a, [wCurMap]
+	ld b, a
+.loop
+	ld a, [hli]
+	cp $ff
+	jp z, ItemUseNotTime
+	cp b
+	jr nz, .nextEntry1
+	ld a, [hli]
+	cp d
+	jr nz, .nextEntry2
+	ld a, [hli]
+	cp e
+	jr nz, .nextEntry3
+	ld a, [hl]
+	ld [wUnusedD71F], a
+	jr .done
+.nextEntry1
+	inc hl
+.nextEntry2
+	inc hl
+.nextEntry3
+	inc hl
+	jr .loop
+.done
+	ld hl, ItemUseText00
+	call PrintText
+	ld hl, wd728
+	set 7, [hl]
+	ret
+
+; These tables are probably supposed to be door locations in Silph Co.,
+; but they are unused.
+; The reason there are 3 tables is unknown.
+
+; Format:
+; 00: Map ID
+; 01: Y
+; 02: X
+; 03: ID?
+
+CardKeyTable1:
+	db  SILPH_CO_2F,$04,$04,$00
+	db  SILPH_CO_2F,$04,$05,$01
+	db  SILPH_CO_4F,$0C,$04,$02
+	db  SILPH_CO_4F,$0C,$05,$03
+	db  SILPH_CO_7F,$06,$0A,$04
+	db  SILPH_CO_7F,$06,$0B,$05
+	db  SILPH_CO_9F,$04,$12,$06
+	db  SILPH_CO_9F,$04,$13,$07
+	db SILPH_CO_10F,$08,$0A,$08
+	db SILPH_CO_10F,$08,$0B,$09
+	db $ff
+
+CardKeyTable2:
+	db SILPH_CO_3F,$08,$09,$0A
+	db SILPH_CO_3F,$09,$09,$0B
+	db SILPH_CO_5F,$04,$07,$0C
+	db SILPH_CO_5F,$05,$07,$0D
+	db SILPH_CO_6F,$0C,$05,$0E
+	db SILPH_CO_6F,$0D,$05,$0F
+	db SILPH_CO_8F,$08,$07,$10
+	db SILPH_CO_8F,$09,$07,$11
+	db SILPH_CO_9F,$08,$03,$12
+	db SILPH_CO_9F,$09,$03,$13
+	db $ff
+
+CardKeyTable3:
+	db SILPH_CO_11F,$08,$09,$14
+	db SILPH_CO_11F,$09,$09,$15
+	db $ff
+
+ItemUsePokedoll:
+	ld a, [wIsInBattle]
+	dec a
+	jp nz, ItemUseNotTime
+	ld a, $01
+	ld [wEscapedFromBattle], a
+	jp PrintItemUseTextAndRemoveItem
+
+ItemUseGuardSpec:
+	ld a, [wIsInBattle]
+	and a
+	jp z, ItemUseNotTime
+	ld hl, wPlayerBattleStatus2
+	set PROTECTED_BY_MIST, [hl] ; Mist bit
+	jp PrintItemUseTextAndRemoveItem
+
+ItemUseSuperRepel:
+	ld b, 200
+	jp ItemUseRepelCommon
+
+ItemUseMaxRepel:
+	ld b, 250
+	jp ItemUseRepelCommon
+
+ItemUseDireHit:
+	ld a, [wIsInBattle]
+	and a
+	jp z, ItemUseNotTime
+	ld hl, wPlayerBattleStatus2
+	set GETTING_PUMPED, [hl] ; Focus Energy bit
+	jp PrintItemUseTextAndRemoveItem
+
+ItemUseXStat:
+	ld a, [wIsInBattle]
+	and a
+	jr nz, .inBattle
+	call ItemUseNotTime
+	ld a, 2
+	ld [wActionResultOrTookBattleTurn], a ; item not used
+	ret
+.inBattle
+	ld hl, wPlayerMoveNum
+	ld a, [hli]
+	push af ; save [wPlayerMoveNum]
+	ld a, [hl]
+	push af ; save [wPlayerMoveEffect]
+	push hl
+	ld a, [wcf91]
+	sub X_ATTACK - ATTACK_UP1_EFFECT
+	ld [hl], a ; store player move effect
+	call PrintItemUseTextAndRemoveItem
+	ld a, XSTATITEM_ANIM ; X stat item animation ID
+	ld [wPlayerMoveNum], a
+	call LoadScreenTilesFromBuffer1 ; restore saved screen
+	call Delay3
+	xor a
+	ld [H_WHOSETURN], a ; set turn to player's turn
+	callba StatModifierUpEffect ; do stat increase move
+	pop hl
+	pop af
+	ld [hld], a ; restore [wPlayerMoveEffect]
+	pop af
+	ld [hl], a ; restore [wPlayerMoveNum]
+	ret
+
+ItemUsePokeflute:
+	ld a, [wIsInBattle]
+	and a
+	jr nz, .inBattle
+; if not in battle
+	call ItemUseReloadOverworldData
+	ld a, [wCurMap]
+	cp ROUTE_12
+	jr nz, .notRoute12
+	CheckEvent EVENT_BEAT_ROUTE12_SNORLAX
+	jr nz, .noSnorlaxToWakeUp
+; if the player hasn't beaten Route 12 Snorlax
+	ld hl, Route12SnorlaxFluteCoords
+	call ArePlayerCoordsInArray
+	jr nc, .noSnorlaxToWakeUp
+	ld hl, PlayedFluteHadEffectText
+	call PrintText
+	SetEvent EVENT_FIGHT_ROUTE12_SNORLAX
+	ret
+.notRoute12
+	cp ROUTE_16
+	jr nz, .noSnorlaxToWakeUp
+	CheckEvent EVENT_BEAT_ROUTE16_SNORLAX
+	jr nz, .noSnorlaxToWakeUp
+; if the player hasn't beaten Route 16 Snorlax
+	ld hl, Route16SnorlaxFluteCoords
+	call ArePlayerCoordsInArray
+	jr nc, .noSnorlaxToWakeUp
+	ld hl, PlayedFluteHadEffectText
+	call PrintText
+	SetEvent EVENT_FIGHT_ROUTE16_SNORLAX
+	ret
+.noSnorlaxToWakeUp
+	ld hl, PlayedFluteNoEffectText
+	jp PrintText
+.inBattle
+	xor a
+	ld [wWereAnyMonsAsleep], a
+	ld b, ~SLP & $ff
+	ld hl, wPartyMon1Status
+	call WakeUpEntireParty
+	ld a, [wIsInBattle]
+	dec a ; is it a trainer battle?
+	jr z, .skipWakingUpEnemyParty
+; if it's a trainer battle
+	ld hl, wEnemyMon1Status
+	call WakeUpEntireParty
+.skipWakingUpEnemyParty
+	ld hl, wBattleMonStatus
+	ld a, [hl]
+	and b ; remove Sleep status
+	ld [hl], a
+	ld hl, wEnemyMonStatus
+	ld a, [hl]
+	and b ; remove Sleep status
+	ld [hl], a
+	call LoadScreenTilesFromBuffer2 ; restore saved screen
+	ld a, [wWereAnyMonsAsleep]
+	and a ; were any pokemon asleep before playing the flute?
+	ld hl, PlayedFluteNoEffectText
+	jp z, PrintText ; if no pokemon were asleep
+; if some pokemon were asleep
+	ld hl, PlayedFluteHadEffectText
+	call PrintText
+	ld a, [wLowHealthAlarm]
+	and $80
+	jr nz, .skipMusic
+	call WaitForSoundToFinish ; wait for sound to end
+	callba Music_PokeFluteInBattle ; play in-battle pokeflute music
+.musicWaitLoop ; wait for music to finish playing
+	ld a, [wChannelSoundIDs + Ch7]
+	and a ; music off?
+	jr nz, .musicWaitLoop
+.skipMusic
+	ld hl, FluteWokeUpText
+	jp PrintText
+
+; wakes up all party pokemon
+; INPUT:
+; hl must point to status of first pokemon in party (player's or enemy's)
+; b must equal ~SLP
+; [wWereAnyMonsAsleep] should be initialized to 0
+; OUTPUT:
+; [wWereAnyMonsAsleep]: set to 1 if any pokemon were asleep
+WakeUpEntireParty:
+	ld de, 44
+	ld c, 6
+.loop
+	ld a, [hl]
+	push af
+	and SLP ; is pokemon asleep?
+	jr z, .notAsleep
+	ld a, 1
+	ld [wWereAnyMonsAsleep], a ; indicate that a pokemon had to be woken up
+.notAsleep
+	pop af
+	and b ; remove Sleep status
+	ld [hl], a
+	add hl, de
+	dec c
+	jr nz, .loop
+	ret
+
+; Format:
+; 00: Y
+; 01: X
+Route12SnorlaxFluteCoords:
+	db 62,9  ; one space West of Snorlax
+	db 61,10 ; one space North of Snorlax
+	db 63,10 ; one space South of Snorlax
+	db 62,11 ; one space East of Snorlax
+	db $ff ; terminator
+
+; Format:
+; 00: Y
+; 01: X
+Route16SnorlaxFluteCoords:
+	db 10,27 ; one space East of Snorlax
+	db 10,25 ; one space West of Snorlax
+	db $ff ; terminator
+
+PlayedFluteNoEffectText:
+	TX_FAR _PlayedFluteNoEffectText
+	db "@"
+
+FluteWokeUpText:
+	TX_FAR _FluteWokeUpText
+	db "@"
+
+PlayedFluteHadEffectText:
+	TX_FAR _PlayedFluteHadEffectText
+	TX_BLINK
+	TX_ASM
+	ld a, [wIsInBattle]
+	and a
+	jr nz, .done
+; play out-of-battle pokeflute music
+	ld a, $ff
+	call PlaySound ; turn off music
+	ld a, SFX_POKEFLUTE
+	ld c, BANK(SFX_Pokeflute)
+	call PlayMusic
+.musicWaitLoop ; wait for music to finish playing
+	ld a, [wChannelSoundIDs + Ch3]
+	cp SFX_POKEFLUTE
+	jr z, .musicWaitLoop
+	call PlayDefaultMusic ; start playing normal music again
+.done
+	jp TextScriptEnd ; end text
+
+ItemUseCoinCase:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	ld hl, CoinCaseNumCoinsText
+	jp PrintText
+
+CoinCaseNumCoinsText:
+	TX_FAR _CoinCaseNumCoinsText
+	db "@"
+
+ItemUseOldRod:
+	call FishingInit
+	jp c, ItemUseNotTime
+	lb bc, 5, MAGIKARP
+	ld a, $1 ; set bite
+	jr RodResponse
+
+ItemUseGoodRod:
+	call FishingInit
+	jp c, ItemUseNotTime
+.RandomLoop
+	call Random
+	srl a
+	jr c, .SetBite
+	and %11
+	cp 2
+	jr nc, .RandomLoop
+	; choose which monster appears
+	ld hl, GoodRodMons
+	add a
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld b, [hl]
+	inc hl
+	ld c, [hl]
+	and a
+.SetBite
+	ld a, 0
+	rla
+	xor 1
+	jr RodResponse
+
+INCLUDE "data/good_rod.asm"
+
+ItemUseSuperRod:
+	call FishingInit
+	jp c, ItemUseNotTime
+	call ReadSuperRodData
+	ld a, e
+RodResponse:
+	ld [wRodResponse], a
+
+	dec a ; is there a bite?
+	jr nz, .next
+	; if yes, store level and species data
+	ld a, 1
+	ld [wMoveMissed], a
+	ld a, b ; level
+	ld [wCurEnemyLVL], a
+	ld a, c ; species
+	ld [wCurOpponent], a
+
+.next
+	ld hl, wWalkBikeSurfState
+	ld a, [hl] ; store the value in a
+	push af
+	push hl
+	ld [hl], 0
+	callba FishingAnim
+	pop hl
+	pop af
+	ld [hl], a
+	ret
+
+; checks if fishing is possible and if so, runs initialization code common to all rods
+; unsets carry if fishing is possible, sets carry if not
+FishingInit:
+	ld a, [wIsInBattle]
+	and a
+	jr z, .notInBattle
+	scf ; can't fish during battle
+	ret
+.notInBattle
+	call IsNextTileShoreOrWater
+	ret c
+	ld a, [wWalkBikeSurfState]
+	cp 2 ; Surfing?
+	jr z, .surfing
+	call ItemUseReloadOverworldData
+	ld hl, ItemUseText00
+	call PrintText
+	ld a, SFX_HEAL_AILMENT
+	call PlaySound
+	ld c, 80
+	call DelayFrames
+	and a
+	ret
+.surfing
+	scf ; can't fish when surfing
+	ret
+
+ItemUseOaksParcel:
+	jp ItemUseNotYoursToUse
+
+ItemUseItemfinder:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	call ItemUseReloadOverworldData
+	callba HiddenItemNear ; check for hidden items
+	ld hl, ItemfinderFoundNothingText
+	jr nc, .printText ; if no hidden items
+	ld c, 4
+.loop
+	ld a, SFX_HEALING_MACHINE
+	call PlaySoundWaitForCurrent
+	ld a, SFX_PURCHASE
+	call PlaySoundWaitForCurrent
+	dec c
+	jr nz, .loop
+	ld hl, ItemfinderFoundItemText
+.printText
+	jp PrintText
+
+ItemfinderFoundItemText:
+	TX_FAR _ItemfinderFoundItemText
+	db "@"
+
+ItemfinderFoundNothingText:
+	TX_FAR _ItemfinderFoundNothingText
+	db "@"
+
+ItemUsePPUp:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+
+ItemUsePPRestore:
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wcf91]
+	ld [wPPRestoreItem], a
+.chooseMon
+	xor a
+	ld [wUpdateSpritesEnabled], a
+	ld a, USE_ITEM_PARTY_MENU
+	ld [wPartyMenuTypeOrMessageID], a
+	call DisplayPartyMenu
+	jr nc, .chooseMove
+	jp .itemNotUsed
+.chooseMove
+	ld a, [wPPRestoreItem]
+	cp ELIXER
+	jp nc, .useElixir ; if Elixir or Max Elixir
+	ld a, $02
+	ld [wMoveMenuType], a
+	ld hl, RaisePPWhichTechniqueText
+	ld a, [wPPRestoreItem]
+	cp ETHER ; is it a PP Up?
+	jr c, .printWhichTechniqueMessage ; if so, print the raise PP message
+	ld hl, RestorePPWhichTechniqueText ; otherwise, print the restore PP message
+.printWhichTechniqueMessage
+	call PrintText
+	xor a
+	ld [wPlayerMoveListIndex], a
+	callab MoveSelectionMenu ; move selection menu
+	ld a, 0
+	ld [wPlayerMoveListIndex], a
+	jr nz, .chooseMon
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2 - wPartyMon1
+	call GetSelectedMoveOffset
+	push hl
+	ld a, [hl]
+	ld [wd11e], a
+	call GetMoveName
+	call CopyStringToCF4B ; copy name to wcf4b
+	pop hl
+	ld a, [wPPRestoreItem]
+	cp ETHER
+	jr nc, .useEther ; if Ether or Max Ether
+.usePPUp
+	ld bc, wPartyMon1PP - wPartyMon1Moves
+	add hl, bc
+	ld a, [hl] ; move PP
+	cp 3 << 6 ; have 3 PP Ups already been used?
+	jr c, .PPNotMaxedOut
+	ld hl, PPMaxedOutText
+	call PrintText
+	jr .chooseMove
+.PPNotMaxedOut
+	ld a, [hl]
+	add 1 << 6 ; increase PP Up count by 1
+	ld [hl], a
+	ld a, 1 ; 1 PP Up used
+	ld [wd11e], a
+	call RestoreBonusPP ; add the bonus PP to current PP
+	ld hl, PPIncreasedText
+	call PrintText
+.done
+	pop af
+	ld [wWhichPokemon], a
+	call GBPalWhiteOut
+	call RunDefaultPaletteCommand
+	jp RemoveUsedItem
+.afterRestoringPP ; after using a (Max) Ether/Elixir
+	ld a, [wWhichPokemon]
+	ld b, a
+	ld a, [wPlayerMonNumber]
+	cp b ; is the pokemon whose PP was restored active in battle?
+	jr nz, .skipUpdatingInBattleData
+	ld hl, wPartyMon1PP
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld de, wBattleMonPP
+	ld bc, 4
+	call CopyData ; copy party data to in-battle data
+.skipUpdatingInBattleData
+	ld a, SFX_HEAL_AILMENT
+	call PlaySound
+	ld hl, PPRestoredText
+	call PrintText
+	jr .done
+.useEther
+	call .restorePP
+	jr nz, .afterRestoringPP
+	jp .noEffect
+; unsets zero flag if PP was restored, sets zero flag if not
+; however, this is bugged for Max Ethers and Max Elixirs (see below)
+.restorePP
+	xor a ; PLAYER_PARTY_DATA
+	ld [wMonDataLocation], a
+	call GetMaxPP
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2 - wPartyMon1
+	call GetSelectedMoveOffset
+	ld bc, wPartyMon1PP - wPartyMon1Moves
+	add hl, bc ; hl now points to move's PP
+	ld a, [wMaxPP]
+	ld b, a
+	ld a, [wPPRestoreItem]
+	cp MAX_ETHER
+	jr z, .fullyRestorePP
+	ld a, [hl] ; move PP
+	and %00111111 ; lower 6 bit bits store current PP
+	cp b ; does current PP equal max PP?
+	ret z ; if so, return
+	add 10 ; increase current PP by 10
+; b holds the max PP amount and b will hold the new PP amount.
+; So, if the new amount meets or exceeds the max amount,
+; cap the amount to the max amount by leaving b unchanged.
+; Otherwise, store the new amount in b.
+	cp b ; does the new amount meet or exceed the maximum?
+	jr nc, .storeNewAmount
+	ld b, a
+.storeNewAmount
+	ld a, [hl] ; move PP
+	and %11000000 ; PP Up counter bits
+	add b
+	ld [hl], a
+	ret
+.fullyRestorePP
+	ld a, [hl] ; move PP
+; Note that this code has a bug. It doesn't mask out the upper two bits, which
+; are used to count how many PP Ups have been used on the move. So, Max Ethers
+; and Max Elixirs will not be detected as having no effect on a move with full
+; PP if the move has had any PP Ups used on it.
+	cp b ; does current PP equal max PP?
+	ret z
+	jr .storeNewAmount
+.useElixir
+; decrement the item ID so that ELIXER becomes ETHER and MAX_ELIXER becomes MAX_ETHER
+	ld hl, wPPRestoreItem
+	dec [hl]
+	dec [hl]
+	xor a
+	ld hl, wCurrentMenuItem
+	ld [hli], a
+	ld [hl], a ; zero the counter for number of moves that had their PP restored
+	ld b, 4
+; loop through each move and restore PP
+.elixirLoop
+	push bc
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2 - wPartyMon1
+	call GetSelectedMoveOffset
+	ld a, [hl]
+	and a ; does the current slot have a move?
+	jr z, .nextMove
+	call .restorePP
+	jr z, .nextMove
+; if some PP was restored
+	ld hl, wTileBehindCursor ; counter for number of moves that had their PP restored
+	inc [hl]
+.nextMove
+	ld hl, wCurrentMenuItem
+	inc [hl]
+	pop bc
+	dec b
+	jr nz, .elixirLoop
+	ld a, [wTileBehindCursor]
+	and a ; did any moves have their PP restored?
+	jp nz, .afterRestoringPP
+.noEffect
+	call ItemUseNoEffect
+.itemNotUsed
+	call GBPalWhiteOut
+	call RunDefaultPaletteCommand
+	pop af
+	xor a
+	ld [wActionResultOrTookBattleTurn], a ; item use failed
+	ret
+
+RaisePPWhichTechniqueText:
+	TX_FAR _RaisePPWhichTechniqueText
+	db "@"
+
+RestorePPWhichTechniqueText:
+	TX_FAR _RestorePPWhichTechniqueText
+	db "@"
+
+PPMaxedOutText:
+	TX_FAR _PPMaxedOutText
+	db "@"
+
+PPIncreasedText:
+	TX_FAR _PPIncreasedText
+	db "@"
+
+PPRestoredText:
+	TX_FAR _PPRestoredText
+	db "@"
+
+; for items that can't be used from the Item menu
+UnusableItem:
+	jp ItemUseNotTime
+
+ItemUseTMHM:
+	ld a, [wIsInBattle]
+	and a
+	jp nz, ItemUseNotTime
+	ld a, [wcf91]
+	sub TM_01
+	push af
+	jr nc, .skipAdding
+	add 55 ; if item is an HM, add 55
+.skipAdding
+	inc a
+	ld [wd11e], a
+	predef TMToMove ; get move ID from TM/HM ID
+	ld a, [wd11e]
+	ld [wMoveNum], a
+	call GetMoveName
+	call CopyStringToCF4B ; copy name to wcf4b
+	pop af
+	ld hl, BootedUpTMText
+	jr nc, .printBootedUpMachineText
+	ld hl, BootedUpHMText
+.printBootedUpMachineText
+	call PrintText
+	ld hl, TeachMachineMoveText
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; yes/no menu
+	ld a, [wCurrentMenuItem]
+	and a
+	jr z, .useMachine
+	ld a, 2
+	ld [wActionResultOrTookBattleTurn], a ; item not used
+	ret
+.useMachine
+	ld a, [wWhichPokemon]
+	push af
+	ld a, [wcf91]
+	push af
+.chooseMon
+	ld hl, wcf4b
+	ld de, wTempMoveNameBuffer
+	ld bc, 14
+	call CopyData ; save the move name because DisplayPartyMenu will overwrite it
+	ld a, $ff
+	ld [wUpdateSpritesEnabled], a
+	ld a, TMHM_PARTY_MENU
+	ld [wPartyMenuTypeOrMessageID], a
+	call DisplayPartyMenu
+	push af
+	ld hl, wTempMoveNameBuffer
+	ld de, wcf4b
+	ld bc, 14
+	call CopyData
+	pop af
+	jr nc, .checkIfAbleToLearnMove
+; if the player canceled teaching the move
+	pop af
+	pop af
+	call GBPalWhiteOutWithDelay3
+	call ClearSprites
+	call RunDefaultPaletteCommand
+	jp LoadScreenTilesFromBuffer1 ; restore saved screen
+.checkIfAbleToLearnMove
+	predef CanLearnTM ; check if the pokemon can learn the move
+	push bc
+	ld a, [wWhichPokemon]
+	ld hl, wPartyMonNicks
+	call GetPartyMonName
+	pop bc
+	ld a, c
+	and a ; can the pokemon learn the move?
+	jr nz, .checkIfAlreadyLearnedMove
+; if the pokemon can't learn the move
+	ld a, SFX_DENIED
+	call PlaySoundWaitForCurrent
+	ld hl, MonCannotLearnMachineMoveText
+	call PrintText
+	jr .chooseMon
+.checkIfAlreadyLearnedMove
+	callab CheckIfMoveIsKnown ; check if the pokemon already knows the move
+	jr c, .chooseMon
+	predef LearnMove ; teach move
+	pop af
+	ld [wcf91], a
+	pop af
+	ld [wWhichPokemon], a
+	ld a, b
+	and a
+	ret z
+	ld a, [wcf91]
+	call IsItemHM
+	ret c
+	jp RemoveUsedItem
+
+BootedUpTMText:
+	TX_FAR _BootedUpTMText
+	db "@"
+
+BootedUpHMText:
+	TX_FAR _BootedUpHMText
+	db "@"
+
+TeachMachineMoveText:
+	TX_FAR _TeachMachineMoveText
+	db "@"
+
+MonCannotLearnMachineMoveText:
+	TX_FAR _MonCannotLearnMachineMoveText
+	db "@"
+
+PrintItemUseTextAndRemoveItem:
+	ld hl, ItemUseText00
+	call PrintText
+	ld a, SFX_HEAL_AILMENT
+	call PlaySound
+	call WaitForTextScrollButtonPress ; wait for button press
+
+RemoveUsedItem:
+	ld hl, wNumBagItems
+	ld a, 1 ; one item
+	ld [wItemQuantity], a
+	jp RemoveItemFromInventory
+
+ItemUseNoEffect:
+	ld hl, ItemUseNoEffectText
+	jr ItemUseFailed
+
+ItemUseNotTime:
+	ld hl, ItemUseNotTimeText
+	jr ItemUseFailed
+
+ItemUseNotYoursToUse:
+	ld hl, ItemUseNotYoursToUseText
+	jr ItemUseFailed
+
+ThrowBallAtTrainerMon:
+	call RunDefaultPaletteCommand
+	call LoadScreenTilesFromBuffer1 ; restore saved screen
+	call Delay3
+	ld a, TOSS_ANIM
+	ld [wAnimationID], a
+	predef MoveAnimation ; do animation
+	ld hl, ThrowBallAtTrainerMonText1
+	call PrintText
+	ld hl, ThrowBallAtTrainerMonText2
+	call PrintText
+	jr RemoveUsedItem
+
+NoCyclingAllowedHere:
+	ld hl, NoCyclingAllowedHereText
+	jr ItemUseFailed
+
+BoxFullCannotThrowBall:
+	ld hl, BoxFullCannotThrowBallText
+	jr ItemUseFailed
+
+SurfingAttemptFailed:
+	ld hl, NoSurfingHereText
+
+ItemUseFailed:
+	xor a
+	ld [wActionResultOrTookBattleTurn], a ; item use failed
+	jp PrintText
+
+ItemUseNotTimeText:
+	TX_FAR _ItemUseNotTimeText
+	db "@"
+
+ItemUseNotYoursToUseText:
+	TX_FAR _ItemUseNotYoursToUseText
+	db "@"
+
+ItemUseNoEffectText:
+	TX_FAR _ItemUseNoEffectText
+	db "@"
+
+ThrowBallAtTrainerMonText1:
+	TX_FAR _ThrowBallAtTrainerMonText1
+	db "@"
+
+ThrowBallAtTrainerMonText2:
+	TX_FAR _ThrowBallAtTrainerMonText2
+	db "@"
+
+NoCyclingAllowedHereText:
+	TX_FAR _NoCyclingAllowedHereText
+	db "@"
+
+NoSurfingHereText:
+	TX_FAR _NoSurfingHereText
+	db "@"
+
+BoxFullCannotThrowBallText:
+	TX_FAR _BoxFullCannotThrowBallText
+	db "@"
+
+ItemUseText00:
+	TX_FAR _ItemUseText001
+	TX_LINE
+	TX_FAR _ItemUseText002
+	db "@"
+
+GotOnBicycleText:
+	TX_FAR _GotOnBicycleText1
+	TX_LINE
+	TX_FAR _GotOnBicycleText2
+	db "@"
+
+GotOffBicycleText:
+	TX_FAR _GotOffBicycleText1
+	TX_LINE
+	TX_FAR _GotOffBicycleText2
+	db "@"
+
+; restores bonus PP (from PP Ups) when healing at a pokemon center
+; also, when a PP Up is used, it increases the current PP by one PP Up bonus
+; INPUT:
+; [wWhichPokemon] = index of pokemon in party
+; [wCurrentMenuItem] = index of move (when using a PP Up)
+RestoreBonusPP:
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2 - wPartyMon1
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	push hl
+	ld de, wNormalMaxPPList - 1
+	predef LoadMovePPs ; loads the normal max PP of each of the pokemon's moves to wNormalMaxPPList
+	pop hl
+	ld c, wPartyMon1PP - wPartyMon1Moves
+	ld b, 0
+	add hl, bc ; hl now points to move 1 PP
+	ld de, wNormalMaxPPList
+	ld b, 0 ; initialize move counter to zero
+; loop through the pokemon's moves
+.loop
+	inc b
+	ld a, b
+	cp 5 ; reached the end of the pokemon's moves?
+	ret z ; if so, return
+	ld a, [wUsingPPUp]
+	dec a ; using a PP Up?
+	jr nz, .skipMenuItemIDCheck
+; if using a PP Up, check if this is the move it's being used on
+	ld a, [wCurrentMenuItem]
+	inc a
+	cp b
+	jr nz, .nextMove
+.skipMenuItemIDCheck
+	ld a, [hl]
+	and %11000000 ; have any PP Ups been used?
+	call nz, AddBonusPP ; if so, add bonus PP
+.nextMove
+	inc hl
+	inc de
+	jr .loop
+
+; adds bonus PP from PP Ups to current PP
+; 1/5 of normal max PP (capped at 7) is added for each PP Up
+; INPUT:
+; [de] = normal max PP
+; [hl] = move PP
+AddBonusPP:
+	push bc
+	ld a, [de] ; normal max PP of move
+	ld [H_DIVIDEND + 3], a
+	xor a
+	ld [H_DIVIDEND], a
+	ld [H_DIVIDEND + 1], a
+	ld [H_DIVIDEND + 2], a
+	ld a, 5
+	ld [H_DIVISOR], a
+	ld b, 4
+	call Divide
+	ld a, [hl] ; move PP
+	ld b, a
+	swap a
+	and %00001111
+	srl a
+	srl a
+	ld c, a ; c = number of PP Ups used
+.loop
+	ld a, [H_QUOTIENT + 3]
+	cp 8 ; is the amount greater than or equal to 8?
+	jr c, .addAmount
+	ld a, 7 ; cap the amount at 7
+.addAmount
+	add b
+	ld b, a
+	ld a, [wUsingPPUp]
+	dec a ; is the player using a PP Up right now?
+	jr z, .done ; if so, only add the bonus once
+	dec c
+	jr nz, .loop
+.done
+	ld [hl], b
+	pop bc
+	ret
+
+; gets max PP of a pokemon's move (including PP from PP Ups)
+; INPUT:
+; [wWhichPokemon] = index of pokemon within party/box
+; [wMonDataLocation] = pokemon source
+; 00: player's party
+; 01: enemy's party
+; 02: current box
+; 03: daycare
+; 04: player's in-battle pokemon
+; [wCurrentMenuItem] = move index
+; OUTPUT:
+; [wMaxPP] = max PP
+GetMaxPP:
+	ld a, [wMonDataLocation]
+	and a
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2 - wPartyMon1
+	jr z, .sourceWithMultipleMon
+	ld hl, wEnemyMon1Moves
+	dec a
+	jr z, .sourceWithMultipleMon
+	ld hl, wBoxMon1Moves
+	ld bc, wBoxMon2 - wBoxMon1
+	dec a
+	jr z, .sourceWithMultipleMon
+	ld hl, wDayCareMonMoves
+	dec a
+	jr z, .sourceWithOneMon
+	ld hl, wBattleMonMoves ; player's in-battle pokemon
+.sourceWithOneMon
+	call GetSelectedMoveOffset2
+	jr .next
+.sourceWithMultipleMon
+	call GetSelectedMoveOffset
+.next
+	ld a, [hl]
+	dec a
+	push hl
+	ld hl, Moves
+	ld bc, MoveEnd - Moves
+	call AddNTimes
+	ld de, wcd6d
+	ld a, BANK(Moves)
+	call FarCopyData
+	ld de, wcd6d + 5 ; PP is byte 5 of move data
+	ld a, [de]
+	ld b, a ; b = normal max PP
+	pop hl
+	push bc
+	ld bc, wPartyMon1PP - wPartyMon1Moves ; PP offset if not player's in-battle pokemon data
+	ld a, [wMonDataLocation]
+	cp 4 ; player's in-battle pokemon?
+	jr nz, .addPPOffset
+	ld bc, wBattleMonPP - wBattleMonMoves ; PP offset if player's in-battle pokemon data
+.addPPOffset
+	add hl, bc
+	ld a, [hl] ; a = current PP
+	and %11000000 ; get PP Up count
+	pop bc
+	or b ; place normal max PP in 6 lower bits of a
+	ld h, d
+	ld l, e
+	inc hl ; hl = wcd73
+	ld [hl], a
+	xor a ; add the bonus for the existing PP Up count
+	ld [wUsingPPUp], a
+	call AddBonusPP ; add bonus PP from PP Ups
+	ld a, [hl]
+	and %00111111 ; mask out the PP Up count
+	ld [wMaxPP], a ; store max PP
+	ret
+
+GetSelectedMoveOffset:
+	ld a, [wWhichPokemon]
+	call AddNTimes
+
+GetSelectedMoveOffset2:
+	ld a, [wCurrentMenuItem]
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ret
+
+; confirms the item toss and then tosses the item
+; INPUT:
+; hl = address of inventory (either wNumBagItems or wNumBoxItems)
+; [wcf91] = item ID
+; [wWhichPokemon] = index of item within inventory
+; [wItemQuantity] = quantity to toss
+; OUTPUT:
+; clears carry flag if the item is tossed, sets carry flag if not
+TossItem_::
+	push hl
+	ld a, [wcf91]
+	call IsItemHM
+	pop hl
+	jr c, .tooImportantToToss
+	push hl
+	call IsKeyItem_
+	ld a, [wIsKeyItem]
+	pop hl
+	and a
+	jr nz, .tooImportantToToss
+	push hl
+	ld a, [wcf91]
+	ld [wd11e], a
+	call GetItemName
+	call CopyStringToCF4B ; copy name to wcf4b
+	ld hl, IsItOKToTossItemText
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; yes/no menu
+	ld a, [wMenuExitMethod]
+	cp CHOSE_SECOND_ITEM
+	pop hl
+	scf
+	ret z ; return if the player chose No
+; if the player chose Yes
+	push hl
+	ld a, [wWhichPokemon]
+	call RemoveItemFromInventory
+	ld a, [wcf91]
+	ld [wd11e], a
+	call GetItemName
+	call CopyStringToCF4B ; copy name to wcf4b
+	ld hl, ThrewAwayItemText
+	call PrintText
+	pop hl
+	and a
+	ret
+.tooImportantToToss
+	push hl
+	ld hl, TooImportantToTossText
+	call PrintText
+	pop hl
+	scf
+	ret
+
+ThrewAwayItemText:
+	TX_FAR _ThrewAwayItemText
+	db "@"
+
+IsItOKToTossItemText:
+	TX_FAR _IsItOKToTossItemText
+	db "@"
+
+TooImportantToTossText:
+	TX_FAR _TooImportantToTossText
+	db "@"
+
+; checks if an item is a key item
+; INPUT:
+; [wcf91] = item ID
+; OUTPUT:
+; [wIsKeyItem] = result
+; 00: item is not key item
+; 01: item is key item
+IsKeyItem_::
+	ld a, $01
+	ld [wIsKeyItem], a
+	ld a, [wcf91]
+	cp HM_01 ; is the item an HM or TM?
+	jr nc, .checkIfItemIsHM
+; if the item is not an HM or TM
+	push af
+	ld hl, KeyItemBitfield
+	ld de, wBuffer
+	ld bc, 15 ; only 11 bytes are actually used
+	call CopyData
+	pop af
+	dec a
+	ld c, a
+	ld hl, wBuffer
+	ld b, FLAG_TEST
+	predef FlagActionPredef
+	ld a, c
+	and a
+	ret nz
+.checkIfItemIsHM
+	ld a, [wcf91]
+	call IsItemHM
+	ret c
+	xor a
+	ld [wIsKeyItem], a
+	ret
+
+INCLUDE "data/key_items.asm"
+
+SendNewMonToBox:
+	ld de, wNumInBox
+	ld a, [de]
+	inc a
+	ld [de], a
+	ld a, [wcf91]
+	ld [wd0b5], a
+	ld c, a
+.asm_e7b1
+	inc de
+	ld a, [de]
+	ld b, a
+	ld a, c
+	ld c, b
+	ld [de], a
+	cp $ff
+	jr nz, .asm_e7b1
+	call GetMonHeader
+	ld hl, wBoxMonOT
+	ld bc, NAME_LENGTH
+	ld a, [wNumInBox]
+	dec a
+	jr z, .asm_e7ee
+	dec a
+	call AddNTimes
+	push hl
+	ld bc, NAME_LENGTH
+	add hl, bc
+	ld d, h
+	ld e, l
+	pop hl
+	ld a, [wNumInBox]
+	dec a
+	ld b, a
+.asm_e7db
+	push bc
+	push hl
+	ld bc, NAME_LENGTH
+	call CopyData
+	pop hl
+	ld d, h
+	ld e, l
+	ld bc, -NAME_LENGTH
+	add hl, bc
+	pop bc
+	dec b
+	jr nz, .asm_e7db
+.asm_e7ee
+	ld hl, wPlayerName
+	ld de, wBoxMonOT
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld a, [wNumInBox]
+	dec a
+	jr z, .asm_e82a
+	ld hl, wBoxMonNicks
+	ld bc, NAME_LENGTH
+	dec a
+	call AddNTimes
+	push hl
+	ld bc, NAME_LENGTH
+	add hl, bc
+	ld d, h
+	ld e, l
+	pop hl
+	ld a, [wNumInBox]
+	dec a
+	ld b, a
+.asm_e817
+	push bc
+	push hl
+	ld bc, NAME_LENGTH
+	call CopyData
+	pop hl
+	ld d, h
+	ld e, l
+	ld bc, -NAME_LENGTH
+	add hl, bc
+	pop bc
+	dec b
+	jr nz, .asm_e817
+.asm_e82a
+	ld hl, wBoxMonNicks
+	ld a, NAME_MON_SCREEN
+	ld [wNamingScreenType], a
+	predef AskName
+	ld a, [wNumInBox]
+	dec a
+	jr z, .asm_e867
+	ld hl, wBoxMons
+	ld bc, wBoxMon2 - wBoxMon1
+	dec a
+	call AddNTimes
+	push hl
+	ld bc, wBoxMon2 - wBoxMon1
+	add hl, bc
+	ld d, h
+	ld e, l
+	pop hl
+	ld a, [wNumInBox]
+	dec a
+	ld b, a
+.asm_e854
+	push bc
+	push hl
+	ld bc, wBoxMon2 - wBoxMon1
+	call CopyData
+	pop hl
+	ld d, h
+	ld e, l
+	ld bc, wBoxMon1 - wBoxMon2
+	add hl, bc
+	pop bc
+	dec b
+	jr nz, .asm_e854
+.asm_e867
+	ld a, [wEnemyMonLevel]
+	ld [wEnemyMonBoxLevel], a
+	ld hl, wEnemyMon
+	ld de, wBoxMon1
+	ld bc, wEnemyMonDVs - wEnemyMon
+	call CopyData
+	ld hl, wPlayerID
+	ld a, [hli]
+	ld [de], a
+	inc de
+	ld a, [hl]
+	ld [de], a
+	inc de
+	push de
+	ld a, [wCurEnemyLVL]
+	ld d, a
+	callab CalcExperience
+	pop de
+	ld a, [hExperience]
+	ld [de], a
+	inc de
+	ld a, [hExperience + 1]
+	ld [de], a
+	inc de
+	ld a, [hExperience + 2]
+	ld [de], a
+	inc de
+	xor a
+	ld b, NUM_STATS * 2
+.asm_e89f
+	ld [de], a
+	inc de
+	dec b
+	jr nz, .asm_e89f
+	ld hl, wEnemyMonDVs
+	ld a, [hli]
+	ld [de], a
+	inc de
+	ld a, [hli]
+	ld [de], a
+	ld hl, wEnemyMonPP
+	ld b, NUM_MOVES
+.asm_e8b1
+	ld a, [hli]
+	inc de
+	ld [de], a
+	dec b
+	jr nz, .asm_e8b1
+	ret
+
+; checks if the tile in front of the player is a shore or water tile
+; used for surfing and fishing
+; unsets carry if it is, sets carry if not
+IsNextTileShoreOrWater:
+	ld a, [wCurMapTileset]
+	ld hl, WaterTilesets
+	ld de, 1
+	call IsInArray
+	jr nc, .notShoreOrWater
+	ld a, [wCurMapTileset]
+	cp SHIP_PORT ; Vermilion Dock tileset
+	ld a, [wTileInFrontOfPlayer] ; tile in front of player
+	jr z, .skipShoreTiles ; if it's the Vermilion Dock tileset
+	cp $48 ; eastern shore tile in Safari Zone
+	jr z, .shoreOrWater
+	cp $32 ; usual eastern shore tile
+	jr z, .shoreOrWater
+.skipShoreTiles
+	cp $14 ; water tile
+	jr z, .shoreOrWater
+.notShoreOrWater
+	scf
+	ret
+.shoreOrWater
+	and a
+	ret
+
+INCLUDE "data/water_tilesets.asm"
+
+ReadSuperRodData:
+; return e = 2 if no fish on this map
+; return e = 1 if a bite, bc = level,species
+; return e = 0 if no bite
+	ld a, [wCurMap]
+	ld de, 3 ; each fishing group is three bytes wide
+	ld hl, SuperRodData
+	call IsInArray
+	jr c, .ReadFishingGroup
+	ld e, $2 ; $2 if no fishing groups found
+	ret
+
+.ReadFishingGroup
+; hl points to the fishing group entry in the index
+	inc hl ; skip map id
+
+	; read fishing group address
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+
+	ld b, [hl] ; how many mons in group
+	inc hl ; point to data
+	ld e, $0 ; no bite yet
+
+.RandomLoop
+	call Random
+	srl a
+	ret c ; 50% chance of no battle
+
+	and %11 ; 2-bit random number
+	cp b
+	jr nc, .RandomLoop ; if a is greater than the number of mons, regenerate
+
+	; get the mon
+	add a
+	ld c, a
+	ld b, $0
+	add hl, bc
+	ld b, [hl] ; level
+	inc hl
+	ld c, [hl] ; species
+	ld e, $1 ; $1 if there's a bite
+	ret
+
+INCLUDE "data/super_rod.asm"
+
+; reloads map view and processes sprite data
+; for items that cause the overworld to be displayed
+ItemUseReloadOverworldData:
+	call LoadCurrentMapView
+	jp UpdateSprites
+
+; creates a list at wBuffer of maps where the mon in [wd11e] can be found.
+; this is used by the pokedex to display locations the mon can be found on the map.
+FindWildLocationsOfMon:
+	ld hl, WildDataPointers
+	ld de, wBuffer
+	ld c, $0
+.loop
+	inc hl
+	ld a, [hld]
+	inc a
+	jr z, .done
+	push hl
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld a, [hli]
+	and a
+	call nz, CheckMapForMon ; land
+	ld a, [hli]
+	and a
+	call nz, CheckMapForMon ; water
+	pop hl
+	inc hl
+	inc hl
+	inc c
+	jr .loop
+.done
+	ld a, $ff ; list terminator
+	ld [de], a
+	ret
+
+CheckMapForMon:
+	inc hl
+	ld b, $a
+.loop
+	ld a, [wd11e]
+	cp [hl]
+	jr nz, .nextEntry
+	ld a, c
+	ld [de], a
+	inc de
+.nextEntry
+	inc hl
+	inc hl
+	dec b
+	jr nz, .loop
+	dec hl
+	ret
--- a/engine/items/items.asm
+++ /dev/null
@@ -1,2986 +1,0 @@
-UseItem_::
-	ld a, 1
-	ld [wActionResultOrTookBattleTurn], a ; initialise to success value
-	ld a, [wcf91] ;contains item_ID
-	cp HM_01
-	jp nc, ItemUseTMHM
-	ld hl, ItemUsePtrTable
-	dec a
-	add a
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	jp hl
-
-ItemUsePtrTable:
-	dw ItemUseBall       ; MASTER_BALL
-	dw ItemUseBall       ; ULTRA_BALL
-	dw ItemUseBall       ; GREAT_BALL
-	dw ItemUseBall       ; POKE_BALL
-	dw ItemUseTownMap    ; TOWN_MAP
-	dw ItemUseBicycle    ; BICYCLE
-	dw ItemUseSurfboard  ; out-of-battle Surf effect
-	dw ItemUseBall       ; SAFARI_BALL
-	dw ItemUsePokedex    ; POKEDEX
-	dw ItemUseEvoStone   ; MOON_STONE
-	dw ItemUseMedicine   ; ANTIDOTE
-	dw ItemUseMedicine   ; BURN_HEAL
-	dw ItemUseMedicine   ; ICE_HEAL
-	dw ItemUseMedicine   ; AWAKENING
-	dw ItemUseMedicine   ; PARLYZ_HEAL
-	dw ItemUseMedicine   ; FULL_RESTORE
-	dw ItemUseMedicine   ; MAX_POTION
-	dw ItemUseMedicine   ; HYPER_POTION
-	dw ItemUseMedicine   ; SUPER_POTION
-	dw ItemUseMedicine   ; POTION
-	dw ItemUseBait       ; BOULDERBADGE
-	dw ItemUseRock       ; CASCADEBADGE
-	dw UnusableItem      ; THUNDERBADGE
-	dw UnusableItem      ; RAINBOWBADGE
-	dw UnusableItem      ; SOULBADGE
-	dw UnusableItem      ; MARSHBADGE
-	dw UnusableItem      ; VOLCANOBADGE
-	dw UnusableItem      ; EARTHBADGE
-	dw ItemUseEscapeRope ; ESCAPE_ROPE
-	dw ItemUseRepel      ; REPEL
-	dw UnusableItem      ; OLD_AMBER
-	dw ItemUseEvoStone   ; FIRE_STONE
-	dw ItemUseEvoStone   ; THUNDER_STONE
-	dw ItemUseEvoStone   ; WATER_STONE
-	dw ItemUseVitamin    ; HP_UP
-	dw ItemUseVitamin    ; PROTEIN
-	dw ItemUseVitamin    ; IRON
-	dw ItemUseVitamin    ; CARBOS
-	dw ItemUseVitamin    ; CALCIUM
-	dw ItemUseVitamin    ; RARE_CANDY
-	dw UnusableItem      ; DOME_FOSSIL
-	dw UnusableItem      ; HELIX_FOSSIL
-	dw UnusableItem      ; SECRET_KEY
-	dw UnusableItem
-	dw UnusableItem      ; BIKE_VOUCHER
-	dw ItemUseXAccuracy  ; X_ACCURACY
-	dw ItemUseEvoStone   ; LEAF_STONE
-	dw ItemUseCardKey    ; CARD_KEY
-	dw UnusableItem      ; NUGGET
-	dw UnusableItem      ; ??? PP_UP
-	dw ItemUsePokedoll   ; POKE_DOLL
-	dw ItemUseMedicine   ; FULL_HEAL
-	dw ItemUseMedicine   ; REVIVE
-	dw ItemUseMedicine   ; MAX_REVIVE
-	dw ItemUseGuardSpec  ; GUARD_SPEC
-	dw ItemUseSuperRepel ; SUPER_REPL
-	dw ItemUseMaxRepel   ; MAX_REPEL
-	dw ItemUseDireHit    ; DIRE_HIT
-	dw UnusableItem      ; COIN
-	dw ItemUseMedicine   ; FRESH_WATER
-	dw ItemUseMedicine   ; SODA_POP
-	dw ItemUseMedicine   ; LEMONADE
-	dw UnusableItem      ; S_S_TICKET
-	dw UnusableItem      ; GOLD_TEETH
-	dw ItemUseXStat      ; X_ATTACK
-	dw ItemUseXStat      ; X_DEFEND
-	dw ItemUseXStat      ; X_SPEED
-	dw ItemUseXStat      ; X_SPECIAL
-	dw ItemUseCoinCase   ; COIN_CASE
-	dw ItemUseOaksParcel ; OAKS_PARCEL
-	dw ItemUseItemfinder ; ITEMFINDER
-	dw UnusableItem      ; SILPH_SCOPE
-	dw ItemUsePokeflute  ; POKE_FLUTE
-	dw UnusableItem      ; LIFT_KEY
-	dw UnusableItem      ; EXP_ALL
-	dw ItemUseOldRod     ; OLD_ROD
-	dw ItemUseGoodRod    ; GOOD_ROD
-	dw ItemUseSuperRod   ; SUPER_ROD
-	dw ItemUsePPUp       ; PP_UP (real one)
-	dw ItemUsePPRestore  ; ETHER
-	dw ItemUsePPRestore  ; MAX_ETHER
-	dw ItemUsePPRestore  ; ELIXER
-	dw ItemUsePPRestore  ; MAX_ELIXER
-
-ItemUseBall:
-
-; Balls can't be used out of battle.
-	ld a, [wIsInBattle]
-	and a
-	jp z, ItemUseNotTime
-
-; Balls can't catch trainers' Pokémon.
-	dec a
-	jp nz, ThrowBallAtTrainerMon
-
-; If this is for the old man battle, skip checking if the party & box are full.
-	ld a, [wBattleType]
-	dec a
-	jr z, .canUseBall
-
-	ld a, [wPartyCount] ; is party full?
-	cp PARTY_LENGTH
-	jr nz, .canUseBall
-	ld a, [wNumInBox] ; is box full?
-	cp MONS_PER_BOX
-	jp z, BoxFullCannotThrowBall
-
-.canUseBall
-	xor a
-	ld [wCapturedMonSpecies], a
-
-	ld a, [wBattleType]
-	cp BATTLE_TYPE_SAFARI
-	jr nz, .skipSafariZoneCode
-
-.safariZone
-	ld hl, wNumSafariBalls
-	dec [hl] ; remove a Safari Ball
-
-.skipSafariZoneCode
-	call RunDefaultPaletteCommand
-
-	ld a, $43 ; successful capture value
-	ld [wPokeBallAnimData], a
-
-	call LoadScreenTilesFromBuffer1
-	ld hl, ItemUseText00
-	call PrintText
-
-; If the player is fighting an unidentified ghost, set the value that indicates
-; the Pokémon can't be caught and skip the capture calculations.
-	callab IsGhostBattle
-	ld b, $10 ; can't be caught value
-	jp z, .setAnimData
-
-	ld a, [wBattleType]
-	dec a
-	jr nz, .notOldManBattle
-
-.oldManBattle
-	ld hl, wGrassRate
-	ld de, wPlayerName
-	ld bc, NAME_LENGTH
-	call CopyData ; save the player's name in the Wild Monster data (part of the Cinnabar Island Missingno. glitch)
-	jp .captured
-
-.notOldManBattle
-; If the player is fighting the ghost Marowak, set the value that indicates the
-; Pokémon can't be caught and skip the capture calculations.
-	ld a, [wCurMap]
-	cp POKEMON_TOWER_6F
-	jr nz, .loop
-	ld a, [wEnemyMonSpecies2]
-	cp MAROWAK
-	ld b, $10 ; can't be caught value
-	jp z, .setAnimData
-
-; Get the first random number. Let it be called Rand1.
-; Rand1 must be within a certain range according the kind of ball being thrown.
-; The ranges are as follows.
-; Poké Ball:         [0, 255]
-; Great Ball:        [0, 200]
-; Ultra/Safari Ball: [0, 150]
-; Loop until an acceptable number is found.
-
-.loop
-	call Random
-	ld b, a
-
-; Get the item ID.
-	ld hl, wcf91
-	ld a, [hl]
-
-; The Master Ball always succeeds.
-	cp MASTER_BALL
-	jp z, .captured
-
-; Anything will do for the basic Poké Ball.
-	cp POKE_BALL
-	jr z, .checkForAilments
-
-; If it's a Great/Ultra/Safari Ball and Rand1 is greater than 200, try again.
-	ld a, 200
-	cp b
-	jr c, .loop
-
-; Less than or equal to 200 is good enough for a Great Ball.
-	ld a, [hl]
-	cp GREAT_BALL
-	jr z, .checkForAilments
-
-; If it's an Ultra/Safari Ball and Rand1 is greater than 150, try again.
-	ld a, 150
-	cp b
-	jr c, .loop
-
-.checkForAilments
-; Pokémon can be caught more easily with a status ailment.
-; Depending on the status ailment, a certain value will be subtracted from
-; Rand1. Let this value be called Status.
-; The larger Status is, the more easily the Pokémon can be caught.
-; no status ailment:     Status = 0
-; Burn/Paralysis/Poison: Status = 12
-; Freeze/Sleep:          Status = 25
-; If Status is greater than Rand1, the Pokémon will be caught for sure.
-	ld a, [wEnemyMonStatus]
-	and a
-	jr z, .skipAilmentValueSubtraction ; no ailments
-	and 1 << FRZ | SLP
-	ld c, 12
-	jr z, .notFrozenOrAsleep
-	ld c, 25
-.notFrozenOrAsleep
-	ld a, b
-	sub c
-	jp c, .captured
-	ld b, a
-
-.skipAilmentValueSubtraction
-	push bc ; save (Rand1 - Status)
-
-; Calculate MaxHP * 255.
-	xor a
-	ld [H_MULTIPLICAND], a
-	ld hl, wEnemyMonMaxHP
-	ld a, [hli]
-	ld [H_MULTIPLICAND + 1], a
-	ld a, [hl]
-	ld [H_MULTIPLICAND + 2], a
-	ld a, 255
-	ld [H_MULTIPLIER], a
-	call Multiply
-
-; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
-	ld a, [wcf91]
-	cp GREAT_BALL
-	ld a, 12
-	jr nz, .skip1
-	ld a, 8
-
-.skip1
-; Note that the results of all division operations are floored.
-
-; Calculate (MaxHP * 255) / BallFactor.
-	ld [H_DIVISOR], a
-	ld b, 4 ; number of bytes in dividend
-	call Divide
-
-; Divide the enemy's current HP by 4. HP is not supposed to exceed 999 so
-; the result should fit in a. If the division results in a quotient of 0,
-; change it to 1.
-	ld hl, wEnemyMonHP
-	ld a, [hli]
-	ld b, a
-	ld a, [hl]
-	srl b
-	rr a
-	srl b
-	rr a
-	and a
-	jr nz, .skip2
-	inc a
-
-.skip2
-; Let W = ((MaxHP * 255) / BallFactor) / max(HP / 4, 1). Calculate W.
-	ld [H_DIVISOR], a
-	ld b, 4
-	call Divide
-
-; If W > 255, store 255 in [H_QUOTIENT + 3].
-; Let X = min(W, 255) = [H_QUOTIENT + 3].
-	ld a, [H_QUOTIENT + 2]
-	and a
-	jr z, .skip3
-	ld a, 255
-	ld [H_QUOTIENT + 3], a
-
-.skip3
-	pop bc ; b = Rand1 - Status
-
-; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon.
-	ld a, [wEnemyMonActualCatchRate]
-	cp b
-	jr c, .failedToCapture
-
-; If W > 255, the ball captures the Pokémon.
-	ld a, [H_QUOTIENT + 2]
-	and a
-	jr nz, .captured
-
-	call Random ; Let this random number be called Rand2.
-
-; If Rand2 > X, the ball fails to capture the Pokémon.
-	ld b, a
-	ld a, [H_QUOTIENT + 3]
-	cp b
-	jr c, .failedToCapture
-
-.captured
-	jr .skipShakeCalculations
-
-.failedToCapture
-	ld a, [H_QUOTIENT + 3]
-	ld [wPokeBallCaptureCalcTemp], a ; Save X.
-
-; Calculate CatchRate * 100.
-	xor a
-	ld [H_MULTIPLICAND], a
-	ld [H_MULTIPLICAND + 1], a
-	ld a, [wEnemyMonActualCatchRate]
-	ld [H_MULTIPLICAND + 2], a
-	ld a, 100
-	ld [H_MULTIPLIER], a
-	call Multiply
-
-; Determine BallFactor2.
-; Poké Ball:         BallFactor2 = 255
-; Great Ball:        BallFactor2 = 200
-; Ultra/Safari Ball: BallFactor2 = 150
-	ld a, [wcf91]
-	ld b, 255
-	cp POKE_BALL
-	jr z, .skip4
-	ld b, 200
-	cp GREAT_BALL
-	jr z, .skip4
-	ld b, 150
-	cp ULTRA_BALL
-	jr z, .skip4
-
-.skip4
-; Let Y = (CatchRate * 100) / BallFactor2. Calculate Y.
-	ld a, b
-	ld [H_DIVISOR], a
-	ld b, 4
-	call Divide
-
-; If Y > 255, there are 3 shakes.
-; Note that this shouldn't be possible.
-; The maximum value of Y is (255 * 100) / 150 = 170.
-	ld a, [H_QUOTIENT + 2]
-	and a
-	ld b, $63 ; 3 shakes
-	jr nz, .setAnimData
-
-; Calculate X * Y.
-	ld a, [wPokeBallCaptureCalcTemp]
-	ld [H_MULTIPLIER], a
-	call Multiply
-
-; Calculate (X * Y) / 255.
-	ld a, 255
-	ld [H_DIVISOR], a
-	ld b, 4
-	call Divide
-
-; Determine Status2.
-; no status ailment:     Status2 = 0
-; Burn/Paralysis/Poison: Status2 = 5
-; Freeze/Sleep:          Status2 = 10
-	ld a, [wEnemyMonStatus]
-	and a
-	jr z, .skip5
-	and 1 << FRZ | SLP
-	ld b, 5
-	jr z, .addAilmentValue
-	ld b, 10
-
-.addAilmentValue
-; If the Pokémon has a status ailment, add Status2.
-	ld a, [H_QUOTIENT + 3]
-	add b
-	ld [H_QUOTIENT + 3], a
-
-.skip5
-; Finally determine the number of shakes.
-; Let Z = ((X * Y) / 255) + Status2 = [H_QUOTIENT + 3].
-; The number of shakes depend on the range Z is in.
-; 0  ≤ Z < 10: 0 shakes (the ball misses)
-; 10 ≤ Z < 30: 1 shake
-; 30 ≤ Z < 70: 2 shakes
-; 70 ≤ Z:      3 shakes
-	ld a, [H_QUOTIENT + 3]
-	cp 10
-	ld b, $20
-	jr c, .setAnimData
-	cp 30
-	ld b, $61
-	jr c, .setAnimData
-	cp 70
-	ld b, $62
-	jr c, .setAnimData
-	ld b, $63
-
-.setAnimData
-	ld a, b
-	ld [wPokeBallAnimData], a
-
-.skipShakeCalculations
-	ld c, 20
-	call DelayFrames
-
-; Do the animation.
-	ld a, TOSS_ANIM
-	ld [wAnimationID], a
-	xor a
-	ld [H_WHOSETURN], a
-	ld [wAnimationType], a
-	ld [wDamageMultipliers], a
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wcf91]
-	push af
-	predef MoveAnimation
-	pop af
-	ld [wcf91], a
-	pop af
-	ld [wWhichPokemon], a
-
-; Determine the message to display from the animation.
-	ld a, [wPokeBallAnimData]
-	cp $10
-	ld hl, ItemUseBallText00
-	jp z, .printMessage
-	cp $20
-	ld hl, ItemUseBallText01
-	jp z, .printMessage
-	cp $61
-	ld hl, ItemUseBallText02
-	jp z, .printMessage
-	cp $62
-	ld hl, ItemUseBallText03
-	jp z, .printMessage
-	cp $63
-	ld hl, ItemUseBallText04
-	jp z, .printMessage
-
-; Save current HP.
-	ld hl, wEnemyMonHP
-	ld a, [hli]
-	push af
-	ld a, [hli]
-	push af
-
-; Save status ailment.
-	inc hl
-	ld a, [hl]
-	push af
-
-	push hl
-
-; If the Pokémon is transformed, the Pokémon is assumed to be a Ditto.
-; This is a bug because a wild Pokémon could have used Transform via
-; Mirror Move even though the only wild Pokémon that knows Transform is Ditto.
-	ld hl, wEnemyBattleStatus3
-	bit TRANSFORMED, [hl]
-	jr z, .notTransformed
-	ld a, DITTO
-	ld [wEnemyMonSpecies2], a
-	jr .skip6
-
-.notTransformed
-; If the Pokémon is not transformed, set the transformed bit and copy the
-; DVs to wTransformedEnemyMonOriginalDVs so that LoadEnemyMonData won't generate
-; new DVs.
-	set TRANSFORMED, [hl]
-	ld hl, wTransformedEnemyMonOriginalDVs
-	ld a, [wEnemyMonDVs]
-	ld [hli], a
-	ld a, [wEnemyMonDVs + 1]
-	ld [hl], a
-
-.skip6
-	ld a, [wcf91]
-	push af
-	ld a, [wEnemyMonSpecies2]
-	ld [wcf91], a
-	ld a, [wEnemyMonLevel]
-	ld [wCurEnemyLVL], a
-	callab LoadEnemyMonData
-	pop af
-	ld [wcf91], a
-	pop hl
-	pop af
-	ld [hld], a
-	dec hl
-	pop af
-	ld [hld], a
-	pop af
-	ld [hl], a
-	ld a, [wEnemyMonSpecies]
-	ld [wCapturedMonSpecies], a
-	ld [wcf91], a
-	ld [wd11e], a
-	ld a, [wBattleType]
-	dec a ; is this the old man battle?
-	jr z, .oldManCaughtMon ; if so, don't give the player the caught Pokémon
-
-	ld hl, ItemUseBallText05
-	call PrintText
-
-; Add the caught Pokémon to the Pokédex.
-	predef IndexToPokedex
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_TEST
-	ld hl, wPokedexOwned
-	predef FlagActionPredef
-	ld a, c
-	push af
-	ld a, [wd11e]
-	dec a
-	ld c, a
-	ld b, FLAG_SET
-	predef FlagActionPredef
-	pop af
-
-	and a ; was the Pokémon already in the Pokédex?
-	jr nz, .skipShowingPokedexData ; if so, don't show the Pokédex data
-
-	ld hl, ItemUseBallText06
-	call PrintText
-	call ClearSprites
-	ld a, [wEnemyMonSpecies]
-	ld [wd11e], a
-	predef ShowPokedexData
-
-.skipShowingPokedexData
-	ld a, [wPartyCount]
-	cp PARTY_LENGTH ; is party full?
-	jr z, .sendToBox
-	xor a ; PLAYER_PARTY_DATA
-	ld [wMonDataLocation], a
-	call ClearSprites
-	call AddPartyMon
-	jr .done
-
-.sendToBox
-	call ClearSprites
-	call SendNewMonToBox
-	ld hl, ItemUseBallText07
-	CheckEvent EVENT_MET_BILL
-	jr nz, .printTransferredToPCText
-	ld hl, ItemUseBallText08
-.printTransferredToPCText
-	call PrintText
-	jr .done
-
-.oldManCaughtMon
-	ld hl, ItemUseBallText05
-
-.printMessage
-	call PrintText
-	call ClearSprites
-
-.done
-	ld a, [wBattleType]
-	and a ; is this the old man battle?
-	ret nz ; if so, don't remove a ball from the bag
-
-; Remove a ball from the bag.
-	ld hl, wNumBagItems
-	inc a
-	ld [wItemQuantity], a
-	jp RemoveItemFromInventory
-
-ItemUseBallText00:
-;"It dodged the thrown ball!"
-;"This pokemon can't be caught"
-	TX_FAR _ItemUseBallText00
-	db "@"
-ItemUseBallText01:
-;"You missed the pokemon!"
-	TX_FAR _ItemUseBallText01
-	db "@"
-ItemUseBallText02:
-;"Darn! The pokemon broke free!"
-	TX_FAR _ItemUseBallText02
-	db "@"
-ItemUseBallText03:
-;"Aww! It appeared to be caught!"
-	TX_FAR _ItemUseBallText03
-	db "@"
-ItemUseBallText04:
-;"Shoot! It was so close too!"
-	TX_FAR _ItemUseBallText04
-	db "@"
-ItemUseBallText05:
-;"All right! {MonName} was caught!"
-;play sound
-	TX_FAR _ItemUseBallText05
-	TX_SFX_CAUGHT_MON
-	TX_BLINK
-	db "@"
-ItemUseBallText07:
-;"X was transferred to Bill's PC"
-	TX_FAR _ItemUseBallText07
-	db "@"
-ItemUseBallText08:
-;"X was transferred to someone's PC"
-	TX_FAR _ItemUseBallText08
-	db "@"
-
-ItemUseBallText06:
-;"New DEX data will be added..."
-;play sound
-	TX_FAR _ItemUseBallText06
-	TX_SFX_DEX_PAGE_ADDED
-	TX_BLINK
-	db "@"
-
-ItemUseTownMap:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	jpba DisplayTownMap
-
-ItemUseBicycle:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	ld a, [wWalkBikeSurfState]
-	ld [wWalkBikeSurfStateCopy], a
-	cp 2 ; is the player surfing?
-	jp z, ItemUseNotTime
-	dec a ; is player already bicycling?
-	jr nz, .tryToGetOnBike
-.getOffBike
-	call ItemUseReloadOverworldData
-	xor a
-	ld [wWalkBikeSurfState], a ; change player state to walking
-	call PlayDefaultMusic ; play walking music
-	ld hl, GotOffBicycleText
-	jr .printText
-.tryToGetOnBike
-	call IsBikeRidingAllowed
-	jp nc, NoCyclingAllowedHere
-	call ItemUseReloadOverworldData
-	xor a ; no keys pressed
-	ld [hJoyHeld], a ; current joypad state
-	inc a
-	ld [wWalkBikeSurfState], a ; change player state to bicycling
-	ld hl, GotOnBicycleText
-	call PlayDefaultMusic ; play bike riding music
-.printText
-	jp PrintText
-
-; used for Surf out-of-battle effect
-ItemUseSurfboard:
-	ld a, [wWalkBikeSurfState]
-	ld [wWalkBikeSurfStateCopy], a
-	cp 2 ; is the player already surfing?
-	jr z, .tryToStopSurfing
-.tryToSurf
-	call IsNextTileShoreOrWater
-	jp c, SurfingAttemptFailed
-	ld hl, TilePairCollisionsWater
-	call CheckForTilePairCollisions
-	jp c, SurfingAttemptFailed
-.surf
-	call .makePlayerMoveForward
-	ld hl, wd730
-	set 7, [hl]
-	ld a, 2
-	ld [wWalkBikeSurfState], a ; change player state to surfing
-	call PlayDefaultMusic ; play surfing music
-	ld hl, SurfingGotOnText
-	jp PrintText
-.tryToStopSurfing
-	xor a
-	ld [hSpriteIndexOrTextID], a
-	ld d, 16 ; talking range in pixels (normal range)
-	call IsSpriteInFrontOfPlayer2
-	res 7, [hl]
-	ld a, [hSpriteIndexOrTextID]
-	and a ; is there a sprite in the way?
-	jr nz, .cannotStopSurfing
-	ld hl, TilePairCollisionsWater
-	call CheckForTilePairCollisions
-	jr c, .cannotStopSurfing
-	ld hl, wTilesetCollisionPtr ; pointer to list of passable tiles
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a ; hl now points to passable tiles
-	ld a, [wTileInFrontOfPlayer] ; tile in front of the player
-	ld b, a
-.passableTileLoop
-	ld a, [hli]
-	cp b
-	jr z, .stopSurfing
-	cp $ff
-	jr nz, .passableTileLoop
-.cannotStopSurfing
-	ld hl, SurfingNoPlaceToGetOffText
-	jp PrintText
-.stopSurfing
-	call .makePlayerMoveForward
-	ld hl, wd730
-	set 7, [hl]
-	xor a
-	ld [wWalkBikeSurfState], a ; change player state to walking
-	dec a
-	ld [wJoyIgnore], a
-	call PlayDefaultMusic ; play walking music
-	jp LoadWalkingPlayerSpriteGraphics
-; uses a simulated button press to make the player move forward
-.makePlayerMoveForward
-	ld a, [wPlayerDirection] ; direction the player is going
-	bit PLAYER_DIR_BIT_UP, a
-	ld b, D_UP
-	jr nz, .storeSimulatedButtonPress
-	bit PLAYER_DIR_BIT_DOWN, a
-	ld b, D_DOWN
-	jr nz, .storeSimulatedButtonPress
-	bit PLAYER_DIR_BIT_LEFT, a
-	ld b, D_LEFT
-	jr nz, .storeSimulatedButtonPress
-	ld b, D_RIGHT
-.storeSimulatedButtonPress
-	ld a, b
-	ld [wSimulatedJoypadStatesEnd], a
-	xor a
-	ld [wWastedByteCD39], a
-	inc a
-	ld [wSimulatedJoypadStatesIndex], a
-	ret
-
-SurfingGotOnText:
-	TX_FAR _SurfingGotOnText
-	db "@"
-
-SurfingNoPlaceToGetOffText:
-	TX_FAR _SurfingNoPlaceToGetOffText
-	db "@"
-
-ItemUsePokedex:
-	predef_jump ShowPokedexMenu
-
-ItemUseEvoStone:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wcf91]
-	ld [wEvoStoneItemID], a
-	push af
-	ld a, EVO_STONE_PARTY_MENU
-	ld [wPartyMenuTypeOrMessageID], a
-	ld a, $ff
-	ld [wUpdateSpritesEnabled], a
-	call DisplayPartyMenu
-	pop bc
-	jr c, .canceledItemUse
-	ld a, b
-	ld [wcf91], a
-	ld a, $01
-	ld [wForceEvolution], a
-	ld a, SFX_HEAL_AILMENT
-	call PlaySoundWaitForCurrent
-	call WaitForSoundToFinish
-	callab TryEvolvingMon ; try to evolve pokemon
-	ld a, [wEvolutionOccurred]
-	and a
-	jr z, .noEffect
-	pop af
-	ld [wWhichPokemon], a
-	ld hl, wNumBagItems
-	ld a, 1 ; remove 1 stone
-	ld [wItemQuantity], a
-	jp RemoveItemFromInventory
-.noEffect
-	call ItemUseNoEffect
-.canceledItemUse
-	xor a
-	ld [wActionResultOrTookBattleTurn], a ; item not used
-	pop af
-	ret
-
-ItemUseVitamin:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-
-ItemUseMedicine:
-	ld a, [wPartyCount]
-	and a
-	jp z, .emptyParty
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wcf91]
-	push af
-	ld a, USE_ITEM_PARTY_MENU
-	ld [wPartyMenuTypeOrMessageID], a
-	ld a, $ff
-	ld [wUpdateSpritesEnabled], a
-	ld a, [wPseudoItemID]
-	and a ; using Softboiled?
-	jr z, .notUsingSoftboiled
-; if using softboiled
-	call GoBackToPartyMenu
-	jr .getPartyMonDataAddress
-.emptyParty
-	ld hl, .emptyPartyText
-	xor a
-	ld [wActionResultOrTookBattleTurn], a ; item use failed
-	jp PrintText
-.emptyPartyText
-	text "You don't have"
-	line "any #MON!"
-	prompt
-.notUsingSoftboiled
-	call DisplayPartyMenu
-.getPartyMonDataAddress
-	jp c, .canceledItemUse
-	ld hl, wPartyMons
-	ld bc, wPartyMon2 - wPartyMon1
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld a, [wWhichPokemon]
-	ld [wUsedItemOnWhichPokemon], a
-	ld d, a
-	ld a, [wcf91]
-	ld e, a
-	ld [wd0b5], a
-	pop af
-	ld [wcf91], a
-	pop af
-	ld [wWhichPokemon], a
-	ld a, [wPseudoItemID]
-	and a ; using Softboiled?
-	jr z, .checkItemType
-; if using softboiled
-	ld a, [wWhichPokemon]
-	cp d ; is the pokemon trying to use softboiled on itself?
-	jr z, ItemUseMedicine ; if so, force another choice
-.checkItemType
-	ld a, [wcf91]
-	cp REVIVE
-	jr nc, .healHP ; if it's a Revive or Max Revive
-	cp FULL_HEAL
-	jr z, .cureStatusAilment ; if it's a Full Heal
-	cp HP_UP
-	jp nc, .useVitamin ; if it's a vitamin or Rare Candy
-	cp FULL_RESTORE
-	jr nc, .healHP ; if it's a Full Restore or one of the potions
-; fall through if it's one of the status-specific healing items
-.cureStatusAilment
-	ld bc, wPartyMon1Status - wPartyMon1
-	add hl, bc ; hl now points to status
-	ld a, [wcf91]
-	lb bc, ANTIDOTE_MSG, 1 << PSN
-	cp ANTIDOTE
-	jr z, .checkMonStatus
-	lb bc, BURN_HEAL_MSG, 1 << BRN
-	cp BURN_HEAL
-	jr z, .checkMonStatus
-	lb bc, ICE_HEAL_MSG, 1 << FRZ
-	cp ICE_HEAL
-	jr z, .checkMonStatus
-	lb bc, AWAKENING_MSG, SLP
-	cp AWAKENING
-	jr z, .checkMonStatus
-	lb bc, PARALYZ_HEAL_MSG, 1 << PAR
-	cp PARLYZ_HEAL
-	jr z, .checkMonStatus
-	lb bc, FULL_HEAL_MSG, $ff ; Full Heal
-.checkMonStatus
-	ld a, [hl] ; pokemon's status
-	and c ; does the pokemon have a status ailment the item can cure?
-	jp z, .healingItemNoEffect
-; if the pokemon has a status the item can heal
-	xor a
-	ld [hl], a ; remove the status ailment in the party data
-	ld a, b
-	ld [wPartyMenuTypeOrMessageID], a ; the message to display for the item used
-	ld a, [wPlayerMonNumber]
-	cp d ; is pokemon the item was used on active in battle?
-	jp nz, .doneHealing
-; if it is active in battle
-	xor a
-	ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data
-	push hl
-	ld hl, wPlayerBattleStatus3
-	res BADLY_POISONED, [hl] ; heal Toxic status
-	pop hl
-	ld bc, wPartyMon1Stats - wPartyMon1Status
-	add hl, bc ; hl now points to party stats
-	ld de, wBattleMonStats
-	ld bc, NUM_STATS * 2
-	call CopyData ; copy party stats to in-battle stat data
-	predef DoubleOrHalveSelectedStats
-	jp .doneHealing
-.healHP
-	inc hl ; hl = address of current HP
-	ld a, [hli]
-	ld b, a
-	ld [wHPBarOldHP+1], a
-	ld a, [hl]
-	ld c, a
-	ld [wHPBarOldHP], a ; current HP stored at wHPBarOldHP (2 bytes, big-endian)
-	or b
-	jr nz, .notFainted
-.fainted
-	ld a, [wcf91]
-	cp REVIVE
-	jr z, .updateInBattleFaintedData
-	cp MAX_REVIVE
-	jr z, .updateInBattleFaintedData
-	jp .healingItemNoEffect
-.updateInBattleFaintedData
-	ld a, [wIsInBattle]
-	and a
-	jr z, .compareCurrentHPToMaxHP
-	push hl
-	push de
-	push bc
-	ld a, [wUsedItemOnWhichPokemon]
-	ld c, a
-	ld hl, wPartyFoughtCurrentEnemyFlags
-	ld b, FLAG_TEST
-	predef FlagActionPredef
-	ld a, c
-	and a
-	jr z, .next
-	ld a, [wUsedItemOnWhichPokemon]
-	ld c, a
-	ld hl, wPartyGainExpFlags
-	ld b, FLAG_SET
-	predef FlagActionPredef
-.next
-	pop bc
-	pop de
-	pop hl
-	jr .compareCurrentHPToMaxHP
-.notFainted
-	ld a, [wcf91]
-	cp REVIVE
-	jp z, .healingItemNoEffect
-	cp MAX_REVIVE
-	jp z, .healingItemNoEffect
-.compareCurrentHPToMaxHP
-	push hl
-	push bc
-	ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1)
-	add hl, bc ; hl now points to max HP
-	pop bc
-	ld a, [hli]
-	cp b
-	jr nz, .skipComparingLSB ; no need to compare the LSB's if the MSB's don't match
-	ld a, [hl]
-	cp c
-.skipComparingLSB
-	pop hl
-	jr nz, .notFullHP
-.fullHP ; if the pokemon's current HP equals its max HP
-	ld a, [wcf91]
-	cp FULL_RESTORE
-	jp nz, .healingItemNoEffect
-	inc hl
-	inc hl
-	ld a, [hld] ; status ailment
-	and a ; does the pokemon have a status ailment?
-	jp z, .healingItemNoEffect
-	ld a, FULL_HEAL
-	ld [wcf91], a
-	dec hl
-	dec hl
-	dec hl
-	jp .cureStatusAilment
-.notFullHP ; if the pokemon's current HP doesn't equal its max HP
-	xor a
-	ld [wLowHealthAlarm], a ;disable low health alarm
-	ld [wChannelSoundIDs + Ch5], a
-	push hl
-	push de
-	ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1)
-	add hl, bc ; hl now points to max HP
-	ld a, [hli]
-	ld [wHPBarMaxHP+1], a
-	ld a, [hl]
-	ld [wHPBarMaxHP], a ; max HP stored at wHPBarMaxHP (2 bytes, big-endian)
-	ld a, [wPseudoItemID]
-	and a ; using Softboiled?
-	jp z, .notUsingSoftboiled2
-; if using softboiled
-	ld hl, wHPBarMaxHP
-	ld a, [hli]
-	push af
-	ld a, [hli]
-	push af
-	ld a, [hli]
-	push af
-	ld a, [hl]
-	push af
-	ld hl, wPartyMon1MaxHP
-	ld a, [wWhichPokemon]
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld a, [hli]
-	ld [wHPBarMaxHP + 1], a
-	ld [H_DIVIDEND], a
-	ld a, [hl]
-	ld [wHPBarMaxHP], a
-	ld [H_DIVIDEND + 1], a
-	ld a, 5
-	ld [H_DIVISOR], a
-	ld b, 2 ; number of bytes
-	call Divide ; get 1/5 of max HP of pokemon that used Softboiled
-	ld bc, (wPartyMon1HP + 1) - (wPartyMon1MaxHP + 1)
-	add hl, bc ; hl now points to LSB of current HP of pokemon that used Softboiled
-; subtract 1/5 of max HP from current HP of pokemon that used Softboiled
-	ld a, [H_QUOTIENT + 3]
-	push af
-	ld b, a
-	ld a, [hl]
-	ld [wHPBarOldHP], a
-	sub b
-	ld [hld], a
-	ld [wHPBarNewHP], a
-	ld a, [H_QUOTIENT + 2]
-	ld b, a
-	ld a, [hl]
-	ld [wHPBarOldHP+1], a
-	sbc b
-	ld [hl], a
-	ld [wHPBarNewHP+1], a
-	coord hl, 4, 1
-	ld a, [wWhichPokemon]
-	ld bc, 2 * SCREEN_WIDTH
-	call AddNTimes ; calculate coordinates of HP bar of pokemon that used Softboiled
-	ld a, SFX_HEAL_HP
-	call PlaySoundWaitForCurrent
-	ld a, [hFlags_0xFFF6]
-	set 0, a
-	ld [hFlags_0xFFF6], a
-	ld a, $02
-	ld [wHPBarType], a
-	predef UpdateHPBar2 ; animate HP bar decrease of pokemon that used Softboiled
-	ld a, [hFlags_0xFFF6]
-	res 0, a
-	ld [hFlags_0xFFF6], a
-	pop af
-	ld b, a ; store heal amount (1/5 of max HP)
-	ld hl, wHPBarOldHP + 1
-	pop af
-	ld [hld], a
-	pop af
-	ld [hld], a
-	pop af
-	ld [hld], a
-	pop af
-	ld [hl], a
-	jr .addHealAmount
-.notUsingSoftboiled2
-	ld a, [wcf91]
-	cp SODA_POP
-	ld b, 60 ; Soda Pop heal amount
-	jr z, .addHealAmount
-	ld b, 80 ; Lemonade heal amount
-	jr nc, .addHealAmount
-	cp FRESH_WATER
-	ld b, 50 ; Fresh Water heal amount
-	jr z, .addHealAmount
-	cp SUPER_POTION
-	ld b, 200 ; Hyper Potion heal amount
-	jr c, .addHealAmount
-	ld b, 50 ; Super Potion heal amount
-	jr z, .addHealAmount
-	ld b, 20 ; Potion heal amount
-.addHealAmount
-	pop de
-	pop hl
-	ld a, [hl]
-	add b
-	ld [hld], a
-	ld [wHPBarNewHP], a
-	ld a, [hl]
-	ld [wHPBarNewHP+1], a
-	jr nc, .noCarry
-	inc [hl]
-	ld a, [hl]
-	ld [wHPBarNewHP + 1], a
-.noCarry
-	push de
-	inc hl
-	ld d, h
-	ld e, l ; de now points to current HP
-	ld hl, (wPartyMon1MaxHP + 1) - (wPartyMon1HP + 1)
-	add hl, de ; hl now points to max HP
-	ld a, [wcf91]
-	cp REVIVE
-	jr z, .setCurrentHPToHalfMaxHP
-	ld a, [hld]
-	ld b, a
-	ld a, [de]
-	sub b
-	dec de
-	ld b, [hl]
-	ld a, [de]
-	sbc b
-	jr nc, .setCurrentHPToMaxHp ; if current HP exceeds max HP after healing
-	ld a, [wcf91]
-	cp HYPER_POTION
-	jr c, .setCurrentHPToMaxHp ; if using a Full Restore or Max Potion
-	cp MAX_REVIVE
-	jr z, .setCurrentHPToMaxHp ; if using a Max Revive
-	jr .updateInBattleData
-.setCurrentHPToHalfMaxHP
-	dec hl
-	dec de
-	ld a, [hli]
-	srl a
-	ld [de], a
-	ld [wHPBarNewHP+1], a
-	ld a, [hl]
-	rr a
-	inc de
-	ld [de], a
-	ld [wHPBarNewHP], a
-	dec de
-	jr .doneHealingPartyHP
-.setCurrentHPToMaxHp
-	ld a, [hli]
-	ld [de], a
-	ld [wHPBarNewHP+1], a
-	inc de
-	ld a, [hl]
-	ld [de], a
-	ld [wHPBarNewHP], a
-	dec de
-.doneHealingPartyHP ; done updating the pokemon's current HP in the party data structure
-	ld a, [wcf91]
-	cp FULL_RESTORE
-	jr nz, .updateInBattleData
-	ld bc, wPartyMon1Status - (wPartyMon1MaxHP + 1)
-	add hl, bc
-	xor a
-	ld [hl], a ; remove the status ailment in the party data
-.updateInBattleData
-	ld h, d
-	ld l, e
-	pop de
-	ld a, [wPlayerMonNumber]
-	cp d ; is pokemon the item was used on active in battle?
-	jr nz, .calculateHPBarCoords
-; copy party HP to in-battle HP
-	ld a, [hli]
-	ld [wBattleMonHP], a
-	ld a, [hld]
-	ld [wBattleMonHP + 1], a
-	ld a, [wcf91]
-	cp FULL_RESTORE
-	jr nz, .calculateHPBarCoords
-	xor a
-	ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data
-.calculateHPBarCoords
-	ld hl, wOAMBuffer + $90
-	ld bc, 2 * SCREEN_WIDTH
-	inc d
-.calculateHPBarCoordsLoop
-	add hl, bc
-	dec d
-	jr nz, .calculateHPBarCoordsLoop
-	jr .doneHealing
-.healingItemNoEffect
-	call ItemUseNoEffect
-	jp .done
-.doneHealing
-	ld a, [wPseudoItemID]
-	and a ; using Softboiled?
-	jr nz, .skipRemovingItem ; no item to remove if using Softboiled
-	push hl
-	call RemoveUsedItem
-	pop hl
-.skipRemovingItem
-	ld a, [wcf91]
-	cp FULL_RESTORE
-	jr c, .playStatusAilmentCuringSound
-	cp FULL_HEAL
-	jr z, .playStatusAilmentCuringSound
-	ld a, SFX_HEAL_HP
-	call PlaySoundWaitForCurrent
-	ld a, [hFlags_0xFFF6]
-	set 0, a
-	ld [hFlags_0xFFF6], a
-	ld a, $02
-	ld [wHPBarType], a
-	predef UpdateHPBar2 ; animate the HP bar lengthening
-	ld a, [hFlags_0xFFF6]
-	res 0, a
-	ld [hFlags_0xFFF6], a
-	ld a, REVIVE_MSG
-	ld [wPartyMenuTypeOrMessageID], a
-	ld a, [wcf91]
-	cp REVIVE
-	jr z, .showHealingItemMessage
-	cp MAX_REVIVE
-	jr z, .showHealingItemMessage
-	ld a, POTION_MSG
-	ld [wPartyMenuTypeOrMessageID], a
-	jr .showHealingItemMessage
-.playStatusAilmentCuringSound
-	ld a, SFX_HEAL_AILMENT
-	call PlaySoundWaitForCurrent
-.showHealingItemMessage
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call ClearScreen
-	dec a
-	ld [wUpdateSpritesEnabled], a
-	call RedrawPartyMenu ; redraws the party menu and displays the message
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld c, 50
-	call DelayFrames
-	call WaitForTextScrollButtonPress
-	jr .done
-.canceledItemUse
-	xor a
-	ld [wActionResultOrTookBattleTurn], a ; item use failed
-	pop af
-	pop af
-.done
-	ld a, [wPseudoItemID]
-	and a ; using Softboiled?
-	ret nz ; if so, return
-	call GBPalWhiteOut
-	call z, RunDefaultPaletteCommand
-	ld a, [wIsInBattle]
-	and a
-	ret nz
-	jp ReloadMapData
-.useVitamin
-	push hl
-	ld a, [hl]
-	ld [wd0b5], a
-	ld [wd11e], a
-	ld bc, wPartyMon1Level - wPartyMon1
-	add hl, bc ; hl now points to level
-	ld a, [hl] ; a = level
-	ld [wCurEnemyLVL], a ; store level
-	call GetMonHeader
-	push de
-	ld a, d
-	ld hl, wPartyMonNicks
-	call GetPartyMonName
-	pop de
-	pop hl
-	ld a, [wcf91]
-	cp RARE_CANDY
-	jp z, .useRareCandy
-	push hl
-	sub HP_UP
-	add a
-	ld bc, wPartyMon1HPExp - wPartyMon1
-	add hl, bc
-	add l
-	ld l, a
-	jr nc, .noCarry2
-	inc h
-.noCarry2
-	ld a, 10
-	ld b, a
-	ld a, [hl] ; a = MSB of stat experience of the appropriate stat
-	cp 100 ; is there already at least 25600 (256 * 100) stat experience?
-	jr nc, .vitaminNoEffect ; if so, vitamins can't add any more
-	add b ; add 2560 (256 * 10) stat experience
-	jr nc, .noCarry3 ; a carry should be impossible here, so this will always jump
-	ld a, 255
-.noCarry3
-	ld [hl], a
-	pop hl
-	call .recalculateStats
-	ld hl, VitaminText
-	ld a, [wcf91]
-	sub HP_UP - 1
-	ld c, a
-.statNameLoop ; loop to get the address of the name of the stat the vitamin increases
-	dec c
-	jr z, .gotStatName
-.statNameInnerLoop
-	ld a, [hli]
-	ld b, a
-	ld a, $50
-	cp b
-	jr nz, .statNameInnerLoop
-	jr .statNameLoop
-.gotStatName
-	ld de, wcf4b
-	ld bc, 10
-	call CopyData ; copy the stat's name to wcf4b
-	ld a, SFX_HEAL_AILMENT
-	call PlaySound
-	ld hl, VitaminStatRoseText
-	call PrintText
-	jp RemoveUsedItem
-.vitaminNoEffect
-	pop hl
-	ld hl, VitaminNoEffectText
-	call PrintText
-	jp GBPalWhiteOut
-.recalculateStats
-	ld bc, wPartyMon1Stats - wPartyMon1
-	add hl, bc
-	ld d, h
-	ld e, l ; de now points to stats
-	ld bc, (wPartyMon1Exp + 2) - wPartyMon1Stats
-	add hl, bc ; hl now points to LSB of experience
-	ld b, 1
-	jp CalcStats ; recalculate stats
-.useRareCandy
-	push hl
-	ld bc, wPartyMon1Level - wPartyMon1
-	add hl, bc ; hl now points to level
-	ld a, [hl] ; a = level
-	cp MAX_LEVEL
-	jr z, .vitaminNoEffect ; can't raise level above 100
-	inc a
-	ld [hl], a ; store incremented level
-	ld [wCurEnemyLVL], a
-	push hl
-	push de
-	ld d, a
-	callab CalcExperience ; calculate experience for next level and store it at $ff96
-	pop de
-	pop hl
-	ld bc, wPartyMon1Exp - wPartyMon1Level
-	add hl, bc ; hl now points to MSB of experience
-; update experience to minimum for new level
-	ld a, [hExperience]
-	ld [hli], a
-	ld a, [hExperience + 1]
-	ld [hli], a
-	ld a, [hExperience + 2]
-	ld [hl], a
-	pop hl
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wcf91]
-	push af
-	push de
-	push hl
-	ld bc, wPartyMon1MaxHP - wPartyMon1
-	add hl, bc ; hl now points to MSB of max HP
-	ld a, [hli]
-	ld b, a
-	ld c, [hl]
-	pop hl
-	push bc
-	push hl
-	call .recalculateStats
-	pop hl
-	ld bc, (wPartyMon1MaxHP + 1) - wPartyMon1
-	add hl, bc ; hl now points to LSB of max HP
-	pop bc
-	ld a, [hld]
-	sub c
-	ld c, a
-	ld a, [hl]
-	sbc b
-	ld b, a ; bc = the amount of max HP gained from leveling up
-; add the amount gained to the current HP
-	ld de, (wPartyMon1HP + 1) - wPartyMon1MaxHP
-	add hl, de ; hl now points to LSB of current HP
-	ld a, [hl]
-	add c
-	ld [hld], a
-	ld a, [hl]
-	adc b
-	ld [hl], a
-	ld a, RARE_CANDY_MSG
-	ld [wPartyMenuTypeOrMessageID], a
-	call RedrawPartyMenu
-	pop de
-	ld a, d
-	ld [wWhichPokemon], a
-	ld a, e
-	ld [wd11e], a
-	xor a ; PLAYER_PARTY_DATA
-	ld [wMonDataLocation], a
-	call LoadMonData
-	ld d, $01
-	callab PrintStatsBox ; display new stats text box
-	call WaitForTextScrollButtonPress ; wait for button press
-	xor a ; PLAYER_PARTY_DATA
-	ld [wMonDataLocation], a
-	predef LearnMoveFromLevelUp ; learn level up move, if any
-	xor a
-	ld [wForceEvolution], a
-	callab TryEvolvingMon ; evolve pokemon, if appropriate
-	ld a, $01
-	ld [wUpdateSpritesEnabled], a
-	pop af
-	ld [wcf91], a
-	pop af
-	ld [wWhichPokemon], a
-	jp RemoveUsedItem
-
-VitaminStatRoseText:
-	TX_FAR _VitaminStatRoseText
-	db "@"
-
-VitaminNoEffectText:
-	TX_FAR _VitaminNoEffectText
-	db "@"
-
-VitaminText:
-	db "HEALTH@"
-	db "ATTACK@"
-	db "DEFENSE@"
-	db "SPEED@"
-	db "SPECIAL@"
-
-ItemUseBait:
-	ld hl, ThrewBaitText
-	call PrintText
-	ld hl, wEnemyMonActualCatchRate ; catch rate
-	srl [hl] ; halve catch rate
-	ld a, BAIT_ANIM
-	ld hl, wSafariBaitFactor ; bait factor
-	ld de, wSafariEscapeFactor ; escape factor
-	jr BaitRockCommon
-
-ItemUseRock:
-	ld hl, ThrewRockText
-	call PrintText
-	ld hl, wEnemyMonActualCatchRate ; catch rate
-	ld a, [hl]
-	add a ; double catch rate
-	jr nc, .noCarry
-	ld a, $ff
-.noCarry
-	ld [hl], a
-	ld a, ROCK_ANIM
-	ld hl, wSafariEscapeFactor ; escape factor
-	ld de, wSafariBaitFactor ; bait factor
-
-BaitRockCommon:
-	ld [wAnimationID], a
-	xor a
-	ld [wAnimationType], a
-	ld [H_WHOSETURN], a
-	ld [de], a ; zero escape factor (for bait), zero bait factor (for rock)
-.randomLoop ; loop until a random number less than 5 is generated
-	call Random
-	and 7
-	cp 5
-	jr nc, .randomLoop
-	inc a ; increment the random number, giving a range from 1 to 5 inclusive
-	ld b, a
-	ld a, [hl]
-	add b ; increase bait factor (for bait), increase escape factor (for rock)
-	jr nc, .noCarry
-	ld a, $ff
-.noCarry
-	ld [hl], a
-	predef MoveAnimation ; do animation
-	ld c, 70
-	jp DelayFrames
-
-ThrewBaitText:
-	TX_FAR _ThrewBaitText
-	db "@"
-
-ThrewRockText:
-	TX_FAR _ThrewRockText
-	db "@"
-
-; also used for Dig out-of-battle effect
-ItemUseEscapeRope:
-	ld a, [wIsInBattle]
-	and a
-	jr nz, .notUsable
-	ld a, [wCurMap]
-	cp AGATHAS_ROOM
-	jr z, .notUsable
-	ld a, [wCurMapTileset]
-	ld b, a
-	ld hl, EscapeRopeTilesets
-.loop
-	ld a, [hli]
-	cp $ff
-	jr z, .notUsable
-	cp b
-	jr nz, .loop
-	ld hl, wd732
-	set 3, [hl]
-	set 6, [hl]
-	ld hl, wd72e
-	res 4, [hl]
-	ResetEvent EVENT_IN_SAFARI_ZONE
-	xor a
-	ld [wNumSafariBalls], a
-	ld [wSafariZoneGateCurScript], a
-	inc a
-	ld [wEscapedFromBattle], a
-	ld [wActionResultOrTookBattleTurn], a ; item used
-	ld a, [wPseudoItemID]
-	and a ; using Dig?
-	ret nz ; if so, return
-	call ItemUseReloadOverworldData
-	ld c, 30
-	call DelayFrames
-	jp RemoveUsedItem
-.notUsable
-	jp ItemUseNotTime
-
-EscapeRopeTilesets:
-	db FOREST, CEMETERY, CAVERN, FACILITY, INTERIOR
-	db $ff ; terminator
-
-ItemUseRepel:
-	ld b, 100
-
-ItemUseRepelCommon:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	ld a, b
-	ld [wRepelRemainingSteps], a
-	jp PrintItemUseTextAndRemoveItem
-
-; handles X Accuracy item
-ItemUseXAccuracy:
-	ld a, [wIsInBattle]
-	and a
-	jp z, ItemUseNotTime
-	ld hl, wPlayerBattleStatus2
-	set USING_X_ACCURACY, [hl] ; X Accuracy bit
-	jp PrintItemUseTextAndRemoveItem
-
-; This function is bugged and never works. It always jumps to ItemUseNotTime.
-; The Card Key is handled in a different way.
-ItemUseCardKey:
-	xor a
-	ld [wUnusedD71F], a
-	call GetTileAndCoordsInFrontOfPlayer
-	ld a, [GetTileAndCoordsInFrontOfPlayer]
-	cp $18
-	jr nz, .next0
-	ld hl, CardKeyTable1
-	jr .next1
-.next0
-	cp $24
-	jr nz, .next2
-	ld hl, CardKeyTable2
-	jr .next1
-.next2
-	cp $5e
-	jp nz, ItemUseNotTime
-	ld hl, CardKeyTable3
-.next1
-	ld a, [wCurMap]
-	ld b, a
-.loop
-	ld a, [hli]
-	cp $ff
-	jp z, ItemUseNotTime
-	cp b
-	jr nz, .nextEntry1
-	ld a, [hli]
-	cp d
-	jr nz, .nextEntry2
-	ld a, [hli]
-	cp e
-	jr nz, .nextEntry3
-	ld a, [hl]
-	ld [wUnusedD71F], a
-	jr .done
-.nextEntry1
-	inc hl
-.nextEntry2
-	inc hl
-.nextEntry3
-	inc hl
-	jr .loop
-.done
-	ld hl, ItemUseText00
-	call PrintText
-	ld hl, wd728
-	set 7, [hl]
-	ret
-
-; These tables are probably supposed to be door locations in Silph Co.,
-; but they are unused.
-; The reason there are 3 tables is unknown.
-
-; Format:
-; 00: Map ID
-; 01: Y
-; 02: X
-; 03: ID?
-
-CardKeyTable1:
-	db  SILPH_CO_2F,$04,$04,$00
-	db  SILPH_CO_2F,$04,$05,$01
-	db  SILPH_CO_4F,$0C,$04,$02
-	db  SILPH_CO_4F,$0C,$05,$03
-	db  SILPH_CO_7F,$06,$0A,$04
-	db  SILPH_CO_7F,$06,$0B,$05
-	db  SILPH_CO_9F,$04,$12,$06
-	db  SILPH_CO_9F,$04,$13,$07
-	db SILPH_CO_10F,$08,$0A,$08
-	db SILPH_CO_10F,$08,$0B,$09
-	db $ff
-
-CardKeyTable2:
-	db SILPH_CO_3F,$08,$09,$0A
-	db SILPH_CO_3F,$09,$09,$0B
-	db SILPH_CO_5F,$04,$07,$0C
-	db SILPH_CO_5F,$05,$07,$0D
-	db SILPH_CO_6F,$0C,$05,$0E
-	db SILPH_CO_6F,$0D,$05,$0F
-	db SILPH_CO_8F,$08,$07,$10
-	db SILPH_CO_8F,$09,$07,$11
-	db SILPH_CO_9F,$08,$03,$12
-	db SILPH_CO_9F,$09,$03,$13
-	db $ff
-
-CardKeyTable3:
-	db SILPH_CO_11F,$08,$09,$14
-	db SILPH_CO_11F,$09,$09,$15
-	db $ff
-
-ItemUsePokedoll:
-	ld a, [wIsInBattle]
-	dec a
-	jp nz, ItemUseNotTime
-	ld a, $01
-	ld [wEscapedFromBattle], a
-	jp PrintItemUseTextAndRemoveItem
-
-ItemUseGuardSpec:
-	ld a, [wIsInBattle]
-	and a
-	jp z, ItemUseNotTime
-	ld hl, wPlayerBattleStatus2
-	set PROTECTED_BY_MIST, [hl] ; Mist bit
-	jp PrintItemUseTextAndRemoveItem
-
-ItemUseSuperRepel:
-	ld b, 200
-	jp ItemUseRepelCommon
-
-ItemUseMaxRepel:
-	ld b, 250
-	jp ItemUseRepelCommon
-
-ItemUseDireHit:
-	ld a, [wIsInBattle]
-	and a
-	jp z, ItemUseNotTime
-	ld hl, wPlayerBattleStatus2
-	set GETTING_PUMPED, [hl] ; Focus Energy bit
-	jp PrintItemUseTextAndRemoveItem
-
-ItemUseXStat:
-	ld a, [wIsInBattle]
-	and a
-	jr nz, .inBattle
-	call ItemUseNotTime
-	ld a, 2
-	ld [wActionResultOrTookBattleTurn], a ; item not used
-	ret
-.inBattle
-	ld hl, wPlayerMoveNum
-	ld a, [hli]
-	push af ; save [wPlayerMoveNum]
-	ld a, [hl]
-	push af ; save [wPlayerMoveEffect]
-	push hl
-	ld a, [wcf91]
-	sub X_ATTACK - ATTACK_UP1_EFFECT
-	ld [hl], a ; store player move effect
-	call PrintItemUseTextAndRemoveItem
-	ld a, XSTATITEM_ANIM ; X stat item animation ID
-	ld [wPlayerMoveNum], a
-	call LoadScreenTilesFromBuffer1 ; restore saved screen
-	call Delay3
-	xor a
-	ld [H_WHOSETURN], a ; set turn to player's turn
-	callba StatModifierUpEffect ; do stat increase move
-	pop hl
-	pop af
-	ld [hld], a ; restore [wPlayerMoveEffect]
-	pop af
-	ld [hl], a ; restore [wPlayerMoveNum]
-	ret
-
-ItemUsePokeflute:
-	ld a, [wIsInBattle]
-	and a
-	jr nz, .inBattle
-; if not in battle
-	call ItemUseReloadOverworldData
-	ld a, [wCurMap]
-	cp ROUTE_12
-	jr nz, .notRoute12
-	CheckEvent EVENT_BEAT_ROUTE12_SNORLAX
-	jr nz, .noSnorlaxToWakeUp
-; if the player hasn't beaten Route 12 Snorlax
-	ld hl, Route12SnorlaxFluteCoords
-	call ArePlayerCoordsInArray
-	jr nc, .noSnorlaxToWakeUp
-	ld hl, PlayedFluteHadEffectText
-	call PrintText
-	SetEvent EVENT_FIGHT_ROUTE12_SNORLAX
-	ret
-.notRoute12
-	cp ROUTE_16
-	jr nz, .noSnorlaxToWakeUp
-	CheckEvent EVENT_BEAT_ROUTE16_SNORLAX
-	jr nz, .noSnorlaxToWakeUp
-; if the player hasn't beaten Route 16 Snorlax
-	ld hl, Route16SnorlaxFluteCoords
-	call ArePlayerCoordsInArray
-	jr nc, .noSnorlaxToWakeUp
-	ld hl, PlayedFluteHadEffectText
-	call PrintText
-	SetEvent EVENT_FIGHT_ROUTE16_SNORLAX
-	ret
-.noSnorlaxToWakeUp
-	ld hl, PlayedFluteNoEffectText
-	jp PrintText
-.inBattle
-	xor a
-	ld [wWereAnyMonsAsleep], a
-	ld b, ~SLP & $ff
-	ld hl, wPartyMon1Status
-	call WakeUpEntireParty
-	ld a, [wIsInBattle]
-	dec a ; is it a trainer battle?
-	jr z, .skipWakingUpEnemyParty
-; if it's a trainer battle
-	ld hl, wEnemyMon1Status
-	call WakeUpEntireParty
-.skipWakingUpEnemyParty
-	ld hl, wBattleMonStatus
-	ld a, [hl]
-	and b ; remove Sleep status
-	ld [hl], a
-	ld hl, wEnemyMonStatus
-	ld a, [hl]
-	and b ; remove Sleep status
-	ld [hl], a
-	call LoadScreenTilesFromBuffer2 ; restore saved screen
-	ld a, [wWereAnyMonsAsleep]
-	and a ; were any pokemon asleep before playing the flute?
-	ld hl, PlayedFluteNoEffectText
-	jp z, PrintText ; if no pokemon were asleep
-; if some pokemon were asleep
-	ld hl, PlayedFluteHadEffectText
-	call PrintText
-	ld a, [wLowHealthAlarm]
-	and $80
-	jr nz, .skipMusic
-	call WaitForSoundToFinish ; wait for sound to end
-	callba Music_PokeFluteInBattle ; play in-battle pokeflute music
-.musicWaitLoop ; wait for music to finish playing
-	ld a, [wChannelSoundIDs + Ch7]
-	and a ; music off?
-	jr nz, .musicWaitLoop
-.skipMusic
-	ld hl, FluteWokeUpText
-	jp PrintText
-
-; wakes up all party pokemon
-; INPUT:
-; hl must point to status of first pokemon in party (player's or enemy's)
-; b must equal ~SLP
-; [wWereAnyMonsAsleep] should be initialized to 0
-; OUTPUT:
-; [wWereAnyMonsAsleep]: set to 1 if any pokemon were asleep
-WakeUpEntireParty:
-	ld de, 44
-	ld c, 6
-.loop
-	ld a, [hl]
-	push af
-	and SLP ; is pokemon asleep?
-	jr z, .notAsleep
-	ld a, 1
-	ld [wWereAnyMonsAsleep], a ; indicate that a pokemon had to be woken up
-.notAsleep
-	pop af
-	and b ; remove Sleep status
-	ld [hl], a
-	add hl, de
-	dec c
-	jr nz, .loop
-	ret
-
-; Format:
-; 00: Y
-; 01: X
-Route12SnorlaxFluteCoords:
-	db 62,9  ; one space West of Snorlax
-	db 61,10 ; one space North of Snorlax
-	db 63,10 ; one space South of Snorlax
-	db 62,11 ; one space East of Snorlax
-	db $ff ; terminator
-
-; Format:
-; 00: Y
-; 01: X
-Route16SnorlaxFluteCoords:
-	db 10,27 ; one space East of Snorlax
-	db 10,25 ; one space West of Snorlax
-	db $ff ; terminator
-
-PlayedFluteNoEffectText:
-	TX_FAR _PlayedFluteNoEffectText
-	db "@"
-
-FluteWokeUpText:
-	TX_FAR _FluteWokeUpText
-	db "@"
-
-PlayedFluteHadEffectText:
-	TX_FAR _PlayedFluteHadEffectText
-	TX_BLINK
-	TX_ASM
-	ld a, [wIsInBattle]
-	and a
-	jr nz, .done
-; play out-of-battle pokeflute music
-	ld a, $ff
-	call PlaySound ; turn off music
-	ld a, SFX_POKEFLUTE
-	ld c, BANK(SFX_Pokeflute)
-	call PlayMusic
-.musicWaitLoop ; wait for music to finish playing
-	ld a, [wChannelSoundIDs + Ch3]
-	cp SFX_POKEFLUTE
-	jr z, .musicWaitLoop
-	call PlayDefaultMusic ; start playing normal music again
-.done
-	jp TextScriptEnd ; end text
-
-ItemUseCoinCase:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	ld hl, CoinCaseNumCoinsText
-	jp PrintText
-
-CoinCaseNumCoinsText:
-	TX_FAR _CoinCaseNumCoinsText
-	db "@"
-
-ItemUseOldRod:
-	call FishingInit
-	jp c, ItemUseNotTime
-	lb bc, 5, MAGIKARP
-	ld a, $1 ; set bite
-	jr RodResponse
-
-ItemUseGoodRod:
-	call FishingInit
-	jp c, ItemUseNotTime
-.RandomLoop
-	call Random
-	srl a
-	jr c, .SetBite
-	and %11
-	cp 2
-	jr nc, .RandomLoop
-	; choose which monster appears
-	ld hl, GoodRodMons
-	add a
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld b, [hl]
-	inc hl
-	ld c, [hl]
-	and a
-.SetBite
-	ld a, 0
-	rla
-	xor 1
-	jr RodResponse
-
-INCLUDE "data/good_rod.asm"
-
-ItemUseSuperRod:
-	call FishingInit
-	jp c, ItemUseNotTime
-	call ReadSuperRodData
-	ld a, e
-RodResponse:
-	ld [wRodResponse], a
-
-	dec a ; is there a bite?
-	jr nz, .next
-	; if yes, store level and species data
-	ld a, 1
-	ld [wMoveMissed], a
-	ld a, b ; level
-	ld [wCurEnemyLVL], a
-	ld a, c ; species
-	ld [wCurOpponent], a
-
-.next
-	ld hl, wWalkBikeSurfState
-	ld a, [hl] ; store the value in a
-	push af
-	push hl
-	ld [hl], 0
-	callba FishingAnim
-	pop hl
-	pop af
-	ld [hl], a
-	ret
-
-; checks if fishing is possible and if so, runs initialization code common to all rods
-; unsets carry if fishing is possible, sets carry if not
-FishingInit:
-	ld a, [wIsInBattle]
-	and a
-	jr z, .notInBattle
-	scf ; can't fish during battle
-	ret
-.notInBattle
-	call IsNextTileShoreOrWater
-	ret c
-	ld a, [wWalkBikeSurfState]
-	cp 2 ; Surfing?
-	jr z, .surfing
-	call ItemUseReloadOverworldData
-	ld hl, ItemUseText00
-	call PrintText
-	ld a, SFX_HEAL_AILMENT
-	call PlaySound
-	ld c, 80
-	call DelayFrames
-	and a
-	ret
-.surfing
-	scf ; can't fish when surfing
-	ret
-
-ItemUseOaksParcel:
-	jp ItemUseNotYoursToUse
-
-ItemUseItemfinder:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	call ItemUseReloadOverworldData
-	callba HiddenItemNear ; check for hidden items
-	ld hl, ItemfinderFoundNothingText
-	jr nc, .printText ; if no hidden items
-	ld c, 4
-.loop
-	ld a, SFX_HEALING_MACHINE
-	call PlaySoundWaitForCurrent
-	ld a, SFX_PURCHASE
-	call PlaySoundWaitForCurrent
-	dec c
-	jr nz, .loop
-	ld hl, ItemfinderFoundItemText
-.printText
-	jp PrintText
-
-ItemfinderFoundItemText:
-	TX_FAR _ItemfinderFoundItemText
-	db "@"
-
-ItemfinderFoundNothingText:
-	TX_FAR _ItemfinderFoundNothingText
-	db "@"
-
-ItemUsePPUp:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-
-ItemUsePPRestore:
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wcf91]
-	ld [wPPRestoreItem], a
-.chooseMon
-	xor a
-	ld [wUpdateSpritesEnabled], a
-	ld a, USE_ITEM_PARTY_MENU
-	ld [wPartyMenuTypeOrMessageID], a
-	call DisplayPartyMenu
-	jr nc, .chooseMove
-	jp .itemNotUsed
-.chooseMove
-	ld a, [wPPRestoreItem]
-	cp ELIXER
-	jp nc, .useElixir ; if Elixir or Max Elixir
-	ld a, $02
-	ld [wMoveMenuType], a
-	ld hl, RaisePPWhichTechniqueText
-	ld a, [wPPRestoreItem]
-	cp ETHER ; is it a PP Up?
-	jr c, .printWhichTechniqueMessage ; if so, print the raise PP message
-	ld hl, RestorePPWhichTechniqueText ; otherwise, print the restore PP message
-.printWhichTechniqueMessage
-	call PrintText
-	xor a
-	ld [wPlayerMoveListIndex], a
-	callab MoveSelectionMenu ; move selection menu
-	ld a, 0
-	ld [wPlayerMoveListIndex], a
-	jr nz, .chooseMon
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2 - wPartyMon1
-	call GetSelectedMoveOffset
-	push hl
-	ld a, [hl]
-	ld [wd11e], a
-	call GetMoveName
-	call CopyStringToCF4B ; copy name to wcf4b
-	pop hl
-	ld a, [wPPRestoreItem]
-	cp ETHER
-	jr nc, .useEther ; if Ether or Max Ether
-.usePPUp
-	ld bc, wPartyMon1PP - wPartyMon1Moves
-	add hl, bc
-	ld a, [hl] ; move PP
-	cp 3 << 6 ; have 3 PP Ups already been used?
-	jr c, .PPNotMaxedOut
-	ld hl, PPMaxedOutText
-	call PrintText
-	jr .chooseMove
-.PPNotMaxedOut
-	ld a, [hl]
-	add 1 << 6 ; increase PP Up count by 1
-	ld [hl], a
-	ld a, 1 ; 1 PP Up used
-	ld [wd11e], a
-	call RestoreBonusPP ; add the bonus PP to current PP
-	ld hl, PPIncreasedText
-	call PrintText
-.done
-	pop af
-	ld [wWhichPokemon], a
-	call GBPalWhiteOut
-	call RunDefaultPaletteCommand
-	jp RemoveUsedItem
-.afterRestoringPP ; after using a (Max) Ether/Elixir
-	ld a, [wWhichPokemon]
-	ld b, a
-	ld a, [wPlayerMonNumber]
-	cp b ; is the pokemon whose PP was restored active in battle?
-	jr nz, .skipUpdatingInBattleData
-	ld hl, wPartyMon1PP
-	ld bc, wPartyMon2 - wPartyMon1
-	call AddNTimes
-	ld de, wBattleMonPP
-	ld bc, 4
-	call CopyData ; copy party data to in-battle data
-.skipUpdatingInBattleData
-	ld a, SFX_HEAL_AILMENT
-	call PlaySound
-	ld hl, PPRestoredText
-	call PrintText
-	jr .done
-.useEther
-	call .restorePP
-	jr nz, .afterRestoringPP
-	jp .noEffect
-; unsets zero flag if PP was restored, sets zero flag if not
-; however, this is bugged for Max Ethers and Max Elixirs (see below)
-.restorePP
-	xor a ; PLAYER_PARTY_DATA
-	ld [wMonDataLocation], a
-	call GetMaxPP
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2 - wPartyMon1
-	call GetSelectedMoveOffset
-	ld bc, wPartyMon1PP - wPartyMon1Moves
-	add hl, bc ; hl now points to move's PP
-	ld a, [wMaxPP]
-	ld b, a
-	ld a, [wPPRestoreItem]
-	cp MAX_ETHER
-	jr z, .fullyRestorePP
-	ld a, [hl] ; move PP
-	and %00111111 ; lower 6 bit bits store current PP
-	cp b ; does current PP equal max PP?
-	ret z ; if so, return
-	add 10 ; increase current PP by 10
-; b holds the max PP amount and b will hold the new PP amount.
-; So, if the new amount meets or exceeds the max amount,
-; cap the amount to the max amount by leaving b unchanged.
-; Otherwise, store the new amount in b.
-	cp b ; does the new amount meet or exceed the maximum?
-	jr nc, .storeNewAmount
-	ld b, a
-.storeNewAmount
-	ld a, [hl] ; move PP
-	and %11000000 ; PP Up counter bits
-	add b
-	ld [hl], a
-	ret
-.fullyRestorePP
-	ld a, [hl] ; move PP
-; Note that this code has a bug. It doesn't mask out the upper two bits, which
-; are used to count how many PP Ups have been used on the move. So, Max Ethers
-; and Max Elixirs will not be detected as having no effect on a move with full
-; PP if the move has had any PP Ups used on it.
-	cp b ; does current PP equal max PP?
-	ret z
-	jr .storeNewAmount
-.useElixir
-; decrement the item ID so that ELIXER becomes ETHER and MAX_ELIXER becomes MAX_ETHER
-	ld hl, wPPRestoreItem
-	dec [hl]
-	dec [hl]
-	xor a
-	ld hl, wCurrentMenuItem
-	ld [hli], a
-	ld [hl], a ; zero the counter for number of moves that had their PP restored
-	ld b, 4
-; loop through each move and restore PP
-.elixirLoop
-	push bc
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2 - wPartyMon1
-	call GetSelectedMoveOffset
-	ld a, [hl]
-	and a ; does the current slot have a move?
-	jr z, .nextMove
-	call .restorePP
-	jr z, .nextMove
-; if some PP was restored
-	ld hl, wTileBehindCursor ; counter for number of moves that had their PP restored
-	inc [hl]
-.nextMove
-	ld hl, wCurrentMenuItem
-	inc [hl]
-	pop bc
-	dec b
-	jr nz, .elixirLoop
-	ld a, [wTileBehindCursor]
-	and a ; did any moves have their PP restored?
-	jp nz, .afterRestoringPP
-.noEffect
-	call ItemUseNoEffect
-.itemNotUsed
-	call GBPalWhiteOut
-	call RunDefaultPaletteCommand
-	pop af
-	xor a
-	ld [wActionResultOrTookBattleTurn], a ; item use failed
-	ret
-
-RaisePPWhichTechniqueText:
-	TX_FAR _RaisePPWhichTechniqueText
-	db "@"
-
-RestorePPWhichTechniqueText:
-	TX_FAR _RestorePPWhichTechniqueText
-	db "@"
-
-PPMaxedOutText:
-	TX_FAR _PPMaxedOutText
-	db "@"
-
-PPIncreasedText:
-	TX_FAR _PPIncreasedText
-	db "@"
-
-PPRestoredText:
-	TX_FAR _PPRestoredText
-	db "@"
-
-; for items that can't be used from the Item menu
-UnusableItem:
-	jp ItemUseNotTime
-
-ItemUseTMHM:
-	ld a, [wIsInBattle]
-	and a
-	jp nz, ItemUseNotTime
-	ld a, [wcf91]
-	sub TM_01
-	push af
-	jr nc, .skipAdding
-	add 55 ; if item is an HM, add 55
-.skipAdding
-	inc a
-	ld [wd11e], a
-	predef TMToMove ; get move ID from TM/HM ID
-	ld a, [wd11e]
-	ld [wMoveNum], a
-	call GetMoveName
-	call CopyStringToCF4B ; copy name to wcf4b
-	pop af
-	ld hl, BootedUpTMText
-	jr nc, .printBootedUpMachineText
-	ld hl, BootedUpHMText
-.printBootedUpMachineText
-	call PrintText
-	ld hl, TeachMachineMoveText
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; yes/no menu
-	ld a, [wCurrentMenuItem]
-	and a
-	jr z, .useMachine
-	ld a, 2
-	ld [wActionResultOrTookBattleTurn], a ; item not used
-	ret
-.useMachine
-	ld a, [wWhichPokemon]
-	push af
-	ld a, [wcf91]
-	push af
-.chooseMon
-	ld hl, wcf4b
-	ld de, wTempMoveNameBuffer
-	ld bc, 14
-	call CopyData ; save the move name because DisplayPartyMenu will overwrite it
-	ld a, $ff
-	ld [wUpdateSpritesEnabled], a
-	ld a, TMHM_PARTY_MENU
-	ld [wPartyMenuTypeOrMessageID], a
-	call DisplayPartyMenu
-	push af
-	ld hl, wTempMoveNameBuffer
-	ld de, wcf4b
-	ld bc, 14
-	call CopyData
-	pop af
-	jr nc, .checkIfAbleToLearnMove
-; if the player canceled teaching the move
-	pop af
-	pop af
-	call GBPalWhiteOutWithDelay3
-	call ClearSprites
-	call RunDefaultPaletteCommand
-	jp LoadScreenTilesFromBuffer1 ; restore saved screen
-.checkIfAbleToLearnMove
-	predef CanLearnTM ; check if the pokemon can learn the move
-	push bc
-	ld a, [wWhichPokemon]
-	ld hl, wPartyMonNicks
-	call GetPartyMonName
-	pop bc
-	ld a, c
-	and a ; can the pokemon learn the move?
-	jr nz, .checkIfAlreadyLearnedMove
-; if the pokemon can't learn the move
-	ld a, SFX_DENIED
-	call PlaySoundWaitForCurrent
-	ld hl, MonCannotLearnMachineMoveText
-	call PrintText
-	jr .chooseMon
-.checkIfAlreadyLearnedMove
-	callab CheckIfMoveIsKnown ; check if the pokemon already knows the move
-	jr c, .chooseMon
-	predef LearnMove ; teach move
-	pop af
-	ld [wcf91], a
-	pop af
-	ld [wWhichPokemon], a
-	ld a, b
-	and a
-	ret z
-	ld a, [wcf91]
-	call IsItemHM
-	ret c
-	jp RemoveUsedItem
-
-BootedUpTMText:
-	TX_FAR _BootedUpTMText
-	db "@"
-
-BootedUpHMText:
-	TX_FAR _BootedUpHMText
-	db "@"
-
-TeachMachineMoveText:
-	TX_FAR _TeachMachineMoveText
-	db "@"
-
-MonCannotLearnMachineMoveText:
-	TX_FAR _MonCannotLearnMachineMoveText
-	db "@"
-
-PrintItemUseTextAndRemoveItem:
-	ld hl, ItemUseText00
-	call PrintText
-	ld a, SFX_HEAL_AILMENT
-	call PlaySound
-	call WaitForTextScrollButtonPress ; wait for button press
-
-RemoveUsedItem:
-	ld hl, wNumBagItems
-	ld a, 1 ; one item
-	ld [wItemQuantity], a
-	jp RemoveItemFromInventory
-
-ItemUseNoEffect:
-	ld hl, ItemUseNoEffectText
-	jr ItemUseFailed
-
-ItemUseNotTime:
-	ld hl, ItemUseNotTimeText
-	jr ItemUseFailed
-
-ItemUseNotYoursToUse:
-	ld hl, ItemUseNotYoursToUseText
-	jr ItemUseFailed
-
-ThrowBallAtTrainerMon:
-	call RunDefaultPaletteCommand
-	call LoadScreenTilesFromBuffer1 ; restore saved screen
-	call Delay3
-	ld a, TOSS_ANIM
-	ld [wAnimationID], a
-	predef MoveAnimation ; do animation
-	ld hl, ThrowBallAtTrainerMonText1
-	call PrintText
-	ld hl, ThrowBallAtTrainerMonText2
-	call PrintText
-	jr RemoveUsedItem
-
-NoCyclingAllowedHere:
-	ld hl, NoCyclingAllowedHereText
-	jr ItemUseFailed
-
-BoxFullCannotThrowBall:
-	ld hl, BoxFullCannotThrowBallText
-	jr ItemUseFailed
-
-SurfingAttemptFailed:
-	ld hl, NoSurfingHereText
-
-ItemUseFailed:
-	xor a
-	ld [wActionResultOrTookBattleTurn], a ; item use failed
-	jp PrintText
-
-ItemUseNotTimeText:
-	TX_FAR _ItemUseNotTimeText
-	db "@"
-
-ItemUseNotYoursToUseText:
-	TX_FAR _ItemUseNotYoursToUseText
-	db "@"
-
-ItemUseNoEffectText:
-	TX_FAR _ItemUseNoEffectText
-	db "@"
-
-ThrowBallAtTrainerMonText1:
-	TX_FAR _ThrowBallAtTrainerMonText1
-	db "@"
-
-ThrowBallAtTrainerMonText2:
-	TX_FAR _ThrowBallAtTrainerMonText2
-	db "@"
-
-NoCyclingAllowedHereText:
-	TX_FAR _NoCyclingAllowedHereText
-	db "@"
-
-NoSurfingHereText:
-	TX_FAR _NoSurfingHereText
-	db "@"
-
-BoxFullCannotThrowBallText:
-	TX_FAR _BoxFullCannotThrowBallText
-	db "@"
-
-ItemUseText00:
-	TX_FAR _ItemUseText001
-	TX_LINE
-	TX_FAR _ItemUseText002
-	db "@"
-
-GotOnBicycleText:
-	TX_FAR _GotOnBicycleText1
-	TX_LINE
-	TX_FAR _GotOnBicycleText2
-	db "@"
-
-GotOffBicycleText:
-	TX_FAR _GotOffBicycleText1
-	TX_LINE
-	TX_FAR _GotOffBicycleText2
-	db "@"
-
-; restores bonus PP (from PP Ups) when healing at a pokemon center
-; also, when a PP Up is used, it increases the current PP by one PP Up bonus
-; INPUT:
-; [wWhichPokemon] = index of pokemon in party
-; [wCurrentMenuItem] = index of move (when using a PP Up)
-RestoreBonusPP:
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2 - wPartyMon1
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	push hl
-	ld de, wNormalMaxPPList - 1
-	predef LoadMovePPs ; loads the normal max PP of each of the pokemon's moves to wNormalMaxPPList
-	pop hl
-	ld c, wPartyMon1PP - wPartyMon1Moves
-	ld b, 0
-	add hl, bc ; hl now points to move 1 PP
-	ld de, wNormalMaxPPList
-	ld b, 0 ; initialize move counter to zero
-; loop through the pokemon's moves
-.loop
-	inc b
-	ld a, b
-	cp 5 ; reached the end of the pokemon's moves?
-	ret z ; if so, return
-	ld a, [wUsingPPUp]
-	dec a ; using a PP Up?
-	jr nz, .skipMenuItemIDCheck
-; if using a PP Up, check if this is the move it's being used on
-	ld a, [wCurrentMenuItem]
-	inc a
-	cp b
-	jr nz, .nextMove
-.skipMenuItemIDCheck
-	ld a, [hl]
-	and %11000000 ; have any PP Ups been used?
-	call nz, AddBonusPP ; if so, add bonus PP
-.nextMove
-	inc hl
-	inc de
-	jr .loop
-
-; adds bonus PP from PP Ups to current PP
-; 1/5 of normal max PP (capped at 7) is added for each PP Up
-; INPUT:
-; [de] = normal max PP
-; [hl] = move PP
-AddBonusPP:
-	push bc
-	ld a, [de] ; normal max PP of move
-	ld [H_DIVIDEND + 3], a
-	xor a
-	ld [H_DIVIDEND], a
-	ld [H_DIVIDEND + 1], a
-	ld [H_DIVIDEND + 2], a
-	ld a, 5
-	ld [H_DIVISOR], a
-	ld b, 4
-	call Divide
-	ld a, [hl] ; move PP
-	ld b, a
-	swap a
-	and %00001111
-	srl a
-	srl a
-	ld c, a ; c = number of PP Ups used
-.loop
-	ld a, [H_QUOTIENT + 3]
-	cp 8 ; is the amount greater than or equal to 8?
-	jr c, .addAmount
-	ld a, 7 ; cap the amount at 7
-.addAmount
-	add b
-	ld b, a
-	ld a, [wUsingPPUp]
-	dec a ; is the player using a PP Up right now?
-	jr z, .done ; if so, only add the bonus once
-	dec c
-	jr nz, .loop
-.done
-	ld [hl], b
-	pop bc
-	ret
-
-; gets max PP of a pokemon's move (including PP from PP Ups)
-; INPUT:
-; [wWhichPokemon] = index of pokemon within party/box
-; [wMonDataLocation] = pokemon source
-; 00: player's party
-; 01: enemy's party
-; 02: current box
-; 03: daycare
-; 04: player's in-battle pokemon
-; [wCurrentMenuItem] = move index
-; OUTPUT:
-; [wMaxPP] = max PP
-GetMaxPP:
-	ld a, [wMonDataLocation]
-	and a
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2 - wPartyMon1
-	jr z, .sourceWithMultipleMon
-	ld hl, wEnemyMon1Moves
-	dec a
-	jr z, .sourceWithMultipleMon
-	ld hl, wBoxMon1Moves
-	ld bc, wBoxMon2 - wBoxMon1
-	dec a
-	jr z, .sourceWithMultipleMon
-	ld hl, wDayCareMonMoves
-	dec a
-	jr z, .sourceWithOneMon
-	ld hl, wBattleMonMoves ; player's in-battle pokemon
-.sourceWithOneMon
-	call GetSelectedMoveOffset2
-	jr .next
-.sourceWithMultipleMon
-	call GetSelectedMoveOffset
-.next
-	ld a, [hl]
-	dec a
-	push hl
-	ld hl, Moves
-	ld bc, MoveEnd - Moves
-	call AddNTimes
-	ld de, wcd6d
-	ld a, BANK(Moves)
-	call FarCopyData
-	ld de, wcd6d + 5 ; PP is byte 5 of move data
-	ld a, [de]
-	ld b, a ; b = normal max PP
-	pop hl
-	push bc
-	ld bc, wPartyMon1PP - wPartyMon1Moves ; PP offset if not player's in-battle pokemon data
-	ld a, [wMonDataLocation]
-	cp 4 ; player's in-battle pokemon?
-	jr nz, .addPPOffset
-	ld bc, wBattleMonPP - wBattleMonMoves ; PP offset if player's in-battle pokemon data
-.addPPOffset
-	add hl, bc
-	ld a, [hl] ; a = current PP
-	and %11000000 ; get PP Up count
-	pop bc
-	or b ; place normal max PP in 6 lower bits of a
-	ld h, d
-	ld l, e
-	inc hl ; hl = wcd73
-	ld [hl], a
-	xor a ; add the bonus for the existing PP Up count
-	ld [wUsingPPUp], a
-	call AddBonusPP ; add bonus PP from PP Ups
-	ld a, [hl]
-	and %00111111 ; mask out the PP Up count
-	ld [wMaxPP], a ; store max PP
-	ret
-
-GetSelectedMoveOffset:
-	ld a, [wWhichPokemon]
-	call AddNTimes
-
-GetSelectedMoveOffset2:
-	ld a, [wCurrentMenuItem]
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ret
-
-; confirms the item toss and then tosses the item
-; INPUT:
-; hl = address of inventory (either wNumBagItems or wNumBoxItems)
-; [wcf91] = item ID
-; [wWhichPokemon] = index of item within inventory
-; [wItemQuantity] = quantity to toss
-; OUTPUT:
-; clears carry flag if the item is tossed, sets carry flag if not
-TossItem_::
-	push hl
-	ld a, [wcf91]
-	call IsItemHM
-	pop hl
-	jr c, .tooImportantToToss
-	push hl
-	call IsKeyItem_
-	ld a, [wIsKeyItem]
-	pop hl
-	and a
-	jr nz, .tooImportantToToss
-	push hl
-	ld a, [wcf91]
-	ld [wd11e], a
-	call GetItemName
-	call CopyStringToCF4B ; copy name to wcf4b
-	ld hl, IsItOKToTossItemText
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; yes/no menu
-	ld a, [wMenuExitMethod]
-	cp CHOSE_SECOND_ITEM
-	pop hl
-	scf
-	ret z ; return if the player chose No
-; if the player chose Yes
-	push hl
-	ld a, [wWhichPokemon]
-	call RemoveItemFromInventory
-	ld a, [wcf91]
-	ld [wd11e], a
-	call GetItemName
-	call CopyStringToCF4B ; copy name to wcf4b
-	ld hl, ThrewAwayItemText
-	call PrintText
-	pop hl
-	and a
-	ret
-.tooImportantToToss
-	push hl
-	ld hl, TooImportantToTossText
-	call PrintText
-	pop hl
-	scf
-	ret
-
-ThrewAwayItemText:
-	TX_FAR _ThrewAwayItemText
-	db "@"
-
-IsItOKToTossItemText:
-	TX_FAR _IsItOKToTossItemText
-	db "@"
-
-TooImportantToTossText:
-	TX_FAR _TooImportantToTossText
-	db "@"
-
-; checks if an item is a key item
-; INPUT:
-; [wcf91] = item ID
-; OUTPUT:
-; [wIsKeyItem] = result
-; 00: item is not key item
-; 01: item is key item
-IsKeyItem_::
-	ld a, $01
-	ld [wIsKeyItem], a
-	ld a, [wcf91]
-	cp HM_01 ; is the item an HM or TM?
-	jr nc, .checkIfItemIsHM
-; if the item is not an HM or TM
-	push af
-	ld hl, KeyItemBitfield
-	ld de, wBuffer
-	ld bc, 15 ; only 11 bytes are actually used
-	call CopyData
-	pop af
-	dec a
-	ld c, a
-	ld hl, wBuffer
-	ld b, FLAG_TEST
-	predef FlagActionPredef
-	ld a, c
-	and a
-	ret nz
-.checkIfItemIsHM
-	ld a, [wcf91]
-	call IsItemHM
-	ret c
-	xor a
-	ld [wIsKeyItem], a
-	ret
-
-INCLUDE "data/key_items.asm"
-
-SendNewMonToBox:
-	ld de, wNumInBox
-	ld a, [de]
-	inc a
-	ld [de], a
-	ld a, [wcf91]
-	ld [wd0b5], a
-	ld c, a
-.asm_e7b1
-	inc de
-	ld a, [de]
-	ld b, a
-	ld a, c
-	ld c, b
-	ld [de], a
-	cp $ff
-	jr nz, .asm_e7b1
-	call GetMonHeader
-	ld hl, wBoxMonOT
-	ld bc, NAME_LENGTH
-	ld a, [wNumInBox]
-	dec a
-	jr z, .asm_e7ee
-	dec a
-	call AddNTimes
-	push hl
-	ld bc, NAME_LENGTH
-	add hl, bc
-	ld d, h
-	ld e, l
-	pop hl
-	ld a, [wNumInBox]
-	dec a
-	ld b, a
-.asm_e7db
-	push bc
-	push hl
-	ld bc, NAME_LENGTH
-	call CopyData
-	pop hl
-	ld d, h
-	ld e, l
-	ld bc, -NAME_LENGTH
-	add hl, bc
-	pop bc
-	dec b
-	jr nz, .asm_e7db
-.asm_e7ee
-	ld hl, wPlayerName
-	ld de, wBoxMonOT
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld a, [wNumInBox]
-	dec a
-	jr z, .asm_e82a
-	ld hl, wBoxMonNicks
-	ld bc, NAME_LENGTH
-	dec a
-	call AddNTimes
-	push hl
-	ld bc, NAME_LENGTH
-	add hl, bc
-	ld d, h
-	ld e, l
-	pop hl
-	ld a, [wNumInBox]
-	dec a
-	ld b, a
-.asm_e817
-	push bc
-	push hl
-	ld bc, NAME_LENGTH
-	call CopyData
-	pop hl
-	ld d, h
-	ld e, l
-	ld bc, -NAME_LENGTH
-	add hl, bc
-	pop bc
-	dec b
-	jr nz, .asm_e817
-.asm_e82a
-	ld hl, wBoxMonNicks
-	ld a, NAME_MON_SCREEN
-	ld [wNamingScreenType], a
-	predef AskName
-	ld a, [wNumInBox]
-	dec a
-	jr z, .asm_e867
-	ld hl, wBoxMons
-	ld bc, wBoxMon2 - wBoxMon1
-	dec a
-	call AddNTimes
-	push hl
-	ld bc, wBoxMon2 - wBoxMon1
-	add hl, bc
-	ld d, h
-	ld e, l
-	pop hl
-	ld a, [wNumInBox]
-	dec a
-	ld b, a
-.asm_e854
-	push bc
-	push hl
-	ld bc, wBoxMon2 - wBoxMon1
-	call CopyData
-	pop hl
-	ld d, h
-	ld e, l
-	ld bc, wBoxMon1 - wBoxMon2
-	add hl, bc
-	pop bc
-	dec b
-	jr nz, .asm_e854
-.asm_e867
-	ld a, [wEnemyMonLevel]
-	ld [wEnemyMonBoxLevel], a
-	ld hl, wEnemyMon
-	ld de, wBoxMon1
-	ld bc, wEnemyMonDVs - wEnemyMon
-	call CopyData
-	ld hl, wPlayerID
-	ld a, [hli]
-	ld [de], a
-	inc de
-	ld a, [hl]
-	ld [de], a
-	inc de
-	push de
-	ld a, [wCurEnemyLVL]
-	ld d, a
-	callab CalcExperience
-	pop de
-	ld a, [hExperience]
-	ld [de], a
-	inc de
-	ld a, [hExperience + 1]
-	ld [de], a
-	inc de
-	ld a, [hExperience + 2]
-	ld [de], a
-	inc de
-	xor a
-	ld b, NUM_STATS * 2
-.asm_e89f
-	ld [de], a
-	inc de
-	dec b
-	jr nz, .asm_e89f
-	ld hl, wEnemyMonDVs
-	ld a, [hli]
-	ld [de], a
-	inc de
-	ld a, [hli]
-	ld [de], a
-	ld hl, wEnemyMonPP
-	ld b, NUM_MOVES
-.asm_e8b1
-	ld a, [hli]
-	inc de
-	ld [de], a
-	dec b
-	jr nz, .asm_e8b1
-	ret
-
-; checks if the tile in front of the player is a shore or water tile
-; used for surfing and fishing
-; unsets carry if it is, sets carry if not
-IsNextTileShoreOrWater:
-	ld a, [wCurMapTileset]
-	ld hl, WaterTilesets
-	ld de, 1
-	call IsInArray
-	jr nc, .notShoreOrWater
-	ld a, [wCurMapTileset]
-	cp SHIP_PORT ; Vermilion Dock tileset
-	ld a, [wTileInFrontOfPlayer] ; tile in front of player
-	jr z, .skipShoreTiles ; if it's the Vermilion Dock tileset
-	cp $48 ; eastern shore tile in Safari Zone
-	jr z, .shoreOrWater
-	cp $32 ; usual eastern shore tile
-	jr z, .shoreOrWater
-.skipShoreTiles
-	cp $14 ; water tile
-	jr z, .shoreOrWater
-.notShoreOrWater
-	scf
-	ret
-.shoreOrWater
-	and a
-	ret
-
-INCLUDE "data/water_tilesets.asm"
-
-ReadSuperRodData:
-; return e = 2 if no fish on this map
-; return e = 1 if a bite, bc = level,species
-; return e = 0 if no bite
-	ld a, [wCurMap]
-	ld de, 3 ; each fishing group is three bytes wide
-	ld hl, SuperRodData
-	call IsInArray
-	jr c, .ReadFishingGroup
-	ld e, $2 ; $2 if no fishing groups found
-	ret
-
-.ReadFishingGroup
-; hl points to the fishing group entry in the index
-	inc hl ; skip map id
-
-	; read fishing group address
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-
-	ld b, [hl] ; how many mons in group
-	inc hl ; point to data
-	ld e, $0 ; no bite yet
-
-.RandomLoop
-	call Random
-	srl a
-	ret c ; 50% chance of no battle
-
-	and %11 ; 2-bit random number
-	cp b
-	jr nc, .RandomLoop ; if a is greater than the number of mons, regenerate
-
-	; get the mon
-	add a
-	ld c, a
-	ld b, $0
-	add hl, bc
-	ld b, [hl] ; level
-	inc hl
-	ld c, [hl] ; species
-	ld e, $1 ; $1 if there's a bite
-	ret
-
-INCLUDE "data/super_rod.asm"
-
-; reloads map view and processes sprite data
-; for items that cause the overworld to be displayed
-ItemUseReloadOverworldData:
-	call LoadCurrentMapView
-	jp UpdateSprites
-
-; creates a list at wBuffer of maps where the mon in [wd11e] can be found.
-; this is used by the pokedex to display locations the mon can be found on the map.
-FindWildLocationsOfMon:
-	ld hl, WildDataPointers
-	ld de, wBuffer
-	ld c, $0
-.loop
-	inc hl
-	ld a, [hld]
-	inc a
-	jr z, .done
-	push hl
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld a, [hli]
-	and a
-	call nz, CheckMapForMon ; land
-	ld a, [hli]
-	and a
-	call nz, CheckMapForMon ; water
-	pop hl
-	inc hl
-	inc hl
-	inc c
-	jr .loop
-.done
-	ld a, $ff ; list terminator
-	ld [de], a
-	ret
-
-CheckMapForMon:
-	inc hl
-	ld b, $a
-.loop
-	ld a, [wd11e]
-	cp [hl]
-	jr nz, .nextEntry
-	ld a, c
-	ld [de], a
-	inc de
-.nextEntry
-	inc hl
-	inc hl
-	dec b
-	jr nz, .loop
-	dec hl
-	ret
--- /dev/null
+++ b/engine/items/subtract_paid_money.asm
@@ -1,0 +1,17 @@
+; subtracts the amount the player paid from their money
+; OUTPUT: carry = 0(success) or 1(fail because there is not enough money)
+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
--- /dev/null
+++ b/engine/items/town_map.asm
@@ -1,0 +1,618 @@
+DisplayTownMap:
+	call LoadTownMap
+	ld hl, wUpdateSpritesEnabled
+	ld a, [hl]
+	push af
+	ld [hl], $ff
+	push hl
+	ld a, $1
+	ld [hJoy7], a
+	ld a, [wCurMap]
+	push af
+	ld b, $0
+	call DrawPlayerOrBirdSprite ; player sprite
+	coord hl, 1, 0
+	ld de, wcd6d
+	call PlaceString
+	ld hl, wOAMBuffer
+	ld de, wTileMapBackup
+	ld bc, $10
+	call CopyData
+	ld hl, vSprites + $40
+	ld de, TownMapCursor
+	lb bc, BANK(TownMapCursor), (TownMapCursorEnd - TownMapCursor) / $8
+	call CopyVideoDataDouble
+	xor a
+	ld [wWhichTownMapLocation], a
+	pop af
+	jr .enterLoop
+
+.townMapLoop
+	coord hl, 0, 0
+	lb bc, 1, 20
+	call ClearScreenArea
+	ld hl, TownMapOrder
+	ld a, [wWhichTownMapLocation]
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hl]
+.enterLoop
+	ld de, wTownMapCoords
+	call LoadTownMapEntry
+	ld a, [de]
+	push hl
+	call TownMapCoordsToOAMCoords
+	ld a, $4
+	ld [wOAMBaseTile], a
+	ld hl, wOAMBuffer + $10
+	call WriteTownMapSpriteOAM ; town map cursor sprite
+	pop hl
+	ld de, wcd6d
+.copyMapName
+	ld a, [hli]
+	ld [de], a
+	inc de
+	cp $50
+	jr nz, .copyMapName
+	coord hl, 1, 0
+	ld de, wcd6d
+	call PlaceString
+	ld hl, wOAMBuffer + $10
+	ld de, wTileMapBackup + 16
+	ld bc, $10
+	call CopyData
+.inputLoop
+	call TownMapSpriteBlinkingAnimation
+	call JoypadLowSensitivity
+	ld a, [hJoy5]
+	ld b, a
+	and A_BUTTON | B_BUTTON | D_UP | D_DOWN
+	jr z, .inputLoop
+	ld a, SFX_TINK
+	call PlaySound
+	bit 6, b
+	jr nz, .pressedUp
+	bit 7, b
+	jr nz, .pressedDown
+	xor a
+	ld [wTownMapSpriteBlinkingEnabled], a
+	ld [hJoy7], a
+	ld [wAnimCounter], a
+	call ExitTownMap
+	pop hl
+	pop af
+	ld [hl], a
+	ret
+.pressedUp
+	ld a, [wWhichTownMapLocation]
+	inc a
+	cp TownMapOrderEnd - TownMapOrder ; number of list items + 1
+	jr nz, .noOverflow
+	xor a
+.noOverflow
+	ld [wWhichTownMapLocation], a
+	jp .townMapLoop
+.pressedDown
+	ld a, [wWhichTownMapLocation]
+	dec a
+	cp -1
+	jr nz, .noUnderflow
+	ld a, TownMapOrderEnd - TownMapOrder - 1 ; number of list items
+.noUnderflow
+	ld [wWhichTownMapLocation], a
+	jp .townMapLoop
+
+INCLUDE "data/town_map_order.asm"
+
+TownMapCursor:
+	INCBIN "gfx/town_map/town_map_cursor.1bpp"
+TownMapCursorEnd:
+
+LoadTownMap_Nest:
+	call LoadTownMap
+	ld hl, wUpdateSpritesEnabled
+	ld a, [hl]
+	push af
+	ld [hl], $ff
+	push hl
+	call DisplayWildLocations
+	call GetMonName
+	coord hl, 1, 0
+	call PlaceString
+	ld h, b
+	ld l, c
+	ld de, MonsNestText
+	call PlaceString
+	call WaitForTextScrollButtonPress
+	call ExitTownMap
+	pop hl
+	pop af
+	ld [hl], a
+	ret
+
+MonsNestText:
+	db "'s NEST@"
+
+LoadTownMap_Fly::
+	call ClearSprites
+	call LoadTownMap
+	call LoadPlayerSpriteGraphics
+	call LoadFontTilePatterns
+	ld de, BirdSprite
+	ld hl, vSprites + $40
+	lb bc, BANK(BirdSprite), $c
+	call CopyVideoData
+	ld de, TownMapUpArrow
+	ld hl, vChars1 + $6d0
+	lb bc, BANK(TownMapUpArrow), (TownMapUpArrowEnd - TownMapUpArrow) / $8
+	call CopyVideoDataDouble
+	call BuildFlyLocationsList
+	ld hl, wUpdateSpritesEnabled
+	ld a, [hl]
+	push af
+	ld [hl], $ff
+	push hl
+	coord hl, 0, 0
+	ld de, ToText
+	call PlaceString
+	ld a, [wCurMap]
+	ld b, $0
+	call DrawPlayerOrBirdSprite
+	ld hl, wFlyLocationsList
+	coord de, 18, 0
+.townMapFlyLoop
+	ld a, " "
+	ld [de], a
+	push hl
+	push hl
+	coord hl, 3, 0
+	lb bc, 1, 15
+	call ClearScreenArea
+	pop hl
+	ld a, [hl]
+	ld b, $4
+	call DrawPlayerOrBirdSprite ; draw bird sprite
+	coord hl, 3, 0
+	ld de, wcd6d
+	call PlaceString
+	ld c, 15
+	call DelayFrames
+	coord hl, 18, 0
+	ld [hl], "▲"
+	coord hl, 19, 0
+	ld [hl], "▼"
+	pop hl
+.inputLoop
+	push hl
+	call DelayFrame
+	call JoypadLowSensitivity
+	ld a, [hJoy5]
+	ld b, a
+	pop hl
+	and A_BUTTON | B_BUTTON | D_UP | D_DOWN
+	jr z, .inputLoop
+	bit 0, b
+	jr nz, .pressedA
+	ld a, SFX_TINK
+	call PlaySound
+	bit 6, b
+	jr nz, .pressedUp
+	bit 7, b
+	jr nz, .pressedDown
+	jr .pressedB
+.pressedA
+	ld a, SFX_HEAL_AILMENT
+	call PlaySound
+	ld a, [hl]
+	ld [wDestinationMap], a
+	ld hl, wd732
+	set 3, [hl]
+	inc hl
+	set 7, [hl]
+.pressedB
+	xor a
+	ld [wTownMapSpriteBlinkingEnabled], a
+	call GBPalWhiteOutWithDelay3
+	pop hl
+	pop af
+	ld [hl], a
+	ret
+.pressedUp
+	coord de, 18, 0
+	inc hl
+	ld a, [hl]
+	cp $ff
+	jr z, .wrapToStartOfList
+	cp $fe
+	jr z, .pressedUp ; skip past unvisited towns
+	jp .townMapFlyLoop
+.wrapToStartOfList
+	ld hl, wFlyLocationsList
+	jp .townMapFlyLoop
+.pressedDown
+	coord de, 19, 0
+	dec hl
+	ld a, [hl]
+	cp $ff
+	jr z, .wrapToEndOfList
+	cp $fe
+	jr z, .pressedDown ; skip past unvisited towns
+	jp .townMapFlyLoop
+.wrapToEndOfList
+	ld hl, wFlyLocationsList + 11
+	jr .pressedDown
+
+ToText:
+	db "To@"
+
+BuildFlyLocationsList:
+	ld hl, wFlyLocationsList - 1
+	ld [hl], $ff
+	inc hl
+	ld a, [wTownVisitedFlag]
+	ld e, a
+	ld a, [wTownVisitedFlag + 1]
+	ld d, a
+	ld bc, SAFFRON_CITY + 1
+.loop
+	srl d
+	rr e
+	ld a, $fe ; store $fe if the town hasn't been visited
+	jr nc, .notVisited
+	ld a, b ; store the map number of the town if it has been visited
+.notVisited
+	ld [hl], a
+	inc hl
+	inc b
+	dec c
+	jr nz, .loop
+	ld [hl], $ff
+	ret
+
+TownMapUpArrow:
+	INCBIN "gfx/town_map/up_arrow.1bpp"
+TownMapUpArrowEnd:
+
+LoadTownMap:
+	call GBPalWhiteOutWithDelay3
+	call ClearScreen
+	call UpdateSprites
+	coord hl, 0, 0
+	ld b, $12
+	ld c, $12
+	call TextBoxBorder
+	call DisableLCD
+	ld hl, WorldMapTileGraphics
+	ld de, vChars2 + $600
+	ld bc, WorldMapTileGraphicsEnd - WorldMapTileGraphics
+	ld a, BANK(WorldMapTileGraphics)
+	call FarCopyData2
+	ld hl, MonNestIcon
+	ld de, vSprites + $40
+	ld bc, MonNestIconEnd - MonNestIcon
+	ld a, BANK(MonNestIcon)
+	call FarCopyDataDouble
+	coord hl, 0, 0
+	ld de, CompressedMap
+.nextTile
+	ld a, [de]
+	and a
+	jr z, .done
+	ld b, a
+	and $f
+	ld c, a
+	ld a, b
+	swap a
+	and $f
+	add $60
+.writeRunLoop
+	ld [hli], a
+	dec c
+	jr nz, .writeRunLoop
+	inc de
+	jr .nextTile
+.done
+	call EnableLCD
+	ld b, SET_PAL_TOWN_MAP
+	call RunPaletteCommand
+	call Delay3
+	call GBPalNormal
+	xor a
+	ld [wAnimCounter], a
+	inc a
+	ld [wTownMapSpriteBlinkingEnabled], a
+	ret
+
+CompressedMap:
+	INCBIN "gfx/town_map/town_map.rle"
+
+ExitTownMap:
+; clear town map graphics data and load usual graphics data
+	xor a
+	ld [wTownMapSpriteBlinkingEnabled], a
+	call GBPalWhiteOut
+	call ClearScreen
+	call ClearSprites
+	call LoadPlayerSpriteGraphics
+	call LoadFontTilePatterns
+	call UpdateSprites
+	jp RunDefaultPaletteCommand
+
+DrawPlayerOrBirdSprite:
+; a = map number
+; b = OAM base tile
+	push af
+	ld a, b
+	ld [wOAMBaseTile], a
+	pop af
+	ld de, wTownMapCoords
+	call LoadTownMapEntry
+	ld a, [de]
+	push hl
+	call TownMapCoordsToOAMCoords
+	call WritePlayerOrBirdSpriteOAM
+	pop hl
+	ld de, wcd6d
+.loop
+	ld a, [hli]
+	ld [de], a
+	inc de
+	cp "@"
+	jr nz, .loop
+	ld hl, wOAMBuffer
+	ld de, wTileMapBackup
+	ld bc, $a0
+	jp CopyData
+
+DisplayWildLocations:
+	callba FindWildLocationsOfMon
+	call ZeroOutDuplicatesInList
+	ld hl, wOAMBuffer
+	ld de, wTownMapCoords
+.loop
+	ld a, [de]
+	cp $ff
+	jr z, .exitLoop
+	and a
+	jr z, .nextEntry
+	push hl
+	call LoadTownMapEntry
+	pop hl
+	ld a, [de]
+	cp $19 ; Cerulean Cave's coordinates
+	jr z, .nextEntry ; skip Cerulean Cave
+	call TownMapCoordsToOAMCoords
+	ld a, $4 ; nest icon tile no.
+	ld [hli], a
+	xor a
+	ld [hli], a
+.nextEntry
+	inc de
+	jr .loop
+.exitLoop
+	ld a, l
+	and a ; were any OAM entries written?
+	jr nz, .drawPlayerSprite
+; if no OAM entries were written, print area unknown text
+	coord hl, 1, 7
+	ld b, 2
+	ld c, 15
+	call TextBoxBorder
+	coord hl, 2, 9
+	ld de, AreaUnknownText
+	call PlaceString
+	jr .done
+.drawPlayerSprite
+	ld a, [wCurMap]
+	ld b, $0
+	call DrawPlayerOrBirdSprite
+.done
+	ld hl, wOAMBuffer
+	ld de, wTileMapBackup
+	ld bc, $a0
+	jp CopyData
+
+AreaUnknownText:
+	db " AREA UNKNOWN@"
+
+TownMapCoordsToOAMCoords:
+; in: lower nybble of a = x, upper nybble of a = y
+; out: b and [hl] = (y * 8) + 24, c and [hl+1] = (x * 8) + 24
+	push af
+	and $f0
+	srl a
+	add 24
+	ld b, a
+	ld [hli], a
+	pop af
+	and $f
+	swap a
+	srl a
+	add 24
+	ld c, a
+	ld [hli], a
+	ret
+
+WritePlayerOrBirdSpriteOAM:
+	ld a, [wOAMBaseTile]
+	and a
+	ld hl, wOAMBuffer + $90 ; for player sprite
+	jr z, WriteTownMapSpriteOAM
+	ld hl, wOAMBuffer + $80 ; for bird sprite
+
+WriteTownMapSpriteOAM:
+	push hl
+
+; Subtract 4 from c (X coord) and 4 from b (Y coord). However, the carry from c
+; is added to b, so the net result is that only 3 is subtracted from b.
+	lb hl, -4, -4
+	add hl, bc
+
+	ld b, h
+	ld c, l
+	pop hl
+
+WriteAsymmetricMonPartySpriteOAM:
+; Writes 4 OAM blocks for a helix mon party sprite, since it does not have
+; a vertical line of symmetry.
+	lb de, 2, 2
+.loop
+	push de
+	push bc
+.innerLoop
+	ld a, b
+	ld [hli], a
+	ld a, c
+	ld [hli], a
+	ld a, [wOAMBaseTile]
+	ld [hli], a
+	inc a
+	ld [wOAMBaseTile], a
+	xor a
+	ld [hli], a
+	inc d
+	ld a, 8
+	add c
+	ld c, a
+	dec e
+	jr nz, .innerLoop
+	pop bc
+	pop de
+	ld a, 8
+	add b
+	ld b, a
+	dec d
+	jr nz, .loop
+	ret
+
+WriteSymmetricMonPartySpriteOAM:
+; Writes 4 OAM blocks for a mon party sprite other than a helix. All the
+; sprites other than the helix one have a vertical line of symmetry which allows
+; the X-flip OAM bit to be used so that only 2 rather than 4 tile patterns are
+; needed.
+	xor a
+	ld [wSymmetricSpriteOAMAttributes], a
+	lb de, 2, 2
+.loop
+	push de
+	push bc
+.innerLoop
+	ld a, b
+	ld [hli], a ; Y
+	ld a, c
+	ld [hli], a ; X
+	ld a, [wOAMBaseTile]
+	ld [hli], a ; tile
+	ld a, [wSymmetricSpriteOAMAttributes]
+	ld [hli], a ; attributes
+	xor (1 << OAM_X_FLIP)
+	ld [wSymmetricSpriteOAMAttributes], a
+	inc d
+	ld a, 8
+	add c
+	ld c, a
+	dec e
+	jr nz, .innerLoop
+	pop bc
+	pop de
+	push hl
+	ld hl, wOAMBaseTile
+	inc [hl]
+	inc [hl]
+	pop hl
+	ld a, 8
+	add b
+	ld b, a
+	dec d
+	jr nz, .loop
+	ret
+
+ZeroOutDuplicatesInList:
+; replace duplicate bytes in the list of wild pokemon locations with 0
+	ld de, wBuffer
+.loop
+	ld a, [de]
+	inc de
+	cp $ff
+	ret z
+	ld c, a
+	ld l, e
+	ld h, d
+.zeroDuplicatesLoop
+	ld a, [hl]
+	cp $ff
+	jr z, .loop
+	cp c
+	jr nz, .skipZeroing
+	xor a
+	ld [hl], a
+.skipZeroing
+	inc hl
+	jr .zeroDuplicatesLoop
+
+LoadTownMapEntry:
+; in: a = map number
+; out: lower nybble of [de] = x, upper nybble of [de] = y, hl = address of name
+	cp REDS_HOUSE_1F
+	jr c, .external
+	ld bc, 4
+	ld hl, InternalMapEntries
+.loop
+	cp [hl]
+	jr c, .foundEntry
+	add hl, bc
+	jr .loop
+.foundEntry
+	inc hl
+	jr .readEntry
+.external
+	ld hl, ExternalMapEntries
+	ld c, a
+	ld b, 0
+	add hl, bc
+	add hl, bc
+	add hl, bc
+.readEntry
+	ld a, [hli]
+	ld [de], a
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ret
+
+INCLUDE "data/town_map_entries.asm"
+
+INCLUDE "text/map_names.asm"
+
+MonNestIcon:
+	INCBIN "gfx/town_map/mon_nest_icon.1bpp"
+MonNestIconEnd:
+
+TownMapSpriteBlinkingAnimation::
+	ld a, [wAnimCounter]
+	inc a
+	cp 25
+	jr z, .hideSprites
+	cp 50
+	jr nz, .done
+; show sprites when the counter reaches 50
+	ld hl, wTileMapBackup
+	ld de, wOAMBuffer
+	ld bc, $90
+	call CopyData
+	xor a
+	jr .done
+.hideSprites
+	ld hl, wOAMBuffer
+	ld b, $24
+	ld de, $4
+.hideSpritesLoop
+	ld [hl], $a0
+	add hl, de
+	dec b
+	jr nz, .hideSpritesLoop
+	ld a, 25
+.done
+	ld [wAnimCounter], a
+	jp DelayFrame
--- a/engine/learn_move.asm
+++ /dev/null
@@ -1,226 +1,0 @@
-LearnMove:
-	call SaveScreenTilesToBuffer1
-	ld a, [wWhichPokemon]
-	ld hl, wPartyMonNicks
-	call GetPartyMonName
-	ld hl, wcd6d
-	ld de, wLearnMoveMonName
-	ld bc, NAME_LENGTH
-	call CopyData
-
-DontAbandonLearning:
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2Moves - wPartyMon1Moves
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld d, h
-	ld e, l
-	ld b, NUM_MOVES
-.findEmptyMoveSlotLoop
-	ld a, [hl]
-	and a
-	jr z, .next
-	inc hl
-	dec b
-	jr nz, .findEmptyMoveSlotLoop
-	push de
-	call TryingToLearn
-	pop de
-	jp c, AbandonLearning
-	push hl
-	push de
-	ld [wd11e], a
-	call GetMoveName
-	ld hl, OneTwoAndText
-	call PrintText
-	pop de
-	pop hl
-.next
-	ld a, [wMoveNum]
-	ld [hl], a
-	ld bc, wPartyMon1PP - wPartyMon1Moves
-	add hl, bc
-	push hl
-	push de
-	dec a
-	ld hl, Moves
-	ld bc, MoveEnd - Moves
-	call AddNTimes
-	ld de, wBuffer
-	ld a, BANK(Moves)
-	call FarCopyData
-	ld a, [wBuffer + 5] ; a = move's max PP
-	pop de
-	pop hl
-	ld [hl], a
-	ld a, [wIsInBattle]
-	and a
-	jp z, PrintLearnedMove
-	ld a, [wWhichPokemon]
-	ld b, a
-	ld a, [wPlayerMonNumber]
-	cp b
-	jp nz, PrintLearnedMove
-	ld h, d
-	ld l, e
-	ld de, wBattleMonMoves
-	ld bc, NUM_MOVES
-	call CopyData
-	ld bc, wPartyMon1PP - wPartyMon1OTID
-	add hl, bc
-	ld de, wBattleMonPP
-	ld bc, NUM_MOVES
-	call CopyData
-	jp PrintLearnedMove
-
-AbandonLearning:
-	ld hl, AbandonLearningText
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; yes/no menu
-	ld a, [wCurrentMenuItem]
-	and a
-	jp nz, DontAbandonLearning
-	ld hl, DidNotLearnText
-	call PrintText
-	ld b, 0
-	ret
-
-PrintLearnedMove:
-	ld hl, LearnedMove1Text
-	call PrintText
-	ld b, 1
-	ret
-
-TryingToLearn:
-	push hl
-	ld hl, TryingToLearnText
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; yes/no menu
-	pop hl
-	ld a, [wCurrentMenuItem]
-	rra
-	ret c
-	ld bc, -NUM_MOVES
-	add hl, bc
-	push hl
-	ld de, wMoves
-	ld bc, NUM_MOVES
-	call CopyData
-	callab FormatMovesString
-	pop hl
-.loop
-	push hl
-	ld hl, WhichMoveToForgetText
-	call PrintText
-	coord hl, 4, 7
-	ld b, 4
-	ld c, 14
-	call TextBoxBorder
-	coord hl, 6, 8
-	ld de, wMovesString
-	ld a, [hFlags_0xFFF6]
-	set 2, a
-	ld [hFlags_0xFFF6], a
-	call PlaceString
-	ld a, [hFlags_0xFFF6]
-	res 2, a
-	ld [hFlags_0xFFF6], a
-	ld hl, wTopMenuItemY
-	ld a, 8
-	ld [hli], a ; wTopMenuItemY
-	ld a, 5
-	ld [hli], a ; wTopMenuItemX
-	xor a
-	ld [hli], a ; wCurrentMenuItem
-	inc hl
-	ld a, [wNumMovesMinusOne]
-	ld [hli], a ; wMaxMenuItem
-	ld a, A_BUTTON | B_BUTTON
-	ld [hli], a ; wMenuWatchedKeys
-	ld [hl], 0 ; wLastMenuItem
-	ld hl, hFlags_0xFFF6
-	set 1, [hl]
-	call HandleMenuInput
-	ld hl, hFlags_0xFFF6
-	res 1, [hl]
-	push af
-	call LoadScreenTilesFromBuffer1
-	pop af
-	pop hl
-	bit 1, a ; pressed b
-	jr nz, .cancel
-	push hl
-	ld a, [wCurrentMenuItem]
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hl]
-	push af
-	push bc
-	call IsMoveHM
-	pop bc
-	pop de
-	ld a, d
-	jr c, .hm
-	pop hl
-	add hl, bc
-	and a
-	ret
-.hm
-	ld hl, HMCantDeleteText
-	call PrintText
-	pop hl
-	jr .loop
-.cancel
-	scf
-	ret
-
-LearnedMove1Text:
-	TX_FAR _LearnedMove1Text
-	TX_SFX_ITEM_1 ; plays SFX_GET_ITEM_1 in the party menu (rare candy) and plays SFX_LEVEL_UP in battle
-	TX_BLINK
-	db "@"
-
-WhichMoveToForgetText:
-	TX_FAR _WhichMoveToForgetText
-	db "@"
-
-AbandonLearningText:
-	TX_FAR _AbandonLearningText
-	db "@"
-
-DidNotLearnText:
-	TX_FAR _DidNotLearnText
-	db "@"
-
-TryingToLearnText:
-	TX_FAR _TryingToLearnText
-	db "@"
-
-OneTwoAndText:
-	TX_FAR _OneTwoAndText
-	TX_DELAY
-	TX_ASM
-	ld a, SFX_SWAP
-	call PlaySoundWaitForCurrent
-	ld hl, PoofText
-	ret
-
-PoofText:
-	TX_FAR _PoofText
-	TX_DELAY
-ForgotAndText:
-	TX_FAR _ForgotAndText
-	db "@"
-
-HMCantDeleteText:
-	TX_FAR _HMCantDeleteText
-	db "@"
--- /dev/null
+++ b/engine/link/cable_club.asm
@@ -1,0 +1,977 @@
+; performs the appropriate action when the player uses the gameboy on the table in the Colosseum or Trade Center
+; In the Colosseum, it starts a battle. In the Trade Center, it displays the trade selection screen.
+; Before doing either action, it swaps random numbers, trainer names and party data with the other gameboy.
+CableClub_DoBattleOrTrade:
+	ld c, 80
+	call DelayFrames
+	call ClearScreen
+	call UpdateSprites
+	call LoadFontTilePatterns
+	call LoadHpBarAndStatusTilePatterns
+	call LoadTrainerInfoTextBoxTiles
+	coord hl, 3, 8
+	ld b, 2
+	ld c, 12
+	call CableClub_TextBoxBorder
+	coord hl, 4, 10
+	ld de, PleaseWaitString
+	call PlaceString
+	ld hl, wPlayerNumHits
+	xor a
+	ld [hli], a
+	ld [hl], $50
+	; fall through
+
+; This is called after completing a trade.
+CableClub_DoBattleOrTradeAgain:
+	ld hl, wSerialPlayerDataBlock
+	ld a, SERIAL_PREAMBLE_BYTE
+	ld b, 6
+.writePlayerDataBlockPreambleLoop
+	ld [hli], a
+	dec b
+	jr nz, .writePlayerDataBlockPreambleLoop
+	ld hl, wSerialRandomNumberListBlock
+	ld a, SERIAL_PREAMBLE_BYTE
+	ld b, 7
+.writeRandomNumberListPreambleLoop
+	ld [hli], a
+	dec b
+	jr nz, .writeRandomNumberListPreambleLoop
+	ld b, 10
+.generateRandomNumberListLoop
+	call Random
+	cp SERIAL_PREAMBLE_BYTE ; all the random numbers have to be less than the preamble byte
+	jr nc, .generateRandomNumberListLoop
+	ld [hli], a
+	dec b
+	jr nz, .generateRandomNumberListLoop
+	ld hl, wSerialPartyMonsPatchList
+	ld a, SERIAL_PREAMBLE_BYTE
+	ld [hli], a
+	ld [hli], a
+	ld [hli], a
+	ld b, $c8
+	xor a
+.zeroPlayerDataPatchListLoop
+	ld [hli], a
+	dec b
+	jr nz, .zeroPlayerDataPatchListLoop
+	ld hl, wGrassRate
+	ld bc, wTrainerHeaderPtr - wGrassRate
+.zeroEnemyPartyLoop
+	xor a
+	ld [hli], a
+	dec bc
+	ld a, b
+	or c
+	jr nz, .zeroEnemyPartyLoop
+	ld hl, wPartyMons - 1
+	ld de, wSerialPartyMonsPatchList + 10
+	ld bc, 0
+.patchPartyMonsLoop
+	inc c
+	ld a, c
+	cp SERIAL_PREAMBLE_BYTE
+	jr z, .startPatchListPart2
+	ld a, b
+	dec a ; are we in part 2 of the patch list?
+	jr nz, .checkPlayerDataByte ; jump if in part 1
+; if we're in part 2
+	ld a, c
+	cp (wPartyMonOT - (wPartyMons - 1)) - (SERIAL_PREAMBLE_BYTE - 1)
+	jr z, .finishedPatchingPlayerData
+.checkPlayerDataByte
+	inc hl
+	ld a, [hl]
+	cp SERIAL_NO_DATA_BYTE
+	jr nz, .patchPartyMonsLoop
+; if the player data byte matches SERIAL_NO_DATA_BYTE, patch it with $FF and record the offset in the patch list
+	ld a, c
+	ld [de], a
+	inc de
+	ld [hl], $ff
+	jr .patchPartyMonsLoop
+.startPatchListPart2
+	ld a, SERIAL_PATCH_LIST_PART_TERMINATOR
+	ld [de], a ; end of part 1
+	inc de
+	lb bc, 1, 0
+	jr .patchPartyMonsLoop
+.finishedPatchingPlayerData
+	ld a, SERIAL_PATCH_LIST_PART_TERMINATOR
+	ld [de], a ; end of part 2
+	call Serial_SyncAndExchangeNybble
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK
+	jr nz, .skipSendingTwoZeroBytes
+; if using internal clock
+; send two zero bytes for syncing purposes?
+	call Delay3
+	xor a
+	ld [hSerialSendData], a
+	ld a, START_TRANSFER_INTERNAL_CLOCK
+	ld [rSC], a
+	call DelayFrame
+	xor a
+	ld [hSerialSendData], a
+	ld a, START_TRANSFER_INTERNAL_CLOCK
+	ld [rSC], a
+.skipSendingTwoZeroBytes
+	call Delay3
+	ld a, (1 << SERIAL)
+	ld [rIE], a
+	ld hl, wSerialRandomNumberListBlock
+	ld de, wSerialOtherGameboyRandomNumberListBlock
+	ld bc, $11
+	call Serial_ExchangeBytes
+	ld a, SERIAL_NO_DATA_BYTE
+	ld [de], a
+	ld hl, wSerialPlayerDataBlock
+	ld de, wSerialEnemyDataBlock
+	ld bc, $1a8
+	call Serial_ExchangeBytes
+	ld a, SERIAL_NO_DATA_BYTE
+	ld [de], a
+	ld hl, wSerialPartyMonsPatchList
+	ld de, wSerialEnemyMonsPatchList
+	ld bc, $c8
+	call Serial_ExchangeBytes
+	ld a, (1 << SERIAL) | (1 << TIMER) | (1 << VBLANK)
+	ld [rIE], a
+	ld a, $ff
+	call PlaySound
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK
+	jr z, .skipCopyingRandomNumberList ; the list generated by the gameboy clocking the connection is used by both gameboys
+	ld hl, wSerialOtherGameboyRandomNumberListBlock
+.findStartOfRandomNumberListLoop
+	ld a, [hli]
+	and a
+	jr z, .findStartOfRandomNumberListLoop
+	cp SERIAL_PREAMBLE_BYTE
+	jr z, .findStartOfRandomNumberListLoop
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .findStartOfRandomNumberListLoop
+	dec hl
+	ld de, wLinkBattleRandomNumberList
+	ld c, 10
+.copyRandomNumberListLoop
+	ld a, [hli]
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .copyRandomNumberListLoop
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .copyRandomNumberListLoop
+.skipCopyingRandomNumberList
+	ld hl, wSerialEnemyDataBlock + 3
+.findStartOfEnemyNameLoop
+	ld a, [hli]
+	and a
+	jr z, .findStartOfEnemyNameLoop
+	cp SERIAL_PREAMBLE_BYTE
+	jr z, .findStartOfEnemyNameLoop
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .findStartOfEnemyNameLoop
+	dec hl
+	ld de, wLinkEnemyTrainerName
+	ld c, NAME_LENGTH
+.copyEnemyNameLoop
+	ld a, [hli]
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .copyEnemyNameLoop
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .copyEnemyNameLoop
+	ld de, wEnemyPartyCount
+	ld bc, wTrainerHeaderPtr - wEnemyPartyCount
+.copyEnemyPartyLoop
+	ld a, [hli]
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .copyEnemyPartyLoop
+	ld [de], a
+	inc de
+	dec bc
+	ld a, b
+	or c
+	jr nz, .copyEnemyPartyLoop
+	ld de, wSerialPartyMonsPatchList
+	ld hl, wPartyMons
+	ld c, 2 ; patch list has 2 parts
+.unpatchPartyMonsLoop
+	ld a, [de]
+	inc de
+	and a
+	jr z, .unpatchPartyMonsLoop
+	cp SERIAL_PREAMBLE_BYTE
+	jr z, .unpatchPartyMonsLoop
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .unpatchPartyMonsLoop
+	cp SERIAL_PATCH_LIST_PART_TERMINATOR
+	jr z, .finishedPartyMonsPatchListPart
+	push hl
+	push bc
+	ld b, 0
+	dec a
+	ld c, a
+	add hl, bc
+	ld a, SERIAL_NO_DATA_BYTE
+	ld [hl], a
+	pop bc
+	pop hl
+	jr .unpatchPartyMonsLoop
+.finishedPartyMonsPatchListPart
+	ld hl, wPartyMons + (SERIAL_PREAMBLE_BYTE - 1)
+	dec c ; is there another part?
+	jr nz, .unpatchPartyMonsLoop
+	ld de, wSerialEnemyMonsPatchList
+	ld hl, wEnemyMons
+	ld c, 2 ; patch list has 2 parts
+.unpatchEnemyMonsLoop
+	ld a, [de]
+	inc de
+	and a
+	jr z, .unpatchEnemyMonsLoop
+	cp SERIAL_PREAMBLE_BYTE
+	jr z, .unpatchEnemyMonsLoop
+	cp SERIAL_NO_DATA_BYTE
+	jr z, .unpatchEnemyMonsLoop
+	cp SERIAL_PATCH_LIST_PART_TERMINATOR
+	jr z, .finishedEnemyMonsPatchListPart
+	push hl
+	push bc
+	ld b, 0
+	dec a
+	ld c, a
+	add hl, bc
+	ld a, SERIAL_NO_DATA_BYTE
+	ld [hl], a
+	pop bc
+	pop hl
+	jr .unpatchEnemyMonsLoop
+.finishedEnemyMonsPatchListPart
+	ld hl, wEnemyMons + (SERIAL_PREAMBLE_BYTE - 1)
+	dec c
+	jr nz, .unpatchEnemyMonsLoop
+	ld a, wEnemyMonOT % $100
+	ld [wUnusedCF8D], a
+	ld a, wEnemyMonOT / $100
+	ld [wUnusedCF8D + 1], a
+	xor a
+	ld [wTradeCenterPointerTableIndex], a
+	ld a, $ff
+	call PlaySound
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK
+	ld c, 66
+	call z, DelayFrames ; delay if using internal clock
+	ld a, [wLinkState]
+	cp LINK_STATE_START_BATTLE
+	ld a, LINK_STATE_TRADING
+	ld [wLinkState], a
+	jr nz, .trading
+	ld a, LINK_STATE_BATTLING
+	ld [wLinkState], a
+	ld a, OPP_SONY1
+	ld [wCurOpponent], a
+	call ClearScreen
+	call Delay3
+	ld hl, wOptions
+	res 7, [hl]
+	predef InitOpponent
+	predef HealParty
+	jp ReturnToCableClubRoom
+.trading
+	ld c, BANK(Music_GameCorner)
+	ld a, MUSIC_GAME_CORNER
+	call PlayMusic
+	jr CallCurrentTradeCenterFunction
+
+PleaseWaitString:
+	db "PLEASE WAIT!@"
+
+CallCurrentTradeCenterFunction:
+	ld hl, TradeCenterPointerTable
+	ld b, 0
+	ld a, [wTradeCenterPointerTableIndex]
+	cp $ff
+	jp z, DisplayTitleScreen
+	add a
+	ld c, a
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	jp hl
+
+TradeCenter_SelectMon:
+	call ClearScreen
+	call LoadTrainerInfoTextBoxTiles
+	call TradeCenter_DrawPartyLists
+	call TradeCenter_DrawCancelBox
+	xor a
+	ld hl, wSerialSyncAndExchangeNybbleReceiveData
+	ld [hli], a
+	ld [hli], a
+	ld [hli], a
+	ld [hl], a
+	ld [wMenuWatchMovingOutOfBounds], a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld [wMenuJoypadPollCount], a
+	inc a
+	ld [wSerialExchangeNybbleSendData], a
+	jp .playerMonMenu
+.enemyMonMenu
+	xor a
+	ld [wMenuWatchMovingOutOfBounds], a
+	inc a
+	ld [wWhichTradeMonSelectionMenu], a
+	ld a, D_DOWN | D_LEFT | A_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, [wEnemyPartyCount]
+	ld [wMaxMenuItem], a
+	ld a, 9
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+.enemyMonMenu_HandleInput
+	ld hl, hFlags_0xFFF6
+	set 1, [hl]
+	call HandleMenuInput
+	ld hl, hFlags_0xFFF6
+	res 1, [hl]
+	and a
+	jp z, .getNewInput
+	bit 0, a ; A button pressed?
+	jr z, .enemyMonMenu_ANotPressed
+; if A button pressed
+	ld a, [wMaxMenuItem]
+	ld c, a
+	ld a, [wCurrentMenuItem]
+	cp c
+	jr c, .displayEnemyMonStats
+	ld a, [wMaxMenuItem]
+	dec a
+	ld [wCurrentMenuItem], a
+.displayEnemyMonStats
+	ld a, INIT_ENEMYOT_LIST
+	ld [wInitListType], a
+	callab InitList ; the list isn't used
+	ld hl, wEnemyMons
+	call TradeCenter_DisplayStats
+	jp .getNewInput
+.enemyMonMenu_ANotPressed
+	bit 5, a ; Left pressed?
+	jr z, .enemyMonMenu_LeftNotPressed
+; if Left pressed, switch back to the player mon menu
+	xor a ; player mon menu
+	ld [wWhichTradeMonSelectionMenu], a
+	ld a, [wMenuCursorLocation]
+	ld l, a
+	ld a, [wMenuCursorLocation + 1]
+	ld h, a
+	ld a, [wTileBehindCursor]
+	ld [hl], a
+	ld a, [wCurrentMenuItem]
+	ld b, a
+	ld a, [wPartyCount]
+	dec a
+	cp b
+	jr nc, .playerMonMenu
+	ld [wCurrentMenuItem], a
+	jr .playerMonMenu
+.enemyMonMenu_LeftNotPressed
+	bit 7, a ; Down pressed?
+	jp z, .getNewInput
+	jp .selectedCancelMenuItem ; jump if Down pressed
+.playerMonMenu
+	xor a ; player mon menu
+	ld [wWhichTradeMonSelectionMenu], a
+	ld [wMenuWatchMovingOutOfBounds], a
+	ld a, D_DOWN | D_RIGHT | A_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, [wPartyCount]
+	ld [wMaxMenuItem], a
+	ld a, 1
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+	coord hl, 1, 1
+	lb bc, 6, 1
+	call ClearScreenArea
+.playerMonMenu_HandleInput
+	ld hl, hFlags_0xFFF6
+	set 1, [hl]
+	call HandleMenuInput
+	ld hl, hFlags_0xFFF6
+	res 1, [hl]
+	and a ; was anything pressed?
+	jr nz, .playerMonMenu_SomethingPressed
+	jp .getNewInput
+.playerMonMenu_SomethingPressed
+	bit 0, a ; A button pressed?
+	jr z, .playerMonMenu_ANotPressed
+	jp .chosePlayerMon ; jump if A button pressed
+; unreachable code
+	ld a, INIT_PLAYEROT_LIST
+	ld [wInitListType], a
+	callab InitList ; the list isn't used
+	call TradeCenter_DisplayStats
+	jp .getNewInput
+.playerMonMenu_ANotPressed
+	bit 4, a ; Right pressed?
+	jr z, .playerMonMenu_RightNotPressed
+; if Right pressed, switch to the enemy mon menu
+	ld a, $1 ; enemy mon menu
+	ld [wWhichTradeMonSelectionMenu], a
+	ld a, [wMenuCursorLocation]
+	ld l, a
+	ld a, [wMenuCursorLocation + 1]
+	ld h, a
+	ld a, [wTileBehindCursor]
+	ld [hl], a
+	ld a, [wCurrentMenuItem]
+	ld b, a
+	ld a, [wEnemyPartyCount]
+	dec a
+	cp b
+	jr nc, .notPastLastEnemyMon
+; when switching to the enemy mon menu, if the menu selection would be past the last enemy mon, select the last enemy mon
+	ld [wCurrentMenuItem], a
+.notPastLastEnemyMon
+	jp .enemyMonMenu
+.playerMonMenu_RightNotPressed
+	bit 7, a ; Down pressed?
+	jr z, .getNewInput
+	jp .selectedCancelMenuItem ; jump if Down pressed
+.getNewInput
+	ld a, [wWhichTradeMonSelectionMenu]
+	and a
+	jp z, .playerMonMenu_HandleInput
+	jp .enemyMonMenu_HandleInput
+.chosePlayerMon
+	call SaveScreenTilesToBuffer1
+	call PlaceUnfilledArrowMenuCursor
+	ld a, [wMaxMenuItem]
+	ld c, a
+	ld a, [wCurrentMenuItem]
+	cp c
+	jr c, .displayStatsTradeMenu
+	ld a, [wMaxMenuItem]
+	dec a
+.displayStatsTradeMenu
+	push af
+	coord hl, 0, 14
+	ld b, 2
+	ld c, 18
+	call CableClub_TextBoxBorder
+	coord hl, 2, 16
+	ld de, .statsTrade
+	call PlaceString
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld [wMenuJoypadPollCount], a
+	ld [wMaxMenuItem], a
+	ld a, 16
+	ld [wTopMenuItemY], a
+.selectStatsMenuItem
+	ld a, " "
+	Coorda 11, 16
+	ld a, D_RIGHT | B_BUTTON | A_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+	call HandleMenuInput
+	bit 4, a ; Right pressed?
+	jr nz, .selectTradeMenuItem
+	bit 1, a ; B button pressed?
+	jr z, .displayPlayerMonStats
+.cancelPlayerMonChoice
+	pop af
+	ld [wCurrentMenuItem], a
+	call LoadScreenTilesFromBuffer1
+	jp .playerMonMenu
+.selectTradeMenuItem
+	ld a, " "
+	Coorda 1, 16
+	ld a, D_LEFT | B_BUTTON | A_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 11
+	ld [wTopMenuItemX], a
+	call HandleMenuInput
+	bit 5, a ; Left pressed?
+	jr nz, .selectStatsMenuItem
+	bit 1, a ; B button pressed?
+	jr nz, .cancelPlayerMonChoice
+	jr .choseTrade
+.displayPlayerMonStats
+	pop af
+	ld [wCurrentMenuItem], a
+	ld a, INIT_PLAYEROT_LIST
+	ld [wInitListType], a
+	callab InitList ; the list isn't used
+	call TradeCenter_DisplayStats
+	call LoadScreenTilesFromBuffer1
+	jp .playerMonMenu
+.choseTrade
+	call PlaceUnfilledArrowMenuCursor
+	pop af
+	ld [wCurrentMenuItem], a
+	ld [wTradingWhichPlayerMon], a
+	ld [wSerialExchangeNybbleSendData], a
+	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+	cp $f
+	jp z, CallCurrentTradeCenterFunction ; go back to the beginning of the trade selection menu if the other person cancelled
+	ld [wTradingWhichEnemyMon], a
+	call TradeCenter_PlaceSelectedEnemyMonMenuCursor
+	ld a, $1 ; TradeCenter_Trade
+	ld [wTradeCenterPointerTableIndex], a
+	jp CallCurrentTradeCenterFunction
+.statsTrade
+	db "STATS     TRADE@"
+.selectedCancelMenuItem
+	ld a, [wCurrentMenuItem]
+	ld b, a
+	ld a, [wMaxMenuItem]
+	cp b
+	jp nz, .getNewInput
+	ld a, [wMenuCursorLocation]
+	ld l, a
+	ld a, [wMenuCursorLocation + 1]
+	ld h, a
+	ld a, " "
+	ld [hl], a
+.cancelMenuItem_Loop
+	ld a, "▶" ; filled arrow cursor
+	Coorda 1, 16
+.cancelMenuItem_JoypadLoop
+	call JoypadLowSensitivity
+	ld a, [hJoy5]
+	and a ; pressed anything?
+	jr z, .cancelMenuItem_JoypadLoop
+	bit 0, a ; A button pressed?
+	jr nz, .cancelMenuItem_APressed
+	bit 6, a ; Up pressed?
+	jr z, .cancelMenuItem_JoypadLoop
+; if Up pressed
+	ld a, " "
+	Coorda 1, 16
+	ld a, [wPartyCount]
+	dec a
+	ld [wCurrentMenuItem], a
+	jp .playerMonMenu
+.cancelMenuItem_APressed
+	ld a, "▷" ; unfilled arrow cursor
+	Coorda 1, 16
+	ld a, $f
+	ld [wSerialExchangeNybbleSendData], a
+	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+	cp $f ; did the other person choose Cancel too?
+	jr nz, .cancelMenuItem_Loop
+	; fall through
+
+ReturnToCableClubRoom:
+	call GBPalWhiteOutWithDelay3
+	ld hl, wFontLoaded
+	ld a, [hl]
+	push af
+	push hl
+	res 0, [hl]
+	xor a
+	ld [wd72d], a
+	dec a
+	ld [wDestinationWarpID], a
+	call LoadMapData
+	callba ClearVariablesOnEnterMap
+	pop hl
+	pop af
+	ld [hl], a
+	call GBFadeInFromWhite
+	ret
+
+TradeCenter_DrawCancelBox:
+	coord hl, 11, 15
+	ld a, $7e
+	ld bc, 2 * SCREEN_WIDTH + 9
+	call FillMemory
+	coord hl, 0, 15
+	ld b, 1
+	ld c, 9
+	call CableClub_TextBoxBorder
+	coord hl, 2, 16
+	ld de, CancelTextString
+	jp PlaceString
+
+CancelTextString:
+	db "CANCEL@"
+
+TradeCenter_PlaceSelectedEnemyMonMenuCursor:
+	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+	coord hl, 1, 9
+	ld bc, SCREEN_WIDTH
+	call AddNTimes
+	ld [hl], "▷" ; cursor
+	ret
+
+TradeCenter_DisplayStats:
+	ld a, [wCurrentMenuItem]
+	ld [wWhichPokemon], a
+	predef StatusScreen
+	predef StatusScreen2
+	call GBPalNormal
+	call LoadTrainerInfoTextBoxTiles
+	call TradeCenter_DrawPartyLists
+	jp TradeCenter_DrawCancelBox
+
+TradeCenter_DrawPartyLists:
+	coord hl, 0, 0
+	ld b, 6
+	ld c, 18
+	call CableClub_TextBoxBorder
+	coord hl, 0, 8
+	ld b, 6
+	ld c, 18
+	call CableClub_TextBoxBorder
+	coord hl, 5, 0
+	ld de, wPlayerName
+	call PlaceString
+	coord hl, 5, 8
+	ld de, wLinkEnemyTrainerName
+	call PlaceString
+	coord hl, 2, 1
+	ld de, wPartySpecies
+	call TradeCenter_PrintPartyListNames
+	coord hl, 2, 9
+	ld de, wEnemyPartyMons
+	; fall through
+
+TradeCenter_PrintPartyListNames:
+	ld c, $0
+.loop
+	ld a, [de]
+	cp $ff
+	ret z
+	ld [wd11e], a
+	push bc
+	push hl
+	push de
+	push hl
+	ld a, c
+	ld [$ff95], a
+	call GetMonName
+	pop hl
+	call PlaceString
+	pop de
+	inc de
+	pop hl
+	ld bc, 20
+	add hl, bc
+	pop bc
+	inc c
+	jr .loop
+
+TradeCenter_Trade:
+	ld c, 100
+	call DelayFrames
+	xor a
+	ld [wSerialExchangeNybbleSendData + 1], a ; unnecessary
+	ld [wSerialExchangeNybbleReceiveData], a
+	ld [wMenuWatchMovingOutOfBounds], a
+	ld [wMenuJoypadPollCount], a
+	coord hl, 0, 12
+	ld b, 4
+	ld c, 18
+	call CableClub_TextBoxBorder
+	ld a, [wTradingWhichPlayerMon]
+	ld hl, wPartySpecies
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hl]
+	ld [wd11e], a
+	call GetMonName
+	ld hl, wcd6d
+	ld de, wNameOfPlayerMonToBeTraded
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld a, [wTradingWhichEnemyMon]
+	ld hl, wEnemyPartyMons
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hl]
+	ld [wd11e], a
+	call GetMonName
+	ld hl, WillBeTradedText
+	coord bc, 1, 14
+	call TextCommandProcessor
+	call SaveScreenTilesToBuffer1
+	coord hl, 10, 7
+	lb bc, 8, 11
+	ld a, TRADE_CANCEL_MENU
+	ld [wTwoOptionMenuID], a
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	call LoadScreenTilesFromBuffer1
+	ld a, [wCurrentMenuItem]
+	and a
+	jr z, .tradeConfirmed
+; if trade cancelled
+	ld a, $1
+	ld [wSerialExchangeNybbleSendData], a
+	coord hl, 0, 12
+	ld b, 4
+	ld c, 18
+	call CableClub_TextBoxBorder
+	coord hl, 1, 14
+	ld de, TradeCanceled
+	call PlaceString
+	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+	jp .tradeCancelled
+.tradeConfirmed
+	ld a, $2
+	ld [wSerialExchangeNybbleSendData], a
+	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+	ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+	dec a ; did the other person cancel?
+	jr nz, .doTrade
+; if the other person cancelled
+	coord hl, 0, 12
+	ld b, 4
+	ld c, 18
+	call CableClub_TextBoxBorder
+	coord hl, 1, 14
+	ld de, TradeCanceled
+	call PlaceString
+	jp .tradeCancelled
+.doTrade
+	ld a, [wTradingWhichPlayerMon]
+	ld hl, wPartyMonOT
+	call SkipFixedLengthTextEntries
+	ld de, wTradedPlayerMonOT
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, wPartyMon1Species
+	ld a, [wTradingWhichPlayerMon]
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld bc, wPartyMon1OTID - wPartyMon1
+	add hl, bc
+	ld a, [hli]
+	ld [wTradedPlayerMonOTID], a
+	ld a, [hl]
+	ld [wTradedPlayerMonOTID + 1], a
+	ld a, [wTradingWhichEnemyMon]
+	ld hl, wEnemyMonOT
+	call SkipFixedLengthTextEntries
+	ld de, wTradedEnemyMonOT
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, wEnemyMons
+	ld a, [wTradingWhichEnemyMon]
+	ld bc, wEnemyMon2 - wEnemyMon1
+	call AddNTimes
+	ld bc, wEnemyMon1OTID - wEnemyMon1
+	add hl, bc
+	ld a, [hli]
+	ld [wTradedEnemyMonOTID], a
+	ld a, [hl]
+	ld [wTradedEnemyMonOTID + 1], a
+	ld a, [wTradingWhichPlayerMon]
+	ld [wWhichPokemon], a
+	ld hl, wPartySpecies
+	ld b, 0
+	ld c, a
+	add hl, bc
+	ld a, [hl]
+	ld [wTradedPlayerMonSpecies], a
+	xor a
+	ld [wRemoveMonFromBox], a
+	call RemovePokemon
+	ld a, [wTradingWhichEnemyMon]
+	ld c, a
+	ld [wWhichPokemon], a
+	ld hl, wEnemyPartyMons
+	ld d, 0
+	ld e, a
+	add hl, de
+	ld a, [hl]
+	ld [wcf91], a
+	ld hl, wEnemyMons
+	ld a, c
+	ld bc, wEnemyMon2 - wEnemyMon1
+	call AddNTimes
+	ld de, wLoadedMon
+	ld bc, wEnemyMon2 - wEnemyMon1
+	call CopyData
+	call AddEnemyMonToPlayerParty
+	ld a, [wPartyCount]
+	dec a
+	ld [wWhichPokemon], a
+	ld a, $1
+	ld [wForceEvolution], a
+	ld a, [wTradingWhichEnemyMon]
+	ld hl, wEnemyPartyMons
+	ld b, 0
+	ld c, a
+	add hl, bc
+	ld a, [hl]
+	ld [wTradedEnemyMonSpecies], a
+	ld a, 10
+	ld [wAudioFadeOutControl], a
+	ld a, BANK(Music_SafariZone)
+	ld [wAudioSavedROMBank], a
+	ld a, MUSIC_SAFARI_ZONE
+	ld [wNewSoundID], a
+	call PlaySound
+	ld c, 100
+	call DelayFrames
+	call ClearScreen
+	call LoadHpBarAndStatusTilePatterns
+	xor a
+	ld [wUnusedCC5B], a
+	ld a, [hSerialConnectionStatus]
+	cp USING_EXTERNAL_CLOCK
+	jr z, .usingExternalClock
+	predef InternalClockTradeAnim
+	jr .tradeCompleted
+.usingExternalClock
+	predef ExternalClockTradeAnim
+.tradeCompleted
+	callab TryEvolvingMon
+	call ClearScreen
+	call LoadTrainerInfoTextBoxTiles
+	call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+	ld c, 40
+	call DelayFrames
+	coord hl, 0, 12
+	ld b, 4
+	ld c, 18
+	call CableClub_TextBoxBorder
+	coord hl, 1, 14
+	ld de, TradeCompleted
+	call PlaceString
+	predef SaveSAVtoSRAM2
+	ld c, 50
+	call DelayFrames
+	xor a
+	ld [wTradeCenterPointerTableIndex], a
+	jp CableClub_DoBattleOrTradeAgain
+.tradeCancelled
+	ld c, 100
+	call DelayFrames
+	xor a ; TradeCenter_SelectMon
+	ld [wTradeCenterPointerTableIndex], a
+	jp CallCurrentTradeCenterFunction
+
+WillBeTradedText:
+	TX_FAR _WillBeTradedText
+	db "@"
+
+TradeCompleted:
+	db "Trade completed!@"
+
+TradeCanceled:
+	db   "Too bad! The trade"
+	next "was canceled!@"
+
+TradeCenterPointerTable:
+	dw TradeCenter_SelectMon
+	dw TradeCenter_Trade
+
+CableClub_Run:
+	ld a, [wLinkState]
+	cp LINK_STATE_START_TRADE
+	jr z, .doBattleOrTrade
+	cp LINK_STATE_START_BATTLE
+	jr z, .doBattleOrTrade
+	cp LINK_STATE_RESET ; this is never used
+	ret nz
+	predef EmptyFunc3
+	jp Init
+.doBattleOrTrade
+	call CableClub_DoBattleOrTrade
+	ld hl, Club_GFX
+	ld a, h
+	ld [wTilesetGfxPtr + 1], a
+	ld a, l
+	ld [wTilesetGfxPtr], a
+	ld a, Bank(Club_GFX)
+	ld [wTilesetBank], a
+	ld hl, Club_Coll
+	ld a, h
+	ld [wTilesetCollisionPtr + 1], a
+	ld a, l
+	ld [wTilesetCollisionPtr], a
+	xor a
+	ld [wGrassRate], a
+	inc a ; LINK_STATE_IN_CABLE_CLUB
+	ld [wLinkState], a
+	ld [hJoy5], a
+	ld a, 10
+	ld [wAudioFadeOutControl], a
+	ld a, BANK(Music_Celadon)
+	ld [wAudioSavedROMBank], a
+	ld a, MUSIC_CELADON
+	ld [wNewSoundID], a
+	jp PlaySound
+
+EmptyFunc3:
+	ret
+
+Diploma_TextBoxBorder:
+	call GetPredefRegisters
+
+; b = height
+; c = width
+CableClub_TextBoxBorder:
+	push hl
+	ld a, $78 ; border upper left corner tile
+	ld [hli], a
+	inc a ; border top horizontal line tile
+	call CableClub_DrawHorizontalLine
+	inc a ; border upper right corner tile
+	ld [hl], a
+	pop hl
+	ld de, 20
+	add hl, de
+.loop
+	push hl
+	ld a, $7b ; border left vertical line tile
+	ld [hli], a
+	ld a, " "
+	call CableClub_DrawHorizontalLine
+	ld [hl], $77 ; border right vertical line tile
+	pop hl
+	ld de, 20
+	add hl, de
+	dec b
+	jr nz, .loop
+	ld a, $7c ; border lower left corner tile
+	ld [hli], a
+	ld a, $76 ; border bottom horizontal line tile
+	call CableClub_DrawHorizontalLine
+	ld [hl], $7d ; border lower right corner tile
+	ret
+
+; c = width
+CableClub_DrawHorizontalLine:
+	ld d, c
+.loop
+	ld [hli], a
+	dec d
+	jr nz, .loop
+	ret
+
+LoadTrainerInfoTextBoxTiles:
+	ld de, TrainerInfoTextBoxTileGraphics
+	ld hl, vChars2 + $760
+	lb bc, BANK(TrainerInfoTextBoxTileGraphics), (TrainerInfoTextBoxTileGraphicsEnd - TrainerInfoTextBoxTileGraphics) / $10
+	jp CopyVideoData
--- /dev/null
+++ b/engine/link/cable_club_npc.asm
@@ -1,0 +1,151 @@
+CableClubNPC::
+	ld hl, CableClubNPCWelcomeText
+	call PrintText
+	CheckEvent EVENT_GOT_POKEDEX
+	jp nz, .receivedPokedex
+; if the player hasn't received the pokedex
+	ld c, 60
+	call DelayFrames
+	ld hl, CableClubNPCMakingPreparationsText
+	call PrintText
+	jp .didNotConnect
+.receivedPokedex
+	ld a, $1
+	ld [wMenuJoypadPollCount], a
+	ld a, 90
+	ld [wLinkTimeoutCounter], a
+.establishConnectionLoop
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK
+	jr z, .establishedConnection
+	cp USING_EXTERNAL_CLOCK
+	jr z, .establishedConnection
+	ld a, CONNECTION_NOT_ESTABLISHED
+	ld [hSerialConnectionStatus], a
+	ld a, ESTABLISH_CONNECTION_WITH_EXTERNAL_CLOCK
+	ld [rSB], a
+	xor a
+	ld [hSerialReceiveData], a
+	ld a, START_TRANSFER_EXTERNAL_CLOCK
+	ld [rSC], a
+	ld a, [wLinkTimeoutCounter]
+	dec a
+	ld [wLinkTimeoutCounter], a
+	jr z, .failedToEstablishConnection
+	ld a, ESTABLISH_CONNECTION_WITH_INTERNAL_CLOCK
+	ld [rSB], a
+	ld a, START_TRANSFER_INTERNAL_CLOCK
+	ld [rSC], a
+	call DelayFrame
+	jr .establishConnectionLoop
+.establishedConnection
+	call Serial_SendZeroByte
+	call DelayFrame
+	call Serial_SendZeroByte
+	ld c, 50
+	call DelayFrames
+	ld hl, CableClubNPCPleaseApplyHereHaveToSaveText
+	call PrintText
+	xor a
+	ld [wMenuJoypadPollCount], a
+	call YesNoChoice
+	ld a, $1
+	ld [wMenuJoypadPollCount], a
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .choseNo
+	callab SaveSAVtoSRAM
+	call WaitForSoundToFinish
+	ld a, SFX_SAVE
+	call PlaySoundWaitForCurrent
+	ld hl, CableClubNPCPleaseWaitText
+	call PrintText
+	ld hl, wUnknownSerialCounter
+	ld a, $3
+	ld [hli], a
+	xor a
+	ld [hl], a
+	ld [hSerialReceivedNewData], a
+	ld [wSerialExchangeNybbleSendData], a
+	call Serial_SyncAndExchangeNybble
+	ld hl, wUnknownSerialCounter
+	ld a, [hli]
+	inc a
+	jr nz, .connected
+	ld a, [hl]
+	inc a
+	jr nz, .connected
+	ld b, 10
+.syncLoop
+	call DelayFrame
+	call Serial_SendZeroByte
+	dec b
+	jr nz, .syncLoop
+	call CloseLinkConnection
+	ld hl, CableClubNPCLinkClosedBecauseOfInactivityText
+	call PrintText
+	jr .didNotConnect
+.failedToEstablishConnection
+	ld hl, CableClubNPCAreaReservedFor2FriendsLinkedByCableText
+	call PrintText
+	jr .didNotConnect
+.choseNo
+	call CloseLinkConnection
+	ld hl, CableClubNPCPleaseComeAgainText
+	call PrintText
+.didNotConnect
+	xor a
+	ld hl, wUnknownSerialCounter
+	ld [hli], a
+	ld [hl], a
+	ld hl, wd72e
+	res 6, [hl]
+	xor a
+	ld [wMenuJoypadPollCount], a
+	ret
+.connected
+	xor a
+	ld [hld], a
+	ld [hl], a
+	jpab LinkMenu
+
+CableClubNPCAreaReservedFor2FriendsLinkedByCableText:
+	TX_FAR _CableClubNPCAreaReservedFor2FriendsLinkedByCableText
+	db "@"
+
+CableClubNPCWelcomeText:
+	TX_FAR _CableClubNPCWelcomeText
+	db "@"
+
+CableClubNPCPleaseApplyHereHaveToSaveText:
+	TX_FAR _CableClubNPCPleaseApplyHereHaveToSaveText
+	db "@"
+
+CableClubNPCPleaseWaitText:
+	TX_FAR _CableClubNPCPleaseWaitText
+	TX_DELAY
+	db "@"
+
+CableClubNPCLinkClosedBecauseOfInactivityText:
+	TX_FAR _CableClubNPCLinkClosedBecauseOfInactivityText
+	db "@"
+
+CableClubNPCPleaseComeAgainText:
+	TX_FAR _CableClubNPCPleaseComeAgainText
+	db "@"
+
+CableClubNPCMakingPreparationsText:
+	TX_FAR _CableClubNPCMakingPreparationsText
+	db "@"
+
+CloseLinkConnection:
+	call Delay3
+	ld a, CONNECTION_NOT_ESTABLISHED
+	ld [hSerialConnectionStatus], a
+	ld a, ESTABLISH_CONNECTION_WITH_EXTERNAL_CLOCK
+	ld [rSB], a
+	xor a
+	ld [hSerialReceiveData], a
+	ld a, START_TRANSFER_EXTERNAL_CLOCK
+	ld [rSC], a
+	ret
--- /dev/null
+++ b/engine/link/print_waiting_text.asm
@@ -1,0 +1,20 @@
+PrintWaitingText::
+	coord hl, 3, 10
+	ld b, $1
+	ld c, $b
+	ld a, [wIsInBattle]
+	and a
+	jr z, .asm_4c17
+	call TextBoxBorder
+	jr .asm_4c1a
+.asm_4c17
+	call CableClub_TextBoxBorder
+.asm_4c1a
+	coord hl, 4, 11
+	ld de, WaitingText
+	call PlaceString
+	ld c, 50
+	jp DelayFrames
+
+WaitingText:
+	db "Waiting...!@"
--- a/engine/load_mon_data.asm
+++ /dev/null
@@ -1,49 +1,0 @@
-LoadMonData_::
-; Load monster [wWhichPokemon] from list [wMonDataLocation]:
-;  0: partymon
-;  1: enemymon
-;  2: boxmon
-;  3: daycaremon
-; Return monster id at wcf91 and its data at wLoadedMon.
-; Also load base stats at wMonHeader for convenience.
-
-	ld a, [wDayCareMonSpecies]
-	ld [wcf91], a
-	ld a, [wMonDataLocation]
-	cp DAYCARE_DATA
-	jr z, .GetMonHeader
-
-	ld a, [wWhichPokemon]
-	ld e, a
-	callab GetMonSpecies
-
-.GetMonHeader
-	ld a, [wcf91]
-	ld [wd0b5], a ; input for GetMonHeader
-	call GetMonHeader
-
-	ld hl, wPartyMons
-	ld bc, wPartyMon2 - wPartyMon1
-	ld a, [wMonDataLocation]
-	cp ENEMY_PARTY_DATA
-	jr c, .getMonEntry
-
-	ld hl, wEnemyMons
-	jr z, .getMonEntry
-
-	cp 2
-	ld hl, wBoxMons
-	ld bc, wBoxMon2 - wBoxMon1
-	jr z, .getMonEntry
-
-	ld hl, wDayCareMon
-	jr .copyMonData
-
-.getMonEntry
-	ld a, [wWhichPokemon]
-	call AddNTimes
-
-.copyMonData
-	ld de, wLoadedMon
-	ld bc, wPartyMon2 - wPartyMon1
-	jp CopyData
--- a/engine/load_pokedex_tiles.asm
+++ /dev/null
@@ -1,11 +1,0 @@
-; Loads tile patterns for tiles used in the pokedex.
-LoadPokedexTilePatterns:
-	call LoadHpBarAndStatusTilePatterns
-	ld de, PokedexTileGraphics
-	ld hl, vChars2 + $600
-	lb bc, BANK(PokedexTileGraphics), (PokedexTileGraphicsEnd - PokedexTileGraphics) / $10
-	call CopyVideoData
-	ld de, PokeballTileGraphics
-	ld hl, vChars2 + $720
-	lb bc, BANK(PokeballTileGraphics), $01
-	jp CopyVideoData ; load pokeball tile for marking caught mons
--- /dev/null
+++ b/engine/math/bcd.asm
@@ -1,0 +1,214 @@
+DivideBCDPredef::
+DivideBCDPredef2::
+DivideBCDPredef3::
+DivideBCDPredef4::
+	call GetPredefRegisters
+
+DivideBCD::
+	xor a
+	ld [hDivideBCDBuffer], a
+	ld [hDivideBCDBuffer+1], a
+	ld [hDivideBCDBuffer+2], a
+	ld d, $1
+.mulBy10Loop 
+; multiply the divisor by 10 until the leading digit is nonzero
+; to set up the standard long division algorithm
+	ld a, [hDivideBCDDivisor]
+	and $f0
+	jr nz, .next
+	inc d
+	ld a, [hDivideBCDDivisor]
+	swap a
+	and $f0
+	ld b, a
+	ld a, [hDivideBCDDivisor+1]
+	swap a
+	ld [hDivideBCDDivisor+1], a
+	and $f
+	or b
+	ld [hDivideBCDDivisor], a
+	ld a, [hDivideBCDDivisor+1]
+	and $f0
+	ld b, a
+	ld a, [hDivideBCDDivisor+2]
+	swap a
+	ld [hDivideBCDDivisor+2], a
+	and $f
+	or b
+	ld [hDivideBCDDivisor+1], a
+	ld a, [hDivideBCDDivisor+2]
+	and $f0
+	ld [hDivideBCDDivisor+2], a
+	jr .mulBy10Loop
+.next
+	push de
+	push de
+	call DivideBCD_getNextDigit
+	pop de
+	ld a, b
+	swap a
+	and $f0
+	ld [hDivideBCDBuffer], a
+	dec d
+	jr z, .next2
+	push de
+	call DivideBCD_divDivisorBy10
+	call DivideBCD_getNextDigit
+	pop de
+	ld a, [hDivideBCDBuffer]
+	or b
+	ld [hDivideBCDBuffer], a
+	dec d
+	jr z, .next2
+	push de
+	call DivideBCD_divDivisorBy10
+	call DivideBCD_getNextDigit
+	pop de
+	ld a, b
+	swap a
+	and $f0
+	ld [hDivideBCDBuffer+1], a
+	dec d
+	jr z, .next2
+	push de
+	call DivideBCD_divDivisorBy10
+	call DivideBCD_getNextDigit
+	pop de
+	ld a, [hDivideBCDBuffer+1]
+	or b
+	ld [hDivideBCDBuffer+1], a
+	dec d
+	jr z, .next2
+	push de
+	call DivideBCD_divDivisorBy10
+	call DivideBCD_getNextDigit
+	pop de
+	ld a, b
+	swap a
+	and $f0
+	ld [hDivideBCDBuffer+2], a
+	dec d
+	jr z, .next2
+	push de
+	call DivideBCD_divDivisorBy10
+	call DivideBCD_getNextDigit
+	pop de
+	ld a, [hDivideBCDBuffer+2]
+	or b
+	ld [hDivideBCDBuffer+2], a
+.next2
+	ld a, [hDivideBCDBuffer]
+	ld [hDivideBCDQuotient], a ; the same memory location as hDivideBCDDivisor
+	ld a, [hDivideBCDBuffer+1]
+	ld [hDivideBCDQuotient+1], a
+	ld a, [hDivideBCDBuffer+2]
+	ld [hDivideBCDQuotient+2], a
+	pop de
+	ld a, $6 
+	sub d
+	and a
+	ret z
+.divResultBy10loop
+	push af
+	call DivideBCD_divDivisorBy10
+	pop af
+	dec a
+	jr nz, .divResultBy10loop
+	ret
+
+DivideBCD_divDivisorBy10:
+	ld a, [hDivideBCDDivisor+2]
+	swap a
+	and $f
+	ld b, a
+	ld a, [hDivideBCDDivisor+1]
+	swap a
+	ld [hDivideBCDDivisor+1], a
+	and $f0
+	or b
+	ld [hDivideBCDDivisor+2], a
+	ld a, [hDivideBCDDivisor+1]
+	and $f
+	ld b, a
+	ld a, [hDivideBCDDivisor]
+	swap a
+	ld [hDivideBCDDivisor], a
+	and $f0
+	or b
+	ld [hDivideBCDDivisor+1], a
+	ld a, [hDivideBCDDivisor]
+	and $f
+	ld [hDivideBCDDivisor], a
+	ret
+
+DivideBCD_getNextDigit:
+	ld bc, $3
+.loop
+	ld de, hMoney ; the dividend
+	ld hl, hDivideBCDDivisor
+	push bc
+	call StringCmp
+	pop bc
+	ret c
+	inc b
+	ld de, hMoney+2 ; since SubBCD works starting from the least significant digit
+	ld hl, hDivideBCDDivisor+2  
+	push bc
+	call SubBCD
+	pop bc
+	jr .loop
+
+
+AddBCDPredef::
+	call GetPredefRegisters
+
+AddBCD::
+	and a
+	ld b, c
+.add
+	ld a, [de]
+	adc [hl]
+	daa
+	ld [de], a
+	dec de
+	dec hl
+	dec c
+	jr nz, .add
+	jr nc, .done
+	ld a, $99
+	inc de
+.fill
+	ld [de], a
+	inc de
+	dec b
+	jr nz, .fill
+.done
+	ret
+
+
+SubBCDPredef::
+	call GetPredefRegisters
+
+SubBCD::
+	and a
+	ld b, c
+.sub
+	ld a, [de]
+	sbc [hl]
+	daa
+	ld [de], a
+	dec de
+	dec hl
+	dec c
+	jr nz, .sub
+	jr nc, .done
+	ld a, $00
+	inc de
+.fill
+	ld [de], a
+	inc de
+	dec b
+	jr nz, .fill
+	scf
+.done
+	ret
--- /dev/null
+++ b/engine/math/multiply_divide.asm
@@ -1,0 +1,143 @@
+_Multiply::
+	ld a, $8
+	ld b, a
+	xor a
+	ld [H_PRODUCT], a
+	ld [H_MULTIPLYBUFFER], a
+	ld [H_MULTIPLYBUFFER+1], a
+	ld [H_MULTIPLYBUFFER+2], a
+	ld [H_MULTIPLYBUFFER+3], a
+.loop
+	ld a, [H_MULTIPLIER]
+	srl a
+	ld [H_MULTIPLIER], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
+	jr nc, .smallMultiplier
+	ld a, [H_MULTIPLYBUFFER+3]
+	ld c, a
+	ld a, [H_MULTIPLICAND+2]
+	add c
+	ld [H_MULTIPLYBUFFER+3], a
+	ld a, [H_MULTIPLYBUFFER+2]
+	ld c, a
+	ld a, [H_MULTIPLICAND+1]
+	adc c
+	ld [H_MULTIPLYBUFFER+2], a
+	ld a, [H_MULTIPLYBUFFER+1]
+	ld c, a
+	ld a, [H_MULTIPLICAND] ; (aliases: H_MULTIPLICAND)
+	adc c
+	ld [H_MULTIPLYBUFFER+1], a
+	ld a, [H_MULTIPLYBUFFER]
+	ld c, a
+	ld a, [H_PRODUCT] ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
+	adc c
+	ld [H_MULTIPLYBUFFER], a
+.smallMultiplier
+	dec b
+	jr z, .done
+	ld a, [H_MULTIPLICAND+2]
+	sla a
+	ld [H_MULTIPLICAND+2], a
+	ld a, [H_MULTIPLICAND+1]
+	rl a
+	ld [H_MULTIPLICAND+1], a
+	ld a, [H_MULTIPLICAND]
+	rl a
+	ld [H_MULTIPLICAND], a
+	ld a, [H_PRODUCT]
+	rl a
+	ld [H_PRODUCT], a
+	jr .loop
+.done
+	ld a, [H_MULTIPLYBUFFER+3]
+	ld [H_PRODUCT+3], a
+	ld a, [H_MULTIPLYBUFFER+2]
+	ld [H_PRODUCT+2], a
+	ld a, [H_MULTIPLYBUFFER+1]
+	ld [H_PRODUCT+1], a
+	ld a, [H_MULTIPLYBUFFER]
+	ld [H_PRODUCT], a
+	ret
+
+_Divide::
+	xor a
+	ld [H_DIVIDEBUFFER], a
+	ld [H_DIVIDEBUFFER+1], a
+	ld [H_DIVIDEBUFFER+2], a
+	ld [H_DIVIDEBUFFER+3], a
+	ld [H_DIVIDEBUFFER+4], a
+	ld a, $9
+	ld e, a
+.asm_37db3
+	ld a, [H_DIVIDEBUFFER]
+	ld c, a
+	ld a, [H_DIVIDEND+1] ; (aliases: H_MULTIPLICAND)
+	sub c
+	ld d, a
+	ld a, [H_DIVISOR] ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
+	ld c, a
+	ld a, [H_DIVIDEND] ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
+	sbc c
+	jr c, .asm_37dce
+	ld [H_DIVIDEND], a ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
+	ld a, d
+	ld [H_DIVIDEND+1], a ; (aliases: H_MULTIPLICAND)
+	ld a, [H_DIVIDEBUFFER+4]
+	inc a
+	ld [H_DIVIDEBUFFER+4], a
+	jr .asm_37db3
+.asm_37dce
+	ld a, b
+	cp $1
+	jr z, .asm_37e18
+	ld a, [H_DIVIDEBUFFER+4]
+	sla a
+	ld [H_DIVIDEBUFFER+4], a
+	ld a, [H_DIVIDEBUFFER+3]
+	rl a
+	ld [H_DIVIDEBUFFER+3], a
+	ld a, [H_DIVIDEBUFFER+2]
+	rl a
+	ld [H_DIVIDEBUFFER+2], a
+	ld a, [H_DIVIDEBUFFER+1]
+	rl a
+	ld [H_DIVIDEBUFFER+1], a
+	dec e
+	jr nz, .asm_37e04
+	ld a, $8
+	ld e, a
+	ld a, [H_DIVIDEBUFFER]
+	ld [H_DIVISOR], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
+	xor a
+	ld [H_DIVIDEBUFFER], a
+	ld a, [H_DIVIDEND+1] ; (aliases: H_MULTIPLICAND)
+	ld [H_DIVIDEND], a ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
+	ld a, [H_DIVIDEND+2]
+	ld [H_DIVIDEND+1], a ; (aliases: H_MULTIPLICAND)
+	ld a, [H_DIVIDEND+3]
+	ld [H_DIVIDEND+2], a
+.asm_37e04
+	ld a, e
+	cp $1
+	jr nz, .asm_37e0a
+	dec b
+.asm_37e0a
+	ld a, [H_DIVISOR] ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
+	srl a
+	ld [H_DIVISOR], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
+	ld a, [H_DIVIDEBUFFER]
+	rr a
+	ld [H_DIVIDEBUFFER], a
+	jr .asm_37db3
+.asm_37e18
+	ld a, [H_DIVIDEND+1] ; (aliases: H_MULTIPLICAND)
+	ld [H_REMAINDER], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
+	ld a, [H_DIVIDEBUFFER+4]
+	ld [H_QUOTIENT+3], a
+	ld a, [H_DIVIDEBUFFER+3]
+	ld [H_QUOTIENT+2], a
+	ld a, [H_DIVIDEBUFFER+2]
+	ld [H_QUOTIENT+1], a ; (aliases: H_MULTIPLICAND)
+	ld a, [H_DIVIDEBUFFER+1]
+	ld [H_DIVIDEND], a ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
+	ret
--- /dev/null
+++ b/engine/math/random.asm
@@ -1,0 +1,13 @@
+Random_::
+; Generate a random 16-bit value.
+	ld a, [rDIV]
+	ld b, a
+	ld a, [hRandomAdd]
+	adc b
+	ld [hRandomAdd], a
+	ld a, [rDIV]
+	ld b, a
+	ld a, [hRandomSub]
+	sbc b
+	ld [hRandomSub], a
+	ret
--- a/engine/menu/bills_pc.asm
+++ /dev/null
@@ -1,554 +1,0 @@
-DisplayPCMainMenu::
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call SaveScreenTilesToBuffer2
-	ld a, [wNumHoFTeams]
-	and a
-	jr nz, .leaguePCAvailable
-	CheckEvent EVENT_GOT_POKEDEX
-	jr z, .noOaksPC
-	ld a, [wNumHoFTeams]
-	and a
-	jr nz, .leaguePCAvailable
-	coord hl, 0, 0
-	ld b, 8
-	ld c, 14
-	jr .next
-.noOaksPC
-	coord hl, 0, 0
-	ld b, 6
-	ld c, 14
-	jr .next
-.leaguePCAvailable
-	coord hl, 0, 0
-	ld b, 10
-	ld c, 14
-.next
-	call TextBoxBorder
-	call UpdateSprites
-	ld a, 3
-	ld [wMaxMenuItem], a
-	CheckEvent EVENT_MET_BILL
-	jr nz, .metBill
-	coord hl, 2, 2
-	ld de, SomeonesPCText
-	jr .next2
-.metBill
-	coord hl, 2, 2
-	ld de, BillsPCText
-.next2
-	call PlaceString
-	coord hl, 2, 4
-	ld de, wPlayerName
-	call PlaceString
-	ld l, c
-	ld h, b
-	ld de, PlayersPCText
-	call PlaceString
-	CheckEvent EVENT_GOT_POKEDEX
-	jr z, .noOaksPC2
-	coord hl, 2, 6
-	ld de, OaksPCText
-	call PlaceString
-	ld a, [wNumHoFTeams]
-	and a
-	jr z, .noLeaguePC
-	ld a, 4
-	ld [wMaxMenuItem], a
-	coord hl, 2, 8
-	ld de, PKMNLeaguePCText
-	call PlaceString
-	coord hl, 2, 10
-	ld de, LogOffPCText
-	jr .next3
-.noLeaguePC
-	coord hl, 2, 8
-	ld de, LogOffPCText
-	jr .next3
-.noOaksPC2
-	ld a, $2
-	ld [wMaxMenuItem], a
-	coord hl, 2, 6
-	ld de, LogOffPCText
-.next3
-	call PlaceString
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ret
-
-SomeonesPCText:   db "SOMEONE's PC@"
-BillsPCText:      db "BILL's PC@"
-PlayersPCText:    db "'s PC@"
-OaksPCText:       db "PROF.OAK's PC@"
-PKMNLeaguePCText: db "<pkmn>LEAGUE@"
-LogOffPCText:     db "LOG OFF@"
-
-BillsPC_::
-	ld hl, wd730
-	set 6, [hl]
-	xor a
-	ld [wParentMenuItem], a
-	inc a               ; MONSTER_NAME
-	ld [wNameListType], a
-	call LoadHpBarAndStatusTilePatterns
-	ld a, [wListScrollOffset]
-	push af
-	ld a, [wFlags_0xcd60]
-	bit 3, a ; accessing Bill's PC through another PC?
-	jr nz, BillsPCMenu
-; accessing it directly
-	ld a, SFX_TURN_ON_PC
-	call PlaySound
-	ld hl, SwitchOnText
-	call PrintText
-
-BillsPCMenu:
-	ld a, [wParentMenuItem]
-	ld [wCurrentMenuItem], a
-	ld hl, vChars2 + $780
-	ld de, PokeballTileGraphics
-	lb bc, BANK(PokeballTileGraphics), $01
-	call CopyVideoData
-	call LoadScreenTilesFromBuffer2DisableBGTransfer
-	coord hl, 0, 0
-	ld b, 10
-	ld c, 12
-	call TextBoxBorder
-	coord hl, 2, 2
-	ld de, BillsPCMenuText
-	call PlaceString
-	ld hl, wTopMenuItemY
-	ld a, 2
-	ld [hli], a ; wTopMenuItemY
-	dec a
-	ld [hli], a ; wTopMenuItemX
-	inc hl
-	inc hl
-	ld a, 4
-	ld [hli], a ; wMaxMenuItem
-	ld a, A_BUTTON | B_BUTTON
-	ld [hli], a ; wMenuWatchedKeys
-	xor a
-	ld [hli], a ; wLastMenuItem
-	ld [hli], a ; wPartyAndBillsPCSavedMenuItem
-	ld hl, wListScrollOffset
-	ld [hli], a ; wListScrollOffset
-	ld [hl], a ; wMenuWatchMovingOutOfBounds
-	ld [wPlayerMonNumber], a
-	ld hl, WhatText
-	call PrintText
-	coord hl, 9, 14
-	ld b, 2
-	ld c, 9
-	call TextBoxBorder
-	ld a, [wCurrentBoxNum]
-	and $7f
-	cp 9
-	jr c, .singleDigitBoxNum
-; two digit box num
-	sub 9
-	coord hl, 17, 16
-	ld [hl], "1"
-	add "0"
-	jr .next
-.singleDigitBoxNum
-	add "1"
-.next
-	Coorda 18, 16
-	coord hl, 10, 16
-	ld de, BoxNoPCText
-	call PlaceString
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Delay3
-	call HandleMenuInput
-	bit 1, a
-	jp nz, ExitBillsPC ; b button
-	call PlaceUnfilledArrowMenuCursor
-	ld a, [wCurrentMenuItem]
-	ld [wParentMenuItem], a
-	and a
-	jp z, BillsPCWithdraw ; withdraw
-	cp $1
-	jp z, BillsPCDeposit ; deposit
-	cp $2
-	jp z, BillsPCRelease ; release
-	cp $3
-	jp z, BillsPCChangeBox ; change box
-
-ExitBillsPC:
-	ld a, [wFlags_0xcd60]
-	bit 3, a ; accessing Bill's PC through another PC?
-	jr nz, .next
-; accessing it directly
-	call LoadTextBoxTilePatterns
-	ld a, SFX_TURN_OFF_PC
-	call PlaySound
-	call WaitForSoundToFinish
-.next
-	ld hl, wFlags_0xcd60
-	res 5, [hl]
-	call LoadScreenTilesFromBuffer2
-	pop af
-	ld [wListScrollOffset], a
-	ld hl, wd730
-	res 6, [hl]
-	ret
-
-BillsPCDeposit:
-	ld a, [wPartyCount]
-	dec a
-	jr nz, .partyLargeEnough
-	ld hl, CantDepositLastMonText
-	call PrintText
-	jp BillsPCMenu
-.partyLargeEnough
-	ld a, [wNumInBox]
-	cp MONS_PER_BOX
-	jr nz, .boxNotFull
-	ld hl, BoxFullText
-	call PrintText
-	jp BillsPCMenu
-.boxNotFull
-	ld hl, wPartyCount
-	call DisplayMonListMenu
-	jp c, BillsPCMenu
-	call DisplayDepositWithdrawMenu
-	jp nc, BillsPCMenu
-	ld a, [wcf91]
-	call GetCryData
-	call PlaySoundWaitForCurrent
-	ld a, PARTY_TO_BOX
-	ld [wMoveMonType], a
-	call MoveMon
-	xor a
-	ld [wRemoveMonFromBox], a
-	call RemovePokemon
-	call WaitForSoundToFinish
-	ld hl, wBoxNumString
-	ld a, [wCurrentBoxNum]
-	and $7f
-	cp 9
-	jr c, .singleDigitBoxNum
-	sub 9
-	ld [hl], "1"
-	inc hl
-	add "0"
-	jr .next
-.singleDigitBoxNum
-	add "1"
-.next
-	ld [hli], a
-	ld [hl], "@"
-	ld hl, MonWasStoredText
-	call PrintText
-	jp BillsPCMenu
-
-BillsPCWithdraw:
-	ld a, [wNumInBox]
-	and a
-	jr nz, .boxNotEmpty
-	ld hl, NoMonText
-	call PrintText
-	jp BillsPCMenu
-.boxNotEmpty
-	ld a, [wPartyCount]
-	cp PARTY_LENGTH
-	jr nz, .partyNotFull
-	ld hl, CantTakeMonText
-	call PrintText
-	jp BillsPCMenu
-.partyNotFull
-	ld hl, wNumInBox
-	call DisplayMonListMenu
-	jp c, BillsPCMenu
-	call DisplayDepositWithdrawMenu
-	jp nc, BillsPCMenu
-	ld a, [wWhichPokemon]
-	ld hl, wBoxMonNicks
-	call GetPartyMonName
-	ld a, [wcf91]
-	call GetCryData
-	call PlaySoundWaitForCurrent
-	xor a ; BOX_TO_PARTY
-	ld [wMoveMonType], a
-	call MoveMon
-	ld a, 1
-	ld [wRemoveMonFromBox], a
-	call RemovePokemon
-	call WaitForSoundToFinish
-	ld hl, MonIsTakenOutText
-	call PrintText
-	jp BillsPCMenu
-
-BillsPCRelease:
-	ld a, [wNumInBox]
-	and a
-	jr nz, .loop
-	ld hl, NoMonText
-	call PrintText
-	jp BillsPCMenu
-.loop
-	ld hl, wNumInBox
-	call DisplayMonListMenu
-	jp c, BillsPCMenu
-	ld hl, OnceReleasedText
-	call PrintText
-	call YesNoChoice
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .loop
-	inc a
-	ld [wRemoveMonFromBox], a
-	call RemovePokemon
-	call WaitForSoundToFinish
-	ld a, [wcf91]
-	call PlayCry
-	ld hl, MonWasReleasedText
-	call PrintText
-	jp BillsPCMenu
-
-BillsPCChangeBox:
-	callba ChangeBox
-	jp BillsPCMenu
-
-DisplayMonListMenu:
-	ld a, l
-	ld [wListPointer], a
-	ld a, h
-	ld [wListPointer + 1], a
-	xor a
-	ld [wPrintItemPrices], a
-	ld [wListMenuID], a
-	inc a                ; MONSTER_NAME
-	ld [wNameListType], a
-	ld a, [wPartyAndBillsPCSavedMenuItem]
-	ld [wCurrentMenuItem], a
-	call DisplayListMenuID
-	ld a, [wCurrentMenuItem]
-	ld [wPartyAndBillsPCSavedMenuItem], a
-	ret
-
-BillsPCMenuText:
-	db   "WITHDRAW <pkmn>"
-	next "DEPOSIT <pkmn>"
-	next "RELEASE <pkmn>"
-	next "CHANGE BOX"
-	next "SEE YA!"
-	db "@"
-
-BoxNoPCText:
-	db "BOX No.@"
-
-KnowsHMMove::
-; returns whether mon with party index [wWhichPokemon] knows an HM move
-	ld hl, wPartyMon1Moves
-	ld bc, wPartyMon2 - wPartyMon1
-	jr .next
-; unreachable
-	ld hl, wBoxMon1Moves
-	ld bc, wBoxMon2 - wBoxMon1
-.next
-	ld a, [wWhichPokemon]
-	call AddNTimes
-	ld b, NUM_MOVES
-.loop
-	ld a, [hli]
-	push hl
-	push bc
-	ld hl, HMMoveArray
-	ld de, 1
-	call IsInArray
-	pop bc
-	pop hl
-	ret c
-	dec b
-	jr nz, .loop
-	and a
-	ret
-
-HMMoveArray:
-	db CUT
-	db FLY
-	db SURF
-	db STRENGTH
-	db FLASH
-	db -1
-
-DisplayDepositWithdrawMenu:
-	coord hl, 9, 10
-	ld b, 6
-	ld c, 9
-	call TextBoxBorder
-	ld a, [wParentMenuItem]
-	and a ; was the Deposit or Withdraw item selected in the parent menu?
-	ld de, DepositPCText
-	jr nz, .next
-	ld de, WithdrawPCText
-.next
-	coord hl, 11, 12
-	call PlaceString
-	coord hl, 11, 14
-	ld de, StatsCancelPCText
-	call PlaceString
-	ld hl, wTopMenuItemY
-	ld a, 12
-	ld [hli], a ; wTopMenuItemY
-	ld a, 10
-	ld [hli], a ; wTopMenuItemX
-	xor a
-	ld [hli], a ; wCurrentMenuItem
-	inc hl
-	ld a, 2
-	ld [hli], a ; wMaxMenuItem
-	ld a, A_BUTTON | B_BUTTON
-	ld [hli], a ; wMenuWatchedKeys
-	xor a
-	ld [hl], a ; wLastMenuItem
-	ld hl, wListScrollOffset
-	ld [hli], a ; wListScrollOffset
-	ld [hl], a ; wMenuWatchMovingOutOfBounds
-	ld [wPlayerMonNumber], a
-	ld [wPartyAndBillsPCSavedMenuItem], a
-.loop
-	call HandleMenuInput
-	bit 1, a ; pressed B?
-	jr nz, .exit
-	ld a, [wCurrentMenuItem]
-	and a
-	jr z, .choseDepositWithdraw
-	dec a
-	jr z, .viewStats
-.exit
-	and a
-	ret
-.choseDepositWithdraw
-	scf
-	ret
-.viewStats
-	call SaveScreenTilesToBuffer1
-	ld a, [wParentMenuItem]
-	and a
-	ld a, PLAYER_PARTY_DATA
-	jr nz, .next2
-	ld a, BOX_DATA
-.next2
-	ld [wMonDataLocation], a
-	predef StatusScreen
-	predef StatusScreen2
-	call LoadScreenTilesFromBuffer1
-	call ReloadTilesetTilePatterns
-	call RunDefaultPaletteCommand
-	call LoadGBPal
-	jr .loop
-
-DepositPCText:  db "DEPOSIT@"
-WithdrawPCText: db "WITHDRAW@"
-StatsCancelPCText:
-	db   "STATS"
-	next "CANCEL@"
-
-SwitchOnText:
-	TX_FAR _SwitchOnText
-	db "@"
-
-WhatText:
-	TX_FAR _WhatText
-	db "@"
-
-DepositWhichMonText:
-	TX_FAR _DepositWhichMonText
-	db "@"
-
-MonWasStoredText:
-	TX_FAR _MonWasStoredText
-	db "@"
-
-CantDepositLastMonText:
-	TX_FAR _CantDepositLastMonText
-	db "@"
-
-BoxFullText:
-	TX_FAR _BoxFullText
-	db "@"
-
-MonIsTakenOutText:
-	TX_FAR _MonIsTakenOutText
-	db "@"
-
-NoMonText:
-	TX_FAR _NoMonText
-	db "@"
-
-CantTakeMonText:
-	TX_FAR _CantTakeMonText
-	db "@"
-
-ReleaseWhichMonText:
-	TX_FAR _ReleaseWhichMonText
-	db "@"
-
-OnceReleasedText:
-	TX_FAR _OnceReleasedText
-	db "@"
-
-MonWasReleasedText:
-	TX_FAR _MonWasReleasedText
-	db "@"
-
-CableClubLeftGameboy::
-	ld a, [hSerialConnectionStatus]
-	cp USING_EXTERNAL_CLOCK
-	ret z
-	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
-	cp SPRITE_FACING_RIGHT
-	ret nz
-	ld a, [wCurMap]
-	cp TRADE_CENTER
-	ld a, LINK_STATE_START_TRADE
-	jr z, .next
-	inc a ; LINK_STATE_START_BATTLE
-.next
-	ld [wLinkState], a
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump JustAMomentText
-
-CableClubRightGameboy::
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK
-	ret z
-	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
-	cp SPRITE_FACING_LEFT
-	ret nz
-	ld a, [wCurMap]
-	cp TRADE_CENTER
-	ld a, LINK_STATE_START_TRADE
-	jr z, .next
-	inc a ; LINK_STATE_START_BATTLE
-.next
-	ld [wLinkState], a
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump JustAMomentText
-
-JustAMomentText::
-	TX_FAR _JustAMomentText
-	db "@"
-
-	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
-	cp SPRITE_FACING_UP
-	ret nz
-	call EnableAutoTextBoxDrawing
-	tx_pre_jump OpenBillsPCText
-
-OpenBillsPCText::
-	TX_BILLS_PC
-
--- a/engine/menu/diploma.asm
+++ /dev/null
@@ -1,113 +1,0 @@
-DisplayDiploma::
-	call SaveScreenTilesToBuffer2
-	call GBPalWhiteOutWithDelay3
-	call ClearScreen
-	xor a
-	ld [wUpdateSpritesEnabled], a
-	ld hl, wd730
-	set 6, [hl]
-	call DisableLCD
-	ld hl, CircleTile
-	ld de, vChars2 + $700
-	ld bc, $0010
-	ld a, BANK(CircleTile)
-	call FarCopyData2
-	coord hl, 0, 0
-	lb bc, 16, 18
-	predef Diploma_TextBoxBorder
-	ld hl, DiplomaTextPointersAndCoords
-	ld c, $5
-.asm_56715
-	push bc
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	ld a, [hli]
-	push hl
-	ld h, [hl]
-	ld l, a
-	call PlaceString
-	pop hl
-	inc hl
-	pop bc
-	dec c
-	jr nz, .asm_56715
-	coord hl, 10, 4
-	ld de, wPlayerName
-	call PlaceString
-	callba DrawPlayerCharacter
-
-; Move the player 33 pixels right and set the priority bit so he appears
-; behind the background layer.
-	ld hl, wOAMBuffer + $01
-	lb bc, $80, $28
-.adjustPlayerGfxLoop
-	ld a, [hl] ; X
-	add 33
-	ld [hli], a
-	inc hl
-	ld a, b
-	ld [hli], a ; attributes
-	inc hl
-	dec c
-	jr nz, .adjustPlayerGfxLoop
-
-	call EnableLCD
-	callba LoadTrainerInfoTextBoxTiles
-	ld b, SET_PAL_GENERIC
-	call RunPaletteCommand
-	call Delay3
-	call GBPalNormal
-	ld a, $90
-	ld [rOBP0], a
-	call WaitForTextScrollButtonPress
-	ld hl, wd730
-	res 6, [hl]
-	call GBPalWhiteOutWithDelay3
-	call RestoreScreenTilesAndReloadTilePatterns
-	call Delay3
-	jp GBPalNormal
-
-UnusedPlayerNameLengthFunc:
-; Unused function that does a calculation involving the length of the player's
-; name.
-	ld hl, wPlayerName
-	ld bc, $ff00
-.loop
-	ld a, [hli]
-	cp "@"
-	ret z
-	dec c
-	jr .loop
-
-DiplomaTextPointersAndCoords:
-	dw DiplomaText
-	dwCoord 5, 2
-	dw DiplomaPlayer
-	dwCoord 3, 4
-	dw DiplomaEmptyText
-	dwCoord 15, 4
-	dw DiplomaCongrats
-	dwCoord 2, 6
-	dw DiplomaGameFreak
-	dwCoord 9, 16
-
-DiplomaText:
-	db $70,"Diploma",$70,"@"
-
-DiplomaPlayer:
-	db "Player@"
-
-DiplomaEmptyText:
-	db "@"
-
-DiplomaCongrats:
-	db   "Congrats! This"
-	next "diploma certifies"
-	next "that you have"
-	next "completed your"
-	next "#DEX.@"
-
-DiplomaGameFreak:
-	db "GAME FREAK@"
--- a/engine/menu/draw_badges.asm
+++ /dev/null
@@ -1,120 +1,0 @@
-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"
--- a/engine/menu/draw_start_menu.asm
+++ /dev/null
@@ -1,89 +1,0 @@
-; 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
--- a/engine/menu/league_pc.asm
+++ /dev/null
@@ -1,120 +1,0 @@
-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 "@"
--- a/engine/menu/main_menu.asm
+++ /dev/null
@@ -1,712 +1,0 @@
-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
--- a/engine/menu/naming_screen.asm
+++ /dev/null
@@ -1,494 +1,0 @@
-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?@"
--- a/engine/menu/oaks_pc.asm
+++ /dev/null
@@ -1,28 +1,0 @@
-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 "@"
--- a/engine/menu/party_menu.asm
+++ /dev/null
@@ -1,325 +1,0 @@
-; [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
--- a/engine/menu/pc.asm
+++ /dev/null
@@ -1,141 +1,0 @@
-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
--- a/engine/menu/players_pc.asm
+++ /dev/null
@@ -1,303 +1,0 @@
-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 "@"
--- a/engine/menu/pokedex.asm
+++ /dev/null
@@ -1,665 +1,0 @@
-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"
--- a/engine/menu/prize_menu.asm
+++ /dev/null
@@ -1,306 +1,0 @@
-CeladonPrizeMenu::
-	ld b, COIN_CASE
-	call IsItemInBag
-	jr nz, .havingCoinCase
-	ld hl, RequireCoinCaseTextPtr
-	jp PrintText
-.havingCoinCase
-	ld hl, wd730
-	set 6, [hl] ; disable letter-printing delay
-	ld hl, ExchangeCoinsForPrizesTextPtr
-	call PrintText
-; the following are the menu settings
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, $03
-	ld [wMaxMenuItem], a
-	ld a, $04
-	ld [wTopMenuItemY], a
-	ld a, $01
-	ld [wTopMenuItemX], a
-	call PrintPrizePrice
-	coord hl, 0, 2
-	ld b, 8
-	ld c, 16
-	call TextBoxBorder
-	call GetPrizeMenuId
-	call UpdateSprites
-	ld hl, WhichPrizeTextPtr
-	call PrintText
-	call HandleMenuInput ; menu choice handler
-	bit 1, a ; keypress = B (Cancel)
-	jr nz, .noChoice
-	ld a, [wCurrentMenuItem]
-	cp 3 ; "NO,THANKS" choice
-	jr z, .noChoice
-	call HandlePrizeChoice
-.noChoice
-	ld hl, wd730
-	res 6, [hl]
-	ret
-
-RequireCoinCaseTextPtr:
-	TX_FAR _RequireCoinCaseText
-	TX_WAIT
-	db "@"
-
-ExchangeCoinsForPrizesTextPtr:
-	TX_FAR _ExchangeCoinsForPrizesText
-	db "@"
-
-WhichPrizeTextPtr:
-	TX_FAR _WhichPrizeText
-	db "@"
-
-GetPrizeMenuId:
-; determine which one among the three
-; prize-texts has been selected
-; using the text ID (stored in [hSpriteIndexOrTextID])
-; load the three prizes at wd13d-wd13f
-; load the three prices at wd141-wd146
-; display the three prizes' names
-; (distinguishing between Pokemon names
-; and Items (specifically TMs) names)
-	ld a, [hSpriteIndexOrTextID]
-	sub 3       ; prize-texts' id are 3, 4 and 5
-	ld [wWhichPrizeWindow], a    ; prize-texts' id (relative, i.e. 0, 1 or 2)
-	add a
-	add a
-	ld d, 0
-	ld e, a
-	ld hl, PrizeDifferentMenuPtrs
-	add hl, de
-	ld a, [hli]
-	ld d, [hl]
-	ld e, a
-	inc hl
-	push hl
-	ld hl, wPrize1
-	call CopyString
-	pop hl
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld de, wPrize1Price
-	ld bc, 6
-	call CopyData
-	ld a, [wWhichPrizeWindow]
-	cp 2        ;is TM_menu?
-	jr nz, .putMonName
-	ld a, [wPrize1]
-	ld [wd11e], a
-	call GetItemName
-	coord hl, 2, 4
-	call PlaceString
-	ld a, [wPrize2]
-	ld [wd11e], a
-	call GetItemName
-	coord hl, 2, 6
-	call PlaceString
-	ld a, [wPrize3]
-	ld [wd11e], a
-	call GetItemName
-	coord hl, 2, 8
-	call PlaceString
-	jr .putNoThanksText
-.putMonName
-	ld a, [wPrize1]
-	ld [wd11e], a
-	call GetMonName
-	coord hl, 2, 4
-	call PlaceString
-	ld a, [wPrize2]
-	ld [wd11e], a
-	call GetMonName
-	coord hl, 2, 6
-	call PlaceString
-	ld a, [wPrize3]
-	ld [wd11e], a
-	call GetMonName
-	coord hl, 2, 8
-	call PlaceString
-.putNoThanksText
-	coord hl, 2, 10
-	ld de, NoThanksText
-	call PlaceString
-; put prices on the right side of the textbox
-	ld de, wPrize1Price
-	coord hl, 13, 5
-; reg. c:
-; [low nybble] number of bytes
-; [bit 765 = %100] space-padding (not zero-padding)
-	ld c, (1 << 7 | 2)
-; Function $15CD displays BCD value (same routine
-; used by text-command $02)
-	call PrintBCDNumber
-	ld de, wPrize2Price
-	coord hl, 13, 7
-	ld c, (1 << 7 | 2)
-	call PrintBCDNumber
-	ld de, wPrize3Price
-	coord hl, 13, 9
-	ld c, (1 << 7 | 2)
-	jp PrintBCDNumber
-
-INCLUDE "data/prizes.asm"
-
-PrintPrizePrice:
-	coord hl, 11, 0
-	ld b, 1
-	ld c, 7
-	call TextBoxBorder
-	call UpdateSprites
-	coord hl, 12, 0
-	ld de, .CoinString
-	call PlaceString
-	coord hl, 13, 1
-	ld de, .SixSpacesString
-	call PlaceString
-	coord hl, 13, 1
-	ld de, wPlayerCoins
-	ld c, %10000010
-	call PrintBCDNumber
-	ret
-
-.CoinString:
-	db "COIN@"
-
-.SixSpacesString:
-	db "      @"
-
-LoadCoinsToSubtract:
-	ld a, [wWhichPrize]
-	add a
-	ld d, 0
-	ld e, a
-	ld hl, wPrize1Price
-	add hl, de ; get selected prize's price
-	xor a
-	ld [hUnusedCoinsByte], a
-	ld a, [hli]
-	ld [hCoins], a
-	ld a, [hl]
-	ld [hCoins + 1], a
-	ret
-
-HandlePrizeChoice:
-	ld a, [wCurrentMenuItem]
-	ld [wWhichPrize], a
-	ld d, 0
-	ld e, a
-	ld hl, wPrize1
-	add hl, de
-	ld a, [hl]
-	ld [wd11e], a
-	ld a, [wWhichPrizeWindow]
-	cp 2 ; is prize a TM?
-	jr nz, .getMonName
-	call GetItemName
-	jr .givePrize
-.getMonName
-	call GetMonName
-.givePrize
-	ld hl, SoYouWantPrizeTextPtr
-	call PrintText
-	call YesNoChoice
-	ld a, [wCurrentMenuItem] ; yes/no answer (Y=0, N=1)
-	and a
-	jr nz, .printOhFineThen
-	call LoadCoinsToSubtract
-	call HasEnoughCoins
-	jr c, .notEnoughCoins
-	ld a, [wWhichPrizeWindow]
-	cp $02
-	jr nz, .giveMon
-	ld a, [wd11e]
-	ld b, a
-	ld a, 1
-	ld c, a
-	call GiveItem
-	jr nc, .bagFull
-	jr .subtractCoins
-.giveMon
-	ld a, [wd11e]
-	ld [wcf91], a
-	push af
-	call GetPrizeMonLevel
-	ld c, a
-	pop af
-	ld b, a
-	call GivePokemon
-
-; If either the party or box was full, wait after displaying message.
-	push af
-	ld a, [wAddedToParty]
-	and a
-	call z, WaitForTextScrollButtonPress
-	pop af
-
-; If the mon couldn't be given to the player (because both the party and box
-; were full), return without subtracting coins.
-	ret nc
-
-.subtractCoins
-	call LoadCoinsToSubtract
-	ld hl, hCoins + 1
-	ld de, wPlayerCoins + 1
-	ld c, $02 ; how many bytes
-	predef SubBCDPredef
-	jp PrintPrizePrice
-.bagFull
-	ld hl, PrizeRoomBagIsFullTextPtr
-	jp PrintText
-.notEnoughCoins
-	ld hl, SorryNeedMoreCoinsText
-	jp PrintText
-.printOhFineThen
-	ld hl, OhFineThenTextPtr
-	jp PrintText
-
-UnknownPrizeData:
-; XXX what's this?
-	db $00,$01,$00,$01,$00,$01,$00,$00,$01
-
-HereYouGoTextPtr:
-	TX_FAR _HereYouGoText
-	TX_WAIT
-	db "@"
-
-SoYouWantPrizeTextPtr:
-	TX_FAR _SoYouWantPrizeText
-	db "@"
-
-SorryNeedMoreCoinsText:
-	TX_FAR _SorryNeedMoreCoinsText
-	TX_WAIT
-	db "@"
-
-PrizeRoomBagIsFullTextPtr:
-	TX_FAR _OopsYouDontHaveEnoughRoomText
-	TX_WAIT
-	db "@"
-
-OhFineThenTextPtr:
-	TX_FAR _OhFineThenText
-	TX_WAIT
-	db "@"
-
-GetPrizeMonLevel:
-	ld a, [wcf91]
-	ld b, a
-	ld hl, PrizeMonLevelDictionary
-.loop
-	ld a, [hli]
-	cp b
-	jr z, .matchFound
-	inc hl
-	jr .loop
-.matchFound
-	ld a, [hl]
-	ld [wCurEnemyLVL], a
-	ret
-
-INCLUDE "data/prize_mon_levels.asm"
--- a/engine/menu/start_menu.asm
+++ /dev/null
@@ -1,85 +1,0 @@
-DisplayStartMenu::
-	ld a, BANK(StartMenu_Pokedex)
-	ld [H_LOADEDROMBANK], a
-	ld [MBC1RomBank], a
-	ld a, [wWalkBikeSurfState] ; walking/biking/surfing
-	ld [wWalkBikeSurfStateCopy], a
-	ld a, SFX_START_MENU
-	call PlaySound
-
-RedisplayStartMenu::
-	callba DrawStartMenu
-	callba PrintSafariZoneSteps ; print Safari Zone info, if in Safari Zone
-	call UpdateSprites
-.loop
-	call HandleMenuInput
-	ld b, a
-.checkIfUpPressed
-	bit 6, a ; was Up pressed?
-	jr z, .checkIfDownPressed
-	ld a, [wCurrentMenuItem] ; menu selection
-	and a
-	jr nz, .loop
-	ld a, [wLastMenuItem]
-	and a
-	jr nz, .loop
-; if the player pressed tried to go past the top item, wrap around to the bottom
-	CheckEvent EVENT_GOT_POKEDEX
-	ld a, 6 ; there are 7 menu items with the pokedex, so the max index is 6
-	jr nz, .wrapMenuItemId
-	dec a ; there are only 6 menu items without the pokedex
-.wrapMenuItemId
-	ld [wCurrentMenuItem], a
-	call EraseMenuCursor
-	jr .loop
-.checkIfDownPressed
-	bit 7, a
-	jr z, .buttonPressed
-; if the player pressed tried to go past the bottom item, wrap around to the top
-	CheckEvent EVENT_GOT_POKEDEX
-	ld a, [wCurrentMenuItem]
-	ld c, 7 ; there are 7 menu items with the pokedex
-	jr nz, .checkIfPastBottom
-	dec c ; there are only 6 menu items without the pokedex
-.checkIfPastBottom
-	cp c
-	jr nz, .loop
-; the player went past the bottom, so wrap to the top
-	xor a
-	ld [wCurrentMenuItem], a
-	call EraseMenuCursor
-	jr .loop
-.buttonPressed ; A, B, or Start button pressed
-	call PlaceUnfilledArrowMenuCursor
-	ld a, [wCurrentMenuItem]
-	ld [wBattleAndStartSavedMenuItem], a ; save current menu selection
-	ld a, b
-	and %00001010 ; was the Start button or B button pressed?
-	jp nz, CloseStartMenu
-	call SaveScreenTilesToBuffer2 ; copy background from wTileMap to wTileMapBackup2
-	CheckEvent EVENT_GOT_POKEDEX
-	ld a, [wCurrentMenuItem]
-	jr nz, .displayMenuItem
-	inc a ; adjust position to account for missing pokedex menu item
-.displayMenuItem
-	cp 0
-	jp z, StartMenu_Pokedex
-	cp 1
-	jp z, StartMenu_Pokemon
-	cp 2
-	jp z, StartMenu_Item
-	cp 3
-	jp z, StartMenu_TrainerInfo
-	cp 4
-	jp z, StartMenu_SaveReset
-	cp 5
-	jp z, StartMenu_Option
-
-; EXIT falls through to here
-CloseStartMenu::
-	call Joypad
-	ld a, [hJoyPressed]
-	bit 0, a ; was A button newly pressed?
-	jr nz, CloseStartMenu
-	call LoadTextBoxTilePatterns
-	jp CloseTextDisplay
--- a/engine/menu/start_sub_menus.asm
+++ /dev/null
@@ -1,808 +1,0 @@
-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
--- a/engine/menu/status_screen.asm
+++ /dev/null
@@ -1,481 +1,0 @@
-DrawHP:
-; Draws the HP bar in the stats screen
-	call GetPredefRegisters
-	ld a, $1
-	jr DrawHP_
-
-DrawHP2:
-; Draws the HP bar in the party screen
-	call GetPredefRegisters
-	ld a, $2
-
-DrawHP_:
-	ld [wHPBarType], a
-	push hl
-	ld a, [wLoadedMonHP]
-	ld b, a
-	ld a, [wLoadedMonHP + 1]
-	ld c, a
-	or b
-	jr nz, .nonzeroHP
-	xor a
-	ld c, a
-	ld e, a
-	ld a, $6
-	ld d, a
-	jp .drawHPBarAndPrintFraction
-.nonzeroHP
-	ld a, [wLoadedMonMaxHP]
-	ld d, a
-	ld a, [wLoadedMonMaxHP + 1]
-	ld e, a
-	predef HPBarLength
-	ld a, $6
-	ld d, a
-	ld c, a
-.drawHPBarAndPrintFraction
-	pop hl
-	push de
-	push hl
-	push hl
-	call DrawHPBar
-	pop hl
-	ld a, [hFlags_0xFFF6]
-	bit 0, a
-	jr z, .printFractionBelowBar
-	ld bc, $9 ; right of bar
-	jr .printFraction
-.printFractionBelowBar
-	ld bc, SCREEN_WIDTH + 1 ; below bar
-.printFraction
-	add hl, bc
-	ld de, wLoadedMonHP
-	lb bc, 2, 3
-	call PrintNumber
-	ld a, "/"
-	ld [hli], a
-	ld de, wLoadedMonMaxHP
-	lb bc, 2, 3
-	call PrintNumber
-	pop hl
-	pop de
-	ret
-
-
-; Predef 0x37
-StatusScreen:
-	call LoadMonData
-	ld a, [wMonDataLocation]
-	cp BOX_DATA
-	jr c, .DontRecalculate
-; mon is in a box or daycare
-	ld a, [wLoadedMonBoxLevel]
-	ld [wLoadedMonLevel], a
-	ld [wCurEnemyLVL], a
-	ld hl, wLoadedMonHPExp - 1
-	ld de, wLoadedMonStats
-	ld b, $1
-	call CalcStats ; Recalculate stats
-.DontRecalculate
-	ld hl, wd72c
-	set 1, [hl]
-	ld a, $33
-	ld [rNR50], a ; Reduce the volume
-	call GBPalWhiteOutWithDelay3
-	call ClearScreen
-	call UpdateSprites
-	call LoadHpBarAndStatusTilePatterns
-	ld de, BattleHudTiles1  ; source
-	ld hl, vChars2 + $6d0 ; dest
-	lb bc, BANK(BattleHudTiles1), $03
-	call CopyVideoDataDouble ; ·│ :L and halfarrow line end
-	ld de, BattleHudTiles2
-	ld hl, vChars2 + $780
-	lb bc, BANK(BattleHudTiles2), $01
-	call CopyVideoDataDouble ; │
-	ld de, BattleHudTiles3
-	ld hl, vChars2 + $760
-	lb bc, BANK(BattleHudTiles3), $02
-	call CopyVideoDataDouble ; ─┘
-	ld de, PTile
-	ld hl, vChars2 + $720
-	lb bc, BANK(PTile), (PTileEnd - PTile) / $8
-	call CopyVideoDataDouble ; P (for PP), inline
-	ld a, [hTilesetType]
-	push af
-	xor a
-	ld [hTilesetType], a
-	coord hl, 19, 1
-	lb bc, 6, 10
-	call DrawLineBox ; Draws the box around name, HP and status
-	ld de, -6
-	add hl, de
-	ld [hl], "⠄" ; . after No ("." is a different one)
-	dec hl
-	ld [hl], "№"
-	coord hl, 19, 9
-	lb bc, 8, 6
-	call DrawLineBox ; Draws the box around types, ID No. and OT
-	coord hl, 10, 9
-	ld de, Type1Text
-	call PlaceString ; "TYPE1/"
-	coord hl, 11, 3
-	predef DrawHP
-	ld hl, wStatusScreenHPBarColor
-	call GetHealthBarColor
-	ld b, SET_PAL_STATUS_SCREEN
-	call RunPaletteCommand
-	coord hl, 16, 6
-	ld de, wLoadedMonStatus
-	call PrintStatusCondition
-	jr nz, .StatusWritten
-	coord hl, 16, 6
-	ld de, OKText
-	call PlaceString ; "OK"
-.StatusWritten
-	coord hl, 9, 6
-	ld de, StatusText
-	call PlaceString ; "STATUS/"
-	coord hl, 14, 2
-	call PrintLevel ; Pokémon level
-	ld a, [wMonHIndex]
-	ld [wd11e], a
-	ld [wd0b5], a
-	predef IndexToPokedex
-	coord hl, 3, 7
-	ld de, wd11e
-	lb bc, LEADING_ZEROES | 1, 3
-	call PrintNumber ; Pokémon no.
-	coord hl, 11, 10
-	predef PrintMonType
-	ld hl, NamePointers2
-	call .GetStringPointer
-	ld d, h
-	ld e, l
-	coord hl, 9, 1
-	call PlaceString ; Pokémon name
-	ld hl, OTPointers
-	call .GetStringPointer
-	ld d, h
-	ld e, l
-	coord hl, 12, 16
-	call PlaceString ; OT
-	coord hl, 12, 14
-	ld de, wLoadedMonOTID
-	lb bc, LEADING_ZEROES | 2, 5
-	call PrintNumber ; ID Number
-	ld d, $0
-	call PrintStatsBox
-	call Delay3
-	call GBPalNormal
-	coord hl, 1, 0
-	call LoadFlippedFrontSpriteByMonIndex ; draw Pokémon picture
-	ld a, [wcf91]
-	call PlayCry ; play Pokémon cry
-	call WaitForTextScrollButtonPress ; wait for button
-	pop af
-	ld [hTilesetType], a
-	ret
-
-.GetStringPointer
-	ld a, [wMonDataLocation]
-	add a
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld a, [wMonDataLocation]
-	cp DAYCARE_DATA
-	ret z
-	ld a, [wWhichPokemon]
-	jp SkipFixedLengthTextEntries
-
-OTPointers:
-	dw wPartyMonOT
-	dw wEnemyMonOT
-	dw wBoxMonOT
-	dw wDayCareMonOT
-
-NamePointers2:
-	dw wPartyMonNicks
-	dw wEnemyMonNicks
-	dw wBoxMonNicks
-	dw wDayCareMonName
-
-Type1Text:
-	db "TYPE1/", $4e
-
-Type2Text:
-	db "TYPE2/", $4e
-
-IDNoText:
-	db $73, "№/", $4e
-
-OTText:
-	db   "OT/"
-	next "@"
-
-StatusText:
-	db "STATUS/@"
-
-OKText:
-	db "OK@"
-
-; Draws a line starting from hl high b and wide c
-DrawLineBox:
-	ld de, SCREEN_WIDTH ; New line
-.PrintVerticalLine
-	ld [hl], $78 ; │
-	add hl, de
-	dec b
-	jr nz, .PrintVerticalLine
-	ld [hl], $77 ; ┘
-	dec hl
-.PrintHorizLine
-	ld [hl], $76 ; ─
-	dec hl
-	dec c
-	jr nz, .PrintHorizLine
-	ld [hl], $6f ; ← (halfarrow ending)
-	ret
-
-PTile:
-	INCBIN "gfx/font/P.1bpp"
-PTileEnd:
-
-PrintStatsBox:
-	ld a, d
-	and a ; a is 0 from the status screen
-	jr nz, .DifferentBox
-	coord hl, 0, 8
-	ld b, 8
-	ld c, 8
-	call TextBoxBorder ; Draws the box
-	coord hl, 1, 9 ; Start printing stats from here
-	ld bc, $0019 ; Number offset
-	jr .PrintStats
-.DifferentBox
-	coord hl, 9, 2
-	ld b, 8
-	ld c, 9
-	call TextBoxBorder
-	coord hl, 11, 3
-	ld bc, $0018
-.PrintStats
-	push bc
-	push hl
-	ld de, StatsText
-	call PlaceString
-	pop hl
-	pop bc
-	add hl, bc
-	ld de, wLoadedMonAttack
-	lb bc, 2, 3
-	call PrintStat
-	ld de, wLoadedMonDefense
-	call PrintStat
-	ld de, wLoadedMonSpeed
-	call PrintStat
-	ld de, wLoadedMonSpecial
-	jp PrintNumber
-PrintStat:
-	push hl
-	call PrintNumber
-	pop hl
-	ld de, SCREEN_WIDTH * 2
-	add hl, de
-	ret
-
-StatsText:
-	db   "ATTACK"
-	next "DEFENSE"
-	next "SPEED"
-	next "SPECIAL@"
-
-StatusScreen2:
-	ld a, [hTilesetType]
-	push af
-	xor a
-	ld [hTilesetType], a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld bc, NUM_MOVES + 1
-	ld hl, wMoves
-	call FillMemory
-	ld hl, wLoadedMonMoves
-	ld de, wMoves
-	ld bc, NUM_MOVES
-	call CopyData
-	callab FormatMovesString
-	coord hl, 9, 2
-	lb bc, 5, 10
-	call ClearScreenArea ; Clear under name
-	coord hl, 19, 3
-	ld [hl], $78
-	coord hl, 0, 8
-	ld b, 8
-	ld c, 18
-	call TextBoxBorder ; Draw move container
-	coord hl, 2, 9
-	ld de, wMovesString
-	call PlaceString ; Print moves
-	ld a, [wNumMovesMinusOne]
-	inc a
-	ld c, a
-	ld a, $4
-	sub c
-	ld b, a ; Number of moves ?
-	coord hl, 11, 10
-	ld de, SCREEN_WIDTH * 2
-	ld a, $72 ; special P tile id
-	call StatusScreen_PrintPP ; Print "PP"
-	ld a, b
-	and a
-	jr z, .InitPP
-	ld c, a
-	ld a, "-"
-	call StatusScreen_PrintPP ; Fill the rest with --
-.InitPP
-	ld hl, wLoadedMonMoves
-	coord de, 14, 10
-	ld b, 0
-.PrintPP
-	ld a, [hli]
-	and a
-	jr z, .PPDone
-	push bc
-	push hl
-	push de
-	ld hl, wCurrentMenuItem
-	ld a, [hl]
-	push af
-	ld a, b
-	ld [hl], a
-	push hl
-	callab GetMaxPP
-	pop hl
-	pop af
-	ld [hl], a
-	pop de
-	pop hl
-	push hl
-	ld bc, wPartyMon1PP - wPartyMon1Moves - 1
-	add hl, bc
-	ld a, [hl]
-	and $3f
-	ld [wStatusScreenCurrentPP], a
-	ld h, d
-	ld l, e
-	push hl
-	ld de, wStatusScreenCurrentPP
-	lb bc, 1, 2
-	call PrintNumber
-	ld a, "/"
-	ld [hli], a
-	ld de, wMaxPP
-	lb bc, 1, 2
-	call PrintNumber
-	pop hl
-	ld de, SCREEN_WIDTH * 2
-	add hl, de
-	ld d, h
-	ld e, l
-	pop hl
-	pop bc
-	inc b
-	ld a, b
-	cp $4
-	jr nz, .PrintPP
-.PPDone
-	coord hl, 9, 3
-	ld de, StatusScreenExpText
-	call PlaceString
-	ld a, [wLoadedMonLevel]
-	push af
-	cp MAX_LEVEL
-	jr z, .Level100
-	inc a
-	ld [wLoadedMonLevel], a ; Increase temporarily if not 100
-.Level100
-	coord hl, 14, 6
-	ld [hl], $70 ; 1-tile "to"
-	inc hl
-	inc hl
-	call PrintLevel
-	pop af
-	ld [wLoadedMonLevel], a
-	ld de, wLoadedMonExp
-	coord hl, 12, 4
-	lb bc, 3, 7
-	call PrintNumber ; exp
-	call CalcExpToLevelUp
-	ld de, wLoadedMonExp
-	coord hl, 7, 6
-	lb bc, 3, 7
-	call PrintNumber ; exp needed to level up
-	coord hl, 9, 0
-	call StatusScreen_ClearName
-	coord hl, 9, 1
-	call StatusScreen_ClearName
-	ld a, [wMonHIndex]
-	ld [wd11e], a
-	call GetMonName
-	coord hl, 9, 1
-	call PlaceString
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Delay3
-	call WaitForTextScrollButtonPress ; wait for button
-	pop af
-	ld [hTilesetType], a
-	ld hl, wd72c
-	res 1, [hl]
-	ld a, $77
-	ld [rNR50], a
-	call GBPalWhiteOut
-	jp ClearScreen
-
-CalcExpToLevelUp:
-	ld a, [wLoadedMonLevel]
-	cp MAX_LEVEL
-	jr z, .atMaxLevel
-	inc a
-	ld d, a
-	callab CalcExperience
-	ld hl, wLoadedMonExp + 2
-	ld a, [hExperience + 2]
-	sub [hl]
-	ld [hld], a
-	ld a, [hExperience + 1]
-	sbc [hl]
-	ld [hld], a
-	ld a, [hExperience]
-	sbc [hl]
-	ld [hld], a
-	ret
-.atMaxLevel
-	ld hl, wLoadedMonExp
-	xor a
-	ld [hli], a
-	ld [hli], a
-	ld [hl], a
-	ret
-
-StatusScreenExpText:
-	db   "EXP POINTS"
-	next "LEVEL UP@"
-
-StatusScreen_ClearName:
-	ld bc, 10
-	ld a, " "
-	jp FillMemory
-
-StatusScreen_PrintPP:
-; print PP or -- c times, going down two rows each time
-	ld [hli], a
-	ld [hld], a
-	add hl, de
-	dec c
-	jr nz, StatusScreen_PrintPP
-	ret
--- a/engine/menu/swap_items.asm
+++ /dev/null
@@ -1,149 +1,0 @@
-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
--- a/engine/menu/text_box.asm
+++ /dev/null
@@ -1,767 +1,0 @@
-; 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
--- a/engine/menu/vending_machine.asm
+++ /dev/null
@@ -1,133 +1,0 @@
-VendingMachineMenu::
-	ld hl, VendingMachineText1
-	call PrintText
-	ld a, MONEY_BOX
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 3
-	ld [wMaxMenuItem], a
-	ld a, 5
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-	ld hl, wd730
-	set 6, [hl]
-	coord hl, 0, 3
-	ld b, 8
-	ld c, 12
-	call TextBoxBorder
-	call UpdateSprites
-	coord hl, 2, 5
-	ld de, DrinkText
-	call PlaceString
-	coord hl, 9, 6
-	ld de, DrinkPriceText
-	call PlaceString
-	ld hl, wd730
-	res 6, [hl]
-	call HandleMenuInput
-	bit 1, a ; pressed B?
-	jr nz, .notThirsty
-	ld a, [wCurrentMenuItem]
-	cp 3 ; chose Cancel?
-	jr z, .notThirsty
-	xor a
-	ld [hMoney], a
-	ld [hMoney + 2], a
-	ld a, $2
-	ld [hMoney + 1], a
-	call HasEnoughMoney
-	jr nc, .enoughMoney
-	ld hl, VendingMachineText4
-	jp PrintText
-.enoughMoney
-	call LoadVendingMachineItem
-	ld a, [hVendingMachineItem]
-	ld b, a
-	ld c, 1
-	call GiveItem
-	jr nc, .BagFull
-
-	ld b, 60 ; number of times to play the "brrrrr" sound
-.playDeliverySound
-	ld c, 2
-	call DelayFrames
-	push bc
-	ld a, SFX_PUSH_BOULDER
-	call PlaySound
-	pop bc
-	dec b
-	jr nz, .playDeliverySound
-
-	ld hl, VendingMachineText5
-	call PrintText
-	ld hl, hVendingMachinePrice + 2
-	ld de, wPlayerMoney + 2
-	ld c, $3
-	predef SubBCDPredef
-	ld a, MONEY_BOX
-	ld [wTextBoxID], a
-	jp DisplayTextBoxID
-.BagFull
-	ld hl, VendingMachineText6
-	jp PrintText
-.notThirsty
-	ld hl, VendingMachineText7
-	jp PrintText
-
-VendingMachineText1:
-	TX_FAR _VendingMachineText1
-	db "@"
-
-DrinkText:
-	db   "FRESH WATER"
-	next "SODA POP"
-	next "LEMONADE"
-	next "CANCEL@"
-
-DrinkPriceText:
-	db   "¥200"
-	next "¥300"
-	next "¥350"
-	next "@"
-
-VendingMachineText4:
-	TX_FAR _VendingMachineText4
-	db "@"
-
-VendingMachineText5:
-	TX_FAR _VendingMachineText5
-	db "@"
-
-VendingMachineText6:
-	TX_FAR _VendingMachineText6
-	db "@"
-
-VendingMachineText7:
-	TX_FAR _VendingMachineText7
-	db "@"
-
-LoadVendingMachineItem:
-	ld hl, VendingPrices
-	ld a, [wCurrentMenuItem]
-	add a
-	add a
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hli]
-	ld [hVendingMachineItem], a
-	ld a, [hli]
-	ld [hVendingMachinePrice], a
-	ld a, [hli]
-	ld [hVendingMachinePrice + 1], a
-	ld a, [hl]
-	ld [hVendingMachinePrice + 2], a
-	ret
-
-INCLUDE "data/vending_prices.asm"
--- /dev/null
+++ b/engine/menus/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 $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
--- /dev/null
+++ b/engine/menus/draw_badges.asm
@@ -1,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"
--- /dev/null
+++ b/engine/menus/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 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
--- /dev/null
+++ b/engine/menus/league_pc.asm
@@ -1,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 "@"
--- /dev/null
+++ b/engine/menus/main_menu.asm
@@ -1,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
--- /dev/null
+++ b/engine/menus/naming_screen.asm
@@ -1,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?@"
--- /dev/null
+++ b/engine/menus/oaks_pc.asm
@@ -1,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 "@"
--- /dev/null
+++ b/engine/menus/party_menu.asm
@@ -1,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
--- /dev/null
+++ b/engine/menus/pc.asm
@@ -1,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
--- /dev/null
+++ b/engine/menus/players_pc.asm
@@ -1,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 "@"
--- /dev/null
+++ b/engine/menus/pokedex.asm
@@ -1,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"
--- /dev/null
+++ b/engine/menus/save.asm
@@ -1,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
--- /dev/null
+++ b/engine/menus/start_sub_menus.asm
@@ -1,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
--- /dev/null
+++ b/engine/menus/swap_items.asm
@@ -1,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
--- /dev/null
+++ b/engine/menus/text_box.asm
@@ -1,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
--- a/engine/mon_party_sprites.asm
+++ /dev/null
@@ -1,295 +1,0 @@
-AnimatePartyMon_ForceSpeed1:
-	xor a
-	ld [wCurrentMenuItem], a
-	ld b, a
-	inc a
-	jr GetAnimationSpeed
-
-; wPartyMenuHPBarColors contains the party mon's health bar colors
-; 0: green
-; 1: yellow
-; 2: red
-AnimatePartyMon::
-	ld hl, wPartyMenuHPBarColors
-	ld a, [wCurrentMenuItem]
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hl]
-
-GetAnimationSpeed:
-	ld c, a
-	ld hl, PartyMonSpeeds
-	add hl, bc
-	ld a, [wOnSGB]
-	xor $1
-	add [hl]
-	ld c, a
-	add a
-	ld b, a
-	ld a, [wAnimCounter]
-	and a
-	jr z, .resetSprites
-	cp c
-	jr z, .animateSprite
-.incTimer
-	inc a
-	cp b
-	jr nz, .skipResetTimer
-	xor a ; reset timer
-.skipResetTimer
-	ld [wAnimCounter], a
-	jp DelayFrame
-.resetSprites
-	push bc
-	ld hl, wMonPartySpritesSavedOAM
-	ld de, wOAMBuffer
-	ld bc, $60
-	call CopyData
-	pop bc
-	xor a
-	jr .incTimer
-.animateSprite
-	push bc
-	ld hl, wOAMBuffer + $02 ; OAM tile id
-	ld bc, $10
-	ld a, [wCurrentMenuItem]
-	call AddNTimes
-	ld c, $40 ; amount to increase the tile id by
-	ld a, [hl]
-	cp $4 ; tile ID for ICON_BALL
-	jr z, .editCoords
-	cp $8 ; tile ID for ICON_HELIX
-	jr nz, .editTileIDS
-; ICON_BALL and ICON_HELIX only shake up and down
-.editCoords
-	dec hl
-	dec hl ; dec hl to the OAM y coord
-	ld c, $1 ; amount to increase the y coord by
-; otherwise, load a second sprite frame
-.editTileIDS
-	ld b, $4
-	ld de, $4
-.loop
-	ld a, [hl]
-	add c
-	ld [hl], a
-	add hl, de
-	dec b
-	jr nz, .loop
-	pop bc
-	ld a, c
-	jr .incTimer
-
-; Party mon animations cycle between 2 frames.
-; The members of the PartyMonSpeeds array specify the number of V-blanks
-; that each frame lasts for green HP, yellow HP, and red HP in order.
-; On the naming screen, the yellow HP speed is always used.
-PartyMonSpeeds:
-	db 5, 16, 32
-
-LoadMonPartySpriteGfx:
-; Load mon party sprite tile patterns into VRAM during V-blank.
-	ld hl, MonPartySpritePointers
-	ld a, $1c
-
-LoadAnimSpriteGfx:
-; Load animated sprite tile patterns into VRAM during V-blank. hl is the address
-; of an array of structures that contain arguments for CopyVideoData and a is
-; the number of structures in the array.
-	ld bc, $0
-.loop
-	push af
-	push bc
-	push hl
-	add hl, bc
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	ld a, [hli]
-	ld c, a
-	ld a, [hli]
-	ld b, a
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	call CopyVideoData
-	pop hl
-	pop bc
-	ld a, $6
-	add c
-	ld c, a
-	pop af
-	dec a
-	jr nz, .loop
-	ret
-
-LoadMonPartySpriteGfxWithLCDDisabled:
-; Load mon party sprite tile patterns into VRAM immediately by disabling the
-; LCD.
-	call DisableLCD
-	ld hl, MonPartySpritePointers
-	ld a, $1c
-	ld bc, $0
-.loop
-	push af
-	push bc
-	push hl
-	add hl, bc
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	push de
-	ld a, [hli]
-	ld c, a
-	swap c
-	ld b, $0
-	ld a, [hli]
-	ld e, [hl]
-	inc hl
-	ld d, [hl]
-	pop hl
-	call FarCopyData2
-	pop hl
-	pop bc
-	ld a, $6
-	add c
-	ld c, a
-	pop af
-	dec a
-	jr nz, .loop
-	jp EnableLCD
-
-INCLUDE "data/mon_party_sprite_pointers.asm"
-
-WriteMonPartySpriteOAMByPartyIndex:
-; Write OAM blocks for the party mon in [hPartyMonIndex].
-	push hl
-	push de
-	push bc
-	ld a, [hPartyMonIndex]
-	ld hl, wPartySpecies
-	ld e, a
-	ld d, 0
-	add hl, de
-	ld a, [hl]
-	call GetPartyMonSpriteID
-	ld [wOAMBaseTile], a
-	call WriteMonPartySpriteOAM
-	pop bc
-	pop de
-	pop hl
-	ret
-
-WriteMonPartySpriteOAMBySpecies:
-; Write OAM blocks for the party sprite of the species in
-; [wMonPartySpriteSpecies].
-	xor a
-	ld [hPartyMonIndex], a
-	ld a, [wMonPartySpriteSpecies]
-	call GetPartyMonSpriteID
-	ld [wOAMBaseTile], a
-	jr WriteMonPartySpriteOAM
-
-UnusedPartyMonSpriteFunction:
-; This function is unused and doesn't appear to do anything useful. It looks
-; like it may have been intended to load the tile patterns and OAM data for
-; the mon party sprite associated with the species in [wcf91].
-; However, its calculations are off and it loads garbage data.
-	ld a, [wcf91]
-	call GetPartyMonSpriteID
-	push af
-	ld hl, vSprites
-	call .LoadTilePatterns
-	pop af
-	add $54
-	ld hl, vSprites + $40
-	call .LoadTilePatterns
-	xor a
-	ld [wMonPartySpriteSpecies], a
-	jr WriteMonPartySpriteOAMBySpecies
-
-.LoadTilePatterns
-	push hl
-	add a
-	ld c, a
-	ld b, 0
-	ld hl, MonPartySpritePointers
-	add hl, bc
-	add hl, bc
-	add hl, bc
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	ld a, [hli]
-	ld c, a
-	ld a, [hli]
-	ld b, a
-	pop hl
-	jp CopyVideoData
-
-WriteMonPartySpriteOAM:
-; Write the OAM blocks for the first animation frame into the OAM buffer and
-; make a copy at wMonPartySpritesSavedOAM.
-	push af
-	ld c, $10
-	ld h, wOAMBuffer / $100
-	ld a, [hPartyMonIndex]
-	swap a
-	ld l, a
-	add $10
-	ld b, a
-	pop af
-	cp ICON_HELIX << 2
-	jr z, .helix
-	call WriteSymmetricMonPartySpriteOAM
-	jr .makeCopy
-.helix
-	call WriteAsymmetricMonPartySpriteOAM
-; Make a copy of the OAM buffer with the first animation frame written so that
-; we can flip back to it from the second frame by copying it back.
-.makeCopy
-	ld hl, wOAMBuffer
-	ld de, wMonPartySpritesSavedOAM
-	ld bc, $60
-	jp CopyData
-
-GetPartyMonSpriteID:
-	ld [wd11e], a
-	predef IndexToPokedex
-	ld a, [wd11e]
-	ld c, a
-	dec a
-	srl a
-	ld hl, MonPartyData
-	ld e, a
-	ld d, 0
-	add hl, de
-	ld a, [hl]
-	bit 0, c
-	jr nz, .skipSwap
-	swap a ; use lower nybble if pokedex num is even
-.skipSwap
-	and $f0
-	srl a
-	srl a
-	ret
-
-INCLUDE "data/mon_party_sprites.asm"
-
-INC_FRAME_1 EQUS "0, $20"
-INC_FRAME_2 EQUS "$20, $20"
-
-BugIconFrame1:       INCBIN "gfx/icons/bug.2bpp", INC_FRAME_1
-PlantIconFrame1:     INCBIN "gfx/icons/plant.2bpp", INC_FRAME_1
-BugIconFrame2:       INCBIN "gfx/icons/bug.2bpp", INC_FRAME_2
-PlantIconFrame2:     INCBIN "gfx/icons/plant.2bpp", INC_FRAME_2
-SnakeIconFrame1:     INCBIN "gfx/icons/snake.2bpp", INC_FRAME_1
-QuadrupedIconFrame1: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_1
-SnakeIconFrame2:     INCBIN "gfx/icons/snake.2bpp", INC_FRAME_2
-QuadrupedIconFrame2: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_2
-
-TradeBubbleIconGFX:  INCBIN "gfx/trade/bubble.2bpp"
--- /dev/null
+++ b/engine/movie/credits.asm
@@ -1,0 +1,270 @@
+HallOfFamePC:
+	callba AnimateHallOfFame
+	call ClearScreen
+	ld c, 100
+	call DelayFrames
+	call DisableLCD
+	ld hl, vFont
+	ld bc, $800 / 2
+	call ZeroMemory
+	ld hl, vChars2 + $600
+	ld bc, $200 / 2
+	call ZeroMemory
+	ld hl, vChars2 + $7e0
+	ld bc, $10
+	ld a, $ff
+	call FillMemory
+	coord hl, 0, 0
+	call FillFourRowsWithBlack
+	coord hl, 0, 14
+	call FillFourRowsWithBlack
+	ld a, %11000000
+	ld [rBGP], a
+	call EnableLCD
+	ld a, $ff
+	call PlaySoundWaitForCurrent
+	ld c, BANK(Music_Credits)
+	ld a, MUSIC_CREDITS
+	call PlayMusic
+	ld c, 128
+	call DelayFrames
+	xor a
+	ld [wUnusedCD3D], a ; not read
+	ld [wNumCreditsMonsDisplayed], a
+	jp Credits
+
+FadeInCreditsText:
+	ld hl, HoFGBPalettes
+	ld b, 4
+.loop
+	ld a, [hli]
+	ld [rBGP], a
+	ld c, 5
+	call DelayFrames
+	dec b
+	jr nz, .loop
+	ret
+
+DisplayCreditsMon:
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call SaveScreenTilesToBuffer1
+	call FillMiddleOfScreenWithWhite
+
+	; display the next monster from CreditsMons
+	ld hl, wNumCreditsMonsDisplayed
+	ld c, [hl] ; how many monsters have we displayed so far?
+	inc [hl]
+	ld b, 0
+	ld hl, CreditsMons
+	add hl, bc ; go that far in the list of monsters and get the next one
+	ld a, [hl]
+	ld [wcf91], a
+	ld [wd0b5], a
+	coord hl, 8, 6
+	call GetMonHeader
+	call LoadFrontSpriteByMonIndex
+	ld hl, vBGMap0 + $c
+	call CreditsCopyTileMapToVRAM
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call LoadScreenTilesFromBuffer1
+	ld hl, vBGMap0
+	call CreditsCopyTileMapToVRAM
+	ld a, $A7
+	ld [rWX], a
+	ld hl, vBGMap1
+	call CreditsCopyTileMapToVRAM
+	call FillMiddleOfScreenWithWhite
+	ld a, %11111100 ; make the mon a black silhouette
+	ld [rBGP], a
+
+; scroll the mon left by one tile 7 times
+	ld bc, 7
+.scrollLoop1
+	call ScrollCreditsMonLeft
+	dec c
+	jr nz, .scrollLoop1
+
+; scroll the mon left by one tile 20 times
+; This time, we have to move the window left too in order to hide the text that
+; is wrapping around to the right side of the screen.
+	ld c, 20
+.scrollLoop2
+	call ScrollCreditsMonLeft
+	ld a, [rWX]
+	sub 8
+	ld [rWX], a
+	dec c
+	jr nz, .scrollLoop2
+
+	xor a
+	ld [hWY], a
+	ld a, %11000000
+	ld [rBGP], a
+	ret
+
+INCLUDE "data/credit_mons.asm"
+
+ScrollCreditsMonLeft:
+	ld h, b
+	ld l, $20
+	call ScrollCreditsMonLeft_SetSCX
+	ld h, $0
+	ld l, $70
+	call ScrollCreditsMonLeft_SetSCX
+	ld a, b
+	add $8
+	ld b, a
+	ret
+
+ScrollCreditsMonLeft_SetSCX:
+	ld a, [rLY]
+	cp l
+	jr nz, ScrollCreditsMonLeft_SetSCX
+	ld a, h
+	ld [rSCX], a
+.loop
+	ld a, [rLY]
+	cp h
+	jr z, .loop
+	ret
+
+HoFGBPalettes:
+	db %11000000
+	db %11010000
+	db %11100000
+	db %11110000
+
+CreditsCopyTileMapToVRAM:
+	ld a, l
+	ld [H_AUTOBGTRANSFERDEST], a
+	ld a, h
+	ld [H_AUTOBGTRANSFERDEST + 1], a
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	jp Delay3
+
+ZeroMemory:
+; zero bc bytes at hl
+	ld [hl], 0
+	inc hl
+	inc hl
+	dec bc
+	ld a, b
+	or c
+	jr nz, ZeroMemory
+	ret
+
+FillFourRowsWithBlack:
+	ld bc, SCREEN_WIDTH * 4
+	ld a, $7e
+	jp FillMemory
+
+FillMiddleOfScreenWithWhite:
+	coord hl, 0, 4
+	ld bc, SCREEN_WIDTH * 10
+	ld a, " "
+	jp FillMemory
+
+Credits:
+	ld de, CreditsOrder
+	push de
+.nextCreditsScreen
+	pop de
+	coord hl, 9, 6
+	push hl
+	call FillMiddleOfScreenWithWhite
+	pop hl
+.nextCreditsCommand
+	ld a, [de]
+	inc de
+	push de
+	cp $ff
+	jr z, .fadeInTextAndShowMon
+	cp $fe
+	jr z, .showTextAndShowMon
+	cp $fd
+	jr z, .fadeInText
+	cp $fc
+	jr z, .showText
+	cp $fb
+	jr z, .showCopyrightText
+	cp $fa
+	jr z, .showTheEnd
+	push hl
+	push hl
+	ld hl, CreditsTextPointers
+	add a
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld e, [hl]
+	inc hl
+	ld d, [hl]
+	ld a, [de]
+	inc de
+	ld c, a
+	ld b, $ff
+	pop hl
+	add hl, bc
+	call PlaceString
+	pop hl
+	ld bc, SCREEN_WIDTH * 2
+	add hl, bc
+	pop de
+	jr .nextCreditsCommand
+.fadeInTextAndShowMon
+	call FadeInCreditsText
+	ld c, 90
+	jr .next1
+.showTextAndShowMon
+	ld c, 110
+.next1
+	call DelayFrames
+	call DisplayCreditsMon
+	jr .nextCreditsScreen
+.fadeInText
+	call FadeInCreditsText
+	ld c, 120
+	jr .next2
+.showText
+	ld c, 140
+.next2
+	call DelayFrames
+	jr .nextCreditsScreen
+.showCopyrightText
+	push de
+	callba LoadCopyrightTiles
+	pop de
+	pop de
+	jr .nextCreditsCommand
+.showTheEnd
+	ld c, 16
+	call DelayFrames
+	call FillMiddleOfScreenWithWhite
+	pop de
+	ld de, TheEndGfx
+	ld hl, vChars2 + $600
+	lb bc, BANK(TheEndGfx), (TheEndGfxEnd - TheEndGfx) / $10
+	call CopyVideoData
+	coord hl, 4, 8
+	ld de, TheEndTextString
+	call PlaceString
+	coord hl, 4, 9
+	inc de
+	call PlaceString
+	jp FadeInCreditsText
+
+TheEndTextString:
+; "T H E  E N D"
+	db $60," ",$62," ",$64,"  ",$64," ",$66," ",$68,"@"
+	db $61," ",$63," ",$65,"  ",$65," ",$67," ",$69,"@"
+
+INCLUDE "data/credits_order.asm"
+
+INCLUDE "text/credits_text.asm"
+
+TheEndGfx:
+	INCBIN "gfx/intro_credits/the_end.2bpp"
+TheEndGfxEnd:
--- /dev/null
+++ b/engine/movie/evolution.asm
@@ -1,0 +1,160 @@
+EvolveMon:
+	push hl
+	push de
+	push bc
+	ld a, [wcf91]
+	push af
+	ld a, [wd0b5]
+	push af
+	xor a
+	ld [wLowHealthAlarm], a
+	ld [wChannelSoundIDs + Ch5], a
+	dec a
+	ld [wNewSoundID], a
+	call PlaySound
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, SFX_TINK
+	call PlaySound
+	call Delay3
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld [hTilesetType], a
+	ld a, [wEvoOldSpecies]
+	ld [wWholeScreenPaletteMonSpecies], a
+	ld c, 0
+	call EvolutionSetWholeScreenPalette
+	ld a, [wEvoNewSpecies]
+	ld [wcf91], a
+	ld [wd0b5], a
+	call Evolution_LoadPic
+	ld de, vFrontPic
+	ld hl, vBackPic
+	ld bc, 7 * 7
+	call CopyVideoData
+	ld a, [wEvoOldSpecies]
+	ld [wcf91], a
+	ld [wd0b5], a
+	call Evolution_LoadPic
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, [wEvoOldSpecies]
+	call PlayCry
+	call WaitForSoundToFinish
+	ld c, BANK(Music_SafariZone)
+	ld a, MUSIC_SAFARI_ZONE
+	call PlayMusic
+	ld c, 80
+	call DelayFrames
+	ld c, 1 ; set PAL_BLACK instead of mon palette
+	call EvolutionSetWholeScreenPalette
+	lb bc, $1, $10
+.animLoop
+	push bc
+	call Evolution_CheckForCancel
+	jr c, .evolutionCancelled
+	call Evolution_BackAndForthAnim
+	pop bc
+	inc b
+	dec c
+	dec c
+	jr nz, .animLoop
+	xor a
+	ld [wEvoCancelled], a
+	ld a, $31
+	ld [wEvoMonTileOffset], a
+	call Evolution_ChangeMonPic ; show the new species pic
+	ld a, [wEvoNewSpecies]
+.done
+	ld [wWholeScreenPaletteMonSpecies], a
+	ld a, $ff
+	ld [wNewSoundID], a
+	call PlaySound
+	ld a, [wWholeScreenPaletteMonSpecies]
+	call PlayCry
+	ld c, 0
+	call EvolutionSetWholeScreenPalette
+	pop af
+	ld [wd0b5], a
+	pop af
+	ld [wcf91], a
+	pop bc
+	pop de
+	pop hl
+	ld a, [wEvoCancelled]
+	and a
+	ret z
+	scf
+	ret
+.evolutionCancelled
+	pop bc
+	ld a, 1
+	ld [wEvoCancelled], a
+	ld a, [wEvoOldSpecies]
+	jr .done
+
+EvolutionSetWholeScreenPalette:
+	ld b, SET_PAL_POKEMON_WHOLE_SCREEN
+	jp RunPaletteCommand
+
+Evolution_LoadPic:
+	call GetMonHeader
+	coord hl, 7, 2
+	jp LoadFlippedFrontSpriteByMonIndex
+
+Evolution_BackAndForthAnim:
+; show the mon change back and forth between the new and old species b times
+	ld a, $31
+	ld [wEvoMonTileOffset], a
+	call Evolution_ChangeMonPic
+	ld a, -$31
+	ld [wEvoMonTileOffset], a
+	call Evolution_ChangeMonPic
+	dec b
+	jr nz, Evolution_BackAndForthAnim
+	ret
+
+Evolution_ChangeMonPic:
+	push bc
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	coord hl, 7, 2
+	lb bc, 7, 7
+	ld de, SCREEN_WIDTH - 7
+.loop
+	push bc
+.innerLoop
+	ld a, [wEvoMonTileOffset]
+	add [hl]
+	ld [hli], a
+	dec c
+	jr nz, .innerLoop
+	pop bc
+	add hl, de
+	dec b
+	jr nz, .loop
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Delay3
+	pop bc
+	ret
+
+Evolution_CheckForCancel:
+	call DelayFrame
+	push bc
+	call JoypadLowSensitivity
+	ld a, [hJoy5]
+	pop bc
+	and B_BUTTON
+	jr nz, .pressedB
+.notAllowedToCancel
+	dec c
+	jr nz, Evolution_CheckForCancel
+	and a
+	ret
+.pressedB
+	ld a, [wForceEvolution]
+	and a
+	jr nz, .notAllowedToCancel
+	scf
+	ret
--- /dev/null
+++ b/engine/movie/gamefreak.asm
@@ -1,0 +1,243 @@
+LoadShootingStarGraphics:
+	ld a, $f9
+	ld [rOBP0], a
+	ld a, $a4
+	ld [rOBP1], a
+	ld de, AnimationTileset2 + $30 ; star tile (top left quadrant)
+	ld hl, vChars1 + $200
+	lb bc, BANK(AnimationTileset2), $01
+	call CopyVideoData
+	ld de, AnimationTileset2 + $130 ; star tile (bottom left quadrant)
+	ld hl, vChars1 + $210
+	lb bc, BANK(AnimationTileset2), $01
+	call CopyVideoData
+	ld de, FallingStar
+	ld hl, vChars1 + $220
+	lb bc, BANK(FallingStar), (FallingStarEnd - FallingStar) / $10
+	call CopyVideoData
+	ld hl, GameFreakLogoOAMData
+	ld de, wOAMBuffer + $60
+	ld bc, GameFreakLogoOAMDataEnd - GameFreakLogoOAMData
+	call CopyData
+	ld hl, GameFreakShootingStarOAMData
+	ld de, wOAMBuffer
+	ld bc, GameFreakShootingStarOAMDataEnd - GameFreakShootingStarOAMData
+	jp CopyData
+
+AnimateShootingStar:
+	call LoadShootingStarGraphics
+	ld a, SFX_SHOOTING_STAR
+	call PlaySound
+
+; Move the big star down and left across the screen.
+	ld hl, wOAMBuffer
+	lb bc, $a0, $4
+.bigStarLoop
+	push hl
+	push bc
+.bigStarInnerLoop
+	ld a, [hl] ; Y
+	add 4
+	ld [hli], a
+	ld a, [hl] ; X
+	add -4
+	ld [hli], a
+	inc hl
+	inc hl
+	dec c
+	jr nz, .bigStarInnerLoop
+	ld c, 1
+	call CheckForUserInterruption
+	pop bc
+	pop hl
+	ret c
+	ld a, [hl]
+	cp 80
+	jr nz, .next
+	jr .bigStarLoop
+.next
+	cp b
+	jr nz, .bigStarLoop
+
+; Clear big star OAM.
+	ld hl, wOAMBuffer
+	ld c, 4
+	ld de, 4
+.clearOAMLoop
+	ld [hl], 160
+	add hl, de
+	dec c
+	jr nz, .clearOAMLoop
+
+; Make Gamefreak logo flash.
+	ld b, 3
+.flashLogoLoop
+	ld hl, rOBP0
+	rrc [hl]
+	rrc [hl]
+	ld c, 10
+	call CheckForUserInterruption
+	ret c
+	dec b
+	jr nz, .flashLogoLoop
+
+; Copy 24 instances of the small stars OAM data.
+; Note that their coordinates put them off-screen.
+	ld de, wOAMBuffer
+	ld a, 24
+.initSmallStarsOAMLoop
+	push af
+	ld hl, SmallStarsOAM
+	ld bc, SmallStarsOAMEnd - SmallStarsOAM
+	call CopyData
+	pop af
+	dec a
+	jr nz, .initSmallStarsOAMLoop
+
+; Animate the small stars falling from the Gamefreak logo.
+	xor a
+	ld [wMoveDownSmallStarsOAMCount], a
+	ld hl, SmallStarsWaveCoordsPointerTable
+	ld c, 6
+.smallStarsLoop
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	push bc
+	push hl
+	ld hl, wOAMBuffer + $50
+	ld c, 4
+.smallStarsInnerLoop ; introduce new wave of 4 small stars OAM entries
+	ld a, [de]
+	cp $ff
+	jr z, .next2
+	ld [hli], a ; Y
+	inc de
+	ld a, [de]
+	ld [hli], a ; X
+	inc de
+	inc hl
+	inc hl
+	dec c
+	jr nz, .smallStarsInnerLoop
+	ld a, [wMoveDownSmallStarsOAMCount]
+	cp 24
+	jr z, .next2
+	add 6 ; should be 4, but the extra 2 aren't visible on screen
+	ld [wMoveDownSmallStarsOAMCount], a
+.next2
+	call MoveDownSmallStars
+	push af
+
+; shift the existing OAM entries down to make room for the next wave
+	ld hl, wOAMBuffer + $10
+	ld de, wOAMBuffer
+	ld bc, $50
+	call CopyData
+
+	pop af
+	pop hl
+	pop bc
+	ret c
+	dec c
+	jr nz, .smallStarsLoop
+	and a
+	ret
+
+SmallStarsOAM:
+	db $00,$00,$A2,$90
+SmallStarsOAMEnd:
+
+SmallStarsWaveCoordsPointerTable:
+	dw SmallStarsWave1Coords
+	dw SmallStarsWave2Coords
+	dw SmallStarsWave3Coords
+	dw SmallStarsWave4Coords
+	dw SmallStarsEmptyWave
+	dw SmallStarsEmptyWave
+
+; The stars that fall from the Gamefreak logo come in 4 waves of 4 OAM entries.
+; These arrays contain the Y and X coordinates of each OAM entry.
+
+SmallStarsWave1Coords:
+	db $68,$30
+	db $68,$40
+	db $68,$58
+	db $68,$78
+
+SmallStarsWave2Coords:
+	db $68,$38
+	db $68,$48
+	db $68,$60
+	db $68,$70
+
+SmallStarsWave3Coords:
+	db $68,$34
+	db $68,$4C
+	db $68,$54
+	db $68,$64
+
+SmallStarsWave4Coords:
+	db $68,$3C
+	db $68,$5C
+	db $68,$6C
+	db $68,$74
+
+SmallStarsEmptyWave:
+	db $FF
+
+MoveDownSmallStars:
+	ld b, 8
+.loop
+	ld hl, wOAMBuffer + $5c
+	ld a, [wMoveDownSmallStarsOAMCount]
+	ld de, -4
+	ld c, a
+.innerLoop
+	inc [hl] ; Y
+	add hl, de
+	dec c
+	jr nz, .innerLoop
+; Toggle the palette so that the lower star in the small stars tile blinks in
+; and out.
+	ld a, [rOBP1]
+	xor %10100000
+	ld [rOBP1], a
+
+	ld c, 3
+	call CheckForUserInterruption
+	ret c
+	dec b
+	jr nz, .loop
+	ret
+
+GameFreakLogoOAMData:
+	db $48,$50,$8D,$00
+	db $48,$58,$8E,$00
+	db $50,$50,$8F,$00
+	db $50,$58,$90,$00
+	db $58,$50,$91,$00
+	db $58,$58,$92,$00
+	db $60,$30,$80,$00
+	db $60,$38,$81,$00
+	db $60,$40,$82,$00
+	db $60,$48,$83,$00
+	db $60,$50,$93,$00
+	db $60,$58,$84,$00
+	db $60,$60,$85,$00
+	db $60,$68,$83,$00
+	db $60,$70,$81,$00
+	db $60,$78,$86,$00
+GameFreakLogoOAMDataEnd:
+
+GameFreakShootingStarOAMData:
+	db $00,$A0,$A0,$10
+	db $00,$A8,$A0,$30
+	db $08,$A0,$A1,$10
+	db $08,$A8,$A1,$30
+GameFreakShootingStarOAMDataEnd:
+
+FallingStar:
+	INCBIN "gfx/intro_credits/falling_star.2bpp"
+FallingStarEnd:
--- /dev/null
+++ b/engine/movie/hall_of_fame.asm
@@ -1,0 +1,288 @@
+AnimateHallOfFame:
+	call HoFFadeOutScreenAndMusic
+	call ClearScreen
+	ld c, 100
+	call DelayFrames
+	call LoadFontTilePatterns
+	call LoadTextBoxTilePatterns
+	call DisableLCD
+	ld hl, vBGMap0
+	ld bc, $800
+	ld a, " "
+	call FillMemory
+	call EnableLCD
+	ld hl, rLCDC
+	set 3, [hl]
+	xor a
+	ld hl, wHallOfFame
+	ld bc, HOF_TEAM
+	call FillMemory
+	xor a
+	ld [wUpdateSpritesEnabled], a
+	ld [hTilesetType], a
+	ld [wSpriteFlipped], a
+	ld [wLetterPrintingDelayFlags], a ; no delay
+	ld [wHoFMonOrPlayer], a ; mon
+	inc a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld hl, wNumHoFTeams
+	ld a, [hl]
+	inc a
+	jr z, .skipInc ; don't wrap around to 0
+	inc [hl]
+.skipInc
+	ld a, $90
+	ld [hWY], a
+	ld c, BANK(Music_HallOfFame)
+	ld a, MUSIC_HALL_OF_FAME
+	call PlayMusic
+	ld hl, wPartySpecies
+	ld c, $ff
+.partyMonLoop
+	ld a, [hli]
+	cp $ff
+	jr z, .doneShowingParty
+	inc c
+	push hl
+	push bc
+	ld [wHoFMonSpecies], a
+	ld a, c
+	ld [wHoFPartyMonIndex], a
+	ld hl, wPartyMon1Level
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld a, [hl]
+	ld [wHoFMonLevel], a
+	call HoFShowMonOrPlayer
+	call HoFDisplayAndRecordMonInfo
+	ld c, 80
+	call DelayFrames
+	coord hl, 2, 13
+	ld b, 3
+	ld c, 14
+	call TextBoxBorder
+	coord hl, 4, 15
+	ld de, HallOfFameText
+	call PlaceString
+	ld c, 180
+	call DelayFrames
+	call GBFadeOutToWhite
+	pop bc
+	pop hl
+	jr .partyMonLoop
+.doneShowingParty
+	ld a, c
+	inc a
+	ld hl, wHallOfFame
+	ld bc, HOF_MON
+	call AddNTimes
+	ld [hl], $ff
+	call SaveHallOfFameTeams
+	xor a
+	ld [wHoFMonSpecies], a
+	inc a
+	ld [wHoFMonOrPlayer], a ; player
+	call HoFShowMonOrPlayer
+	call HoFDisplayPlayerStats
+	call HoFFadeOutScreenAndMusic
+	xor a
+	ld [hWY], a
+	ld hl, rLCDC
+	res 3, [hl]
+	ret
+
+HallOfFameText:
+	db "HALL OF FAME@"
+
+HoFShowMonOrPlayer:
+	call ClearScreen
+	ld a, $d0
+	ld [hSCY], a
+	ld a, $c0
+	ld [hSCX], a
+	ld a, [wHoFMonSpecies]
+	ld [wcf91], a
+	ld [wd0b5], a
+	ld [wBattleMonSpecies2], a
+	ld [wWholeScreenPaletteMonSpecies], a
+	ld a, [wHoFMonOrPlayer]
+	and a
+	jr z, .showMon
+; show player
+	call HoFLoadPlayerPics
+	jr .next1
+.showMon
+	coord hl, 12, 5
+	call GetMonHeader
+	call LoadFrontSpriteByMonIndex
+	predef LoadMonBackPic
+.next1
+	ld b, SET_PAL_POKEMON_WHOLE_SCREEN
+	ld c, 0
+	call RunPaletteCommand
+	ld a, %11100100
+	ld [rBGP], a
+	ld c, $31 ; back pic
+	call HoFLoadMonPlayerPicTileIDs
+	ld d, $a0
+	ld e, 4
+	ld a, [wOnSGB]
+	and a
+	jr z, .next2
+	sla e ; scroll more slowly on SGB
+.next2
+	call .ScrollPic ; scroll back pic left
+	xor a
+	ld [hSCY], a
+	ld c, a ; front pic
+	call HoFLoadMonPlayerPicTileIDs
+	ld d, 0
+	ld e, -4
+; scroll front pic right
+
+.ScrollPic
+	call DelayFrame
+	ld a, [hSCX]
+	add e
+	ld [hSCX], a
+	cp d
+	jr nz, .ScrollPic
+	ret
+
+HoFDisplayAndRecordMonInfo:
+	ld a, [wHoFPartyMonIndex]
+	ld hl, wPartyMonNicks
+	call GetPartyMonName
+	call HoFDisplayMonInfo
+	jp HoFRecordMonInfo
+
+HoFDisplayMonInfo:
+	coord hl, 0, 2
+	ld b, 9
+	ld c, 10
+	call TextBoxBorder
+	coord hl, 2, 6
+	ld de, HoFMonInfoText
+	call PlaceString
+	coord hl, 1, 4
+	ld de, wcd6d
+	call PlaceString
+	ld a, [wHoFMonLevel]
+	coord hl, 8, 7
+	call PrintLevelCommon
+	ld a, [wHoFMonSpecies]
+	ld [wd0b5], a
+	coord hl, 3, 9
+	predef PrintMonType
+	ld a, [wHoFMonSpecies]
+	jp PlayCry
+
+HoFMonInfoText:
+	db   "LEVEL/"
+	next "TYPE1/"
+	next "TYPE2/@"
+
+HoFLoadPlayerPics:
+	ld de, RedPicFront
+	ld a, BANK(RedPicFront)
+	call UncompressSpriteFromDE
+	ld hl, sSpriteBuffer1
+	ld de, sSpriteBuffer0
+	ld bc, $310
+	call CopyData
+	ld de, vFrontPic
+	call InterlaceMergeSpriteBuffers
+	ld de, RedPicBack
+	ld a, BANK(RedPicBack)
+	call UncompressSpriteFromDE
+	predef ScaleSpriteByTwo
+	ld de, vBackPic
+	call InterlaceMergeSpriteBuffers
+	ld c, $1
+
+HoFLoadMonPlayerPicTileIDs:
+; c = base tile ID
+	ld b, 0
+	coord hl, 12, 5
+	predef_jump CopyTileIDsFromList
+
+HoFDisplayPlayerStats:
+	SetEvent EVENT_HALL_OF_FAME_DEX_RATING
+	predef DisplayDexRating
+	coord hl, 0, 4
+	ld b, 6
+	ld c, 10
+	call TextBoxBorder
+	coord hl, 5, 0
+	ld b, 2
+	ld c, 9
+	call TextBoxBorder
+	coord hl, 7, 2
+	ld de, wPlayerName
+	call PlaceString
+	coord hl, 1, 6
+	ld de, HoFPlayTimeText
+	call PlaceString
+	coord hl, 5, 7
+	ld de, wPlayTimeHours
+	lb bc, 1, 3
+	call PrintNumber
+	ld [hl], $6d
+	inc hl
+	ld de, wPlayTimeMinutes
+	lb bc, LEADING_ZEROES | 1, 2
+	call PrintNumber
+	coord hl, 1, 9
+	ld de, HoFMoneyText
+	call PlaceString
+	coord hl, 4, 10
+	ld de, wPlayerMoney
+	ld c, $a3
+	call PrintBCDNumber
+	ld hl, DexSeenOwnedText
+	call HoFPrintTextAndDelay
+	ld hl, DexRatingText
+	call HoFPrintTextAndDelay
+	ld hl, wDexRatingText
+
+HoFPrintTextAndDelay:
+	call PrintText
+	ld c, 120
+	jp DelayFrames
+
+HoFPlayTimeText:
+	db "PLAY TIME@"
+
+HoFMoneyText:
+	db "MONEY@"
+
+DexSeenOwnedText:
+	TX_FAR _DexSeenOwnedText
+	db "@"
+
+DexRatingText:
+	TX_FAR _DexRatingText
+	db "@"
+
+HoFRecordMonInfo:
+	ld hl, wHallOfFame
+	ld bc, HOF_MON
+	ld a, [wHoFPartyMonIndex]
+	call AddNTimes
+	ld a, [wHoFMonSpecies]
+	ld [hli], a
+	ld a, [wHoFMonLevel]
+	ld [hli], a
+	ld e, l
+	ld d, h
+	ld hl, wcd6d
+	ld bc, NAME_LENGTH
+	jp CopyData
+
+HoFFadeOutScreenAndMusic:
+	ld a, 10
+	ld [wAudioFadeOutCounterReloadValue], a
+	ld [wAudioFadeOutCounter], a
+	ld a, $ff
+	ld [wAudioFadeOutControl], a
+	jp GBFadeOutToWhite
--- /dev/null
+++ b/engine/movie/intro.asm
@@ -1,0 +1,470 @@
+const_value = -1
+	const MOVE_NIDORINO_RIGHT
+	const MOVE_GENGAR_RIGHT
+	const MOVE_GENGAR_LEFT
+
+ANIMATION_END EQU 80
+
+const_value = 3
+	const GENGAR_INTRO_TILES1
+	const GENGAR_INTRO_TILES2
+	const GENGAR_INTRO_TILES3
+
+PlayIntro:
+	xor a
+	ld [hJoyHeld], a
+	inc a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call PlayShootingStar
+	call PlayIntroScene
+	call GBFadeOutToWhite
+	xor a
+	ld [hSCX], a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call ClearSprites
+	call DelayFrame
+	ret
+
+PlayIntroScene:
+	ld b, SET_PAL_NIDORINO_INTRO
+	call RunPaletteCommand
+	ldPal a, BLACK, DARK_GRAY, LIGHT_GRAY, WHITE
+	ld [rBGP], a
+	ld [rOBP0], a
+	ld [rOBP1], a
+	xor a
+	ld [hSCX], a
+	ld b, GENGAR_INTRO_TILES1
+	call IntroCopyTiles
+	ld a, 0
+	ld [wBaseCoordX], a
+	ld a, 80
+	ld [wBaseCoordY], a
+	lb bc, 6, 6
+	call InitIntroNidorinoOAM
+	lb de, 80 / 2, MOVE_NIDORINO_RIGHT
+	call IntroMoveMon
+	ret c
+
+; hip
+	ld a, SFX_INTRO_HIP
+	call PlaySound
+	xor a
+	ld [wIntroNidorinoBaseTile], a
+	ld de, IntroNidorinoAnimation1
+	call AnimateIntroNidorino
+; hop
+	ld a, SFX_INTRO_HOP
+	call PlaySound
+	ld de, IntroNidorinoAnimation2
+	call AnimateIntroNidorino
+	ld c, 10
+	call CheckForUserInterruption
+	ret c
+
+; hip
+	ld a, SFX_INTRO_HIP
+	call PlaySound
+	ld de, IntroNidorinoAnimation1
+	call AnimateIntroNidorino
+; hop
+	ld a, SFX_INTRO_HOP
+	call PlaySound
+	ld de, IntroNidorinoAnimation2
+	call AnimateIntroNidorino
+	ld c, 30
+	call CheckForUserInterruption
+	ret c
+
+; raise
+	ld b, GENGAR_INTRO_TILES2
+	call IntroCopyTiles
+	ld a, SFX_INTRO_RAISE
+	call PlaySound
+	lb de, 8 / 2, MOVE_GENGAR_LEFT
+	call IntroMoveMon
+	ld c, 30
+	call CheckForUserInterruption
+	ret c
+
+; slash
+	ld b, GENGAR_INTRO_TILES3
+	call IntroCopyTiles
+	ld a, SFX_INTRO_CRASH
+	call PlaySound
+	lb de, 16 / 2, MOVE_GENGAR_RIGHT
+	call IntroMoveMon
+; hip
+	ld a, SFX_INTRO_HIP
+	call PlaySound
+	ld a, (FightIntroFrontMon2 - FightIntroFrontMon) / BYTES_PER_TILE
+	ld [wIntroNidorinoBaseTile], a
+	ld de, IntroNidorinoAnimation3
+	call AnimateIntroNidorino
+	ld c, 30
+	call CheckForUserInterruption
+	ret c
+
+	lb de, 8 / 2, MOVE_GENGAR_LEFT
+	call IntroMoveMon
+	ld b, GENGAR_INTRO_TILES1
+	call IntroCopyTiles
+	ld c, 60
+	call CheckForUserInterruption
+	ret c
+
+; hip
+	ld a, SFX_INTRO_HIP
+	call PlaySound
+	xor a
+	ld [wIntroNidorinoBaseTile], a
+	ld de, IntroNidorinoAnimation4
+	call AnimateIntroNidorino
+; hop
+	ld a, SFX_INTRO_HOP
+	call PlaySound
+	ld de, IntroNidorinoAnimation5
+	call AnimateIntroNidorino
+	ld c, 20
+	call CheckForUserInterruption
+	ret c
+
+	ld a, (FightIntroFrontMon2 - FightIntroFrontMon) / BYTES_PER_TILE
+	ld [wIntroNidorinoBaseTile], a
+	ld de, IntroNidorinoAnimation6
+	call AnimateIntroNidorino
+	ld c, 30
+	call CheckForUserInterruption
+	ret c
+
+; lunge
+	ld a, SFX_INTRO_LUNGE
+	call PlaySound
+	ld a, (FightIntroFrontMon3 - FightIntroFrontMon) / BYTES_PER_TILE
+	ld [wIntroNidorinoBaseTile], a
+	ld de, IntroNidorinoAnimation7
+	jp AnimateIntroNidorino
+
+AnimateIntroNidorino:
+	ld a, [de]
+	cp ANIMATION_END
+	ret z
+	ld [wBaseCoordY], a
+	inc de
+	ld a, [de]
+	ld [wBaseCoordX], a
+	push de
+	ld c, 6 * 6
+	call UpdateIntroNidorinoOAM
+	ld c, 5
+	call DelayFrames
+	pop de
+	inc de
+	jr AnimateIntroNidorino
+
+UpdateIntroNidorinoOAM:
+	ld hl, wOAMBuffer
+	ld a, [wIntroNidorinoBaseTile]
+	ld d, a
+.loop
+	ld a, [wBaseCoordY]
+	add [hl]
+	ld [hli], a ; Y
+	ld a, [wBaseCoordX]
+	add [hl]
+	ld [hli], a ; X
+	ld a, d
+	ld [hli], a ; tile
+	inc hl
+	inc d
+	dec c
+	jr nz, .loop
+	ret
+
+InitIntroNidorinoOAM:
+	ld hl, wOAMBuffer
+	ld d, 0
+.loop
+	push bc
+	ld a, [wBaseCoordY]
+	ld e, a
+.innerLoop
+	ld a, e
+	add 8
+	ld e, a
+	ld [hli], a ; Y
+	ld a, [wBaseCoordX]
+	ld [hli], a ; X
+	ld a, d
+	ld [hli], a ; tile
+	ld a, OAM_BEHIND_BG
+	ld [hli], a ; attributes
+	inc d
+	dec c
+	jr nz, .innerLoop
+	ld a, [wBaseCoordX]
+	add 8
+	ld [wBaseCoordX], a
+	pop bc
+	dec b
+	jr nz, .loop
+	ret
+
+IntroClearScreen:
+	ld hl, vBGMap1
+	ld bc, BG_MAP_WIDTH * SCREEN_HEIGHT
+	jr IntroClearCommon
+
+IntroClearMiddleOfScreen:
+; clear the area of the tile map between the black bars on the top and bottom
+	coord hl, 0, 4
+	ld bc, SCREEN_WIDTH * 10
+
+IntroClearCommon:
+	ld [hl], 0
+	inc hl
+	dec bc
+	ld a, b
+	or c
+	jr nz, IntroClearCommon
+	ret
+
+IntroPlaceBlackTiles:
+	ld a, 1
+.loop
+	ld [hli], a
+	dec c
+	jr nz, .loop
+	ret
+
+IntroMoveMon:
+; d = number of times to move the mon (2 pixels each time)
+	ld a, e
+	cp MOVE_NIDORINO_RIGHT
+	jr z, .moveNidorinoRight
+	cp MOVE_GENGAR_LEFT
+	jr z, .moveGengarLeft
+; move Gengar right
+	ld a, [hSCX]
+	dec a
+	dec a
+	jr .next
+.moveNidorinoRight
+	push de
+	ld a, 2
+	ld [wBaseCoordX], a
+	xor a
+	ld [wBaseCoordY], a
+	ld c, 6 * 6
+	call UpdateIntroNidorinoOAM
+	pop de
+.moveGengarLeft
+	ld a, [hSCX]
+	inc a
+	inc a
+.next
+	ld [hSCX], a
+	push de
+	ld c, 2
+	call CheckForUserInterruption
+	pop de
+	ret c
+	dec d
+	jr nz, IntroMoveMon
+	ret
+
+IntroCopyTiles:
+	coord hl, 13, 7
+
+CopyTileIDsFromList_ZeroBaseTileID:
+	ld c, 0
+	predef_jump CopyTileIDsFromList
+
+PlayMoveSoundB:
+; unused
+	predef GetMoveSoundB
+	ld a, b
+	jp PlaySound
+
+LoadIntroGraphics:
+	ld hl, FightIntroBackMon
+	ld de, vChars2
+	ld bc, FightIntroBackMonEnd - FightIntroBackMon
+	ld a, BANK(FightIntroBackMon)
+	call FarCopyData2
+	ld hl, GameFreakIntro
+	ld de, vChars2 + (FightIntroBackMonEnd - FightIntroBackMon)
+	ld bc, GameFreakIntroEnd - GameFreakIntro
+	ld a, BANK(GameFreakIntro)
+	call FarCopyData2
+	ld hl, GameFreakIntro
+	ld de, vChars1
+	ld bc, GameFreakIntroEnd - GameFreakIntro
+	ld a, BANK(GameFreakIntro)
+	call FarCopyData2
+	ld hl, FightIntroFrontMon
+	ld de, vChars0
+	ld bc, FightIntroFrontMonEnd - FightIntroFrontMon
+	ld a, BANK(FightIntroFrontMon)
+	jp FarCopyData2
+
+PlayShootingStar:
+	ld b, SET_PAL_GAME_FREAK_INTRO
+	call RunPaletteCommand
+	callba LoadCopyrightAndTextBoxTiles
+	ldPal a, BLACK, DARK_GRAY, LIGHT_GRAY, WHITE
+	ld [rBGP], a
+	ld c, 180
+	call DelayFrames
+	call ClearScreen
+	call DisableLCD
+	xor a
+	ld [wCurOpponent], a
+	call IntroDrawBlackBars
+	call LoadIntroGraphics
+	call EnableLCD
+	ld hl, rLCDC
+	res 5, [hl]
+	set 3, [hl]
+	ld c, 64
+	call DelayFrames
+	callba AnimateShootingStar
+	push af
+	pop af
+	jr c, .next ; skip the delay if the user interrupted the animation
+	ld c, 40
+	call DelayFrames
+.next
+	ld a, BANK(Music_IntroBattle)
+	ld [wAudioROMBank], a
+	ld [wAudioSavedROMBank], a
+	ld a, MUSIC_INTRO_BATTLE
+	ld [wNewSoundID], a
+	call PlaySound
+	call IntroClearMiddleOfScreen
+	call ClearSprites
+	jp Delay3
+
+IntroDrawBlackBars:
+; clear the screen and draw black bars on the top and bottom
+	call IntroClearScreen
+	coord hl, 0, 0
+	ld c, SCREEN_WIDTH * 4
+	call IntroPlaceBlackTiles
+	coord hl, 0, 14
+	ld c, SCREEN_WIDTH * 4
+	call IntroPlaceBlackTiles
+	ld hl, vBGMap1
+	ld c,  BG_MAP_WIDTH * 4
+	call IntroPlaceBlackTiles
+	ld hl, vBGMap1 + BG_MAP_WIDTH * 14
+	ld c,  BG_MAP_WIDTH * 4
+	jp IntroPlaceBlackTiles
+
+EmptyFunc4:
+	ret
+
+IntroNidorinoAnimation0:
+	db 0, 0
+	db ANIMATION_END
+
+IntroNidorinoAnimation1:
+; This is a sequence of pixel movements for part of the Nidorino animation. This
+; list describes how Nidorino should hop.
+; First byte is y movement, second byte is x movement
+	db  0, 0
+	db -2, 2
+	db -1, 2
+	db  1, 2
+	db  2, 2
+	db ANIMATION_END
+
+IntroNidorinoAnimation2:
+; This is a sequence of pixel movements for part of the Nidorino animation.
+; First byte is y movement, second byte is x movement
+	db  0,  0
+	db -2, -2
+	db -1, -2
+	db  1, -2
+	db  2, -2
+	db ANIMATION_END
+
+IntroNidorinoAnimation3:
+; This is a sequence of pixel movements for part of the Nidorino animation.
+; First byte is y movement, second byte is x movement
+	db   0, 0
+	db -12, 6
+	db  -8, 6
+	db   8, 6
+	db  12, 6
+	db ANIMATION_END
+
+IntroNidorinoAnimation4:
+; This is a sequence of pixel movements for part of the Nidorino animation.
+; First byte is y movement, second byte is x movement
+	db  0,  0
+	db -8, -4
+	db -4, -4
+	db  4, -4
+	db  8, -4
+	db ANIMATION_END
+
+IntroNidorinoAnimation5:
+; This is a sequence of pixel movements for part of the Nidorino animation.
+; First byte is y movement, second byte is x movement
+	db  0, 0
+	db -8, 4
+	db -4, 4
+	db  4, 4
+	db  8, 4
+	db ANIMATION_END
+
+IntroNidorinoAnimation6:
+; This is a sequence of pixel movements for part of the Nidorino animation.
+; First byte is y movement, second byte is x movement
+	db 0, 0
+	db 2, 0
+	db 2, 0
+	db 0, 0
+	db ANIMATION_END
+
+IntroNidorinoAnimation7:
+; This is a sequence of pixel movements for part of the Nidorino animation.
+; First byte is y movement, second byte is x movement
+	db -8, -16
+	db -7, -14
+	db -6, -12
+	db -4, -10
+	db ANIMATION_END
+
+GameFreakIntro:
+	INCBIN "gfx/intro_credits/gamefreak_presents.2bpp"
+	INCBIN "gfx/intro_credits/gamefreak_logo.2bpp"
+	ds 16, $00 ; blank tile
+GameFreakIntroEnd:
+
+FightIntroBackMon:
+	INCBIN "gfx/intro_credits/gengar.2bpp"
+FightIntroBackMonEnd:
+
+FightIntroFrontMon:
+
+IF DEF(_RED)
+	INCBIN "gfx/intro_credits/red_nidorino_1.2bpp"
+FightIntroFrontMon2:
+	INCBIN "gfx/intro_credits/red_nidorino_2.2bpp"
+FightIntroFrontMon3:
+	INCBIN "gfx/intro_credits/red_nidorino_3.2bpp"
+ENDC
+
+IF DEF(_BLUE)
+	INCBIN "gfx/intro_credits/blue_jigglypuff_1.2bpp"
+FightIntroFrontMon2:
+	INCBIN "gfx/intro_credits/blue_jigglypuff_2.2bpp"
+FightIntroFrontMon3:
+	INCBIN "gfx/intro_credits/blue_jigglypuff_3.2bpp"
+ENDC
+
+FightIntroFrontMonEnd:
+
+	ds 16, $00 ; blank tile
--- /dev/null
+++ b/engine/movie/oak_speech/clear_save.asm
@@ -1,0 +1,23 @@
+DoClearSaveDialogue:
+	call ClearScreen
+	call RunDefaultPaletteCommand
+	call LoadFontTilePatterns
+	call LoadTextBoxTilePatterns
+	ld hl, ClearSaveDataText
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, NO_YES_MENU
+	ld [wTwoOptionMenuID], a
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	ld a, [wCurrentMenuItem]
+	and a
+	jp z, Init
+	callba ClearSAV
+	jp Init
+
+ClearSaveDataText:
+	TX_FAR _ClearSaveDataText
+	db "@"
--- /dev/null
+++ b/engine/movie/oak_speech/init_player_data.asm
@@ -1,0 +1,55 @@
+InitPlayerData:
+InitPlayerData2:
+
+	call Random
+	ld a, [hRandomSub]
+	ld [wPlayerID], a
+
+	call Random
+	ld a, [hRandomAdd]
+	ld [wPlayerID + 1], a
+
+	ld a, $ff
+	ld [wUnusedD71B], a
+
+	ld hl, wPartyCount
+	call InitializeEmptyList
+	ld hl, wNumInBox
+	call InitializeEmptyList
+	ld hl, wNumBagItems
+	call InitializeEmptyList
+	ld hl, wNumBoxItems
+	call InitializeEmptyList
+
+START_MONEY EQU $3000
+	ld hl, wPlayerMoney + 1
+	ld a, START_MONEY / $100
+	ld [hld], a
+	xor a
+	ld [hli], a
+	inc hl
+	ld [hl], a
+
+	ld [wMonDataLocation], a
+
+	ld hl, wObtainedBadges
+	ld [hli], a
+
+	ld [hl], a
+
+	ld hl, wPlayerCoins
+	ld [hli], a
+	ld [hl], a
+
+	ld hl, wGameProgressFlags
+	ld bc, wGameProgressFlagsEnd - wGameProgressFlags
+	call FillMemory ; clear all game progress flags
+
+	jp InitializeMissableObjectsFlags
+
+InitializeEmptyList:
+	xor a ; count
+	ld [hli], a
+	dec a ; terminator
+	ld [hl], a
+	ret
--- /dev/null
+++ b/engine/movie/oak_speech/oak_speech.asm
@@ -1,0 +1,233 @@
+SetDefaultNames:
+	ld a, [wLetterPrintingDelayFlags]
+	push af
+	ld a, [wOptions]
+	push af
+	ld a, [wd732]
+	push af
+	ld hl, wPlayerName
+	ld bc, wBoxDataEnd - wPlayerName
+	xor a
+	call FillMemory
+	ld hl, wSpriteStateData1
+	ld bc, $200
+	xor a
+	call FillMemory
+	pop af
+	ld [wd732], a
+	pop af
+	ld [wOptions], a
+	pop af
+	ld [wLetterPrintingDelayFlags], a
+	ld a, [wOptionsInitialized]
+	and a
+	call z, InitOptions
+	ld hl, NintenText
+	ld de, wPlayerName
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, SonyText
+	ld de, wRivalName
+	ld bc, NAME_LENGTH
+	jp CopyData
+
+OakSpeech:
+	ld a, $FF
+	call PlaySound ; stop music
+	ld a, BANK(Music_Routes2)
+	ld c, a
+	ld a, MUSIC_ROUTES2
+	call PlayMusic
+	call ClearScreen
+	call LoadTextBoxTilePatterns
+	call SetDefaultNames
+	predef InitPlayerData2
+	ld hl, wNumBoxItems
+	ld a, POTION
+	ld [wcf91], a
+	ld a, 1
+	ld [wItemQuantity], a
+	call AddItemToInventory  ; give one potion
+	ld a, [wDefaultMap]
+	ld [wDestinationMap], a
+	call SpecialWarpIn
+	xor a
+	ld [hTilesetType], a
+	ld a, [wd732]
+	bit 1, a ; possibly a debug mode bit
+	jp nz, .skipChoosingNames
+	ld de, ProfOakPic
+	lb bc, Bank(ProfOakPic), $00
+	call IntroDisplayPicCenteredOrUpperRight
+	call FadeInIntroPic
+	ld hl, OakSpeechText1
+	call PrintText
+	call GBFadeOutToWhite
+	call ClearScreen
+	ld a, NIDORINO
+	ld [wd0b5], a
+	ld [wcf91], a
+	call GetMonHeader
+	coord hl, 6, 4
+	call LoadFlippedFrontSpriteByMonIndex
+	call MovePicLeft
+	ld hl, OakSpeechText2
+	call PrintText
+	call GBFadeOutToWhite
+	call ClearScreen
+	ld de, RedPicFront
+	lb bc, Bank(RedPicFront), $00
+	call IntroDisplayPicCenteredOrUpperRight
+	call MovePicLeft
+	ld hl, IntroducePlayerText
+	call PrintText
+	call ChoosePlayerName
+	call GBFadeOutToWhite
+	call ClearScreen
+	ld de, Rival1Pic
+	lb bc, Bank(Rival1Pic), $00
+	call IntroDisplayPicCenteredOrUpperRight
+	call FadeInIntroPic
+	ld hl, IntroduceRivalText
+	call PrintText
+	call ChooseRivalName
+.skipChoosingNames
+	call GBFadeOutToWhite
+	call ClearScreen
+	ld de, RedPicFront
+	lb bc, Bank(RedPicFront), $00
+	call IntroDisplayPicCenteredOrUpperRight
+	call GBFadeInFromWhite
+	ld a, [wd72d]
+	and a
+	jr nz, .next
+	ld hl, OakSpeechText3
+	call PrintText
+.next
+	ld a, [H_LOADEDROMBANK]
+	push af
+	ld a, SFX_SHRINK
+	call PlaySound
+	pop af
+	ld [H_LOADEDROMBANK], a
+	ld [MBC1RomBank], a
+	ld c, 4
+	call DelayFrames
+	ld de, RedSprite
+	ld hl, vSprites
+	lb bc, BANK(RedSprite), $0C
+	call CopyVideoData
+	ld de, ShrinkPic1
+	lb bc, BANK(ShrinkPic1), $00
+	call IntroDisplayPicCenteredOrUpperRight
+	ld c, 4
+	call DelayFrames
+	ld de, ShrinkPic2
+	lb bc, BANK(ShrinkPic2), $00
+	call IntroDisplayPicCenteredOrUpperRight
+	call ResetPlayerSpriteData
+	ld a, [H_LOADEDROMBANK]
+	push af
+	ld a, BANK(Music_PalletTown)
+	ld [wAudioROMBank], a
+	ld [wAudioSavedROMBank], a
+	ld a, 10
+	ld [wAudioFadeOutControl], a
+	ld a, $FF
+	ld [wNewSoundID], a
+	call PlaySound ; stop music
+	pop af
+	ld [H_LOADEDROMBANK], a
+	ld [MBC1RomBank], a
+	ld c, 20
+	call DelayFrames
+	coord hl, 6, 5
+	ld b, 7
+	ld c, 7
+	call ClearScreenArea
+	call LoadTextBoxTilePatterns
+	ld a, 1
+	ld [wUpdateSpritesEnabled], a
+	ld c, 50
+	call DelayFrames
+	call GBFadeOutToWhite
+	jp ClearScreen
+OakSpeechText1:
+	TX_FAR _OakSpeechText1
+	db "@"
+OakSpeechText2:
+	TX_FAR _OakSpeechText2A
+	TX_CRY_NIDORINA
+	TX_FAR _OakSpeechText2B
+	db "@"
+IntroducePlayerText:
+	TX_FAR _IntroducePlayerText
+	db "@"
+IntroduceRivalText:
+	TX_FAR _IntroduceRivalText
+	db "@"
+OakSpeechText3:
+	TX_FAR _OakSpeechText3
+	db "@"
+
+FadeInIntroPic:
+	ld hl, IntroFadePalettes
+	ld b, 6
+.next
+	ld a, [hli]
+	ld [rBGP], a
+	ld c, 10
+	call DelayFrames
+	dec b
+	jr nz, .next
+	ret
+
+IntroFadePalettes:
+	db %01010100
+	db %10101000
+	db %11111100
+	db %11111000
+	db %11110100
+	db %11100100
+
+MovePicLeft:
+	ld a, 119
+	ld [rWX], a
+	call DelayFrame
+
+	ld a, %11100100
+	ld [rBGP], a
+.next
+	call DelayFrame
+	ld a, [rWX]
+	sub 8
+	cp $FF
+	ret z
+	ld [rWX], a
+	jr .next
+
+DisplayPicCenteredOrUpperRight:
+	call GetPredefRegisters
+IntroDisplayPicCenteredOrUpperRight:
+; b = bank
+; de = address of compressed pic
+; c: 0 = centred, non-zero = upper-right
+	push bc
+	ld a, b
+	call UncompressSpriteFromDE
+	ld hl, sSpriteBuffer1
+	ld de, sSpriteBuffer0
+	ld bc, $310
+	call CopyData
+	ld de, vFrontPic
+	call InterlaceMergeSpriteBuffers
+	pop bc
+	ld a, c
+	and a
+	coord hl, 15, 1
+	jr nz, .next
+	coord hl, 6, 4
+.next
+	xor a
+	ld [hStartTileID], a
+	predef_jump CopyUncompressedPicToTilemap
--- /dev/null
+++ b/engine/movie/oak_speech/oak_speech2.asm
@@ -1,0 +1,219 @@
+ChoosePlayerName:
+	call OakSpeechSlidePicRight
+	ld de, DefaultNamesPlayer
+	call DisplayIntroNameTextBox
+	ld a, [wCurrentMenuItem]
+	and a
+	jr z, .customName
+	ld hl, DefaultNamesPlayerList
+	call GetDefaultName
+	ld de, wPlayerName
+	call OakSpeechSlidePicLeft
+	jr .done
+.customName
+	ld hl, wPlayerName
+	xor a ; NAME_PLAYER_SCREEN
+	ld [wNamingScreenType], a
+	call DisplayNamingScreen
+	ld a, [wcf4b]
+	cp "@"
+	jr z, .customName
+	call ClearScreen
+	call Delay3
+	ld de, RedPicFront
+	ld b, BANK(RedPicFront)
+	call IntroDisplayPicCenteredOrUpperRight
+.done
+	ld hl, YourNameIsText
+	jp PrintText
+
+YourNameIsText:
+	TX_FAR _YourNameIsText
+	db "@"
+
+ChooseRivalName:
+	call OakSpeechSlidePicRight
+	ld de, DefaultNamesRival
+	call DisplayIntroNameTextBox
+	ld a, [wCurrentMenuItem]
+	and a
+	jr z, .customName
+	ld hl, DefaultNamesRivalList
+	call GetDefaultName
+	ld de, wRivalName
+	call OakSpeechSlidePicLeft
+	jr .done
+.customName
+	ld hl, wRivalName
+	ld a, NAME_RIVAL_SCREEN
+	ld [wNamingScreenType], a
+	call DisplayNamingScreen
+	ld a, [wcf4b]
+	cp "@"
+	jr z, .customName
+	call ClearScreen
+	call Delay3
+	ld de, Rival1Pic
+	ld b, $13
+	call IntroDisplayPicCenteredOrUpperRight
+.done
+	ld hl, HisNameIsText
+	jp PrintText
+
+HisNameIsText:
+	TX_FAR _HisNameIsText
+	db "@"
+
+OakSpeechSlidePicLeft:
+	push de
+	coord hl, 0, 0
+	lb bc, 12, 11
+	call ClearScreenArea ; clear the name list text box
+	ld c, 10
+	call DelayFrames
+	pop de
+	ld hl, wcd6d
+	ld bc, NAME_LENGTH
+	call CopyData
+	call Delay3
+	coord hl, 12, 4
+	lb de, 6, 6 * SCREEN_WIDTH + 5
+	ld a, $ff
+	jr OakSpeechSlidePicCommon
+
+OakSpeechSlidePicRight:
+	coord hl, 5, 4
+	lb de, 6, 6 * SCREEN_WIDTH + 5
+	xor a
+
+OakSpeechSlidePicCommon:
+	push hl
+	push de
+	push bc
+	ld [hSlideDirection], a
+	ld a, d
+	ld [hSlideAmount], a
+	ld a, e
+	ld [hSlidingRegionSize], a
+	ld c, a
+	ld a, [hSlideDirection]
+	and a
+	jr nz, .next
+; If sliding right, point hl to the end of the pic's tiles.
+	ld d, 0
+	add hl, de
+.next
+	ld d, h
+	ld e, l
+.loop
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, [hSlideDirection]
+	and a
+	jr nz, .slideLeft
+; sliding right
+	ld a, [hli]
+	ld [hld], a
+	dec hl
+	jr .next2
+.slideLeft
+	ld a, [hld]
+	ld [hli], a
+	inc hl
+.next2
+	dec c
+	jr nz, .loop
+	ld a, [hSlideDirection]
+	and a
+	jr z, .next3
+; If sliding left, we need to zero the last tile in the pic (there is no need
+; to take a corresponding action when sliding right because hl initially points
+; to a 0 tile in that case).
+	xor a
+	dec hl
+	ld [hl], a
+.next3
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Delay3
+	ld a, [hSlidingRegionSize]
+	ld c, a
+	ld h, d
+	ld l, e
+	ld a, [hSlideDirection]
+	and a
+	jr nz, .slideLeft2
+	inc hl
+	jr .next4
+.slideLeft2
+	dec hl
+.next4
+	ld d, h
+	ld e, l
+	ld a, [hSlideAmount]
+	dec a
+	ld [hSlideAmount], a
+	jr nz, .loop
+	pop bc
+	pop de
+	pop hl
+	ret
+
+DisplayIntroNameTextBox:
+	push de
+	coord hl, 0, 0
+	ld b, $a
+	ld c, $9
+	call TextBoxBorder
+	coord hl, 3, 0
+	ld de, .namestring
+	call PlaceString
+	pop de
+	coord hl, 2, 2
+	call PlaceString
+	call UpdateSprites
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	inc a
+	ld [wTopMenuItemX], a
+	ld [wMenuWatchedKeys], a ; A_BUTTON
+	inc a
+	ld [wTopMenuItemY], a
+	inc a
+	ld [wMaxMenuItem], a
+	jp HandleMenuInput
+
+.namestring
+	db "NAME@"
+
+INCLUDE "text/player_names.asm"
+
+GetDefaultName:
+; a = name index
+; hl = name list
+	ld b, a
+	ld c, 0
+.loop
+	ld d, h
+	ld e, l
+.innerLoop
+	ld a, [hli]
+	cp "@"
+	jr nz, .innerLoop
+	ld a, b
+	cp c
+	jr z, .foundName
+	inc c
+	jr .loop
+.foundName
+	ld h, d
+	ld l, e
+	ld de, wcd6d
+	ld bc, $14
+	jp CopyData
+
+INCLUDE "text/player_names_list.asm"
+
+TextTerminator_6b20:
+	db "@"
--- /dev/null
+++ b/engine/movie/titlescreen.asm
@@ -1,0 +1,403 @@
+; copy text of fixed length NAME_LENGTH (like player name, rival name, mon names, ...)
+CopyFixedLengthText:
+	ld bc, NAME_LENGTH
+	jp CopyData
+
+SetDefaultNamesBeforeTitlescreen::
+	ld hl, NintenText
+	ld de, wPlayerName
+	call CopyFixedLengthText
+	ld hl, SonyText
+	ld de, wRivalName
+	call CopyFixedLengthText
+	xor a
+	ld [hWY], a
+	ld [wLetterPrintingDelayFlags], a
+	ld hl, wd732
+	ld [hli], a
+	ld [hli], a
+	ld [hl], a
+	ld a, BANK(Music_TitleScreen)
+	ld [wAudioROMBank], a
+	ld [wAudioSavedROMBank], a
+
+DisplayTitleScreen:
+	call GBPalWhiteOut
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	xor a
+	ld [hTilesetType], a
+	ld [hSCX], a
+	ld a, $40
+	ld [hSCY], a
+	ld a, $90
+	ld [hWY], a
+	call ClearScreen
+	call DisableLCD
+	call LoadFontTilePatterns
+	ld hl, NintendoCopyrightLogoGraphics
+	ld de, vTitleLogo2 + $100
+	ld bc, $50
+	ld a, BANK(NintendoCopyrightLogoGraphics)
+	call FarCopyData2
+	ld hl, GamefreakLogoGraphics
+	ld de, vTitleLogo2 + $100 + $50
+	ld bc, $90
+	ld a, BANK(GamefreakLogoGraphics)
+	call FarCopyData2
+	ld hl, PokemonLogoGraphics
+	ld de, vTitleLogo
+	ld bc, $600
+	ld a, BANK(PokemonLogoGraphics)
+	call FarCopyData2          ; first chunk
+	ld hl, PokemonLogoGraphics+$600
+	ld de, vTitleLogo2
+	ld bc, $100
+	ld a, BANK(PokemonLogoGraphics)
+	call FarCopyData2          ; second chunk
+	ld hl, Version_GFX
+	ld de, vChars2 + $600 - (Version_GFXEnd - Version_GFX - $50)
+	ld bc, Version_GFXEnd - Version_GFX
+	ld a, BANK(Version_GFX)
+	call FarCopyDataDouble
+	call ClearBothBGMaps
+
+; place tiles for pokemon logo (except for the last row)
+	coord hl, 2, 1
+	ld a, $80
+	ld de, SCREEN_WIDTH
+	ld c, 6
+.pokemonLogoTileLoop
+	ld b, $10
+	push hl
+.pokemonLogoTileRowLoop ; place tiles for one row
+	ld [hli], a
+	inc a
+	dec b
+	jr nz, .pokemonLogoTileRowLoop
+	pop hl
+	add hl, de
+	dec c
+	jr nz, .pokemonLogoTileLoop
+
+; place tiles for the last row of the pokemon logo
+	coord hl, 2, 7
+	ld a, $31
+	ld b, $10
+.pokemonLogoLastTileRowLoop
+	ld [hli], a
+	inc a
+	dec b
+	jr nz, .pokemonLogoLastTileRowLoop
+
+	call DrawPlayerCharacter
+
+; put a pokeball in the player's hand
+	ld hl, wOAMBuffer + $28
+	ld a, $74
+	ld [hl], a
+
+; place tiles for title screen copyright
+	coord hl, 2, 17
+	ld de, .tileScreenCopyrightTiles
+	ld b, $10
+.tileScreenCopyrightTilesLoop
+	ld a, [de]
+	ld [hli], a
+	inc de
+	dec b
+	jr nz, .tileScreenCopyrightTilesLoop
+
+	jr .next
+
+.tileScreenCopyrightTiles
+	db $41,$42,$43,$42,$44,$42,$45,$46,$47,$48,$49,$4A,$4B,$4C,$4D,$4E ; ©'95.'96.'98 GAME FREAK inc.
+
+.next
+	call SaveScreenTilesToBuffer2
+	call LoadScreenTilesFromBuffer2
+	call EnableLCD
+IF DEF(_RED)
+	ld a, CHARMANDER ; which Pokemon to show first on the title screen
+ENDC
+IF DEF(_BLUE)
+	ld a, SQUIRTLE ; which Pokemon to show first on the title screen
+ENDC
+
+	ld [wTitleMonSpecies], a
+	call LoadTitleMonSprite
+	ld a, (vBGMap0 + $300) / $100
+	call TitleScreenCopyTileMapToVRAM
+	call SaveScreenTilesToBuffer1
+	ld a, $40
+	ld [hWY], a
+	call LoadScreenTilesFromBuffer2
+	ld a, vBGMap0 / $100
+	call TitleScreenCopyTileMapToVRAM
+	ld b, SET_PAL_TITLE_SCREEN
+	call RunPaletteCommand
+	call GBPalNormal
+	ld a, %11100100
+	ld [rOBP0], a
+
+; make pokemon logo bounce up and down
+	ld bc, hSCY ; background scroll Y
+	ld hl, .TitleScreenPokemonLogoYScrolls
+.bouncePokemonLogoLoop
+	ld a, [hli]
+	and a
+	jr z, .finishedBouncingPokemonLogo
+	ld d, a
+	cp -3
+	jr nz, .skipPlayingSound
+	ld a, SFX_INTRO_CRASH
+	call PlaySound
+.skipPlayingSound
+	ld a, [hli]
+	ld e, a
+	call .ScrollTitleScreenPokemonLogo
+	jr .bouncePokemonLogoLoop
+
+.TitleScreenPokemonLogoYScrolls:
+; Controls the bouncing effect of the Pokemon logo on the title screen
+	db -4,16  ; y scroll amount, number of times to scroll
+	db 3,4
+	db -3,4
+	db 2,2
+	db -2,2
+	db 1,2
+	db -1,2
+	db 0      ; terminate list with 0
+
+.ScrollTitleScreenPokemonLogo:
+; Scrolls the Pokemon logo on the title screen to create the bouncing effect
+; Scrolls d pixels e times
+	call DelayFrame
+	ld a, [bc] ; background scroll Y
+	add d
+	ld [bc], a
+	dec e
+	jr nz, .ScrollTitleScreenPokemonLogo
+	ret
+
+.finishedBouncingPokemonLogo
+	call LoadScreenTilesFromBuffer1
+	ld c, 36
+	call DelayFrames
+	ld a, SFX_INTRO_WHOOSH
+	call PlaySound
+
+; scroll game version in from the right
+	call PrintGameVersionOnTitleScreen
+	ld a, SCREEN_HEIGHT_PIXELS
+	ld [hWY], a
+	ld d, 144
+.scrollTitleScreenGameVersionLoop
+	ld h, d
+	ld l, 64
+	call ScrollTitleScreenGameVersion
+	ld h, 0
+	ld l, 80
+	call ScrollTitleScreenGameVersion
+	ld a, d
+	add 4
+	ld d, a
+	and a
+	jr nz, .scrollTitleScreenGameVersionLoop
+
+	ld a, vBGMap1 / $100
+	call TitleScreenCopyTileMapToVRAM
+	call LoadScreenTilesFromBuffer2
+	call PrintGameVersionOnTitleScreen
+	call Delay3
+	call WaitForSoundToFinish
+	ld a, MUSIC_TITLE_SCREEN
+	ld [wNewSoundID], a
+	call PlaySound
+	xor a
+	ld [wUnusedCC5B], a
+
+; Keep scrolling in new mons indefinitely until the user performs input.
+.awaitUserInterruptionLoop
+	ld c, 200
+	call CheckForUserInterruption
+	jr c, .finishedWaiting
+	call TitleScreenScrollInMon
+	ld c, 1
+	call CheckForUserInterruption
+	jr c, .finishedWaiting
+	callba TitleScreenAnimateBallIfStarterOut
+	call TitleScreenPickNewMon
+	jr .awaitUserInterruptionLoop
+
+.finishedWaiting
+	ld a, [wTitleMonSpecies]
+	call PlayCry
+	call WaitForSoundToFinish
+	call GBPalWhiteOutWithDelay3
+	call ClearSprites
+	xor a
+	ld [hWY], a
+	inc a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call ClearScreen
+	ld a, vBGMap0 / $100
+	call TitleScreenCopyTileMapToVRAM
+	ld a, vBGMap1 / $100
+	call TitleScreenCopyTileMapToVRAM
+	call Delay3
+	call LoadGBPal
+	ld a, [hJoyHeld]
+	ld b, a
+	and D_UP | SELECT | B_BUTTON
+	cp D_UP | SELECT | B_BUTTON
+	jp z, .doClearSaveDialogue
+	jp MainMenu
+
+.doClearSaveDialogue
+	jpba DoClearSaveDialogue
+
+TitleScreenPickNewMon:
+	ld a, vBGMap0 / $100
+	call TitleScreenCopyTileMapToVRAM
+
+.loop
+; Keep looping until a mon different from the current one is picked.
+	call Random
+	and $f
+	ld c, a
+	ld b, 0
+	ld hl, TitleMons
+	add hl, bc
+	ld a, [hl]
+	ld hl, wTitleMonSpecies
+
+; Can't be the same as before.
+	cp [hl]
+	jr z, .loop
+
+	ld [hl], a
+	call LoadTitleMonSprite
+
+	ld a, $90
+	ld [hWY], a
+	ld d, 1 ; scroll out
+	callba TitleScroll
+	ret
+
+TitleScreenScrollInMon:
+	ld d, 0 ; scroll in
+	callba TitleScroll
+	xor a
+	ld [hWY], a
+	ret
+
+ScrollTitleScreenGameVersion:
+.wait
+	ld a, [rLY]
+	cp l
+	jr nz, .wait
+
+	ld a, h
+	ld [rSCX], a
+
+.wait2
+	ld a, [rLY]
+	cp h
+	jr z, .wait2
+	ret
+
+DrawPlayerCharacter:
+	ld hl, PlayerCharacterTitleGraphics
+	ld de, vSprites
+	ld bc, PlayerCharacterTitleGraphicsEnd - PlayerCharacterTitleGraphics
+	ld a, BANK(PlayerCharacterTitleGraphics)
+	call FarCopyData2
+	call ClearSprites
+	xor a
+	ld [wPlayerCharacterOAMTile], a
+	ld hl, wOAMBuffer
+	ld de, $605a
+	ld b, 7
+.loop
+	push de
+	ld c, 5
+.innerLoop
+	ld a, d
+	ld [hli], a ; Y
+	ld a, e
+	ld [hli], a ; X
+	add 8
+	ld e, a
+	ld a, [wPlayerCharacterOAMTile]
+	ld [hli], a ; tile
+	inc a
+	ld [wPlayerCharacterOAMTile], a
+	inc hl
+	dec c
+	jr nz, .innerLoop
+	pop de
+	ld a, 8
+	add d
+	ld d, a
+	dec b
+	jr nz, .loop
+	ret
+
+ClearBothBGMaps:
+	ld hl, vBGMap0
+	ld bc, $400 * 2
+	ld a, " "
+	jp FillMemory
+
+LoadTitleMonSprite:
+	ld [wcf91], a
+	ld [wd0b5], a
+	coord hl, 5, 10
+	call GetMonHeader
+	jp LoadFrontSpriteByMonIndex
+
+TitleScreenCopyTileMapToVRAM:
+	ld [H_AUTOBGTRANSFERDEST + 1], a
+	jp Delay3
+
+LoadCopyrightAndTextBoxTiles:
+	xor a
+	ld [hWY], a
+	call ClearScreen
+	call LoadTextBoxTilePatterns
+
+LoadCopyrightTiles:
+	ld de, NintendoCopyrightLogoGraphics
+	ld hl, vChars2 + $600
+	lb bc, BANK(NintendoCopyrightLogoGraphics), (GamefreakLogoGraphicsEnd - NintendoCopyrightLogoGraphics) / $10
+	call CopyVideoData
+	coord hl, 2, 7
+	ld de, CopyrightTextString
+	jp PlaceString
+
+CopyrightTextString:
+	db   $60,$61,$62,$61,$63,$61,$64,$7F,$65,$66,$67,$68,$69,$6A             ; ©'95.'96.'98 Nintendo
+	next $60,$61,$62,$61,$63,$61,$64,$7F,$6B,$6C,$6D,$6E,$6F,$70,$71,$72     ; ©'95.'96.'98 Creatures inc.
+	next $60,$61,$62,$61,$63,$61,$64,$7F,$73,$74,$75,$76,$77,$78,$79,$7A,$7B ; ©'95.'96.'98 GAME FREAK inc.
+	db   "@"
+
+INCLUDE "data/title_mons.asm"
+
+; prints version text (red, blue)
+PrintGameVersionOnTitleScreen:
+	coord hl, 7, 8
+	ld de, VersionOnTitleScreenText
+	jp PlaceString
+
+; these point to special tiles specifically loaded for that purpose and are not usual text
+VersionOnTitleScreenText:
+IF DEF(_RED)
+	db $60,$61,$7F,$65,$66,$67,$68,$69,"@" ; "Red Version"
+ENDC
+IF DEF(_BLUE)
+	db $61,$62,$63,$64,$65,$66,$67,$68,"@" ; "Blue Version"
+ENDC
+
+NintenText: db "NINTEN@"
+SonyText:   db "SONY@"
--- /dev/null
+++ b/engine/movie/titlescreen2.asm
@@ -1,0 +1,120 @@
+TitleScroll_WaitBall:
+; Wait around for the TitleBall animation to play out.
+; hi: speed
+; lo: duration
+	db $05, $05, 0
+
+TitleScroll_In:
+; Scroll a TitleMon in from the right.
+; hi: speed
+; lo: duration
+	db $a2, $94, $84, $63, $52, $31, $11, 0
+
+TitleScroll_Out:
+; Scroll a TitleMon out to the left.
+; hi: speed
+; lo: duration
+	db $12, $22, $32, $42, $52, $62, $83, $93, 0
+
+TitleScroll:
+	ld a, d
+
+	ld bc, TitleScroll_In
+	ld d, $88
+	ld e, 0 ; don't animate titleball
+
+	and a
+	jr nz, .ok
+
+	ld bc, TitleScroll_Out
+	ld d, $00
+	ld e, 0 ; don't animate titleball
+.ok
+
+_TitleScroll:
+	ld a, [bc]
+	and a
+	ret z
+
+	inc bc
+	push bc
+
+	ld b, a
+	and $f
+	ld c, a
+	ld a, b
+	and $f0
+	swap a
+	ld b, a
+
+.loop
+	ld h, d
+	ld l, $48
+	call .ScrollBetween
+
+	ld h, $00
+	ld l, $88
+	call .ScrollBetween
+
+	ld a, d
+	add b
+	ld d, a
+
+	call GetTitleBallY
+	dec c
+	jr nz, .loop
+
+	pop bc
+	jr _TitleScroll
+
+.ScrollBetween:
+.wait
+	ld a, [rLY] ; rLY
+	cp l
+	jr nz, .wait
+
+	ld a, h
+	ld [rSCX], a
+
+.wait2
+	ld a, [rLY] ; rLY
+	cp h
+	jr z, .wait2
+	ret
+
+TitleBallYTable:
+; OBJ y-positions for the Poke Ball held by Red in the title screen.
+; This is really two 0-terminated lists. Initiated with an index of 1.
+	db 0, $71, $6f, $6e, $6d, $6c, $6d, $6e, $6f, $71, $74, 0
+
+TitleScreenAnimateBallIfStarterOut:
+; Animate the TitleBall if a starter just got scrolled out.
+	ld a, [wTitleMonSpecies]
+	cp STARTER1
+	jr z, .ok
+	cp STARTER2
+	jr z, .ok
+	cp STARTER3
+	ret nz
+.ok
+	ld e, 1 ; animate titleball
+	ld bc, TitleScroll_WaitBall
+	ld d, 0
+	jp _TitleScroll
+
+GetTitleBallY:
+; Get position e from TitleBallYTable
+	push de
+	push hl
+	xor a
+	ld d, a
+	ld hl, TitleBallYTable
+	add hl, de
+	ld a, [hl]
+	pop hl
+	pop de
+	and a
+	ret z
+	ld [wOAMBuffer + $28], a
+	inc e
+	ret
--- /dev/null
+++ b/engine/movie/trade.asm
@@ -1,0 +1,853 @@
+InternalClockTradeAnim:
+; Do the trading animation with the player's gameboy on the left.
+; In-game trades and internally clocked link cable trades use this.
+	ld a, [wTradedPlayerMonSpecies]
+	ld [wLeftGBMonSpecies], a
+	ld a, [wTradedEnemyMonSpecies]
+	ld [wRightGBMonSpecies], a
+	ld de, InternalClockTradeFuncSequence
+	jr TradeAnimCommon
+
+ExternalClockTradeAnim:
+; Do the trading animation with the player's gameboy on the right.
+; Externally clocked link cable trades use this.
+	ld a, [wTradedEnemyMonSpecies]
+	ld [wLeftGBMonSpecies], a
+	ld a, [wTradedPlayerMonSpecies]
+	ld [wRightGBMonSpecies], a
+	ld de, ExternalClockTradeFuncSequence
+
+TradeAnimCommon:
+	ld a, [wOptions]
+	push af
+	ld a, [hSCY]
+	push af
+	ld a, [hSCX]
+	push af
+	xor a
+	ld [wOptions], a
+	ld [hSCY], a
+	ld [hSCX], a
+	push de
+.loop
+	pop de
+	ld a, [de]
+	cp $ff
+	jr z, .done
+	inc de
+	push de
+	ld hl, TradeFuncPointerTable
+	add a
+	ld c, a
+	ld b, $0
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld de, .loop
+	push de
+	jp hl ; call trade func, which will return to the top of the loop
+.done
+	pop af
+	ld [hSCX], a
+	pop af
+	ld [hSCY], a
+	pop af
+	ld [wOptions], a
+	ret
+
+addtradefunc: MACRO
+\1TradeFunc::
+	dw \1
+	ENDM
+
+tradefunc: MACRO
+	db (\1TradeFunc - TradeFuncPointerTable) / 2
+	ENDM
+
+; The functions in the sequences below are executed in order by TradeFuncCommon.
+; They are from opposite perspectives. The external clock one makes use of
+; Trade_SwapNames to swap the player and enemy names for some functions.
+
+InternalClockTradeFuncSequence:
+	tradefunc LoadTradingGFXAndMonNames
+	tradefunc Trade_ShowPlayerMon
+	tradefunc Trade_DrawOpenEndOfLinkCable
+	tradefunc Trade_AnimateBallEnteringLinkCable
+	tradefunc Trade_AnimLeftToRight
+	tradefunc Trade_Delay100
+	tradefunc Trade_ShowClearedWindow
+	tradefunc PrintTradeWentToText
+	tradefunc PrintTradeForSendsText
+	tradefunc PrintTradeFarewellText
+	tradefunc Trade_AnimRightToLeft
+	tradefunc Trade_ShowClearedWindow
+	tradefunc Trade_DrawOpenEndOfLinkCable
+	tradefunc Trade_ShowEnemyMon
+	tradefunc Trade_Delay100
+	tradefunc Trade_Cleanup
+	db $FF
+
+ExternalClockTradeFuncSequence:
+	tradefunc LoadTradingGFXAndMonNames
+	tradefunc Trade_ShowClearedWindow
+	tradefunc PrintTradeWillTradeText
+	tradefunc PrintTradeFarewellText
+	tradefunc Trade_SwapNames
+	tradefunc Trade_AnimLeftToRight
+	tradefunc Trade_SwapNames
+	tradefunc Trade_ShowClearedWindow
+	tradefunc Trade_DrawOpenEndOfLinkCable
+	tradefunc Trade_ShowEnemyMon
+	tradefunc Trade_SlideTextBoxOffScreen
+	tradefunc Trade_ShowPlayerMon
+	tradefunc Trade_DrawOpenEndOfLinkCable
+	tradefunc Trade_AnimateBallEnteringLinkCable
+	tradefunc Trade_SwapNames
+	tradefunc Trade_AnimRightToLeft
+	tradefunc Trade_SwapNames
+	tradefunc Trade_Delay100
+	tradefunc Trade_ShowClearedWindow
+	tradefunc PrintTradeWentToText
+	tradefunc Trade_Cleanup
+	db $FF
+
+TradeFuncPointerTable:
+	addtradefunc LoadTradingGFXAndMonNames
+	addtradefunc Trade_ShowPlayerMon
+	addtradefunc Trade_DrawOpenEndOfLinkCable
+	addtradefunc Trade_AnimateBallEnteringLinkCable
+	addtradefunc Trade_ShowEnemyMon
+	addtradefunc Trade_AnimLeftToRight
+	addtradefunc Trade_AnimRightToLeft
+	addtradefunc Trade_Delay100
+	addtradefunc Trade_ShowClearedWindow
+	addtradefunc PrintTradeWentToText
+	addtradefunc PrintTradeForSendsText
+	addtradefunc PrintTradeFarewellText
+	addtradefunc PrintTradeTakeCareText
+	addtradefunc PrintTradeWillTradeText
+	addtradefunc Trade_Cleanup
+	addtradefunc Trade_SlideTextBoxOffScreen
+	addtradefunc Trade_SwapNames
+
+Trade_Delay100:
+	ld c, 100
+	jp DelayFrames
+
+Trade_CopyTileMapToVRAM:
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Delay3
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ret
+
+Trade_Delay80:
+	ld c, 80
+	jp DelayFrames
+
+Trade_ClearTileMap:
+	coord hl, 0, 0
+	ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+	ld a, " "
+	jp FillMemory
+
+LoadTradingGFXAndMonNames:
+	call Trade_ClearTileMap
+	call DisableLCD
+	ld hl, TradingAnimationGraphics
+	ld de, vChars2 + $310
+	ld bc, TradingAnimationGraphicsEnd - TradingAnimationGraphics
+	ld a, BANK(TradingAnimationGraphics)
+	call FarCopyData2
+	ld hl, TradingAnimationGraphics2
+	ld de, vSprites + $7c0
+	ld bc, TradingAnimationGraphics2End - TradingAnimationGraphics2
+	ld a, BANK(TradingAnimationGraphics2)
+	call FarCopyData2
+	ld hl, vBGMap0
+	ld bc, $800
+	ld a, " "
+	call FillMemory
+	call ClearSprites
+	ld a, $ff
+	ld [wUpdateSpritesEnabled], a
+	ld hl, wd730
+	set 6, [hl] ; turn on instant text printing
+	ld a, [wOnSGB]
+	and a
+	ld a, $e4 ; non-SGB OBP0
+	jr z, .next
+	ld a, $f0 ; SGB OBP0
+.next
+	ld [rOBP0], a
+	call EnableLCD
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, [wTradedPlayerMonSpecies]
+	ld [wd11e], a
+	call GetMonName
+	ld hl, wcd6d
+	ld de, wcf4b
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld a, [wTradedEnemyMonSpecies]
+	ld [wd11e], a
+	jp GetMonName
+
+Trade_LoadMonPartySpriteGfx:
+	ld a, %11010000
+	ld [rOBP1], a
+	jpba LoadMonPartySpriteGfx
+
+Trade_SwapNames:
+	ld hl, wPlayerName
+	ld de, wBuffer
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, wLinkEnemyTrainerName
+	ld de, wPlayerName
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld hl, wBuffer
+	ld de, wLinkEnemyTrainerName
+	ld bc, NAME_LENGTH
+	jp CopyData
+
+Trade_Cleanup:
+	xor a
+	call LoadGBPal
+	ld hl, wd730
+	res 6, [hl] ; turn off instant text printing
+	ret
+
+Trade_ShowPlayerMon:
+	ld a, %10101011
+	ld [rLCDC], a
+	ld a, $50
+	ld [hWY], a
+	ld a, $86
+	ld [rWX], a
+	ld [hSCX], a
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	coord hl, 4, 0
+	ld b, 6
+	ld c, 10
+	call TextBoxBorder
+	call Trade_PrintPlayerMonInfoText
+	ld b, vBGMap0 / $100
+	call CopyScreenTileBufferToVRAM
+	call ClearScreen
+	ld a, [wTradedPlayerMonSpecies]
+	call Trade_LoadMonSprite
+	ld a, $7e
+.slideScreenLoop
+	push af
+	call DelayFrame
+	pop af
+	ld [rWX], a
+	ld [hSCX], a
+	dec a
+	dec a
+	and a
+	jr nz, .slideScreenLoop
+	call Trade_Delay80
+	ld a, TRADE_BALL_POOF_ANIM
+	call Trade_ShowAnimation
+	ld a, TRADE_BALL_DROP_ANIM
+	call Trade_ShowAnimation ; clears mon pic
+	ld a, [wTradedPlayerMonSpecies]
+	call PlayCry
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ret
+
+Trade_DrawOpenEndOfLinkCable:
+	call Trade_ClearTileMap
+	ld b, vBGMap0 / $100
+	call CopyScreenTileBufferToVRAM
+	ld b, SET_PAL_GENERIC
+	call RunPaletteCommand
+
+; This function call is pointless. It just copies blank tiles to VRAM that was
+; already filled with blank tiles.
+	ld hl, vBGMap1 + $8c
+	call Trade_CopyCableTilesOffScreen
+
+	ld a, $a0
+	ld [hSCX], a
+	call DelayFrame
+	ld a, %10001011
+	ld [rLCDC], a
+	coord hl, 6, 2
+	ld b, $7 ; open end of link cable tile ID list index
+	call CopyTileIDsFromList_ZeroBaseTileID
+	call Trade_CopyTileMapToVRAM
+	ld a, SFX_HEAL_HP
+	call PlaySound
+	ld c, 20
+.loop
+	ld a, [hSCX]
+	add 4
+	ld [hSCX], a
+	dec c
+	jr nz, .loop
+	ret
+
+Trade_AnimateBallEnteringLinkCable:
+	ld a, TRADE_BALL_SHAKE_ANIM
+	call Trade_ShowAnimation
+	ld c, 10
+	call DelayFrames
+	ld a, %11100100
+	ld [rOBP0], a
+	xor a
+	ld [wLinkCableAnimBulgeToggle], a
+	lb bc, $20, $60
+.moveBallInsideLinkCableLoop
+	push bc
+	xor a
+	ld de, Trade_BallInsideLinkCableOAM
+	call WriteOAMBlock
+	ld a, [wLinkCableAnimBulgeToggle]
+	xor $1
+	ld [wLinkCableAnimBulgeToggle], a
+	add $7e
+	ld hl, wOAMBuffer + $02
+	ld de, 4
+	ld c, e
+.cycleLinkCableBulgeTile
+	ld [hl], a
+	add hl, de
+	dec c
+	jr nz, .cycleLinkCableBulgeTile
+	call Delay3
+	pop bc
+	ld a, c
+	add $4
+	ld c, a
+	cp $a0
+	jr nc, .ballSpriteReachedEdgeOfScreen
+	ld a, SFX_TINK
+	call PlaySound
+	jr .moveBallInsideLinkCableLoop
+.ballSpriteReachedEdgeOfScreen
+	call ClearSprites
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call ClearScreen
+	ld b, $98
+	call CopyScreenTileBufferToVRAM
+	call Delay3
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ret
+
+Trade_BallInsideLinkCableOAM:
+	db $7E,$00,$7E,$20
+	db $7E,$40,$7E,$60
+
+Trade_ShowEnemyMon:
+	ld a, TRADE_BALL_TILT_ANIM
+	call Trade_ShowAnimation
+	call Trade_ShowClearedWindow
+	coord hl, 4, 10
+	ld b, 6
+	ld c, 10
+	call TextBoxBorder
+	call Trade_PrintEnemyMonInfoText
+	call Trade_CopyTileMapToVRAM
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, [wTradedEnemyMonSpecies]
+	call Trade_LoadMonSprite
+	ld a, TRADE_BALL_POOF_ANIM
+	call Trade_ShowAnimation
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, [wTradedEnemyMonSpecies]
+	call PlayCry
+	call Trade_Delay100
+	coord hl, 4, 10
+	lb bc, 8, 12
+	call ClearScreenArea
+	jp PrintTradeTakeCareText
+
+Trade_AnimLeftToRight:
+; Animates the mon moving from the left GB to the right one.
+	call Trade_InitGameboyTransferGfx
+	ld a, $1
+	ld [wTradedMonMovingRight], a
+	ld a, %11100100
+	ld [rOBP0], a
+	ld a, $54
+	ld [wBaseCoordX], a
+	ld a, $1c
+	ld [wBaseCoordY], a
+	ld a, [wLeftGBMonSpecies]
+	ld [wMonPartySpriteSpecies], a
+	call Trade_WriteCircledMonOAM
+	call Trade_DrawLeftGameboy
+	call Trade_CopyTileMapToVRAM
+	call Trade_DrawCableAcrossScreen
+	ld hl, vBGMap1 + $8c
+	call Trade_CopyCableTilesOffScreen
+	ld b, $6
+	call Trade_AnimMonMoveHorizontal
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Trade_DrawCableAcrossScreen
+	ld b, $4
+	call Trade_AnimMonMoveHorizontal
+	call Trade_DrawRightGameboy
+	ld b, $6
+	call Trade_AnimMonMoveHorizontal
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Trade_AnimMonMoveVertical
+	jp ClearSprites
+
+Trade_AnimRightToLeft:
+; Animates the mon moving from the right GB to the left one.
+	call Trade_InitGameboyTransferGfx
+	xor a
+	ld [wTradedMonMovingRight], a
+	ld a, $64
+	ld [wBaseCoordX], a
+	ld a, $44
+	ld [wBaseCoordY], a
+	ld a, [wRightGBMonSpecies]
+	ld [wMonPartySpriteSpecies], a
+	call Trade_WriteCircledMonOAM
+	call Trade_DrawRightGameboy
+	call Trade_CopyTileMapToVRAM
+	call Trade_DrawCableAcrossScreen
+	ld hl, vBGMap1 + $94
+	call Trade_CopyCableTilesOffScreen
+	call Trade_AnimMonMoveVertical
+	ld b, $6
+	call Trade_AnimMonMoveHorizontal
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Trade_DrawCableAcrossScreen
+	ld b, $4
+	call Trade_AnimMonMoveHorizontal
+	call Trade_DrawLeftGameboy
+	ld b, $6
+	call Trade_AnimMonMoveHorizontal
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	jp ClearSprites
+
+Trade_InitGameboyTransferGfx:
+; Initialises the graphics for showing a mon moving between gameboys.
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call ClearScreen
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Trade_LoadMonPartySpriteGfx
+	call DelayFrame
+	ld a, %10101011
+	ld [rLCDC], a
+	xor a
+	ld [hSCX], a
+	ld a, $90
+	ld [hWY], a
+	ret
+
+Trade_DrawLeftGameboy:
+	call Trade_ClearTileMap
+
+; draw link cable
+	coord hl, 11, 4
+	ld a, $5d
+	ld [hli], a
+	ld a, $5e
+	ld c, 8
+.loop
+	ld [hli], a
+	dec c
+	jr nz, .loop
+
+; draw gameboy pic
+	coord hl, 5, 3
+	ld b, $6
+	call CopyTileIDsFromList_ZeroBaseTileID
+
+; draw text box with player name below gameboy pic
+	coord hl, 4, 12
+	ld b, 2
+	ld c, 7
+	call TextBoxBorder
+	coord hl, 5, 14
+	ld de, wPlayerName
+	call PlaceString
+
+	jp DelayFrame
+
+Trade_DrawRightGameboy:
+	call Trade_ClearTileMap
+
+; draw horizontal segment of link cable
+	coord hl, 0, 4
+	ld a, $5e
+	ld c, $e
+.loop
+	ld [hli], a
+	dec c
+	jr nz, .loop
+
+; draw vertical segment of link cable
+	ld a, $5f
+	ld [hl], a
+	ld de, SCREEN_WIDTH
+	add hl, de
+	ld a, $61
+	ld [hl], a
+	add hl, de
+	ld [hl], a
+	add hl, de
+	ld [hl], a
+	add hl, de
+	ld [hl], a
+	add hl, de
+	ld a, $60
+	ld [hld], a
+	ld a, $5d
+	ld [hl], a
+
+; draw gameboy pic
+	coord hl, 7, 8
+	ld b, $6
+	call CopyTileIDsFromList_ZeroBaseTileID
+
+; draw text box with enemy name above link cable
+	coord hl, 6, 0
+	ld b, 2
+	ld c, 7
+	call TextBoxBorder
+	coord hl, 7, 2
+	ld de, wLinkEnemyTrainerName
+	call PlaceString
+
+	jp DelayFrame
+
+Trade_DrawCableAcrossScreen:
+; Draws the link cable across the screen.
+	call Trade_ClearTileMap
+	coord hl, 0, 4
+	ld a, $5e
+	ld c, SCREEN_WIDTH
+.loop
+	ld [hli], a
+	dec c
+	jr nz, .loop
+	ret
+
+Trade_CopyCableTilesOffScreen:
+; This is used to copy the link cable tiles off screen so that the cable
+; continues when the screen is scrolled.
+	push hl
+	coord hl, 0, 4
+	call CopyToRedrawRowOrColumnSrcTiles
+	pop hl
+	ld a, h
+	ld [hRedrawRowOrColumnDest + 1], a
+	ld a, l
+	ld [hRedrawRowOrColumnDest], a
+	ld a, REDRAW_ROW
+	ld [hRedrawRowOrColumnMode], a
+	ld c, 10
+	jp DelayFrames
+
+Trade_AnimMonMoveHorizontal:
+; Animates the mon going through the link cable horizontally over a distance of
+; b 16-pixel units.
+	ld a, [wTradedMonMovingRight]
+	ld e, a
+	ld d, $8
+.scrollLoop
+	ld a, e
+	dec a
+	jr z, .movingRight
+; moving left
+	ld a, [hSCX]
+	sub $2
+	jr .next
+.movingRight
+	ld a, [hSCX]
+	add $2
+.next
+	ld [hSCX], a
+	call DelayFrame
+	dec d
+	jr nz, .scrollLoop
+	call Trade_AnimCircledMon
+	dec b
+	jr nz, Trade_AnimMonMoveHorizontal
+	ret
+
+Trade_AnimCircledMon:
+; Cycles between the two animation frames of the mon party sprite, cycles
+; between a circle and an oval around the mon sprite, and makes the cable flash.
+	push de
+	push bc
+	push hl
+	ld a, [rBGP]
+	xor $3c ; make link cable flash
+	ld [rBGP], a
+	ld hl, wOAMBuffer + $02
+	ld de, $4
+	ld c, $14
+.loop
+	ld a, [hl]
+	xor $40
+	ld [hl], a
+	add hl, de
+	dec c
+	jr nz, .loop
+	pop hl
+	pop bc
+	pop de
+	ret
+
+Trade_WriteCircledMonOAM:
+	callba WriteMonPartySpriteOAMBySpecies
+	call Trade_WriteCircleOAM
+
+Trade_AddOffsetsToOAMCoords:
+	ld hl, wOAMBuffer
+	ld c, $14
+.loop
+	ld a, [wBaseCoordY]
+	add [hl]
+	ld [hli], a
+	ld a, [wBaseCoordX]
+	add [hl]
+	ld [hli], a
+	inc hl
+	inc hl
+	dec c
+	jr nz, .loop
+	ret
+
+Trade_AnimMonMoveVertical:
+; Animates the mon going through the link cable vertically as well as
+; horizontally for a bit. The last bit of horizontal movement (when moving
+; right) or the first bit of horizontal movement (when moving left) are done
+; here instead of Trade_AnimMonMoveHorizontal because this function moves the
+; sprite itself rather than scrolling the screen around the sprite. Moving the
+; sprite itself is necessary because the vertical segment of the link cable is
+; to the right of the screen position that the mon sprite has when
+; Trade_AnimMonMoveHorizontal is executing.
+	ld a, [wTradedMonMovingRight]
+	and a
+	jr z, .movingLeft
+; moving right
+	lb bc, 4, 0 ; move right
+	call .doAnim
+	lb bc, 0, 10 ; move down
+	jr .doAnim
+.movingLeft
+	lb bc, 0, -10 ; move up
+	call .doAnim
+	lb bc, -4, 0 ; move left
+.doAnim
+	ld a, b
+	ld [wBaseCoordX], a
+	ld a, c
+	ld [wBaseCoordY], a
+	ld d, $4
+.loop
+	call Trade_AddOffsetsToOAMCoords
+	call Trade_AnimCircledMon
+	ld c, 8
+	call DelayFrames
+	dec d
+	jr nz, .loop
+	ret
+
+Trade_WriteCircleOAM:
+; Writes the OAM blocks for the circle around the traded mon as it passes
+; the link cable.
+	ld hl, Trade_CircleOAMPointers
+	ld c, $4
+	xor a
+.loop
+	push bc
+	ld e, [hl]
+	inc hl
+	ld d, [hl]
+	inc hl
+	ld c, [hl]
+	inc hl
+	ld b, [hl]
+	inc hl
+	push hl
+	inc a
+	push af
+	call WriteOAMBlock
+	pop af
+	pop hl
+	pop bc
+	dec c
+	jr nz, .loop
+	ret
+
+Trade_CircleOAMPointers:
+	dw Trade_CircleOAM0
+	db $08,$08
+	dw Trade_CircleOAM1
+	db $18,$08
+	dw Trade_CircleOAM2
+	db $08,$18
+	dw Trade_CircleOAM3
+	db $18,$18
+
+Trade_CircleOAM0:
+	db $38,$10,$39,$10
+	db $3A,$10,$3B,$10
+
+Trade_CircleOAM1:
+	db $39,$30,$38,$30
+	db $3B,$30,$3A,$30
+
+Trade_CircleOAM2:
+	db $3A,$50,$3B,$50
+	db $38,$50,$39,$50
+
+Trade_CircleOAM3:
+	db $3B,$70,$3A,$70
+	db $39,$70,$38,$70
+
+; a = species
+Trade_LoadMonSprite:
+	ld [wcf91], a
+	ld [wd0b5], a
+	ld [wWholeScreenPaletteMonSpecies], a
+	ld b, SET_PAL_POKEMON_WHOLE_SCREEN
+	ld c, 0
+	call RunPaletteCommand
+	ld a, [H_AUTOBGTRANSFERENABLED]
+	xor $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call GetMonHeader
+	coord hl, 7, 2
+	call LoadFlippedFrontSpriteByMonIndex
+	ld c, 10
+	jp DelayFrames
+
+Trade_ShowClearedWindow:
+; clears the window and covers the BG entirely with the window
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call ClearScreen
+	ld a, %11100011
+	ld [rLCDC], a
+	ld a, $7
+	ld [rWX], a
+	xor a
+	ld [hWY], a
+	ld a, $90
+	ld [hSCX], a
+	ret
+
+Trade_SlideTextBoxOffScreen:
+; Slides the window right until it's off screen. The window usually just has
+; a text box at the bottom when this is called. However, when this is called
+; after Trade_ShowEnemyMon in the external clock sequence, there is a mon pic
+; above the text box and it is also scrolled off the screen.
+	ld c, 50
+	call DelayFrames
+.loop
+	call DelayFrame
+	ld a, [rWX]
+	inc a
+	inc a
+	ld [rWX], a
+	cp $a1
+	jr nz, .loop
+	call Trade_ClearTileMap
+	ld c, 10
+	call DelayFrames
+	ld a, $7
+	ld [rWX], a
+	ret
+
+PrintTradeWentToText:
+	ld hl, TradeWentToText
+	call PrintText
+	ld c, 200
+	call DelayFrames
+	jp Trade_SlideTextBoxOffScreen
+
+TradeWentToText:
+	TX_FAR _TradeWentToText
+	db "@"
+
+PrintTradeForSendsText:
+	ld hl, TradeForText
+	call PrintText
+	call Trade_Delay80
+	ld hl, TradeSendsText
+	call PrintText
+	jp Trade_Delay80
+
+TradeForText:
+	TX_FAR _TradeForText
+	db "@"
+
+TradeSendsText:
+	TX_FAR _TradeSendsText
+	db "@"
+
+PrintTradeFarewellText:
+	ld hl, TradeWavesFarewellText
+	call PrintText
+	call Trade_Delay80
+	ld hl, TradeTransferredText
+	call PrintText
+	call Trade_Delay80
+	jp Trade_SlideTextBoxOffScreen
+
+TradeWavesFarewellText:
+	TX_FAR _TradeWavesFarewellText
+	db "@"
+
+TradeTransferredText:
+	TX_FAR _TradeTransferredText
+	db "@"
+
+PrintTradeTakeCareText:
+	ld hl, TradeTakeCareText
+	call PrintText
+	jp Trade_Delay80
+
+TradeTakeCareText:
+	TX_FAR _TradeTakeCareText
+	db "@"
+
+PrintTradeWillTradeText:
+	ld hl, TradeWillTradeText
+	call PrintText
+	call Trade_Delay80
+	ld hl, TradeforText
+	call PrintText
+	jp Trade_Delay80
+
+TradeWillTradeText:
+	TX_FAR _TradeWillTradeText
+	db "@"
+
+TradeforText:
+	TX_FAR _TradeforText
+	db "@"
+
+Trade_ShowAnimation:
+	ld [wAnimationID], a
+	xor a
+	ld [wAnimationType], a
+	predef_jump MoveAnimation
--- /dev/null
+++ b/engine/movie/trade2.asm
@@ -1,0 +1,48 @@
+Trade_PrintPlayerMonInfoText:
+	coord hl, 5, 0
+	ld de, Trade_MonInfoText
+	call PlaceString
+	ld a, [wTradedPlayerMonSpecies]
+	ld [wd11e], a
+	predef IndexToPokedex
+	coord hl, 9, 0
+	ld de, wd11e
+	lb bc, LEADING_ZEROES | 1, 3
+	call PrintNumber
+	coord hl, 5, 2
+	ld de, wcf4b
+	call PlaceString
+	coord hl, 8, 4
+	ld de, wTradedPlayerMonOT
+	call PlaceString
+	coord hl, 8, 6
+	ld de, wTradedPlayerMonOTID
+	lb bc, LEADING_ZEROES | 2, 5
+	jp PrintNumber
+
+Trade_PrintEnemyMonInfoText:
+	coord hl, 5, 10
+	ld de, Trade_MonInfoText
+	call PlaceString
+	ld a, [wTradedEnemyMonSpecies]
+	ld [wd11e], a
+	predef IndexToPokedex
+	coord hl, 9, 10
+	ld de, wd11e
+	lb bc, LEADING_ZEROES | 1, 3
+	call PrintNumber
+	coord hl, 5, 12
+	ld de, wcd6d
+	call PlaceString
+	coord hl, 8, 14
+	ld de, wTradedEnemyMonOT
+	call PlaceString
+	coord hl, 8, 16
+	ld de, wTradedEnemyMonOTID
+	lb bc, LEADING_ZEROES | 2, 5
+	jp PrintNumber
+
+Trade_MonInfoText:
+	db "──№⠄",$4E
+	next "OT/"
+	next $73,"№⠄","@"
--- a/engine/multiply_divide.asm
+++ /dev/null
@@ -1,143 +1,0 @@
-_Multiply::
-	ld a, $8
-	ld b, a
-	xor a
-	ld [H_PRODUCT], a
-	ld [H_MULTIPLYBUFFER], a
-	ld [H_MULTIPLYBUFFER+1], a
-	ld [H_MULTIPLYBUFFER+2], a
-	ld [H_MULTIPLYBUFFER+3], a
-.loop
-	ld a, [H_MULTIPLIER]
-	srl a
-	ld [H_MULTIPLIER], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
-	jr nc, .smallMultiplier
-	ld a, [H_MULTIPLYBUFFER+3]
-	ld c, a
-	ld a, [H_MULTIPLICAND+2]
-	add c
-	ld [H_MULTIPLYBUFFER+3], a
-	ld a, [H_MULTIPLYBUFFER+2]
-	ld c, a
-	ld a, [H_MULTIPLICAND+1]
-	adc c
-	ld [H_MULTIPLYBUFFER+2], a
-	ld a, [H_MULTIPLYBUFFER+1]
-	ld c, a
-	ld a, [H_MULTIPLICAND] ; (aliases: H_MULTIPLICAND)
-	adc c
-	ld [H_MULTIPLYBUFFER+1], a
-	ld a, [H_MULTIPLYBUFFER]
-	ld c, a
-	ld a, [H_PRODUCT] ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
-	adc c
-	ld [H_MULTIPLYBUFFER], a
-.smallMultiplier
-	dec b
-	jr z, .done
-	ld a, [H_MULTIPLICAND+2]
-	sla a
-	ld [H_MULTIPLICAND+2], a
-	ld a, [H_MULTIPLICAND+1]
-	rl a
-	ld [H_MULTIPLICAND+1], a
-	ld a, [H_MULTIPLICAND]
-	rl a
-	ld [H_MULTIPLICAND], a
-	ld a, [H_PRODUCT]
-	rl a
-	ld [H_PRODUCT], a
-	jr .loop
-.done
-	ld a, [H_MULTIPLYBUFFER+3]
-	ld [H_PRODUCT+3], a
-	ld a, [H_MULTIPLYBUFFER+2]
-	ld [H_PRODUCT+2], a
-	ld a, [H_MULTIPLYBUFFER+1]
-	ld [H_PRODUCT+1], a
-	ld a, [H_MULTIPLYBUFFER]
-	ld [H_PRODUCT], a
-	ret
-
-_Divide::
-	xor a
-	ld [H_DIVIDEBUFFER], a
-	ld [H_DIVIDEBUFFER+1], a
-	ld [H_DIVIDEBUFFER+2], a
-	ld [H_DIVIDEBUFFER+3], a
-	ld [H_DIVIDEBUFFER+4], a
-	ld a, $9
-	ld e, a
-.asm_37db3
-	ld a, [H_DIVIDEBUFFER]
-	ld c, a
-	ld a, [H_DIVIDEND+1] ; (aliases: H_MULTIPLICAND)
-	sub c
-	ld d, a
-	ld a, [H_DIVISOR] ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
-	ld c, a
-	ld a, [H_DIVIDEND] ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
-	sbc c
-	jr c, .asm_37dce
-	ld [H_DIVIDEND], a ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
-	ld a, d
-	ld [H_DIVIDEND+1], a ; (aliases: H_MULTIPLICAND)
-	ld a, [H_DIVIDEBUFFER+4]
-	inc a
-	ld [H_DIVIDEBUFFER+4], a
-	jr .asm_37db3
-.asm_37dce
-	ld a, b
-	cp $1
-	jr z, .asm_37e18
-	ld a, [H_DIVIDEBUFFER+4]
-	sla a
-	ld [H_DIVIDEBUFFER+4], a
-	ld a, [H_DIVIDEBUFFER+3]
-	rl a
-	ld [H_DIVIDEBUFFER+3], a
-	ld a, [H_DIVIDEBUFFER+2]
-	rl a
-	ld [H_DIVIDEBUFFER+2], a
-	ld a, [H_DIVIDEBUFFER+1]
-	rl a
-	ld [H_DIVIDEBUFFER+1], a
-	dec e
-	jr nz, .asm_37e04
-	ld a, $8
-	ld e, a
-	ld a, [H_DIVIDEBUFFER]
-	ld [H_DIVISOR], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
-	xor a
-	ld [H_DIVIDEBUFFER], a
-	ld a, [H_DIVIDEND+1] ; (aliases: H_MULTIPLICAND)
-	ld [H_DIVIDEND], a ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
-	ld a, [H_DIVIDEND+2]
-	ld [H_DIVIDEND+1], a ; (aliases: H_MULTIPLICAND)
-	ld a, [H_DIVIDEND+3]
-	ld [H_DIVIDEND+2], a
-.asm_37e04
-	ld a, e
-	cp $1
-	jr nz, .asm_37e0a
-	dec b
-.asm_37e0a
-	ld a, [H_DIVISOR] ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
-	srl a
-	ld [H_DIVISOR], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
-	ld a, [H_DIVIDEBUFFER]
-	rr a
-	ld [H_DIVIDEBUFFER], a
-	jr .asm_37db3
-.asm_37e18
-	ld a, [H_DIVIDEND+1] ; (aliases: H_MULTIPLICAND)
-	ld [H_REMAINDER], a ; (aliases: H_DIVISOR, H_MULTIPLIER, H_POWEROFTEN)
-	ld a, [H_DIVIDEBUFFER+4]
-	ld [H_QUOTIENT+3], a
-	ld a, [H_DIVIDEBUFFER+3]
-	ld [H_QUOTIENT+2], a
-	ld a, [H_DIVIDEBUFFER+2]
-	ld [H_QUOTIENT+1], a ; (aliases: H_MULTIPLICAND)
-	ld a, [H_DIVIDEBUFFER+1]
-	ld [H_DIVIDEND], a ; (aliases: H_PRODUCT, H_PASTLEADINGZEROES, H_QUOTIENT)
-	ret
--- a/engine/oak_speech.asm
+++ /dev/null
@@ -1,233 +1,0 @@
-SetDefaultNames:
-	ld a, [wLetterPrintingDelayFlags]
-	push af
-	ld a, [wOptions]
-	push af
-	ld a, [wd732]
-	push af
-	ld hl, wPlayerName
-	ld bc, wBoxDataEnd - wPlayerName
-	xor a
-	call FillMemory
-	ld hl, wSpriteStateData1
-	ld bc, $200
-	xor a
-	call FillMemory
-	pop af
-	ld [wd732], a
-	pop af
-	ld [wOptions], a
-	pop af
-	ld [wLetterPrintingDelayFlags], a
-	ld a, [wOptionsInitialized]
-	and a
-	call z, InitOptions
-	ld hl, NintenText
-	ld de, wPlayerName
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, SonyText
-	ld de, wRivalName
-	ld bc, NAME_LENGTH
-	jp CopyData
-
-OakSpeech:
-	ld a, $FF
-	call PlaySound ; stop music
-	ld a, BANK(Music_Routes2)
-	ld c, a
-	ld a, MUSIC_ROUTES2
-	call PlayMusic
-	call ClearScreen
-	call LoadTextBoxTilePatterns
-	call SetDefaultNames
-	predef InitPlayerData2
-	ld hl, wNumBoxItems
-	ld a, POTION
-	ld [wcf91], a
-	ld a, 1
-	ld [wItemQuantity], a
-	call AddItemToInventory  ; give one potion
-	ld a, [wDefaultMap]
-	ld [wDestinationMap], a
-	call SpecialWarpIn
-	xor a
-	ld [hTilesetType], a
-	ld a, [wd732]
-	bit 1, a ; possibly a debug mode bit
-	jp nz, .skipChoosingNames
-	ld de, ProfOakPic
-	lb bc, Bank(ProfOakPic), $00
-	call IntroDisplayPicCenteredOrUpperRight
-	call FadeInIntroPic
-	ld hl, OakSpeechText1
-	call PrintText
-	call GBFadeOutToWhite
-	call ClearScreen
-	ld a, NIDORINO
-	ld [wd0b5], a
-	ld [wcf91], a
-	call GetMonHeader
-	coord hl, 6, 4
-	call LoadFlippedFrontSpriteByMonIndex
-	call MovePicLeft
-	ld hl, OakSpeechText2
-	call PrintText
-	call GBFadeOutToWhite
-	call ClearScreen
-	ld de, RedPicFront
-	lb bc, Bank(RedPicFront), $00
-	call IntroDisplayPicCenteredOrUpperRight
-	call MovePicLeft
-	ld hl, IntroducePlayerText
-	call PrintText
-	call ChoosePlayerName
-	call GBFadeOutToWhite
-	call ClearScreen
-	ld de, Rival1Pic
-	lb bc, Bank(Rival1Pic), $00
-	call IntroDisplayPicCenteredOrUpperRight
-	call FadeInIntroPic
-	ld hl, IntroduceRivalText
-	call PrintText
-	call ChooseRivalName
-.skipChoosingNames
-	call GBFadeOutToWhite
-	call ClearScreen
-	ld de, RedPicFront
-	lb bc, Bank(RedPicFront), $00
-	call IntroDisplayPicCenteredOrUpperRight
-	call GBFadeInFromWhite
-	ld a, [wd72d]
-	and a
-	jr nz, .next
-	ld hl, OakSpeechText3
-	call PrintText
-.next
-	ld a, [H_LOADEDROMBANK]
-	push af
-	ld a, SFX_SHRINK
-	call PlaySound
-	pop af
-	ld [H_LOADEDROMBANK], a
-	ld [MBC1RomBank], a
-	ld c, 4
-	call DelayFrames
-	ld de, RedSprite
-	ld hl, vSprites
-	lb bc, BANK(RedSprite), $0C
-	call CopyVideoData
-	ld de, ShrinkPic1
-	lb bc, BANK(ShrinkPic1), $00
-	call IntroDisplayPicCenteredOrUpperRight
-	ld c, 4
-	call DelayFrames
-	ld de, ShrinkPic2
-	lb bc, BANK(ShrinkPic2), $00
-	call IntroDisplayPicCenteredOrUpperRight
-	call ResetPlayerSpriteData
-	ld a, [H_LOADEDROMBANK]
-	push af
-	ld a, BANK(Music_PalletTown)
-	ld [wAudioROMBank], a
-	ld [wAudioSavedROMBank], a
-	ld a, 10
-	ld [wAudioFadeOutControl], a
-	ld a, $FF
-	ld [wNewSoundID], a
-	call PlaySound ; stop music
-	pop af
-	ld [H_LOADEDROMBANK], a
-	ld [MBC1RomBank], a
-	ld c, 20
-	call DelayFrames
-	coord hl, 6, 5
-	ld b, 7
-	ld c, 7
-	call ClearScreenArea
-	call LoadTextBoxTilePatterns
-	ld a, 1
-	ld [wUpdateSpritesEnabled], a
-	ld c, 50
-	call DelayFrames
-	call GBFadeOutToWhite
-	jp ClearScreen
-OakSpeechText1:
-	TX_FAR _OakSpeechText1
-	db "@"
-OakSpeechText2:
-	TX_FAR _OakSpeechText2A
-	TX_CRY_NIDORINA
-	TX_FAR _OakSpeechText2B
-	db "@"
-IntroducePlayerText:
-	TX_FAR _IntroducePlayerText
-	db "@"
-IntroduceRivalText:
-	TX_FAR _IntroduceRivalText
-	db "@"
-OakSpeechText3:
-	TX_FAR _OakSpeechText3
-	db "@"
-
-FadeInIntroPic:
-	ld hl, IntroFadePalettes
-	ld b, 6
-.next
-	ld a, [hli]
-	ld [rBGP], a
-	ld c, 10
-	call DelayFrames
-	dec b
-	jr nz, .next
-	ret
-
-IntroFadePalettes:
-	db %01010100
-	db %10101000
-	db %11111100
-	db %11111000
-	db %11110100
-	db %11100100
-
-MovePicLeft:
-	ld a, 119
-	ld [rWX], a
-	call DelayFrame
-
-	ld a, %11100100
-	ld [rBGP], a
-.next
-	call DelayFrame
-	ld a, [rWX]
-	sub 8
-	cp $FF
-	ret z
-	ld [rWX], a
-	jr .next
-
-DisplayPicCenteredOrUpperRight:
-	call GetPredefRegisters
-IntroDisplayPicCenteredOrUpperRight:
-; b = bank
-; de = address of compressed pic
-; c: 0 = centred, non-zero = upper-right
-	push bc
-	ld a, b
-	call UncompressSpriteFromDE
-	ld hl, sSpriteBuffer1
-	ld de, sSpriteBuffer0
-	ld bc, $310
-	call CopyData
-	ld de, vFrontPic
-	call InterlaceMergeSpriteBuffers
-	pop bc
-	ld a, c
-	and a
-	coord hl, 15, 1
-	jr nz, .next
-	coord hl, 6, 4
-.next
-	xor a
-	ld [hStartTileID], a
-	predef_jump CopyUncompressedPicToTilemap
--- a/engine/oak_speech2.asm
+++ /dev/null
@@ -1,219 +1,0 @@
-ChoosePlayerName:
-	call OakSpeechSlidePicRight
-	ld de, DefaultNamesPlayer
-	call DisplayIntroNameTextBox
-	ld a, [wCurrentMenuItem]
-	and a
-	jr z, .customName
-	ld hl, DefaultNamesPlayerList
-	call GetDefaultName
-	ld de, wPlayerName
-	call OakSpeechSlidePicLeft
-	jr .done
-.customName
-	ld hl, wPlayerName
-	xor a ; NAME_PLAYER_SCREEN
-	ld [wNamingScreenType], a
-	call DisplayNamingScreen
-	ld a, [wcf4b]
-	cp "@"
-	jr z, .customName
-	call ClearScreen
-	call Delay3
-	ld de, RedPicFront
-	ld b, BANK(RedPicFront)
-	call IntroDisplayPicCenteredOrUpperRight
-.done
-	ld hl, YourNameIsText
-	jp PrintText
-
-YourNameIsText:
-	TX_FAR _YourNameIsText
-	db "@"
-
-ChooseRivalName:
-	call OakSpeechSlidePicRight
-	ld de, DefaultNamesRival
-	call DisplayIntroNameTextBox
-	ld a, [wCurrentMenuItem]
-	and a
-	jr z, .customName
-	ld hl, DefaultNamesRivalList
-	call GetDefaultName
-	ld de, wRivalName
-	call OakSpeechSlidePicLeft
-	jr .done
-.customName
-	ld hl, wRivalName
-	ld a, NAME_RIVAL_SCREEN
-	ld [wNamingScreenType], a
-	call DisplayNamingScreen
-	ld a, [wcf4b]
-	cp "@"
-	jr z, .customName
-	call ClearScreen
-	call Delay3
-	ld de, Rival1Pic
-	ld b, $13
-	call IntroDisplayPicCenteredOrUpperRight
-.done
-	ld hl, HisNameIsText
-	jp PrintText
-
-HisNameIsText:
-	TX_FAR _HisNameIsText
-	db "@"
-
-OakSpeechSlidePicLeft:
-	push de
-	coord hl, 0, 0
-	lb bc, 12, 11
-	call ClearScreenArea ; clear the name list text box
-	ld c, 10
-	call DelayFrames
-	pop de
-	ld hl, wcd6d
-	ld bc, NAME_LENGTH
-	call CopyData
-	call Delay3
-	coord hl, 12, 4
-	lb de, 6, 6 * SCREEN_WIDTH + 5
-	ld a, $ff
-	jr OakSpeechSlidePicCommon
-
-OakSpeechSlidePicRight:
-	coord hl, 5, 4
-	lb de, 6, 6 * SCREEN_WIDTH + 5
-	xor a
-
-OakSpeechSlidePicCommon:
-	push hl
-	push de
-	push bc
-	ld [hSlideDirection], a
-	ld a, d
-	ld [hSlideAmount], a
-	ld a, e
-	ld [hSlidingRegionSize], a
-	ld c, a
-	ld a, [hSlideDirection]
-	and a
-	jr nz, .next
-; If sliding right, point hl to the end of the pic's tiles.
-	ld d, 0
-	add hl, de
-.next
-	ld d, h
-	ld e, l
-.loop
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, [hSlideDirection]
-	and a
-	jr nz, .slideLeft
-; sliding right
-	ld a, [hli]
-	ld [hld], a
-	dec hl
-	jr .next2
-.slideLeft
-	ld a, [hld]
-	ld [hli], a
-	inc hl
-.next2
-	dec c
-	jr nz, .loop
-	ld a, [hSlideDirection]
-	and a
-	jr z, .next3
-; If sliding left, we need to zero the last tile in the pic (there is no need
-; to take a corresponding action when sliding right because hl initially points
-; to a 0 tile in that case).
-	xor a
-	dec hl
-	ld [hl], a
-.next3
-	ld a, 1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Delay3
-	ld a, [hSlidingRegionSize]
-	ld c, a
-	ld h, d
-	ld l, e
-	ld a, [hSlideDirection]
-	and a
-	jr nz, .slideLeft2
-	inc hl
-	jr .next4
-.slideLeft2
-	dec hl
-.next4
-	ld d, h
-	ld e, l
-	ld a, [hSlideAmount]
-	dec a
-	ld [hSlideAmount], a
-	jr nz, .loop
-	pop bc
-	pop de
-	pop hl
-	ret
-
-DisplayIntroNameTextBox:
-	push de
-	coord hl, 0, 0
-	ld b, $a
-	ld c, $9
-	call TextBoxBorder
-	coord hl, 3, 0
-	ld de, .namestring
-	call PlaceString
-	pop de
-	coord hl, 2, 2
-	call PlaceString
-	call UpdateSprites
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	inc a
-	ld [wTopMenuItemX], a
-	ld [wMenuWatchedKeys], a ; A_BUTTON
-	inc a
-	ld [wTopMenuItemY], a
-	inc a
-	ld [wMaxMenuItem], a
-	jp HandleMenuInput
-
-.namestring
-	db "NAME@"
-
-INCLUDE "text/player_names.asm"
-
-GetDefaultName:
-; a = name index
-; hl = name list
-	ld b, a
-	ld c, 0
-.loop
-	ld d, h
-	ld e, l
-.innerLoop
-	ld a, [hli]
-	cp "@"
-	jr nz, .innerLoop
-	ld a, b
-	cp c
-	jr z, .foundName
-	inc c
-	jr .loop
-.foundName
-	ld h, d
-	ld l, e
-	ld de, wcd6d
-	ld bc, $14
-	jp CopyData
-
-INCLUDE "text/player_names_list.asm"
-
-TextTerminator_6b20:
-	db "@"
--- a/engine/oam_dma.asm
+++ /dev/null
@@ -1,26 +1,0 @@
-WriteDMACodeToHRAM::
-; Since no other memory is available during OAM DMA,
-; DMARoutine is copied to HRAM and executed there.
-	ld c, $ff80 % $100
-	ld b, DMARoutineEnd - DMARoutine
-	ld hl, DMARoutine
-.copy
-	ld a, [hli]
-	ld [$ff00+c], a
-	inc c
-	dec b
-	jr nz, .copy
-	ret
-
-DMARoutine:
-	; initiate DMA
-	ld a, wOAMBuffer / $100
-	ld [rDMA], a
-
-	; wait for DMA to finish
-	ld a, $28
-.wait
-	dec a
-	jr nz, .wait
-	ret
-DMARoutineEnd:
--- /dev/null
+++ b/engine/overworld/auto_movement.asm
@@ -1,0 +1,292 @@
+PlayerStepOutFromDoor::
+	ld hl, wd730
+	res 1, [hl]
+	call IsPlayerStandingOnDoorTile
+	jr nc, .notStandingOnDoor
+	ld a, $fc
+	ld [wJoyIgnore], a
+	ld hl, wd736
+	set 1, [hl]
+	ld a, $1
+	ld [wSimulatedJoypadStatesIndex], a
+	ld a, D_DOWN
+	ld [wSimulatedJoypadStatesEnd], a
+	xor a
+	ld [wSpriteStateData1 + 2], a
+	call StartSimulatingJoypadStates
+	ret
+.notStandingOnDoor
+	xor a
+	ld [wWastedByteCD3A], a
+	ld [wSimulatedJoypadStatesIndex], a
+	ld [wSimulatedJoypadStatesEnd], a
+	ld hl, wd736
+	res 0, [hl]
+	res 1, [hl]
+	ld hl, wd730
+	res 7, [hl]
+	ret
+
+_EndNPCMovementScript::
+	ld hl, wd730
+	res 7, [hl]
+	ld hl, wd72e
+	res 7, [hl]
+	ld hl, wd736
+	res 0, [hl]
+	res 1, [hl]
+	xor a
+	ld [wNPCMovementScriptSpriteOffset], a
+	ld [wNPCMovementScriptPointerTableNum], a
+	ld [wNPCMovementScriptFunctionNum], a
+	ld [wWastedByteCD3A], a
+	ld [wSimulatedJoypadStatesIndex], a
+	ld [wSimulatedJoypadStatesEnd], a
+	ret
+
+PalletMovementScriptPointerTable::
+	dw PalletMovementScript_OakMoveLeft
+	dw PalletMovementScript_PlayerMoveLeft
+	dw PalletMovementScript_WaitAndWalkToLab
+	dw PalletMovementScript_WalkToLab
+	dw PalletMovementScript_Done
+
+PalletMovementScript_OakMoveLeft:
+	ld a, [wXCoord]
+	sub $a
+	ld [wNumStepsToTake], a
+	jr z, .playerOnLeftTile
+; The player is on the right tile of the northern path out of Pallet Town and
+; Prof. Oak is below.
+; Make Prof. Oak step to the left.
+	ld b, 0
+	ld c, a
+	ld hl, wNPCMovementDirections2
+	ld a, NPC_MOVEMENT_LEFT
+	call FillMemory
+	ld [hl], $ff
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	ld de, wNPCMovementDirections2
+	call MoveSprite
+	ld a, $1
+	ld [wNPCMovementScriptFunctionNum], a
+	jr .done
+; The player is on the left tile of the northern path out of Pallet Town and
+; Prof. Oak is below.
+; Prof. Oak is already where he needs to be.
+.playerOnLeftTile
+	ld a, $3
+	ld [wNPCMovementScriptFunctionNum], a
+.done
+	ld hl, wFlags_D733
+	set 1, [hl]
+	ld a, $fc
+	ld [wJoyIgnore], a
+	ret
+
+PalletMovementScript_PlayerMoveLeft:
+	ld a, [wd730]
+	bit 0, a ; is an NPC being moved by a script?
+	ret nz ; return if Oak is still moving
+	ld a, [wNumStepsToTake]
+	ld [wSimulatedJoypadStatesIndex], a
+	ld [hNPCMovementDirections2Index], a
+	predef ConvertNPCMovementDirectionsToJoypadMasks
+	call StartSimulatingJoypadStates
+	ld a, $2
+	ld [wNPCMovementScriptFunctionNum], a
+	ret
+
+PalletMovementScript_WaitAndWalkToLab:
+	ld a, [wSimulatedJoypadStatesIndex]
+	and a ; is the player done moving left yet?
+	ret nz
+
+PalletMovementScript_WalkToLab:
+	xor a
+	ld [wOverrideSimulatedJoypadStatesMask], a
+	ld a, [wSpriteIndex]
+	swap a
+	ld [wNPCMovementScriptSpriteOffset], a
+	xor a
+	ld [wSpriteStateData2 + $06], a
+	ld hl, wSimulatedJoypadStatesEnd
+	ld de, RLEList_PlayerWalkToLab
+	call DecodeRLEList
+	dec a
+	ld [wSimulatedJoypadStatesIndex], a
+	ld hl, wNPCMovementDirections2
+	ld de, RLEList_ProfOakWalkToLab
+	call DecodeRLEList
+	ld hl, wd72e
+	res 7, [hl]
+	ld hl, wd730
+	set 7, [hl]
+	ld a, $4
+	ld [wNPCMovementScriptFunctionNum], a
+	ret
+
+RLEList_ProfOakWalkToLab:
+	db NPC_MOVEMENT_DOWN, $05
+	db NPC_MOVEMENT_LEFT, $01
+	db NPC_MOVEMENT_DOWN, $05
+	db NPC_MOVEMENT_RIGHT, $03
+	db NPC_MOVEMENT_UP, $01
+	db $E0, $01 ; stand still
+	db $FF
+
+RLEList_PlayerWalkToLab:
+	db D_UP, $02
+	db D_RIGHT, $03
+	db D_DOWN, $05
+	db D_LEFT, $01
+	db D_DOWN, $06
+	db $FF
+
+PalletMovementScript_Done:
+	ld a, [wSimulatedJoypadStatesIndex]
+	and a
+	ret nz
+	ld a, HS_PALLET_TOWN_OAK
+	ld [wMissableObjectIndex], a
+	predef HideObject
+	ld hl, wd730
+	res 7, [hl]
+	ld hl, wd72e
+	res 7, [hl]
+	jp EndNPCMovementScript
+
+PewterMuseumGuyMovementScriptPointerTable::
+	dw PewterMovementScript_WalkToMuseum
+	dw PewterMovementScript_Done
+
+PewterMovementScript_WalkToMuseum:
+	ld a, BANK(Music_MuseumGuy)
+	ld [wAudioROMBank], a
+	ld [wAudioSavedROMBank], a
+	ld a, MUSIC_MUSEUM_GUY
+	ld [wNewSoundID], a
+	call PlaySound
+	ld a, [wSpriteIndex]
+	swap a
+	ld [wNPCMovementScriptSpriteOffset], a
+	call StartSimulatingJoypadStates
+	ld hl, wSimulatedJoypadStatesEnd
+	ld de, RLEList_PewterMuseumPlayer
+	call DecodeRLEList
+	dec a
+	ld [wSimulatedJoypadStatesIndex], a
+	xor a
+	ld [wWhichPewterGuy], a
+	predef PewterGuys
+	ld hl, wNPCMovementDirections2
+	ld de, RLEList_PewterMuseumGuy
+	call DecodeRLEList
+	ld hl, wd72e
+	res 7, [hl]
+	ld a, $1
+	ld [wNPCMovementScriptFunctionNum], a
+	ret
+
+RLEList_PewterMuseumPlayer:
+	db 0, $01
+	db D_UP, $03
+	db D_LEFT, $0D
+	db D_UP, $06
+	db $FF
+
+RLEList_PewterMuseumGuy:
+	db NPC_MOVEMENT_UP, $06
+	db NPC_MOVEMENT_LEFT, $0D
+	db NPC_MOVEMENT_UP, $03
+	db NPC_MOVEMENT_LEFT, $01
+	db $FF
+
+PewterMovementScript_Done:
+	ld a, [wSimulatedJoypadStatesIndex]
+	and a
+	ret nz
+	ld hl, wd730
+	res 7, [hl]
+	ld hl, wd72e
+	res 7, [hl]
+	jp EndNPCMovementScript
+
+PewterGymGuyMovementScriptPointerTable::
+	dw PewterMovementScript_WalkToGym
+	dw PewterMovementScript_Done
+
+PewterMovementScript_WalkToGym:
+	ld a, BANK(Music_MuseumGuy)
+	ld [wAudioROMBank], a
+	ld [wAudioSavedROMBank], a
+	ld a, MUSIC_MUSEUM_GUY
+	ld [wNewSoundID], a
+	call PlaySound
+	ld a, [wSpriteIndex]
+	swap a
+	ld [wNPCMovementScriptSpriteOffset], a
+	xor a
+	ld [wSpriteStateData2 + $06], a
+	ld hl, wSimulatedJoypadStatesEnd
+	ld de, RLEList_PewterGymPlayer
+	call DecodeRLEList
+	dec a
+	ld [wSimulatedJoypadStatesIndex], a
+	ld a, 1
+	ld [wWhichPewterGuy], a
+	predef PewterGuys
+	ld hl, wNPCMovementDirections2
+	ld de, RLEList_PewterGymGuy
+	call DecodeRLEList
+	ld hl, wd72e
+	res 7, [hl]
+	ld hl, wd730
+	set 7, [hl]
+	ld a, $1
+	ld [wNPCMovementScriptFunctionNum], a
+	ret
+
+RLEList_PewterGymPlayer:
+	db 0, $01
+	db D_RIGHT, $02
+	db D_DOWN, $05
+	db D_LEFT, $0B
+	db D_UP, $05
+	db D_LEFT, $0F
+	db $FF
+
+RLEList_PewterGymGuy:
+	db NPC_MOVEMENT_DOWN, $02
+	db NPC_MOVEMENT_LEFT, $0F
+	db NPC_MOVEMENT_UP, $05
+	db NPC_MOVEMENT_LEFT, $0B
+	db NPC_MOVEMENT_DOWN, $05
+	db NPC_MOVEMENT_RIGHT, $03
+	db $FF
+
+FreezeEnemyTrainerSprite::
+	ld a, [wCurMap]
+	cp POKEMON_TOWER_7F
+	ret z ; the Rockets on Pokemon Tower 7F leave after battling, so don't freeze them
+	ld hl, RivalIDs
+	ld a, [wEngagedTrainerClass]
+	ld b, a
+.loop
+	ld a, [hli]
+	cp $ff
+	jr z, .notRival
+	cp b
+	ret z ; the rival leaves after battling, so don't freeze him
+	jr .loop
+.notRival
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	jp SetSpriteMovementBytesToFF
+
+RivalIDs:
+	db OPP_SONY1
+	db OPP_SONY2
+	db OPP_SONY3
+	db $ff
--- a/engine/overworld/cable_club_npc.asm
+++ /dev/null
@@ -1,151 +1,0 @@
-CableClubNPC::
-	ld hl, CableClubNPCWelcomeText
-	call PrintText
-	CheckEvent EVENT_GOT_POKEDEX
-	jp nz, .receivedPokedex
-; if the player hasn't received the pokedex
-	ld c, 60
-	call DelayFrames
-	ld hl, CableClubNPCMakingPreparationsText
-	call PrintText
-	jp .didNotConnect
-.receivedPokedex
-	ld a, $1
-	ld [wMenuJoypadPollCount], a
-	ld a, 90
-	ld [wLinkTimeoutCounter], a
-.establishConnectionLoop
-	ld a, [hSerialConnectionStatus]
-	cp USING_INTERNAL_CLOCK
-	jr z, .establishedConnection
-	cp USING_EXTERNAL_CLOCK
-	jr z, .establishedConnection
-	ld a, CONNECTION_NOT_ESTABLISHED
-	ld [hSerialConnectionStatus], a
-	ld a, ESTABLISH_CONNECTION_WITH_EXTERNAL_CLOCK
-	ld [rSB], a
-	xor a
-	ld [hSerialReceiveData], a
-	ld a, START_TRANSFER_EXTERNAL_CLOCK
-	ld [rSC], a
-	ld a, [wLinkTimeoutCounter]
-	dec a
-	ld [wLinkTimeoutCounter], a
-	jr z, .failedToEstablishConnection
-	ld a, ESTABLISH_CONNECTION_WITH_INTERNAL_CLOCK
-	ld [rSB], a
-	ld a, START_TRANSFER_INTERNAL_CLOCK
-	ld [rSC], a
-	call DelayFrame
-	jr .establishConnectionLoop
-.establishedConnection
-	call Serial_SendZeroByte
-	call DelayFrame
-	call Serial_SendZeroByte
-	ld c, 50
-	call DelayFrames
-	ld hl, CableClubNPCPleaseApplyHereHaveToSaveText
-	call PrintText
-	xor a
-	ld [wMenuJoypadPollCount], a
-	call YesNoChoice
-	ld a, $1
-	ld [wMenuJoypadPollCount], a
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .choseNo
-	callab SaveSAVtoSRAM
-	call WaitForSoundToFinish
-	ld a, SFX_SAVE
-	call PlaySoundWaitForCurrent
-	ld hl, CableClubNPCPleaseWaitText
-	call PrintText
-	ld hl, wUnknownSerialCounter
-	ld a, $3
-	ld [hli], a
-	xor a
-	ld [hl], a
-	ld [hSerialReceivedNewData], a
-	ld [wSerialExchangeNybbleSendData], a
-	call Serial_SyncAndExchangeNybble
-	ld hl, wUnknownSerialCounter
-	ld a, [hli]
-	inc a
-	jr nz, .connected
-	ld a, [hl]
-	inc a
-	jr nz, .connected
-	ld b, 10
-.syncLoop
-	call DelayFrame
-	call Serial_SendZeroByte
-	dec b
-	jr nz, .syncLoop
-	call CloseLinkConnection
-	ld hl, CableClubNPCLinkClosedBecauseOfInactivityText
-	call PrintText
-	jr .didNotConnect
-.failedToEstablishConnection
-	ld hl, CableClubNPCAreaReservedFor2FriendsLinkedByCableText
-	call PrintText
-	jr .didNotConnect
-.choseNo
-	call CloseLinkConnection
-	ld hl, CableClubNPCPleaseComeAgainText
-	call PrintText
-.didNotConnect
-	xor a
-	ld hl, wUnknownSerialCounter
-	ld [hli], a
-	ld [hl], a
-	ld hl, wd72e
-	res 6, [hl]
-	xor a
-	ld [wMenuJoypadPollCount], a
-	ret
-.connected
-	xor a
-	ld [hld], a
-	ld [hl], a
-	jpab LinkMenu
-
-CableClubNPCAreaReservedFor2FriendsLinkedByCableText:
-	TX_FAR _CableClubNPCAreaReservedFor2FriendsLinkedByCableText
-	db "@"
-
-CableClubNPCWelcomeText:
-	TX_FAR _CableClubNPCWelcomeText
-	db "@"
-
-CableClubNPCPleaseApplyHereHaveToSaveText:
-	TX_FAR _CableClubNPCPleaseApplyHereHaveToSaveText
-	db "@"
-
-CableClubNPCPleaseWaitText:
-	TX_FAR _CableClubNPCPleaseWaitText
-	TX_DELAY
-	db "@"
-
-CableClubNPCLinkClosedBecauseOfInactivityText:
-	TX_FAR _CableClubNPCLinkClosedBecauseOfInactivityText
-	db "@"
-
-CableClubNPCPleaseComeAgainText:
-	TX_FAR _CableClubNPCPleaseComeAgainText
-	db "@"
-
-CableClubNPCMakingPreparationsText:
-	TX_FAR _CableClubNPCMakingPreparationsText
-	db "@"
-
-CloseLinkConnection:
-	call Delay3
-	ld a, CONNECTION_NOT_ESTABLISHED
-	ld [hSerialConnectionStatus], a
-	ld a, ESTABLISH_CONNECTION_WITH_EXTERNAL_CLOCK
-	ld [rSB], a
-	xor a
-	ld [hSerialReceiveData], a
-	ld a, START_TRANSFER_EXTERNAL_CLOCK
-	ld [rSC], a
-	ret
--- a/engine/overworld/card_key.asm
+++ /dev/null
@@ -1,112 +1,0 @@
-PrintCardKeyText:
-	ld hl, SilphCoMapList
-	ld a, [wCurMap]
-	ld b, a
-.silphCoMapListLoop
-	ld a, [hli]
-	cp $ff
-	ret z
-	cp b
-	jr nz, .silphCoMapListLoop
-	predef GetTileAndCoordsInFrontOfPlayer
-	ld a, [wTileInFrontOfPlayer]
-	cp $18
-	jr z, .cardKeyDoorInFrontOfPlayer
-	cp $24
-	jr z, .cardKeyDoorInFrontOfPlayer
-	ld b, a
-	ld a, [wCurMap]
-	cp SILPH_CO_11F
-	ret nz
-	ld a, b
-	cp $5e
-	ret nz
-.cardKeyDoorInFrontOfPlayer
-	ld b, CARD_KEY
-	call IsItemInBag
-	jr z, .noCardKey
-	call GetCoordsInFrontOfPlayer
-	push de
-	tx_pre_id CardKeySuccessText
-	ld [hSpriteIndexOrTextID], a
-	call PrintPredefTextID
-	pop de
-	srl d
-	ld a, d
-	ld b, a
-	ld [wCardKeyDoorY], a
-	srl e
-	ld a, e
-	ld c, a
-	ld [wCardKeyDoorX], a
-	ld a, [wCurMap]
-	cp SILPH_CO_11F
-	jr nz, .notSilphCo11F
-	ld a, $3
-	jr .replaceCardKeyDoorTileBlock
-.notSilphCo11F
-	ld a, $e
-.replaceCardKeyDoorTileBlock
-	ld [wNewTileBlockID], a
-	predef ReplaceTileBlock
-	ld hl, wCurrentMapScriptFlags
-	set 5, [hl]
-	ld a, SFX_GO_INSIDE
-	jp PlaySound
-.noCardKey
-	tx_pre_id CardKeyFailText
-	ld [hSpriteIndexOrTextID], a
-	jp PrintPredefTextID
-
-SilphCoMapList:
-	db SILPH_CO_2F
-	db SILPH_CO_3F
-	db SILPH_CO_4F
-	db SILPH_CO_5F
-	db SILPH_CO_6F
-	db SILPH_CO_7F
-	db SILPH_CO_8F
-	db SILPH_CO_9F
-	db SILPH_CO_10F
-	db SILPH_CO_11F
-	db $FF
-
-CardKeySuccessText::
-	TX_FAR _CardKeySuccessText1
-	TX_SFX_ITEM_1
-	TX_FAR _CardKeySuccessText2
-	db "@"
-
-CardKeyFailText::
-	TX_FAR _CardKeyFailText
-	db "@"
-
-; d = Y
-; e = X
-GetCoordsInFrontOfPlayer:
-	ld a, [wYCoord]
-	ld d, a
-	ld a, [wXCoord]
-	ld e, a
-	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
-	and a
-	jr nz, .notFacingDown
-; facing down
-	inc d
-	ret
-.notFacingDown
-	cp SPRITE_FACING_UP
-	jr nz, .notFacingUp
-; facing up
-	dec d
-	ret
-.notFacingUp
-	cp SPRITE_FACING_LEFT
-	jr nz, .notFacingLeft
-; facing left
-	dec e
-	ret
-.notFacingLeft
-; facing right
-	inc e
-	ret
--- a/engine/overworld/cinnabar_lab.asm
+++ /dev/null
@@ -1,123 +1,0 @@
-GiveFossilToCinnabarLab::
-	ld hl, wd730
-	set 6, [hl]
-	xor a
-	ld [wCurrentMenuItem], a
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, [wFilteredBagItemsCount]
-	dec a
-	ld [wMaxMenuItem], a
-	ld a, 2
-	ld [wTopMenuItemY], a
-	ld a, 1
-	ld [wTopMenuItemX], a
-	ld a, [wFilteredBagItemsCount]
-	dec a
-	ld bc, 2
-	ld hl, 3
-	call AddNTimes
-	dec l
-	ld b, l
-	ld c, $d
-	coord hl, 0, 0
-	call TextBoxBorder
-	call UpdateSprites
-	call PrintFossilsInBag
-	ld hl, wd730
-	res 6, [hl]
-	call HandleMenuInput
-	bit 1, a ; pressed B?
-	jr nz, .cancelledGivingFossil
-	ld hl, wFilteredBagItems
-	ld a, [wCurrentMenuItem]
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hl]
-	ld [$ffdb], a
-	cp DOME_FOSSIL
-	jr z, .choseDomeFossil
-	cp HELIX_FOSSIL
-	jr z, .choseHelixFossil
-	ld b, AERODACTYL
-	jr .fossilSelected
-.choseHelixFossil
-	ld b, OMANYTE
-	jr .fossilSelected
-.choseDomeFossil
-	ld b, KABUTO
-.fossilSelected
-	ld [wFossilItem], a
-	ld a, b
-	ld [wFossilMon], a
-	call LoadFossilItemAndMonName
-	ld hl, LabFossil_610ae
-	call PrintText
-	call YesNoChoice
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .cancelledGivingFossil
-	ld hl, LabFossil_610b3
-	call PrintText
-	ld a, [wFossilItem]
-	ld [hItemToRemoveID], a
-	callba RemoveItemByID
-	ld hl, LabFossil_610b8
-	call PrintText
-	SetEvents EVENT_GAVE_FOSSIL_TO_LAB, EVENT_LAB_STILL_REVIVING_FOSSIL
-	ret
-.cancelledGivingFossil
-	ld hl, LabFossil_610bd
-	call PrintText
-	ret
-
-LabFossil_610ae:
-	TX_FAR _Lab4Text_610ae
-	db "@"
-
-LabFossil_610b3:
-	TX_FAR _Lab4Text_610b3
-	db "@"
-
-LabFossil_610b8:
-	TX_FAR _Lab4Text_610b8
-	db "@"
-
-LabFossil_610bd:
-	TX_FAR _Lab4Text_610bd
-	db "@"
-
-PrintFossilsInBag:
-; Prints each fossil in the player's bag on a separate line in the menu.
-	ld hl, wFilteredBagItems
-	xor a
-	ld [hItemCounter], a
-.loop
-	ld a, [hli]
-	cp $ff
-	ret z
-	push hl
-	ld [wd11e], a
-	call GetItemName
-	coord hl, 2, 2
-	ld a, [hItemCounter]
-	ld bc, SCREEN_WIDTH * 2
-	call AddNTimes
-	ld de, wcd6d
-	call PlaceString
-	ld hl, hItemCounter
-	inc [hl]
-	pop hl
-	jr .loop
-
-; loads the names of the fossil item and the resulting mon
-LoadFossilItemAndMonName::
-	ld a, [wFossilMon]
-	ld [wd11e], a
-	call GetMonName
-	call CopyStringToCF4B
-	ld a, [wFossilItem]
-	ld [wd11e], a
-	call GetItemName
-	ret
--- /dev/null
+++ b/engine/overworld/dust_smoke.asm
@@ -1,0 +1,93 @@
+AnimateBoulderDust:
+	ld a, $1
+	ld [wWhichAnimationOffsets], a ; select the boulder dust offsets
+	ld a, [wUpdateSpritesEnabled]
+	push af
+	ld a, $ff
+	ld [wUpdateSpritesEnabled], a
+	ld a, %11100100
+	ld [rOBP1], a
+	call LoadSmokeTileFourTimes
+	callba WriteCutOrBoulderDustAnimationOAMBlock
+	ld c, 8 ; number of steps in animation
+.loop
+	push bc
+	call GetMoveBoulderDustFunctionPointer
+	ld bc, .returnAddress
+	push bc
+	ld c, 4
+	jp hl
+.returnAddress
+	ld a, [rOBP1]
+	xor %01100100
+	ld [rOBP1], a
+	call Delay3
+	pop bc
+	dec c
+	jr nz, .loop
+	pop af
+	ld [wUpdateSpritesEnabled], a
+	jp LoadPlayerSpriteGraphics
+
+GetMoveBoulderDustFunctionPointer:
+	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+	ld hl, MoveBoulderDustFunctionPointerTable
+	ld c, a
+	ld b, $0
+	add hl, bc
+	ld a, [hli]
+	ld [wCoordAdjustmentAmount], a
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	push hl
+	ld hl, wOAMBuffer + $90
+	ld d, $0
+	add hl, de
+	ld e, l
+	ld d, h
+	pop hl
+	ret
+
+MoveBoulderDustFunctionPointerTable:
+; facing down
+	db $FF,$00
+	dw AdjustOAMBlockYPos
+
+; facing up
+	db $01,$00
+	dw AdjustOAMBlockYPos
+
+; facing left
+	db $01,$01
+	dw AdjustOAMBlockXPos
+
+; facing right
+	db $FF,$01
+	dw AdjustOAMBlockXPos
+
+LoadSmokeTileFourTimes::
+	ld hl, vChars1 + $7c0
+	ld c, $4
+.loop
+	push bc
+	push hl
+	call LoadSmokeTile
+	pop hl
+	ld bc, $10
+	add hl, bc
+	pop bc
+	dec c
+	jr nz, .loop
+	ret
+
+LoadSmokeTile:
+	ld de, SSAnneSmokePuffTile
+	lb bc, BANK(SSAnneSmokePuffTile), (SSAnneSmokePuffTileEnd - SSAnneSmokePuffTile) / $10
+	jp CopyVideoData
+
+SSAnneSmokePuffTile:
+	INCBIN "gfx/overworld/smoke.2bpp"
+SSAnneSmokePuffTileEnd:
--- a/engine/overworld/hidden_items.asm
+++ /dev/null
@@ -1,161 +1,0 @@
-HiddenItems:
-	ld hl, HiddenItemCoords
-	call FindHiddenItemOrCoinsIndex
-	ld [wHiddenItemOrCoinsIndex], a
-	ld hl, wObtainedHiddenItemsFlags
-	ld a, [wHiddenItemOrCoinsIndex]
-	ld c, a
-	ld b, FLAG_TEST
-	predef FlagActionPredef
-	ld a, c
-	and a
-	ret nz
-	call EnableAutoTextBoxDrawing
-	ld a, 1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld a, [wHiddenObjectFunctionArgument] ; item ID
-	ld [wd11e], a
-	call GetItemName
-	tx_pre_jump FoundHiddenItemText
-
-INCLUDE "data/hidden_item_coords.asm"
-
-FoundHiddenItemText::
-	TX_FAR _FoundHiddenItemText
-	TX_ASM
-	ld a, [wHiddenObjectFunctionArgument] ; item ID
-	ld b, a
-	ld c, 1
-	call GiveItem
-	jr nc, .bagFull
-	ld hl, wObtainedHiddenItemsFlags
-	ld a, [wHiddenItemOrCoinsIndex]
-	ld c, a
-	ld b, FLAG_SET
-	predef FlagActionPredef
-	ld a, SFX_GET_ITEM_2
-	call PlaySoundWaitForCurrent
-	call WaitForSoundToFinish
-	jp TextScriptEnd
-.bagFull
-	call WaitForTextScrollButtonPress ; wait for button press
-	xor a
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld hl, HiddenItemBagFullText
-	call PrintText
-	jp TextScriptEnd
-
-HiddenItemBagFullText::
-	TX_FAR _HiddenItemBagFullText
-	db "@"
-
-HiddenCoins:
-	ld b, COIN_CASE
-	predef GetQuantityOfItemInBag
-	ld a, b
-	and a
-	ret z
-	ld hl, HiddenCoinCoords
-	call FindHiddenItemOrCoinsIndex
-	ld [wHiddenItemOrCoinsIndex], a
-	ld hl, wObtainedHiddenCoinsFlags
-	ld a, [wHiddenItemOrCoinsIndex]
-	ld c, a
-	ld b, FLAG_TEST
-	predef FlagActionPredef
-	ld a, c
-	and a
-	ret nz
-	xor a
-	ld [hUnusedCoinsByte], a
-	ld [hCoins], a
-	ld [hCoins + 1], a
-	ld a, [wHiddenObjectFunctionArgument]
-	sub COIN
-	cp 10
-	jr z, .bcd10
-	cp 20
-	jr z, .bcd20
-	cp 40
-	jr z, .bcd20 ; should be bcd40
-	jr .bcd100
-.bcd10
-	ld a, $10
-	ld [hCoins + 1], a
-	jr .bcdDone
-.bcd20
-	ld a, $20
-	ld [hCoins + 1], a
-	jr .bcdDone
-.bcd40 ; due to a typo, this is never used
-	ld a, $40
-	ld [hCoins + 1], a
-	jr .bcdDone
-.bcd100
-	ld a, $1
-	ld [hCoins], a
-.bcdDone
-	ld de, wPlayerCoins + 1
-	ld hl, hCoins + 1
-	ld c, $2
-	predef AddBCDPredef
-	ld hl, wObtainedHiddenCoinsFlags
-	ld a, [wHiddenItemOrCoinsIndex]
-	ld c, a
-	ld b, FLAG_SET
-	predef FlagActionPredef
-	call EnableAutoTextBoxDrawing
-	ld a, [wPlayerCoins]
-	cp $99
-	jr nz, .roomInCoinCase
-	ld a, [wPlayerCoins + 1]
-	cp $99
-	jr nz, .roomInCoinCase
-	tx_pre_id DroppedHiddenCoinsText
-	jr .done
-.roomInCoinCase
-	tx_pre_id FoundHiddenCoinsText
-.done
-	jp PrintPredefTextID
-
-INCLUDE "data/hidden_coins.asm"
-
-FoundHiddenCoinsText::
-	TX_FAR _FoundHiddenCoinsText
-	TX_SFX_ITEM_2
-	db "@"
-
-DroppedHiddenCoinsText::
-	TX_FAR _FoundHiddenCoins2Text
-	TX_SFX_ITEM_2
-	TX_FAR _DroppedHiddenCoinsText
-	db "@"
-
-FindHiddenItemOrCoinsIndex:
-	ld a, [wHiddenObjectY]
-	ld d, a
-	ld a, [wHiddenObjectX]
-	ld e, a
-	ld a, [wCurMap]
-	ld b, a
-	ld c, -1
-.loop
-	inc c
-	ld a, [hli]
-	cp $ff ; end of the list?
-	ret z  ; if so, we're done here
-	cp b
-	jr nz, .next1
-	ld a, [hli]
-	cp d
-	jr nz, .next2
-	ld a, [hli]
-	cp e
-	jr nz, .loop
-	ld a, c
-	ret
-.next1
-	inc hl
-.next2
-	inc hl
-	jr .loop
--- a/engine/overworld/item.asm
+++ /dev/null
@@ -1,54 +1,0 @@
-PickUpItem:
-	call EnableAutoTextBoxDrawing
-
-	ld a, [hSpriteIndexOrTextID]
-	ld b, a
-	ld hl, wMissableObjectList
-.missableObjectsListLoop
-	ld a, [hli]
-	cp $ff
-	ret z
-	cp b
-	jr z, .isMissable
-	inc hl
-	jr .missableObjectsListLoop
-
-.isMissable
-	ld a, [hl]
-	ld [$ffdb], a
-
-	ld hl, wMapSpriteExtraData
-	ld a, [hSpriteIndexOrTextID]
-	dec a
-	add a
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hl]
-	ld b, a ; item
-	ld c, 1 ; quantity
-	call GiveItem
-	jr nc, .BagFull
-
-	ld a, [$ffdb]
-	ld [wMissableObjectIndex], a
-	predef HideObject
-	ld a, 1
-	ld [wDoNotWaitForButtonPressAfterDisplayingText], a
-	ld hl, FoundItemText
-	jr .print
-
-.BagFull
-	ld hl, NoMoreRoomForItemText
-.print
-	call PrintText
-	ret
-
-FoundItemText:
-	TX_FAR _FoundItemText
-	TX_SFX_ITEM_1
-	db "@"
-
-NoMoreRoomForItemText:
-	TX_FAR _NoMoreRoomForItemText
-	db "@"
--- a/engine/overworld/map_sprite_functions1.asm
+++ /dev/null
@@ -1,356 +1,0 @@
-_UpdateSprites::
-	ld h, $c1
-	inc h
-	ld a, $e    ; wSpriteStateData2 + $0e
-.spriteLoop
-	ld l, a
-	sub $e
-	ld c, a
-	ld [H_CURRENTSPRITEOFFSET], a
-	ld a, [hl]
-	and a
-	jr z, .skipSprite   ; tests $c2Xe
-	push hl
-	push de
-	push bc
-	call .updateCurrentSprite
-	pop bc
-	pop de
-	pop hl
-.skipSprite
-	ld a, l
-	add $10             ; move to next sprite
-	cp $e               ; test for overflow (back at $0e)
-	jr nz, .spriteLoop
-	ret
-.updateCurrentSprite
-	cp $1
-	jp nz, UpdateNonPlayerSprite
-	jp UpdatePlayerSprite
-
-UpdateNonPlayerSprite:
-	dec a
-	swap a
-	ld [$ff93], a  ; $10 * sprite#
-	ld a, [wNPCMovementScriptSpriteOffset] ; some sprite offset?
-	ld b, a
-	ld a, [H_CURRENTSPRITEOFFSET]
-	cp b
-	jr nz, .unequal
-	jp DoScriptedNPCMovement
-.unequal
-	jp UpdateNPCSprite
-
-; This detects if the current sprite (whose offset is at H_CURRENTSPRITEOFFSET)
-; is going to collide with another sprite by looping over the other sprites.
-; The current sprite's offset will be labelled with i (e.g. $c1i0).
-; The loop sprite's offset will labelled with j (e.g. $c1j0).
-;
-; Note that the Y coordinate of the sprite (in [$c1k4]) is one of the following
-; 9 values when the sprite is aligned with the grid: $fc, $0c, $1c, $2c, ..., $7c.
-; The reason that 4 is added below to the coordinate is to make it align with a
-; multiple of $10 to make comparisons easier.
-DetectCollisionBetweenSprites:
-	nop
-
-	ld h, wSpriteStateData1 / $100
-	ld a, [H_CURRENTSPRITEOFFSET]
-	add wSpriteStateData1 % $100
-	ld l, a
-
-	ld a, [hl] ; a = [$c1i0] (picture) (0 if slot is unused)
-	and a ; is this sprite slot slot used?
-	ret z ; return if not used
-
-	ld a, l
-	add 3
-	ld l, a
-
-	ld a, [hli] ; a = [$c1i3] (delta Y) (-1, 0, or 1)
-	call SetSpriteCollisionValues
-
-	ld a, [hli] ; a = [$C1i4] (Y screen coordinate)
-	add 4 ; align with multiple of $10
-
-; The effect of the following 3 lines is to
-; add 7 to a if moving south or
-; subtract 7 from a if moving north.
-	add b
-	and $f0
-	or c
-
-	ld [$ff90], a ; store Y coordinate adjusted for direction of movement
-
-	ld a, [hli] ; a = [$c1i5] (delta X) (-1, 0, or 1)
-	call SetSpriteCollisionValues
-	ld a, [hl] ; a = [$C1i6] (X screen coordinate)
-
-; The effect of the following 3 lines is to
-; add 7 to a if moving east or
-; subtract 7 from a if moving west.
-	add b
-	and $f0
-	or c
-
-	ld [$ff91], a ; store X coordinate adjusted for direction of movement
-
-	ld a, l
-	add 7
-	ld l, a
-
-	xor a
-	ld [hld], a ; zero [$c1id] XXX what's [$c1id] for?
-	ld [hld], a ; zero [$c1ic] (directions in which collisions occurred)
-
-	ld a, [$ff91]
-	ld [hld], a ; [$c1ib] = adjusted X coordinate
-	ld a, [$ff90]
-	ld [hl], a ; [$c1ia] = adjusted Y coordinate
-
-	xor a ; zero the loop counter
-
-.loop
-	ld [$ff8f], a ; store loop counter
-	swap a
-	ld e, a
-	ld a, [H_CURRENTSPRITEOFFSET]
-	cp e ; does the loop sprite match the current sprite?
-	jp z, .next ; go to the next sprite if they match
-
-	ld d, h
-	ld a, [de] ; a = [$c1j0] (picture) (0 if slot is unused)
-	and a ; is this sprite slot slot used?
-	jp z, .next ; go the next sprite if not used
-
-	inc e
-	inc e
-	ld a, [de] ; a = [$c1j2] ($ff means the sprite is offscreen)
-	inc a
-	jp z, .next ; go the next sprite if offscreen
-
-	ld a, [H_CURRENTSPRITEOFFSET]
-	add 10
-	ld l, a
-
-	inc e
-	ld a, [de] ; a = [$c1j3] (delta Y)
-	call SetSpriteCollisionValues
-
-	inc e
-	ld a, [de] ; a = [$C1j4] (Y screen coordinate)
-	add 4 ; align with multiple of $10
-
-; The effect of the following 3 lines is to
-; add 7 to a if moving south or
-; subtract 7 from a if moving north.
-	add b
-	and $f0
-	or c
-
-	sub [hl] ; subtract the adjusted Y coordinate of sprite i ([$c1ia]) from that of sprite j
-
-; calculate the absolute value of the difference to get the distance
-	jr nc, .noCarry1
-	cpl
-	inc a
-.noCarry1
-	ld [$ff90], a ; store the distance between the two sprites' adjusted Y values
-
-; Use the carry flag set by the above subtraction to determine which sprite's
-; Y coordinate is larger. This information is used later to set [$c1ic],
-; which stores which direction the collision occurred in.
-; The following 5 lines set the lowest 2 bits of c, which are later shifted left by 2.
-; If sprite i's Y is larger, set lowest 2 bits of c to 10.
-; If sprite j's Y is larger or both are equal, set lowest 2 bits of c to 01.
-	push af
-	rl c
-	pop af
-	ccf
-	rl c
-
-; If sprite i's delta Y is 0, then b = 7, else b = 9.
-	ld b, 7
-	ld a, [hl] ; a = [$c1ia] (adjusted Y coordinate)
-	and $f
-	jr z, .next1
-	ld b, 9
-
-.next1
-	ld a, [$ff90] ; a = distance between adjusted Y coordinates
-	sub b
-	ld [$ff92], a ; store distance adjusted using sprite i's direction
-	ld a, b
-	ld [$ff90], a ; store 7 or 9 depending on sprite i's delta Y
-	jr c, .checkXDistance
-
-; If sprite j's delta Y is 0, then b = 7, else b = 9.
-	ld b, 7
-	dec e
-	ld a, [de] ; a = [$c1j3] (delta Y)
-	inc e
-	and a
-	jr z, .next2
-	ld b, 9
-
-.next2
-	ld a, [$ff92] ; a = distance adjusted using sprite i's direction
-	sub b ; adjust distance using sprite j's direction
-	jr z, .checkXDistance
-	jr nc, .next ; go to next sprite if distance is still positive after both adjustments
-
-.checkXDistance
-	inc e
-	inc l
-	ld a, [de] ; a = [$c1j5] (delta X)
-
-	push bc
-
-	call SetSpriteCollisionValues
-	inc e
-	ld a, [de] ; a = [$c1j6] (X screen coordinate)
-
-; The effect of the following 3 lines is to
-; add 7 to a if moving east or
-; subtract 7 from a if moving west.
-	add b
-	and $f0
-	or c
-
-	pop bc
-
-	sub [hl] ; subtract the adjusted X coordinate of sprite i ([$c1ib]) from that of sprite j
-
-; calculate the absolute value of the difference to get the distance
-	jr nc, .noCarry2
-	cpl
-	inc a
-.noCarry2
-	ld [$ff91], a ; store the distance between the two sprites' adjusted X values
-
-; Use the carry flag set by the above subtraction to determine which sprite's
-; X coordinate is larger. This information is used later to set [$c1ic],
-; which stores which direction the collision occurred in.
-; The following 5 lines set the lowest 2 bits of c.
-; If sprite i's X is larger, set lowest 2 bits of c to 10.
-; If sprite j's X is larger or both are equal, set lowest 2 bits of c to 01.
-	push af
-	rl c
-	pop af
-	ccf
-	rl c
-
-; If sprite i's delta X is 0, then b = 7, else b = 9.
-	ld b, 7
-	ld a, [hl] ; a = [$c1ib] (adjusted X coordinate)
-	and $f
-	jr z, .next3
-	ld b, 9
-
-.next3
-	ld a, [$ff91] ; a = distance between adjusted X coordinates
-	sub b
-	ld [$ff92], a ; store distance adjusted using sprite i's direction
-	ld a, b
-	ld [$ff91], a ; store 7 or 9 depending on sprite i's delta X
-	jr c, .collision
-
-; If sprite j's delta X is 0, then b = 7, else b = 9.
-	ld b, 7
-	dec e
-	ld a, [de] ; a = [$c1j5] (delta X)
-	inc e
-	and a
-	jr z, .next4
-	ld b, 9
-
-.next4
-	ld a, [$ff92] ; a = distance adjusted using sprite i's direction
-	sub b ; adjust distance using sprite j's direction
-	jr z, .collision
-	jr nc, .next ; go to next sprite if distance is still positive after both adjustments
-
-.collision
-	ld a, [$ff91] ; a = 7 or 9 depending on sprite i's delta X
-	ld b, a
-	ld a, [$ff90] ; a = 7 or 9 depending on sprite i's delta Y
-	inc l
-
-; If delta X isn't 0 and delta Y is 0, then b = %0011, else b = %1100.
-; (note that normally if delta X isn't 0, then delta Y must be 0 and vice versa)
-	cp b
-	jr c, .next5
-	ld b, %1100
-	jr .next6
-.next5
-	ld b, %0011
-
-.next6
-	ld a, c ; c has 2 bits set (one of bits 0-1 is set for the X axis and one of bits 2-3 for the Y axis)
-	and b ; we select either the bit in bits 0-1 or bits 2-3 based on the calculation immediately above
-	or [hl] ; or with existing collision direction bits in [$c1ic]
-	ld [hl], a ; store new value
-	ld a, c ; useless code because a is overwritten before being used again
-
-; set bit in [$c1ie] or [$c1if] to indicate which sprite the collision occurred with
-	inc l
-	inc l
-	ld a, [$ff8f] ; a = loop counter
-	ld de, SpriteCollisionBitTable
-	add a
-	add e
-	ld e, a
-	jr nc, .noCarry3
-	inc d
-.noCarry3
-	ld a, [de]
-	or [hl]
-	ld [hli], a
-	inc de
-	ld a, [de]
-	or [hl]
-	ld [hl], a
-
-.next
-	ld a, [$ff8f] ; a = loop counter
-	inc a
-	cp $10
-	jp nz, .loop
-	ret
-
-; takes delta X or delta Y in a
-; b = delta X/Y
-; c = 0 if delta X/Y is 0
-; c = 7 if delta X/Y is 1
-; c = 9 if delta X/Y is -1
-SetSpriteCollisionValues:
-	and a
-	ld b, 0
-	ld c, 0
-	jr z, .done
-	ld c, 9
-	cp -1
-	jr z, .ok
-	ld c, 7
-	ld a, 0
-.ok
-	ld b, a
-.done
-	ret
-
-SpriteCollisionBitTable:
-	db %00000000,%00000001
-	db %00000000,%00000010
-	db %00000000,%00000100
-	db %00000000,%00001000
-	db %00000000,%00010000
-	db %00000000,%00100000
-	db %00000000,%01000000
-	db %00000000,%10000000
-	db %00000001,%00000000
-	db %00000010,%00000000
-	db %00000100,%00000000
-	db %00001000,%00000000
-	db %00010000,%00000000
-	db %00100000,%00000000
-	db %01000000,%00000000
-	db %10000000,%00000000
--- a/engine/overworld/npc_movement.asm
+++ /dev/null
@@ -1,292 +1,0 @@
-PlayerStepOutFromDoor::
-	ld hl, wd730
-	res 1, [hl]
-	call IsPlayerStandingOnDoorTile
-	jr nc, .notStandingOnDoor
-	ld a, $fc
-	ld [wJoyIgnore], a
-	ld hl, wd736
-	set 1, [hl]
-	ld a, $1
-	ld [wSimulatedJoypadStatesIndex], a
-	ld a, D_DOWN
-	ld [wSimulatedJoypadStatesEnd], a
-	xor a
-	ld [wSpriteStateData1 + 2], a
-	call StartSimulatingJoypadStates
-	ret
-.notStandingOnDoor
-	xor a
-	ld [wWastedByteCD3A], a
-	ld [wSimulatedJoypadStatesIndex], a
-	ld [wSimulatedJoypadStatesEnd], a
-	ld hl, wd736
-	res 0, [hl]
-	res 1, [hl]
-	ld hl, wd730
-	res 7, [hl]
-	ret
-
-_EndNPCMovementScript::
-	ld hl, wd730
-	res 7, [hl]
-	ld hl, wd72e
-	res 7, [hl]
-	ld hl, wd736
-	res 0, [hl]
-	res 1, [hl]
-	xor a
-	ld [wNPCMovementScriptSpriteOffset], a
-	ld [wNPCMovementScriptPointerTableNum], a
-	ld [wNPCMovementScriptFunctionNum], a
-	ld [wWastedByteCD3A], a
-	ld [wSimulatedJoypadStatesIndex], a
-	ld [wSimulatedJoypadStatesEnd], a
-	ret
-
-PalletMovementScriptPointerTable::
-	dw PalletMovementScript_OakMoveLeft
-	dw PalletMovementScript_PlayerMoveLeft
-	dw PalletMovementScript_WaitAndWalkToLab
-	dw PalletMovementScript_WalkToLab
-	dw PalletMovementScript_Done
-
-PalletMovementScript_OakMoveLeft:
-	ld a, [wXCoord]
-	sub $a
-	ld [wNumStepsToTake], a
-	jr z, .playerOnLeftTile
-; The player is on the right tile of the northern path out of Pallet Town and
-; Prof. Oak is below.
-; Make Prof. Oak step to the left.
-	ld b, 0
-	ld c, a
-	ld hl, wNPCMovementDirections2
-	ld a, NPC_MOVEMENT_LEFT
-	call FillMemory
-	ld [hl], $ff
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	ld de, wNPCMovementDirections2
-	call MoveSprite
-	ld a, $1
-	ld [wNPCMovementScriptFunctionNum], a
-	jr .done
-; The player is on the left tile of the northern path out of Pallet Town and
-; Prof. Oak is below.
-; Prof. Oak is already where he needs to be.
-.playerOnLeftTile
-	ld a, $3
-	ld [wNPCMovementScriptFunctionNum], a
-.done
-	ld hl, wFlags_D733
-	set 1, [hl]
-	ld a, $fc
-	ld [wJoyIgnore], a
-	ret
-
-PalletMovementScript_PlayerMoveLeft:
-	ld a, [wd730]
-	bit 0, a ; is an NPC being moved by a script?
-	ret nz ; return if Oak is still moving
-	ld a, [wNumStepsToTake]
-	ld [wSimulatedJoypadStatesIndex], a
-	ld [hNPCMovementDirections2Index], a
-	predef ConvertNPCMovementDirectionsToJoypadMasks
-	call StartSimulatingJoypadStates
-	ld a, $2
-	ld [wNPCMovementScriptFunctionNum], a
-	ret
-
-PalletMovementScript_WaitAndWalkToLab:
-	ld a, [wSimulatedJoypadStatesIndex]
-	and a ; is the player done moving left yet?
-	ret nz
-
-PalletMovementScript_WalkToLab:
-	xor a
-	ld [wOverrideSimulatedJoypadStatesMask], a
-	ld a, [wSpriteIndex]
-	swap a
-	ld [wNPCMovementScriptSpriteOffset], a
-	xor a
-	ld [wSpriteStateData2 + $06], a
-	ld hl, wSimulatedJoypadStatesEnd
-	ld de, RLEList_PlayerWalkToLab
-	call DecodeRLEList
-	dec a
-	ld [wSimulatedJoypadStatesIndex], a
-	ld hl, wNPCMovementDirections2
-	ld de, RLEList_ProfOakWalkToLab
-	call DecodeRLEList
-	ld hl, wd72e
-	res 7, [hl]
-	ld hl, wd730
-	set 7, [hl]
-	ld a, $4
-	ld [wNPCMovementScriptFunctionNum], a
-	ret
-
-RLEList_ProfOakWalkToLab:
-	db NPC_MOVEMENT_DOWN, $05
-	db NPC_MOVEMENT_LEFT, $01
-	db NPC_MOVEMENT_DOWN, $05
-	db NPC_MOVEMENT_RIGHT, $03
-	db NPC_MOVEMENT_UP, $01
-	db $E0, $01 ; stand still
-	db $FF
-
-RLEList_PlayerWalkToLab:
-	db D_UP, $02
-	db D_RIGHT, $03
-	db D_DOWN, $05
-	db D_LEFT, $01
-	db D_DOWN, $06
-	db $FF
-
-PalletMovementScript_Done:
-	ld a, [wSimulatedJoypadStatesIndex]
-	and a
-	ret nz
-	ld a, HS_PALLET_TOWN_OAK
-	ld [wMissableObjectIndex], a
-	predef HideObject
-	ld hl, wd730
-	res 7, [hl]
-	ld hl, wd72e
-	res 7, [hl]
-	jp EndNPCMovementScript
-
-PewterMuseumGuyMovementScriptPointerTable::
-	dw PewterMovementScript_WalkToMuseum
-	dw PewterMovementScript_Done
-
-PewterMovementScript_WalkToMuseum:
-	ld a, BANK(Music_MuseumGuy)
-	ld [wAudioROMBank], a
-	ld [wAudioSavedROMBank], a
-	ld a, MUSIC_MUSEUM_GUY
-	ld [wNewSoundID], a
-	call PlaySound
-	ld a, [wSpriteIndex]
-	swap a
-	ld [wNPCMovementScriptSpriteOffset], a
-	call StartSimulatingJoypadStates
-	ld hl, wSimulatedJoypadStatesEnd
-	ld de, RLEList_PewterMuseumPlayer
-	call DecodeRLEList
-	dec a
-	ld [wSimulatedJoypadStatesIndex], a
-	xor a
-	ld [wWhichPewterGuy], a
-	predef PewterGuys
-	ld hl, wNPCMovementDirections2
-	ld de, RLEList_PewterMuseumGuy
-	call DecodeRLEList
-	ld hl, wd72e
-	res 7, [hl]
-	ld a, $1
-	ld [wNPCMovementScriptFunctionNum], a
-	ret
-
-RLEList_PewterMuseumPlayer:
-	db 0, $01
-	db D_UP, $03
-	db D_LEFT, $0D
-	db D_UP, $06
-	db $FF
-
-RLEList_PewterMuseumGuy:
-	db NPC_MOVEMENT_UP, $06
-	db NPC_MOVEMENT_LEFT, $0D
-	db NPC_MOVEMENT_UP, $03
-	db NPC_MOVEMENT_LEFT, $01
-	db $FF
-
-PewterMovementScript_Done:
-	ld a, [wSimulatedJoypadStatesIndex]
-	and a
-	ret nz
-	ld hl, wd730
-	res 7, [hl]
-	ld hl, wd72e
-	res 7, [hl]
-	jp EndNPCMovementScript
-
-PewterGymGuyMovementScriptPointerTable::
-	dw PewterMovementScript_WalkToGym
-	dw PewterMovementScript_Done
-
-PewterMovementScript_WalkToGym:
-	ld a, BANK(Music_MuseumGuy)
-	ld [wAudioROMBank], a
-	ld [wAudioSavedROMBank], a
-	ld a, MUSIC_MUSEUM_GUY
-	ld [wNewSoundID], a
-	call PlaySound
-	ld a, [wSpriteIndex]
-	swap a
-	ld [wNPCMovementScriptSpriteOffset], a
-	xor a
-	ld [wSpriteStateData2 + $06], a
-	ld hl, wSimulatedJoypadStatesEnd
-	ld de, RLEList_PewterGymPlayer
-	call DecodeRLEList
-	dec a
-	ld [wSimulatedJoypadStatesIndex], a
-	ld a, 1
-	ld [wWhichPewterGuy], a
-	predef PewterGuys
-	ld hl, wNPCMovementDirections2
-	ld de, RLEList_PewterGymGuy
-	call DecodeRLEList
-	ld hl, wd72e
-	res 7, [hl]
-	ld hl, wd730
-	set 7, [hl]
-	ld a, $1
-	ld [wNPCMovementScriptFunctionNum], a
-	ret
-
-RLEList_PewterGymPlayer:
-	db 0, $01
-	db D_RIGHT, $02
-	db D_DOWN, $05
-	db D_LEFT, $0B
-	db D_UP, $05
-	db D_LEFT, $0F
-	db $FF
-
-RLEList_PewterGymGuy:
-	db NPC_MOVEMENT_DOWN, $02
-	db NPC_MOVEMENT_LEFT, $0F
-	db NPC_MOVEMENT_UP, $05
-	db NPC_MOVEMENT_LEFT, $0B
-	db NPC_MOVEMENT_DOWN, $05
-	db NPC_MOVEMENT_RIGHT, $03
-	db $FF
-
-FreezeEnemyTrainerSprite::
-	ld a, [wCurMap]
-	cp POKEMON_TOWER_7F
-	ret z ; the Rockets on Pokemon Tower 7F leave after battling, so don't freeze them
-	ld hl, RivalIDs
-	ld a, [wEngagedTrainerClass]
-	ld b, a
-.loop
-	ld a, [hli]
-	cp $ff
-	jr z, .notRival
-	cp b
-	ret z ; the rival leaves after battling, so don't freeze him
-	jr .loop
-.notRival
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	jp SetSpriteMovementBytesToFF
-
-RivalIDs:
-	db OPP_SONY1
-	db OPP_SONY2
-	db OPP_SONY3
-	db $ff
--- a/engine/overworld/oaks_aide.asm
+++ /dev/null
@@ -1,71 +1,0 @@
-OaksAideScript:
-	ld hl, OaksAideHiText
-	call PrintText
-	call YesNoChoice
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .choseNo
-	ld hl, wPokedexOwned
-	ld b, wPokedexOwnedEnd - wPokedexOwned
-	call CountSetBits
-	ld a, [wNumSetBits]
-	ld [hOaksAideNumMonsOwned], a
-	ld b, a
-	ld a, [hOaksAideRequirement]
-	cp b
-	jr z, .giveItem
-	jr nc, .notEnoughOwnedMons
-.giveItem
-	ld hl, OaksAideHereYouGoText
-	call PrintText
-	ld a, [hOaksAideRewardItem]
-	ld b, a
-	ld c, 1
-	call GiveItem
-	jr nc, .bagFull
-	ld hl, OaksAideGotItemText
-	call PrintText
-	ld a, $1
-	jr .done
-.bagFull
-	ld hl, OaksAideNoRoomText
-	call PrintText
-	xor a
-	jr .done
-.notEnoughOwnedMons
-	ld hl, OaksAideUhOhText
-	call PrintText
-	ld a, $80
-	jr .done
-.choseNo
-	ld hl, OaksAideComeBackText
-	call PrintText
-	ld a, $ff
-.done
-	ld [hOaksAideResult], a
-	ret
-
-OaksAideHiText:
-	TX_FAR _OaksAideHiText
-	db "@"
-
-OaksAideUhOhText:
-	TX_FAR _OaksAideUhOhText
-	db "@"
-
-OaksAideComeBackText:
-	TX_FAR _OaksAideComeBackText
-	db "@"
-
-OaksAideHereYouGoText:
-	TX_FAR _OaksAideHereYouGoText
-	db "@"
-
-OaksAideGotItemText:
-	TX_FAR _OaksAideGotItemText
-	TX_SFX_ITEM_1
-	db "@"
-
-OaksAideNoRoomText:
-	TX_FAR _OaksAideNoRoomText
-	db "@"
--- a/engine/overworld/oam.asm
+++ /dev/null
@@ -1,189 +1,0 @@
-PrepareOAMData::
-; Determine OAM data for currently visible
-; sprites and write it to wOAMBuffer.
-
-	ld a, [wUpdateSpritesEnabled]
-	dec a
-	jr z, .updateEnabled
-
-	cp -1
-	ret nz
-	ld [wUpdateSpritesEnabled], a
-	jp HideSprites
-
-.updateEnabled
-	xor a
-	ld [hOAMBufferOffset], a
-
-.spriteLoop
-	ld [hSpriteOffset2], a
-
-	ld d, wSpriteStateData1 / $100
-	ld a, [hSpriteOffset2]
-	ld e, a
-	ld a, [de] ; c1x0
-	and a
-	jp z, .nextSprite
-
-	inc e
-	inc e
-	ld a, [de] ; c1x2 (facing/anim)
-	ld [wd5cd], a
-	cp $ff ; off-screen (don't draw)
-	jr nz, .visible
-
-	call GetSpriteScreenXY
-	jr .nextSprite
-
-.visible
-	cp $a0 ; is the sprite unchanging like an item ball or boulder?
-	jr c, .usefacing
-
-; unchanging
-	and $f
-	add $10 ; skip to the second half of the table which doesn't account for facing direction
-	jr .next
-
-.usefacing
-	and $f
-
-.next
-	ld l, a
-
-; get sprite priority
-	push de
-	inc d
-	ld a, e
-	add $5
-	ld e, a
-	ld a, [de] ; c2x7
-	and $80
-	ld [hSpritePriority], a ; temp store sprite priority
-	pop de
-
-; read the entry from the table
-	ld h, 0
-	ld bc, SpriteFacingAndAnimationTable
-	add hl, hl
-	add hl, hl
-	add hl, bc
-	ld a, [hli]
-	ld c, a
-	ld a, [hli]
-	ld b, a
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-
-	call GetSpriteScreenXY
-
-	ld a, [hOAMBufferOffset]
-	ld e, a
-	ld d, wOAMBuffer / $100
-
-.tileLoop
-	ld a, [hSpriteScreenY]   ; temp for sprite Y position
-	add $10                  ; Y=16 is top of screen (Y=0 is invisible)
-	add [hl]                 ; add Y offset from table
-	ld [de], a               ; write new sprite OAM Y position
-	inc hl
-	ld a, [hSpriteScreenX]   ; temp for sprite X position
-	add $8                   ; X=8 is left of screen (X=0 is invisible)
-	add [hl]                 ; add X offset from table
-	inc e
-	ld [de], a               ; write new sprite OAM X position
-	inc e
-	ld a, [bc]               ; read pattern number offset (accommodates orientation (offset 0,4 or 8) and animation (offset 0 or $80))
-	inc bc
-	push bc
-	ld b, a
-
-	ld a, [wd5cd]            ; temp copy of c1x2
-	swap a                   ; high nybble determines sprite used (0 is always player sprite, next are some npcs)
-	and $f
-
-	; Sprites $a and $b have one face (and therefore 4 tiles instead of 12).
-	; As a result, sprite $b's tile offset is less than normal.
-	cp $b
-	jr nz, .notFourTileSprite
-	ld a, $a * 12 + 4
-	jr .next2
-
-.notFourTileSprite
-	; a *= 12
-	sla a
-	sla a
-	ld c, a
-	sla a
-	add c
-
-.next2
-	add b ; add the tile offset from the table (based on frame and facing direction)
-	pop bc
-	ld [de], a ; tile id
-	inc hl
-	inc e
-	ld a, [hl]
-	bit 1, a ; is the tile allowed to set the sprite priority bit?
-	jr z, .skipPriority
-	ld a, [hSpritePriority]
-	or [hl]
-.skipPriority
-	inc hl
-	ld [de], a
-	inc e
-	bit 0, a ; OAMFLAG_ENDOFDATA
-	jr z, .tileLoop
-
-	ld a, e
-	ld [hOAMBufferOffset], a
-
-.nextSprite
-	ld a, [hSpriteOffset2]
-	add $10
-	cp $100 % $100
-	jp nz, .spriteLoop
-
-	; Clear unused OAM.
-	ld a, [hOAMBufferOffset]
-	ld l, a
-	ld h, wOAMBuffer / $100
-	ld de, $4
-	ld b, $a0
-	ld a, [wd736]
-	bit 6, a ; jumping down ledge or fishing animation?
-	ld a, $a0
-	jr z, .clear
-
-; Don't clear the last 4 entries because they are used for the shadow in the
-; jumping down ledge animation and the rod in the fishing animation.
-	ld a, $90
-
-.clear
-	cp l
-	ret z
-	ld [hl], b
-	add hl, de
-	jr .clear
-
-GetSpriteScreenXY:
-	inc e
-	inc e
-	ld a, [de] ; c1x4
-	ld [hSpriteScreenY], a
-	inc e
-	inc e
-	ld a, [de] ; c1x6
-	ld [hSpriteScreenX], a
-	ld a, 4
-	add e
-	ld e, a
-	ld a, [hSpriteScreenY]
-	add 4
-	and $f0
-	ld [de], a ; c1xa (y)
-	inc e
-	ld a, [hSpriteScreenX]
-	and $f0
-	ld [de], a  ; c1xb (x)
-	ret
--- /dev/null
+++ b/engine/overworld/pathfinding.asm
@@ -1,0 +1,201 @@
+FindPathToPlayer:
+	xor a
+	ld hl, hFindPathNumSteps
+	ld [hli], a ; hFindPathNumSteps
+	ld [hli], a ; hFindPathFlags
+	ld [hli], a ; hFindPathYProgress
+	ld [hl], a  ; hFindPathXProgress
+	ld hl, wNPCMovementDirections2
+	ld de, $0
+.loop
+	ld a, [hFindPathYProgress]
+	ld b, a
+	ld a, [hNPCPlayerYDistance] ; Y distance in steps
+	call CalcDifference
+	ld d, a
+	and a
+	jr nz, .asm_f8da
+	ld a, [hFindPathFlags]
+	set 0, a ; current end of path matches the player's Y coordinate
+	ld [hFindPathFlags], a
+.asm_f8da
+	ld a, [hFindPathXProgress]
+	ld b, a
+	ld a, [hNPCPlayerXDistance] ; X distance in steps
+	call CalcDifference
+	ld e, a
+	and a
+	jr nz, .asm_f8ec
+	ld a, [hFindPathFlags]
+	set 1, a ; current end of path matches the player's X coordinate
+	ld [hFindPathFlags], a
+.asm_f8ec
+	ld a, [hFindPathFlags]
+	cp $3 ; has the end of the path reached the player's position?
+	jr z, .done
+; Compare whether the X distance between the player and the current of the path
+; is greater or if the Y distance is. Then, try to reduce whichever is greater.
+	ld a, e
+	cp d
+	jr c, .yDistanceGreater
+; x distance is greater
+	ld a, [hNPCPlayerRelativePosFlags]
+	bit 1, a
+	jr nz, .playerIsLeftOfNPC
+	ld d, NPC_MOVEMENT_RIGHT
+	jr .next1
+.playerIsLeftOfNPC
+	ld d, NPC_MOVEMENT_LEFT
+.next1
+	ld a, [hFindPathXProgress]
+	add 1
+	ld [hFindPathXProgress], a
+	jr .storeDirection
+.yDistanceGreater
+	ld a, [hNPCPlayerRelativePosFlags]
+	bit 0, a
+	jr nz, .playerIsAboveNPC
+	ld d, NPC_MOVEMENT_DOWN
+	jr .next2
+.playerIsAboveNPC
+	ld d, NPC_MOVEMENT_UP
+.next2
+	ld a, [hFindPathYProgress]
+	add 1
+	ld [hFindPathYProgress], a
+.storeDirection
+	ld a, d
+	ld [hli], a
+	ld a, [hFindPathNumSteps]
+	inc a
+	ld [hFindPathNumSteps], a
+	jp .loop
+.done
+	ld [hl], $ff
+	ret
+
+CalcPositionOfPlayerRelativeToNPC:
+	xor a
+	ld [hNPCPlayerRelativePosFlags], a
+	ld a, [wSpriteStateData1 + 4] ; player's sprite screen Y position in pixels
+	ld d, a
+	ld a, [wSpriteStateData1 + 6] ; player's sprite screen X position in pixels
+	ld e, a
+	ld hl, wSpriteStateData1
+	ld a, [hNPCSpriteOffset]
+	add l
+	add $4
+	ld l, a
+	jr nc, .noCarry
+	inc h
+.noCarry
+	ld a, d
+	ld b, a
+	ld a, [hli] ; NPC sprite screen Y position in pixels
+	call CalcDifference
+	jr nc, .NPCSouthOfOrAlignedWithPlayer
+.NPCNorthOfPlayer
+	push hl
+	ld hl, hNPCPlayerRelativePosFlags
+	bit 0, [hl]
+	set 0, [hl]
+	pop hl
+	jr .divideYDistance
+.NPCSouthOfOrAlignedWithPlayer
+	push hl
+	ld hl, hNPCPlayerRelativePosFlags
+	bit 0, [hl]
+	res 0, [hl]
+	pop hl
+.divideYDistance
+	push hl
+	ld hl, hDividend2
+	ld [hli], a
+	ld a, 16
+	ld [hli], a
+	call DivideBytes ; divide Y absolute distance by 16
+	ld a, [hl] ; quotient
+	ld [hNPCPlayerYDistance], a
+	pop hl
+	inc hl
+	ld b, e
+	ld a, [hl] ; NPC sprite screen X position in pixels
+	call CalcDifference
+	jr nc, .NPCEastOfOrAlignedWithPlayer
+.NPCWestOfPlayer
+	push hl
+	ld hl, hNPCPlayerRelativePosFlags
+	bit 1, [hl]
+	set 1, [hl]
+	pop hl
+	jr .divideXDistance
+.NPCEastOfOrAlignedWithPlayer
+	push hl
+	ld hl, hNPCPlayerRelativePosFlags
+	bit 1, [hl]
+	res 1, [hl]
+	pop hl
+.divideXDistance
+	ld [hDividend2], a
+	ld a, 16
+	ld [hDivisor2], a
+	call DivideBytes ; divide X absolute distance by 16
+	ld a, [hQuotient2]
+	ld [hNPCPlayerXDistance], a
+	ld a, [hNPCPlayerRelativePosPerspective]
+	and a
+	ret z
+	ld a, [hNPCPlayerRelativePosFlags]
+	cpl
+	and $3
+	ld [hNPCPlayerRelativePosFlags], a
+	ret
+
+ConvertNPCMovementDirectionsToJoypadMasks:
+	ld a, [hNPCMovementDirections2Index]
+	ld [wNPCMovementDirections2Index], a
+	dec a
+	ld de, wSimulatedJoypadStatesEnd
+	ld hl, wNPCMovementDirections2
+	add l
+	ld l, a
+	jr nc, .loop
+	inc h
+.loop
+	ld a, [hld]
+	call ConvertNPCMovementDirectionToJoypadMask
+	ld [de], a
+	inc de
+	ld a, [hNPCMovementDirections2Index]
+	dec a
+	ld [hNPCMovementDirections2Index], a
+	jr nz, .loop
+	ret
+
+ConvertNPCMovementDirectionToJoypadMask:
+	push hl
+	ld b, a
+	ld hl, NPCMovementDirectionsToJoypadMasksTable
+.loop
+	ld a, [hli]
+	cp $ff
+	jr z, .done
+	cp b
+	jr z, .loadJoypadMask
+	inc hl
+	jr .loop
+.loadJoypadMask
+	ld a, [hl]
+.done
+	pop hl
+	ret
+
+NPCMovementDirectionsToJoypadMasksTable:
+	db NPC_MOVEMENT_UP, D_UP
+	db NPC_MOVEMENT_DOWN, D_DOWN
+	db NPC_MOVEMENT_LEFT, D_LEFT
+	db NPC_MOVEMENT_RIGHT, D_RIGHT
+	db $ff
+
+; unreferenced
+	ret
--- a/engine/overworld/pewter_guys.asm
+++ /dev/null
@@ -1,102 +1,0 @@
-PewterGuys:
-	ld hl, wSimulatedJoypadStatesEnd
-	ld a, [wSimulatedJoypadStatesIndex]
-	dec a ; this decrement causes it to overwrite the last byte before $FF in the list
-	ld [wSimulatedJoypadStatesIndex], a
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld d, h
-	ld e, l
-	ld hl, PointerTable_37ce6
-	ld a, [wWhichPewterGuy]
-	add a
-	ld b, 0
-	ld c, a
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld a, [wYCoord]
-	ld b, a
-	ld a, [wXCoord]
-	ld c, a
-.findMatchingCoordsLoop
-	ld a, [hli]
-	cp b
-	jr nz, .nextEntry1
-	ld a, [hli]
-	cp c
-	jr nz, .nextEntry2
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-.copyMovementDataLoop
-	ld a, [hli]
-	cp $ff
-	ret z
-	ld [de], a
-	inc de
-	ld a, [wSimulatedJoypadStatesIndex]
-	inc a
-	ld [wSimulatedJoypadStatesIndex], a
-	jr .copyMovementDataLoop
-.nextEntry1
-	inc hl
-.nextEntry2
-	inc hl
-	inc hl
-	jr .findMatchingCoordsLoop
-
-PointerTable_37ce6:
-	dw PewterMuseumGuyCoords
-	dw PewterGymGuyCoords
-
-; these are the four coordinates of the spaces below, above, to the left and
-; to the right of the museum guy, and pointers to different movements for
-; the player to make to get positioned before the main movement.
-PewterMuseumGuyCoords:
-	db 18, 27
-	dw .down
-	db 16, 27
-	dw .up
-	db 17, 26
-	dw .left
-	db 17, 28
-	dw .right
-
-.down
-	db D_UP, D_UP, $ff
-.up
-	db D_RIGHT, D_LEFT, $ff
-.left
-	db D_UP, D_RIGHT, $ff
-.right
-	db D_UP, D_LEFT, $ff
-
-; these are the five coordinates which trigger the gym guy and pointers to
-; different movements for the player to make to get positioned before the
-; main movement
-; $00 is a pause
-PewterGymGuyCoords:
-	db 16, 34
-	dw .one
-	db 17, 35
-	dw .two
-	db 18, 37
-	dw .three
-	db 19, 37
-	dw .four
-	db 17, 36
-	dw .five
-
-.one
-	db D_LEFT, D_DOWN, D_DOWN, D_RIGHT, $ff
-.two
-	db D_LEFT, D_DOWN, D_RIGHT, D_LEFT, $ff
-.three
-	db D_LEFT, D_LEFT, D_LEFT, $00, $00, $00, $00, $00, $00, $00, $00, $ff
-.four
-	db D_LEFT, D_LEFT, D_UP, D_LEFT, $ff
-.five
-	db D_LEFT, D_DOWN, D_LEFT, $00, $00, $00, $00, $00, $00, $00, $00, $ff
--- a/engine/overworld/poison.asm
+++ /dev/null
@@ -1,112 +1,0 @@
-ApplyOutOfBattlePoisonDamage:
-	ld a, [wd730]
-	add a
-	jp c, .noBlackOut ; no black out if joypad states are being simulated
-	ld a, [wPartyCount]
-	and a
-	jp z, .noBlackOut
-	call IncrementDayCareMonExp
-	ld a, [wStepCounter]
-	and $3 ; is the counter a multiple of 4?
-	jp nz, .noBlackOut ; only apply poison damage every fourth step
-	ld [wWhichPokemon], a
-	ld hl, wPartyMon1Status
-	ld de, wPartySpecies
-.applyDamageLoop
-	ld a, [hl]
-	and (1 << PSN)
-	jr z, .nextMon2 ; not poisoned
-	dec hl
-	dec hl
-	ld a, [hld]
-	ld b, a
-	ld a, [hli]
-	or b
-	jr z, .nextMon ; already fainted
-; subtract 1 from HP
-	ld a, [hl]
-	dec a
-	ld [hld], a
-	inc a
-	jr nz, .noBorrow
-; borrow 1 from upper byte of HP
-	dec [hl]
-	inc hl
-	jr .nextMon
-.noBorrow
-	ld a, [hli]
-	or [hl]
-	jr nz, .nextMon ; didn't faint from damage
-; the mon fainted from the damage
-	push hl
-	inc hl
-	inc hl
-	ld [hl], a
-	ld a, [de]
-	ld [wd11e], a
-	push de
-	ld a, [wWhichPokemon]
-	ld hl, wPartyMonNicks
-	call GetPartyMonName
-	xor a
-	ld [wJoyIgnore], a
-	call EnableAutoTextBoxDrawing
-	ld a, TEXT_MON_FAINTED
-	ld [hSpriteIndexOrTextID], a
-	call DisplayTextID
-	pop de
-	pop hl
-.nextMon
-	inc hl
-	inc hl
-.nextMon2
-	inc de
-	ld a, [de]
-	inc a
-	jr z, .applyDamageLoopDone
-	ld bc, wPartyMon2 - wPartyMon1
-	add hl, bc
-	push hl
-	ld hl, wWhichPokemon
-	inc [hl]
-	pop hl
-	jr .applyDamageLoop
-.applyDamageLoopDone
-	ld hl, wPartyMon1Status
-	ld a, [wPartyCount]
-	ld d, a
-	ld e, 0
-.countPoisonedLoop
-	ld a, [hl]
-	and (1 << PSN)
-	or e
-	ld e, a
-	ld bc, wPartyMon2 - wPartyMon1
-	add hl, bc
-	dec d
-	jr nz, .countPoisonedLoop
-	ld a, e
-	and a ; are any party members poisoned?
-	jr z, .skipPoisonEffectAndSound
-	ld b, $2
-	predef ChangeBGPalColor0_4Frames ; change BG white to dark grey for 4 frames
-	ld a, SFX_POISONED
-	call PlaySound
-.skipPoisonEffectAndSound
-	predef AnyPartyAlive
-	ld a, d
-	and a
-	jr nz, .noBlackOut
-	call EnableAutoTextBoxDrawing
-	ld a, TEXT_BLACKED_OUT
-	ld [hSpriteIndexOrTextID], a
-	call DisplayTextID
-	ld hl, wd72e
-	set 5, [hl]
-	ld a, $ff
-	jr .done
-.noBlackOut
-	xor a
-.done
-	ld [wOutOfBattleBlackout], a
-	ret
--- a/engine/overworld/pokecenter.asm
+++ /dev/null
@@ -1,68 +1,0 @@
-DisplayPokemonCenterDialogue_::
-	call SaveScreenTilesToBuffer1 ; save screen
-	ld hl, PokemonCenterWelcomeText
-	call PrintText
-	ld hl, wd72e
-	bit 2, [hl]
-	set 1, [hl]
-	set 2, [hl]
-	jr nz, .skipShallWeHealYourPokemon
-	ld hl, ShallWeHealYourPokemonText
-	call PrintText
-.skipShallWeHealYourPokemon
-	call YesNoChoicePokeCenter ; yes/no menu
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .declinedHealing ; if the player chose No
-	call SetLastBlackoutMap
-	call LoadScreenTilesFromBuffer1 ; restore screen
-	ld hl, NeedYourPokemonText
-	call PrintText
-	ld a, $18
-	ld [wSpriteStateData1 + $12], a ; make the nurse turn to face the machine
-	call Delay3
-	predef HealParty
-	callba AnimateHealingMachine ; do the healing machine animation
-	xor a
-	ld [wAudioFadeOutControl], a
-	ld a, [wAudioSavedROMBank]
-	ld [wAudioROMBank], a
-	ld a, [wMapMusicSoundID]
-	ld [wLastMusicSoundID], a
-	ld [wNewSoundID], a
-	call PlaySound
-	ld hl, PokemonFightingFitText
-	call PrintText
-	ld a, $14
-	ld [wSpriteStateData1 + $12], a ; make the nurse bow
-	ld c, a
-	call DelayFrames
-	jr .done
-.declinedHealing
-	call LoadScreenTilesFromBuffer1 ; restore screen
-.done
-	ld hl, PokemonCenterFarewellText
-	call PrintText
-	jp UpdateSprites
-
-PokemonCenterWelcomeText:
-	TX_FAR _PokemonCenterWelcomeText
-	db "@"
-
-ShallWeHealYourPokemonText:
-	TX_DELAY
-	TX_FAR _ShallWeHealYourPokemonText
-	db "@"
-
-NeedYourPokemonText:
-	TX_FAR _NeedYourPokemonText
-	db "@"
-
-PokemonFightingFitText:
-	TX_FAR _PokemonFightingFitText
-	db "@"
-
-PokemonCenterFarewellText:
-	TX_DELAY
-	TX_FAR _PokemonCenterFarewellText
-	db "@"
--- a/engine/overworld/pokemart.asm
+++ /dev/null
@@ -1,272 +1,0 @@
-DisplayPokemartDialogue_::
-	ld a, [wListScrollOffset]
-	ld [wSavedListScrollOffset], a
-	call UpdateSprites
-	xor a
-	ld [wBoughtOrSoldItemInMart], a
-.loop
-	xor a
-	ld [wListScrollOffset], a
-	ld [wCurrentMenuItem], a
-	ld [wPlayerMonNumber], a
-	inc a
-	ld [wPrintItemPrices], a
-	ld a, MONEY_BOX
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	ld a, BUY_SELL_QUIT_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-
-; This code is useless. It copies the address of the pokemart's inventory to hl,
-; but the address is never used.
-	ld hl, wItemListPointer
-	ld a, [hli]
-	ld l, [hl]
-	ld h, a
-
-	ld a, [wMenuExitMethod]
-	cp CANCELLED_MENU
-	jp z, .done
-	ld a, [wChosenMenuItem]
-	and a ; buying?
-	jp z, .buyMenu
-	dec a ; selling?
-	jp z, .sellMenu
-	dec a ; quitting?
-	jp z, .done
-.sellMenu
-
-; the same variables are set again below, so this code has no effect
-	xor a
-	ld [wPrintItemPrices], a
-	ld a, INIT_BAG_ITEM_LIST
-	ld [wInitListType], a
-	callab InitList
-
-	ld a, [wNumBagItems]
-	and a
-	jp z, .bagEmpty
-	ld hl, PokemonSellingGreetingText
-	call PrintText
-	call SaveScreenTilesToBuffer1 ; save screen
-.sellMenuLoop
-	call LoadScreenTilesFromBuffer1 ; restore saved screen
-	ld a, MONEY_BOX
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; draw money text box
-	ld hl, wNumBagItems
-	ld a, l
-	ld [wListPointer], a
-	ld a, h
-	ld [wListPointer + 1], a
-	xor a
-	ld [wPrintItemPrices], a
-	ld [wCurrentMenuItem], a
-	ld a, ITEMLISTMENU
-	ld [wListMenuID], a
-	call DisplayListMenuID
-	jp c, .returnToMainPokemartMenu ; if the player closed the menu
-.confirmItemSale ; if the player is trying to sell a specific item
-	call IsKeyItem
-	ld a, [wIsKeyItem]
-	and a
-	jr nz, .unsellableItem
-	ld a, [wcf91]
-	call IsItemHM
-	jr c, .unsellableItem
-	ld a, PRICEDITEMLISTMENU
-	ld [wListMenuID], a
-	ld [hHalveItemPrices], a ; halve prices when selling
-	call DisplayChooseQuantityMenu
-	inc a
-	jr z, .sellMenuLoop ; if the player closed the choose quantity menu with the B button
-	ld hl, PokemartTellSellPriceText
-	lb bc, 14, 1 ; location that PrintText always prints to, this is useless
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; yes/no menu
-	ld a, [wMenuExitMethod]
-	cp CHOSE_SECOND_ITEM
-	jr z, .sellMenuLoop ; if the player chose No or pressed the B button
-
-; The following code is supposed to check if the player chose No, but the above
-; check already catches it.
-	ld a, [wChosenMenuItem]
-	dec a
-	jr z, .sellMenuLoop
-
-.sellItem
-	ld a, [wBoughtOrSoldItemInMart]
-	and a
-	jr nz, .skipSettingFlag1
-	inc a
-	ld [wBoughtOrSoldItemInMart], a
-.skipSettingFlag1
-	call AddAmountSoldToMoney
-	ld hl, wNumBagItems
-	call RemoveItemFromInventory
-	jp .sellMenuLoop
-.unsellableItem
-	ld hl, PokemartUnsellableItemText
-	call PrintText
-	jp .returnToMainPokemartMenu
-.bagEmpty
-	ld hl, PokemartItemBagEmptyText
-	call PrintText
-	call SaveScreenTilesToBuffer1
-	jp .returnToMainPokemartMenu
-.buyMenu
-
-; the same variables are set again below, so this code has no effect
-	ld a, 1
-	ld [wPrintItemPrices], a
-	ld a, INIT_OTHER_ITEM_LIST
-	ld [wInitListType], a
-	callab InitList
-
-	ld hl, PokemartBuyingGreetingText
-	call PrintText
-	call SaveScreenTilesToBuffer1
-.buyMenuLoop
-	call LoadScreenTilesFromBuffer1
-	ld a, MONEY_BOX
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	ld hl, wItemList
-	ld a, l
-	ld [wListPointer], a
-	ld a, h
-	ld [wListPointer + 1], a
-	xor a
-	ld [wCurrentMenuItem], a
-	inc a
-	ld [wPrintItemPrices], a
-	inc a ; a = 2 (PRICEDITEMLISTMENU)
-	ld [wListMenuID], a
-	call DisplayListMenuID
-	jr c, .returnToMainPokemartMenu ; if the player closed the menu
-	ld a, 99
-	ld [wMaxItemQuantity], a
-	xor a
-	ld [hHalveItemPrices], a ; don't halve item prices when buying
-	call DisplayChooseQuantityMenu
-	inc a
-	jr z, .buyMenuLoop ; if the player closed the choose quantity menu with the B button
-	ld a, [wcf91] ; item ID
-	ld [wd11e], a ; store item ID for GetItemName
-	call GetItemName
-	call CopyStringToCF4B ; copy name to wcf4b
-	ld hl, PokemartTellBuyPriceText
-	call PrintText
-	coord hl, 14, 7
-	lb bc, 8, 15
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID ; yes/no menu
-	ld a, [wMenuExitMethod]
-	cp CHOSE_SECOND_ITEM
-	jp z, .buyMenuLoop ; if the player chose No or pressed the B button
-
-; The following code is supposed to check if the player chose No, but the above
-; check already catches it.
-	ld a, [wChosenMenuItem]
-	dec a
-	jr z, .buyMenuLoop
-
-.buyItem
-	call .isThereEnoughMoney
-	jr c, .notEnoughMoney
-	ld hl, wNumBagItems
-	call AddItemToInventory
-	jr nc, .bagFull
-	call SubtractAmountPaidFromMoney
-	ld a, [wBoughtOrSoldItemInMart]
-	and a
-	jr nz, .skipSettingFlag2
-	ld a, 1
-	ld [wBoughtOrSoldItemInMart], a
-.skipSettingFlag2
-	ld a, SFX_PURCHASE
-	call PlaySoundWaitForCurrent
-	call WaitForSoundToFinish
-	ld hl, PokemartBoughtItemText
-	call PrintText
-	jp .buyMenuLoop
-.returnToMainPokemartMenu
-	call LoadScreenTilesFromBuffer1
-	ld a, MONEY_BOX
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	ld hl, PokemartAnythingElseText
-	call PrintText
-	jp .loop
-.isThereEnoughMoney
-	ld de, wPlayerMoney
-	ld hl, hMoney
-	ld c, 3 ; length of money in bytes
-	jp StringCmp
-.notEnoughMoney
-	ld hl, PokemartNotEnoughMoneyText
-	call PrintText
-	jr .returnToMainPokemartMenu
-.bagFull
-	ld hl, PokemartItemBagFullText
-	call PrintText
-	jr .returnToMainPokemartMenu
-.done
-	ld hl, PokemartThankYouText
-	call PrintText
-	ld a, 1
-	ld [wUpdateSpritesEnabled], a
-	call UpdateSprites
-	ld a, [wSavedListScrollOffset]
-	ld [wListScrollOffset], a
-	ret
-
-PokemartBuyingGreetingText:
-	TX_FAR _PokemartBuyingGreetingText
-	db "@"
-
-PokemartTellBuyPriceText:
-	TX_FAR _PokemartTellBuyPriceText
-	db "@"
-
-PokemartBoughtItemText:
-	TX_FAR _PokemartBoughtItemText
-	db "@"
-
-PokemartNotEnoughMoneyText:
-	TX_FAR _PokemartNotEnoughMoneyText
-	db "@"
-
-PokemartItemBagFullText:
-	TX_FAR _PokemartItemBagFullText
-	db "@"
-
-PokemonSellingGreetingText:
-	TX_FAR _PokemonSellingGreetingText
-	db "@"
-
-PokemartTellSellPriceText:
-	TX_FAR _PokemartTellSellPriceText
-	db "@"
-
-PokemartItemBagEmptyText:
-	TX_FAR _PokemartItemBagEmptyText
-	db "@"
-
-PokemartUnsellableItemText:
-	TX_FAR _PokemartUnsellableItemText
-	db "@"
-
-PokemartThankYouText:
-	TX_FAR _PokemartThankYouText
-	db "@"
-
-PokemartAnythingElseText:
-	TX_FAR _PokemartAnythingElseText
-	db "@"
--- a/engine/overworld/saffron_guards.asm
+++ /dev/null
@@ -1,15 +1,0 @@
-RemoveGuardDrink::
-	ld hl, GuardDrinksList
-.drinkLoop
-	ld a, [hli]
-	ld [$ffdb], a
-	and a
-	ret z
-	push hl
-	ld b, a
-	call IsItemInBag
-	pop hl
-	jr z, .drinkLoop
-	jpba RemoveItemByID
-
-INCLUDE "data/guard_drink_items.asm"
--- a/engine/overworld/set_blackout_map.asm
+++ /dev/null
@@ -1,25 +1,0 @@
-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
-
-INCLUDE "data/rest_house_maps.asm"
--- /dev/null
+++ b/engine/overworld/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/overworld/sprite_collisions.asm
@@ -1,0 +1,356 @@
+_UpdateSprites::
+	ld h, $c1
+	inc h
+	ld a, $e    ; wSpriteStateData2 + $0e
+.spriteLoop
+	ld l, a
+	sub $e
+	ld c, a
+	ld [H_CURRENTSPRITEOFFSET], a
+	ld a, [hl]
+	and a
+	jr z, .skipSprite   ; tests $c2Xe
+	push hl
+	push de
+	push bc
+	call .updateCurrentSprite
+	pop bc
+	pop de
+	pop hl
+.skipSprite
+	ld a, l
+	add $10             ; move to next sprite
+	cp $e               ; test for overflow (back at $0e)
+	jr nz, .spriteLoop
+	ret
+.updateCurrentSprite
+	cp $1
+	jp nz, UpdateNonPlayerSprite
+	jp UpdatePlayerSprite
+
+UpdateNonPlayerSprite:
+	dec a
+	swap a
+	ld [$ff93], a  ; $10 * sprite#
+	ld a, [wNPCMovementScriptSpriteOffset] ; some sprite offset?
+	ld b, a
+	ld a, [H_CURRENTSPRITEOFFSET]
+	cp b
+	jr nz, .unequal
+	jp DoScriptedNPCMovement
+.unequal
+	jp UpdateNPCSprite
+
+; This detects if the current sprite (whose offset is at H_CURRENTSPRITEOFFSET)
+; is going to collide with another sprite by looping over the other sprites.
+; The current sprite's offset will be labelled with i (e.g. $c1i0).
+; The loop sprite's offset will labelled with j (e.g. $c1j0).
+;
+; Note that the Y coordinate of the sprite (in [$c1k4]) is one of the following
+; 9 values when the sprite is aligned with the grid: $fc, $0c, $1c, $2c, ..., $7c.
+; The reason that 4 is added below to the coordinate is to make it align with a
+; multiple of $10 to make comparisons easier.
+DetectCollisionBetweenSprites:
+	nop
+
+	ld h, wSpriteStateData1 / $100
+	ld a, [H_CURRENTSPRITEOFFSET]
+	add wSpriteStateData1 % $100
+	ld l, a
+
+	ld a, [hl] ; a = [$c1i0] (picture) (0 if slot is unused)
+	and a ; is this sprite slot slot used?
+	ret z ; return if not used
+
+	ld a, l
+	add 3
+	ld l, a
+
+	ld a, [hli] ; a = [$c1i3] (delta Y) (-1, 0, or 1)
+	call SetSpriteCollisionValues
+
+	ld a, [hli] ; a = [$C1i4] (Y screen coordinate)
+	add 4 ; align with multiple of $10
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving south or
+; subtract 7 from a if moving north.
+	add b
+	and $f0
+	or c
+
+	ld [$ff90], a ; store Y coordinate adjusted for direction of movement
+
+	ld a, [hli] ; a = [$c1i5] (delta X) (-1, 0, or 1)
+	call SetSpriteCollisionValues
+	ld a, [hl] ; a = [$C1i6] (X screen coordinate)
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving east or
+; subtract 7 from a if moving west.
+	add b
+	and $f0
+	or c
+
+	ld [$ff91], a ; store X coordinate adjusted for direction of movement
+
+	ld a, l
+	add 7
+	ld l, a
+
+	xor a
+	ld [hld], a ; zero [$c1id] XXX what's [$c1id] for?
+	ld [hld], a ; zero [$c1ic] (directions in which collisions occurred)
+
+	ld a, [$ff91]
+	ld [hld], a ; [$c1ib] = adjusted X coordinate
+	ld a, [$ff90]
+	ld [hl], a ; [$c1ia] = adjusted Y coordinate
+
+	xor a ; zero the loop counter
+
+.loop
+	ld [$ff8f], a ; store loop counter
+	swap a
+	ld e, a
+	ld a, [H_CURRENTSPRITEOFFSET]
+	cp e ; does the loop sprite match the current sprite?
+	jp z, .next ; go to the next sprite if they match
+
+	ld d, h
+	ld a, [de] ; a = [$c1j0] (picture) (0 if slot is unused)
+	and a ; is this sprite slot slot used?
+	jp z, .next ; go the next sprite if not used
+
+	inc e
+	inc e
+	ld a, [de] ; a = [$c1j2] ($ff means the sprite is offscreen)
+	inc a
+	jp z, .next ; go the next sprite if offscreen
+
+	ld a, [H_CURRENTSPRITEOFFSET]
+	add 10
+	ld l, a
+
+	inc e
+	ld a, [de] ; a = [$c1j3] (delta Y)
+	call SetSpriteCollisionValues
+
+	inc e
+	ld a, [de] ; a = [$C1j4] (Y screen coordinate)
+	add 4 ; align with multiple of $10
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving south or
+; subtract 7 from a if moving north.
+	add b
+	and $f0
+	or c
+
+	sub [hl] ; subtract the adjusted Y coordinate of sprite i ([$c1ia]) from that of sprite j
+
+; calculate the absolute value of the difference to get the distance
+	jr nc, .noCarry1
+	cpl
+	inc a
+.noCarry1
+	ld [$ff90], a ; store the distance between the two sprites' adjusted Y values
+
+; Use the carry flag set by the above subtraction to determine which sprite's
+; Y coordinate is larger. This information is used later to set [$c1ic],
+; which stores which direction the collision occurred in.
+; The following 5 lines set the lowest 2 bits of c, which are later shifted left by 2.
+; If sprite i's Y is larger, set lowest 2 bits of c to 10.
+; If sprite j's Y is larger or both are equal, set lowest 2 bits of c to 01.
+	push af
+	rl c
+	pop af
+	ccf
+	rl c
+
+; If sprite i's delta Y is 0, then b = 7, else b = 9.
+	ld b, 7
+	ld a, [hl] ; a = [$c1ia] (adjusted Y coordinate)
+	and $f
+	jr z, .next1
+	ld b, 9
+
+.next1
+	ld a, [$ff90] ; a = distance between adjusted Y coordinates
+	sub b
+	ld [$ff92], a ; store distance adjusted using sprite i's direction
+	ld a, b
+	ld [$ff90], a ; store 7 or 9 depending on sprite i's delta Y
+	jr c, .checkXDistance
+
+; If sprite j's delta Y is 0, then b = 7, else b = 9.
+	ld b, 7
+	dec e
+	ld a, [de] ; a = [$c1j3] (delta Y)
+	inc e
+	and a
+	jr z, .next2
+	ld b, 9
+
+.next2
+	ld a, [$ff92] ; a = distance adjusted using sprite i's direction
+	sub b ; adjust distance using sprite j's direction
+	jr z, .checkXDistance
+	jr nc, .next ; go to next sprite if distance is still positive after both adjustments
+
+.checkXDistance
+	inc e
+	inc l
+	ld a, [de] ; a = [$c1j5] (delta X)
+
+	push bc
+
+	call SetSpriteCollisionValues
+	inc e
+	ld a, [de] ; a = [$c1j6] (X screen coordinate)
+
+; The effect of the following 3 lines is to
+; add 7 to a if moving east or
+; subtract 7 from a if moving west.
+	add b
+	and $f0
+	or c
+
+	pop bc
+
+	sub [hl] ; subtract the adjusted X coordinate of sprite i ([$c1ib]) from that of sprite j
+
+; calculate the absolute value of the difference to get the distance
+	jr nc, .noCarry2
+	cpl
+	inc a
+.noCarry2
+	ld [$ff91], a ; store the distance between the two sprites' adjusted X values
+
+; Use the carry flag set by the above subtraction to determine which sprite's
+; X coordinate is larger. This information is used later to set [$c1ic],
+; which stores which direction the collision occurred in.
+; The following 5 lines set the lowest 2 bits of c.
+; If sprite i's X is larger, set lowest 2 bits of c to 10.
+; If sprite j's X is larger or both are equal, set lowest 2 bits of c to 01.
+	push af
+	rl c
+	pop af
+	ccf
+	rl c
+
+; If sprite i's delta X is 0, then b = 7, else b = 9.
+	ld b, 7
+	ld a, [hl] ; a = [$c1ib] (adjusted X coordinate)
+	and $f
+	jr z, .next3
+	ld b, 9
+
+.next3
+	ld a, [$ff91] ; a = distance between adjusted X coordinates
+	sub b
+	ld [$ff92], a ; store distance adjusted using sprite i's direction
+	ld a, b
+	ld [$ff91], a ; store 7 or 9 depending on sprite i's delta X
+	jr c, .collision
+
+; If sprite j's delta X is 0, then b = 7, else b = 9.
+	ld b, 7
+	dec e
+	ld a, [de] ; a = [$c1j5] (delta X)
+	inc e
+	and a
+	jr z, .next4
+	ld b, 9
+
+.next4
+	ld a, [$ff92] ; a = distance adjusted using sprite i's direction
+	sub b ; adjust distance using sprite j's direction
+	jr z, .collision
+	jr nc, .next ; go to next sprite if distance is still positive after both adjustments
+
+.collision
+	ld a, [$ff91] ; a = 7 or 9 depending on sprite i's delta X
+	ld b, a
+	ld a, [$ff90] ; a = 7 or 9 depending on sprite i's delta Y
+	inc l
+
+; If delta X isn't 0 and delta Y is 0, then b = %0011, else b = %1100.
+; (note that normally if delta X isn't 0, then delta Y must be 0 and vice versa)
+	cp b
+	jr c, .next5
+	ld b, %1100
+	jr .next6
+.next5
+	ld b, %0011
+
+.next6
+	ld a, c ; c has 2 bits set (one of bits 0-1 is set for the X axis and one of bits 2-3 for the Y axis)
+	and b ; we select either the bit in bits 0-1 or bits 2-3 based on the calculation immediately above
+	or [hl] ; or with existing collision direction bits in [$c1ic]
+	ld [hl], a ; store new value
+	ld a, c ; useless code because a is overwritten before being used again
+
+; set bit in [$c1ie] or [$c1if] to indicate which sprite the collision occurred with
+	inc l
+	inc l
+	ld a, [$ff8f] ; a = loop counter
+	ld de, SpriteCollisionBitTable
+	add a
+	add e
+	ld e, a
+	jr nc, .noCarry3
+	inc d
+.noCarry3
+	ld a, [de]
+	or [hl]
+	ld [hli], a
+	inc de
+	ld a, [de]
+	or [hl]
+	ld [hl], a
+
+.next
+	ld a, [$ff8f] ; a = loop counter
+	inc a
+	cp $10
+	jp nz, .loop
+	ret
+
+; takes delta X or delta Y in a
+; b = delta X/Y
+; c = 0 if delta X/Y is 0
+; c = 7 if delta X/Y is 1
+; c = 9 if delta X/Y is -1
+SetSpriteCollisionValues:
+	and a
+	ld b, 0
+	ld c, 0
+	jr z, .done
+	ld c, 9
+	cp -1
+	jr z, .ok
+	ld c, 7
+	ld a, 0
+.ok
+	ld b, a
+.done
+	ret
+
+SpriteCollisionBitTable:
+	db %00000000,%00000001
+	db %00000000,%00000010
+	db %00000000,%00000100
+	db %00000000,%00001000
+	db %00000000,%00010000
+	db %00000000,%00100000
+	db %00000000,%01000000
+	db %00000000,%10000000
+	db %00000001,%00000000
+	db %00000010,%00000000
+	db %00000100,%00000000
+	db %00001000,%00000000
+	db %00010000,%00000000
+	db %00100000,%00000000
+	db %01000000,%00000000
+	db %10000000,%00000000
--- a/engine/overworld/ssanne.asm
+++ /dev/null
@@ -1,93 +1,0 @@
-AnimateBoulderDust:
-	ld a, $1
-	ld [wWhichAnimationOffsets], a ; select the boulder dust offsets
-	ld a, [wUpdateSpritesEnabled]
-	push af
-	ld a, $ff
-	ld [wUpdateSpritesEnabled], a
-	ld a, %11100100
-	ld [rOBP1], a
-	call LoadSmokeTileFourTimes
-	callba WriteCutOrBoulderDustAnimationOAMBlock
-	ld c, 8 ; number of steps in animation
-.loop
-	push bc
-	call GetMoveBoulderDustFunctionPointer
-	ld bc, .returnAddress
-	push bc
-	ld c, 4
-	jp hl
-.returnAddress
-	ld a, [rOBP1]
-	xor %01100100
-	ld [rOBP1], a
-	call Delay3
-	pop bc
-	dec c
-	jr nz, .loop
-	pop af
-	ld [wUpdateSpritesEnabled], a
-	jp LoadPlayerSpriteGraphics
-
-GetMoveBoulderDustFunctionPointer:
-	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
-	ld hl, MoveBoulderDustFunctionPointerTable
-	ld c, a
-	ld b, $0
-	add hl, bc
-	ld a, [hli]
-	ld [wCoordAdjustmentAmount], a
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	push hl
-	ld hl, wOAMBuffer + $90
-	ld d, $0
-	add hl, de
-	ld e, l
-	ld d, h
-	pop hl
-	ret
-
-MoveBoulderDustFunctionPointerTable:
-; facing down
-	db $FF,$00
-	dw AdjustOAMBlockYPos
-
-; facing up
-	db $01,$00
-	dw AdjustOAMBlockYPos
-
-; facing left
-	db $01,$01
-	dw AdjustOAMBlockXPos
-
-; facing right
-	db $FF,$01
-	dw AdjustOAMBlockXPos
-
-LoadSmokeTileFourTimes::
-	ld hl, vChars1 + $7c0
-	ld c, $4
-.loop
-	push bc
-	push hl
-	call LoadSmokeTile
-	pop hl
-	ld bc, $10
-	add hl, bc
-	pop bc
-	dec c
-	jr nz, .loop
-	ret
-
-LoadSmokeTile:
-	ld de, SSAnneSmokePuffTile
-	lb bc, BANK(SSAnneSmokePuffTile), (SSAnneSmokePuffTileEnd - SSAnneSmokePuffTile) / $10
-	jp CopyVideoData
-
-SSAnneSmokePuffTile:
-	INCBIN "gfx/overworld/smoke.2bpp"
-SSAnneSmokePuffTileEnd:
--- a/engine/overworld/tileset_header.asm
+++ /dev/null
@@ -1,60 +1,0 @@
-LoadTilesetHeader:
-	call GetPredefRegisters
-	push hl
-	ld d, 0
-	ld a, [wCurMapTileset]
-	add a
-	add a
-	ld b, a
-	add a
-	add b ; a = tileset * 12
-	jr nc, .noCarry
-	inc d
-.noCarry
-	ld e, a
-	ld hl, Tilesets
-	add hl, de
-	ld de, wTilesetBank
-	ld c, $b
-.copyTilesetHeaderLoop
-	ld a, [hli]
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .copyTilesetHeaderLoop
-	ld a, [hl]
-	ld [hTilesetType], a
-	xor a
-	ld [$ffd8], a
-	pop hl
-	ld a, [wCurMapTileset]
-	push hl
-	push de
-	ld hl, DungeonTilesets
-	ld de, $1
-	call IsInArray
-	pop de
-	pop hl
-	jr c, .asm_c797
-	ld a, [wCurMapTileset]
-	ld b, a
-	ld a, [hPreviousTileset]
-	cp b
-	jr z, .done
-.asm_c797
-	ld a, [wDestinationWarpID]
-	cp $ff
-	jr z, .done
-	call LoadDestinationWarpPosition
-	ld a, [wYCoord]
-	and $1
-	ld [wYBlockCoord], a
-	ld a, [wXCoord]
-	and $1
-	ld [wXBlockCoord], a
-.done
-	ret
-
-INCLUDE "data/dungeon_tilesets.asm"
-
-INCLUDE "data/tileset_headers.asm"
--- /dev/null
+++ b/engine/overworld/tilesets.asm
@@ -1,0 +1,60 @@
+LoadTilesetHeader:
+	call GetPredefRegisters
+	push hl
+	ld d, 0
+	ld a, [wCurMapTileset]
+	add a
+	add a
+	ld b, a
+	add a
+	add b ; a = tileset * 12
+	jr nc, .noCarry
+	inc d
+.noCarry
+	ld e, a
+	ld hl, Tilesets
+	add hl, de
+	ld de, wTilesetBank
+	ld c, $b
+.copyTilesetHeaderLoop
+	ld a, [hli]
+	ld [de], a
+	inc de
+	dec c
+	jr nz, .copyTilesetHeaderLoop
+	ld a, [hl]
+	ld [hTilesetType], a
+	xor a
+	ld [$ffd8], a
+	pop hl
+	ld a, [wCurMapTileset]
+	push hl
+	push de
+	ld hl, DungeonTilesets
+	ld de, $1
+	call IsInArray
+	pop de
+	pop hl
+	jr c, .asm_c797
+	ld a, [wCurMapTileset]
+	ld b, a
+	ld a, [hPreviousTileset]
+	cp b
+	jr z, .done
+.asm_c797
+	ld a, [wDestinationWarpID]
+	cp $ff
+	jr z, .done
+	call LoadDestinationWarpPosition
+	ld a, [wYCoord]
+	and $1
+	ld [wYBlockCoord], a
+	ld a, [wXCoord]
+	and $1
+	ld [wXBlockCoord], a
+.done
+	ret
+
+INCLUDE "data/dungeon_tilesets.asm"
+
+INCLUDE "data/tileset_headers.asm"
--- /dev/null
+++ b/engine/overworld/trainer_sight.asm
@@ -1,0 +1,349 @@
+_GetSpritePosition1::
+	ld hl, wSpriteStateData1
+	ld de, $4
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	call GetSpriteDataPointer
+	ld a, [hli] ; c1x4 (screen Y pos)
+	ld [$ffeb], a
+	inc hl
+	ld a, [hl] ; c1x6 (screen X pos)
+	ld [$ffec], a
+	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
+	add hl, de
+	ld a, [hli] ; c2x4 (map Y pos)
+	ld [$ffed], a
+	ld a, [hl] ; c2x5 (map X pos)
+	ld [$ffee], a
+	ret
+
+_GetSpritePosition2::
+	ld hl, wSpriteStateData1
+	ld de, $4
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	call GetSpriteDataPointer
+	ld a, [hli] ; c1x4 (screen Y pos)
+	ld [wSavedSpriteScreenY], a
+	inc hl
+	ld a, [hl] ; c1x6 (screen X pos)
+	ld [wSavedSpriteScreenX], a
+	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
+	add hl, de
+	ld a, [hli] ; c2x4 (map Y pos)
+	ld [wSavedSpriteMapY], a
+	ld a, [hl] ; c2x5 (map X pos)
+	ld [wSavedSpriteMapX], a
+	ret
+
+_SetSpritePosition1::
+	ld hl, wSpriteStateData1
+	ld de, $4
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	call GetSpriteDataPointer
+	ld a, [$ffeb] ; c1x4 (screen Y pos)
+	ld [hli], a
+	inc hl
+	ld a, [$ffec] ; c1x6 (screen X pos)
+	ld [hl], a
+	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
+	add hl, de
+	ld a, [$ffed] ; c2x4 (map Y pos)
+	ld [hli], a
+	ld a, [$ffee] ; c2x5 (map X pos)
+	ld [hl], a
+	ret
+
+_SetSpritePosition2::
+	ld hl, wSpriteStateData1
+	ld de, 4
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	call GetSpriteDataPointer
+	ld a, [wSavedSpriteScreenY]
+	ld [hli], a ; c1x4 (screen Y pos)
+	inc hl
+	ld a, [wSavedSpriteScreenX]
+	ld [hl], a ; c1x6 (screen X pos)
+	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
+	add hl, de
+	ld a, [wSavedSpriteMapY]
+	ld [hli], a ; c2x4 (map Y pos)
+	ld a, [wSavedSpriteMapX]
+	ld [hl], a ; c2x5 (map X pos)
+	ret
+
+TrainerWalkUpToPlayer::
+	ld a, [wSpriteIndex]
+	swap a
+	ld [wTrainerSpriteOffset], a
+	call ReadTrainerScreenPosition
+	ld a, [wTrainerFacingDirection]
+	and a ; SPRITE_FACING_DOWN
+	jr z, .facingDown
+	cp SPRITE_FACING_UP
+	jr z, .facingUp
+	cp SPRITE_FACING_LEFT
+	jr z, .facingLeft
+	jr .facingRight
+.facingDown
+	ld a, [wTrainerScreenY]
+	ld b, a
+	ld a, $3c           ; (fixed) player screen Y pos
+	call CalcDifference
+	cp $10              ; trainer is right above player
+	ret z
+	swap a
+	dec a
+	ld c, a             ; bc = steps yet to go to reach player
+	xor a
+	ld b, a           ; a = direction to go to
+	jr .writeWalkScript
+.facingUp
+	ld a, [wTrainerScreenY]
+	ld b, a
+	ld a, $3c           ; (fixed) player screen Y pos
+	call CalcDifference
+	cp $10              ; trainer is right below player
+	ret z
+	swap a
+	dec a
+	ld c, a             ; bc = steps yet to go to reach player
+	ld b, $0
+	ld a, $40           ; a = direction to go to
+	jr .writeWalkScript
+.facingRight
+	ld a, [wTrainerScreenX]
+	ld b, a
+	ld a, $40           ; (fixed) player screen X pos
+	call CalcDifference
+	cp $10              ; trainer is directly left of player
+	ret z
+	swap a
+	dec a
+	ld c, a             ; bc = steps yet to go to reach player
+	ld b, $0
+	ld a, $c0           ; a = direction to go to
+	jr .writeWalkScript
+.facingLeft
+	ld a, [wTrainerScreenX]
+	ld b, a
+	ld a, $40           ; (fixed) player screen X pos
+	call CalcDifference
+	cp $10              ; trainer is directly right of player
+	ret z
+	swap a
+	dec a
+	ld c, a             ; bc = steps yet to go to reach player
+	ld b, $0
+	ld a, $80           ; a = direction to go to
+.writeWalkScript
+	ld hl, wNPCMovementDirections2
+	ld de, wNPCMovementDirections2
+	call FillMemory     ; write the necessary steps to reach player
+	ld [hl], $ff        ; write end of list sentinel
+	ld a, [wSpriteIndex]
+	ld [H_SPRITEINDEX], a
+	jp MoveSprite_
+
+; input: de = offset within sprite entry
+; output: hl = pointer to sprite data
+GetSpriteDataPointer:
+	push de
+	add hl, de
+	ld a, [H_SPRITEINDEX]
+	swap a
+	ld d, $0
+	ld e, a
+	add hl, de
+	pop de
+	ret
+
+; tests if this trainer is in the right position to engage the player and do so if she is.
+TrainerEngage:
+	push hl
+	push de
+	ld a, [wTrainerSpriteOffset]
+	add $2
+	ld d, $0
+	ld e, a
+	ld hl, wSpriteStateData1
+	add hl, de
+	ld a, [hl]             ; c1x2: sprite image index
+	sub $ff
+	jr nz, .spriteOnScreen ; test if sprite is on screen
+	jp .noEngage
+.spriteOnScreen
+	ld a, [wTrainerSpriteOffset]
+	add $9
+	ld d, $0
+	ld e, a
+	ld hl, wSpriteStateData1
+	add hl, de
+	ld a, [hl]             ; c1x9: facing direction
+	ld [wTrainerFacingDirection], a
+	call ReadTrainerScreenPosition
+	ld a, [wTrainerScreenY]          ; sprite screen Y pos
+	ld b, a
+	ld a, $3c
+	cp b
+	jr z, .linedUpY
+	ld a, [wTrainerScreenX]          ; sprite screen X pos
+	ld b, a
+	ld a, $40
+	cp b
+	jr z, .linedUpX
+	xor a
+	jp .noEngage
+.linedUpY
+	ld a, [wTrainerScreenX]        ; sprite screen X pos
+	ld b, a
+	ld a, $40            ; (fixed) player X position
+	call CalcDifference  ; calc distance
+	jr z, .noEngage      ; exact same position as player
+	call CheckSpriteCanSeePlayer
+	jr c, .engage
+	xor a
+	jr .noEngage
+.linedUpX
+	ld a, [wTrainerScreenY]        ; sprite screen Y pos
+	ld b, a
+	ld a, $3c            ; (fixed) player Y position
+	call CalcDifference  ; calc distance
+	jr z, .noEngage      ; exact same position as player
+	call CheckSpriteCanSeePlayer
+	jr c, .engage
+	xor a
+	jp .noEngage
+.engage
+	call CheckPlayerIsInFrontOfSprite
+	ld a, [wTrainerSpriteOffset]
+	and a
+	jr z, .noEngage
+	ld hl, wFlags_0xcd60
+	set 0, [hl]
+	call EngageMapTrainer
+	ld a, $ff
+.noEngage
+	ld [wTrainerSpriteOffset], a
+	pop de
+	pop hl
+	ret
+
+; reads trainer's Y position to wTrainerScreenY and X position to wTrainerScreenX
+ReadTrainerScreenPosition:
+	ld a, [wTrainerSpriteOffset]
+	add $4
+	ld d, $0
+	ld e, a
+	ld hl, wSpriteStateData1
+	add hl, de
+	ld a, [hl] ; c1x4 (sprite Y pos)
+	ld [wTrainerScreenY], a
+	ld a, [wTrainerSpriteOffset]
+	add $6
+	ld d, $0
+	ld e, a
+	ld hl, wSpriteStateData1
+	add hl, de
+	ld a, [hl] ; c1x6 (sprite X pos)
+	ld [wTrainerScreenX], a
+	ret
+
+; checks if the sprite is properly lined up with the player with respect to the direction it's looking. Also checks the distance between player and sprite
+; note that this does not necessarily mean the sprite is seeing the player, he could be behind it's back
+; a: distance player to sprite
+CheckSpriteCanSeePlayer:
+	ld b, a
+	ld a, [wTrainerEngageDistance] ; how far the trainer can see
+	cp b
+	jr nc, .checkIfLinedUp
+	jr .notInLine         ; player too far away
+.checkIfLinedUp
+	ld a, [wTrainerFacingDirection]         ; sprite facing direction
+	cp SPRITE_FACING_DOWN
+	jr z, .checkXCoord
+	cp SPRITE_FACING_UP
+	jr z, .checkXCoord
+	cp SPRITE_FACING_LEFT
+	jr z, .checkYCoord
+	cp SPRITE_FACING_RIGHT
+	jr z, .checkYCoord
+	jr .notInLine
+.checkXCoord
+	ld a, [wTrainerScreenX]         ; sprite screen X position
+	ld b, a
+	cp $40
+	jr z, .inLine
+	jr .notInLine
+.checkYCoord
+	ld a, [wTrainerScreenY]         ; sprite screen Y position
+	ld b, a
+	cp $3c
+	jr nz, .notInLine
+.inLine
+	scf
+	ret
+.notInLine
+	and a
+	ret
+
+; tests if the player is in front of the sprite (rather than behind it)
+CheckPlayerIsInFrontOfSprite:
+	ld a, [wCurMap]
+	cp POWER_PLANT
+	jp z, .engage       ; bypass this for power plant to get voltorb fake items to work
+	ld a, [wTrainerSpriteOffset]
+	add $4
+	ld d, $0
+	ld e, a
+	ld hl, wSpriteStateData1
+	add hl, de
+	ld a, [hl]          ; c1x4 (sprite screen Y pos)
+	cp $fc
+	jr nz, .notOnTopmostTile ; special case if sprite is on topmost tile (Y = $fc (-4)), make it come down a block
+	ld a, $c
+.notOnTopmostTile
+	ld [wTrainerScreenY], a
+	ld a, [wTrainerSpriteOffset]
+	add $6
+	ld d, $0
+	ld e, a
+	ld hl, wSpriteStateData1
+	add hl, de
+	ld a, [hl]          ; c1x6 (sprite screen X pos)
+	ld [wTrainerScreenX], a
+	ld a, [wTrainerFacingDirection]       ; facing direction
+	cp SPRITE_FACING_DOWN
+	jr nz, .notFacingDown
+	ld a, [wTrainerScreenY]       ; sprite screen Y pos
+	cp $3c
+	jr c, .engage       ; sprite above player
+	jr .noEngage        ; sprite below player
+.notFacingDown
+	cp SPRITE_FACING_UP
+	jr nz, .notFacingUp
+	ld a, [wTrainerScreenY]       ; sprite screen Y pos
+	cp $3c
+	jr nc, .engage      ; sprite below player
+	jr .noEngage        ; sprite above player
+.notFacingUp
+	cp SPRITE_FACING_LEFT
+	jr nz, .notFacingLeft
+	ld a, [wTrainerScreenX]       ; sprite screen X pos
+	cp $40
+	jr nc, .engage      ; sprite right of player
+	jr .noEngage        ; sprite left of player
+.notFacingLeft
+	ld a, [wTrainerScreenX]       ; sprite screen X pos
+	cp $40
+	jr nc, .noEngage    ; sprite right of player
+.engage
+	ld a, $ff
+	jr .done
+.noEngage
+	xor a
+.done
+	ld [wTrainerSpriteOffset], a
+	ret
--- a/engine/overworld/trainers.asm
+++ /dev/null
@@ -1,349 +1,0 @@
-_GetSpritePosition1::
-	ld hl, wSpriteStateData1
-	ld de, $4
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	call GetSpriteDataPointer
-	ld a, [hli] ; c1x4 (screen Y pos)
-	ld [$ffeb], a
-	inc hl
-	ld a, [hl] ; c1x6 (screen X pos)
-	ld [$ffec], a
-	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
-	add hl, de
-	ld a, [hli] ; c2x4 (map Y pos)
-	ld [$ffed], a
-	ld a, [hl] ; c2x5 (map X pos)
-	ld [$ffee], a
-	ret
-
-_GetSpritePosition2::
-	ld hl, wSpriteStateData1
-	ld de, $4
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	call GetSpriteDataPointer
-	ld a, [hli] ; c1x4 (screen Y pos)
-	ld [wSavedSpriteScreenY], a
-	inc hl
-	ld a, [hl] ; c1x6 (screen X pos)
-	ld [wSavedSpriteScreenX], a
-	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
-	add hl, de
-	ld a, [hli] ; c2x4 (map Y pos)
-	ld [wSavedSpriteMapY], a
-	ld a, [hl] ; c2x5 (map X pos)
-	ld [wSavedSpriteMapX], a
-	ret
-
-_SetSpritePosition1::
-	ld hl, wSpriteStateData1
-	ld de, $4
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	call GetSpriteDataPointer
-	ld a, [$ffeb] ; c1x4 (screen Y pos)
-	ld [hli], a
-	inc hl
-	ld a, [$ffec] ; c1x6 (screen X pos)
-	ld [hl], a
-	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
-	add hl, de
-	ld a, [$ffed] ; c2x4 (map Y pos)
-	ld [hli], a
-	ld a, [$ffee] ; c2x5 (map X pos)
-	ld [hl], a
-	ret
-
-_SetSpritePosition2::
-	ld hl, wSpriteStateData1
-	ld de, 4
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	call GetSpriteDataPointer
-	ld a, [wSavedSpriteScreenY]
-	ld [hli], a ; c1x4 (screen Y pos)
-	inc hl
-	ld a, [wSavedSpriteScreenX]
-	ld [hl], a ; c1x6 (screen X pos)
-	ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6)
-	add hl, de
-	ld a, [wSavedSpriteMapY]
-	ld [hli], a ; c2x4 (map Y pos)
-	ld a, [wSavedSpriteMapX]
-	ld [hl], a ; c2x5 (map X pos)
-	ret
-
-TrainerWalkUpToPlayer::
-	ld a, [wSpriteIndex]
-	swap a
-	ld [wTrainerSpriteOffset], a
-	call ReadTrainerScreenPosition
-	ld a, [wTrainerFacingDirection]
-	and a ; SPRITE_FACING_DOWN
-	jr z, .facingDown
-	cp SPRITE_FACING_UP
-	jr z, .facingUp
-	cp SPRITE_FACING_LEFT
-	jr z, .facingLeft
-	jr .facingRight
-.facingDown
-	ld a, [wTrainerScreenY]
-	ld b, a
-	ld a, $3c           ; (fixed) player screen Y pos
-	call CalcDifference
-	cp $10              ; trainer is right above player
-	ret z
-	swap a
-	dec a
-	ld c, a             ; bc = steps yet to go to reach player
-	xor a
-	ld b, a           ; a = direction to go to
-	jr .writeWalkScript
-.facingUp
-	ld a, [wTrainerScreenY]
-	ld b, a
-	ld a, $3c           ; (fixed) player screen Y pos
-	call CalcDifference
-	cp $10              ; trainer is right below player
-	ret z
-	swap a
-	dec a
-	ld c, a             ; bc = steps yet to go to reach player
-	ld b, $0
-	ld a, $40           ; a = direction to go to
-	jr .writeWalkScript
-.facingRight
-	ld a, [wTrainerScreenX]
-	ld b, a
-	ld a, $40           ; (fixed) player screen X pos
-	call CalcDifference
-	cp $10              ; trainer is directly left of player
-	ret z
-	swap a
-	dec a
-	ld c, a             ; bc = steps yet to go to reach player
-	ld b, $0
-	ld a, $c0           ; a = direction to go to
-	jr .writeWalkScript
-.facingLeft
-	ld a, [wTrainerScreenX]
-	ld b, a
-	ld a, $40           ; (fixed) player screen X pos
-	call CalcDifference
-	cp $10              ; trainer is directly right of player
-	ret z
-	swap a
-	dec a
-	ld c, a             ; bc = steps yet to go to reach player
-	ld b, $0
-	ld a, $80           ; a = direction to go to
-.writeWalkScript
-	ld hl, wNPCMovementDirections2
-	ld de, wNPCMovementDirections2
-	call FillMemory     ; write the necessary steps to reach player
-	ld [hl], $ff        ; write end of list sentinel
-	ld a, [wSpriteIndex]
-	ld [H_SPRITEINDEX], a
-	jp MoveSprite_
-
-; input: de = offset within sprite entry
-; output: hl = pointer to sprite data
-GetSpriteDataPointer:
-	push de
-	add hl, de
-	ld a, [H_SPRITEINDEX]
-	swap a
-	ld d, $0
-	ld e, a
-	add hl, de
-	pop de
-	ret
-
-; tests if this trainer is in the right position to engage the player and do so if she is.
-TrainerEngage:
-	push hl
-	push de
-	ld a, [wTrainerSpriteOffset]
-	add $2
-	ld d, $0
-	ld e, a
-	ld hl, wSpriteStateData1
-	add hl, de
-	ld a, [hl]             ; c1x2: sprite image index
-	sub $ff
-	jr nz, .spriteOnScreen ; test if sprite is on screen
-	jp .noEngage
-.spriteOnScreen
-	ld a, [wTrainerSpriteOffset]
-	add $9
-	ld d, $0
-	ld e, a
-	ld hl, wSpriteStateData1
-	add hl, de
-	ld a, [hl]             ; c1x9: facing direction
-	ld [wTrainerFacingDirection], a
-	call ReadTrainerScreenPosition
-	ld a, [wTrainerScreenY]          ; sprite screen Y pos
-	ld b, a
-	ld a, $3c
-	cp b
-	jr z, .linedUpY
-	ld a, [wTrainerScreenX]          ; sprite screen X pos
-	ld b, a
-	ld a, $40
-	cp b
-	jr z, .linedUpX
-	xor a
-	jp .noEngage
-.linedUpY
-	ld a, [wTrainerScreenX]        ; sprite screen X pos
-	ld b, a
-	ld a, $40            ; (fixed) player X position
-	call CalcDifference  ; calc distance
-	jr z, .noEngage      ; exact same position as player
-	call CheckSpriteCanSeePlayer
-	jr c, .engage
-	xor a
-	jr .noEngage
-.linedUpX
-	ld a, [wTrainerScreenY]        ; sprite screen Y pos
-	ld b, a
-	ld a, $3c            ; (fixed) player Y position
-	call CalcDifference  ; calc distance
-	jr z, .noEngage      ; exact same position as player
-	call CheckSpriteCanSeePlayer
-	jr c, .engage
-	xor a
-	jp .noEngage
-.engage
-	call CheckPlayerIsInFrontOfSprite
-	ld a, [wTrainerSpriteOffset]
-	and a
-	jr z, .noEngage
-	ld hl, wFlags_0xcd60
-	set 0, [hl]
-	call EngageMapTrainer
-	ld a, $ff
-.noEngage
-	ld [wTrainerSpriteOffset], a
-	pop de
-	pop hl
-	ret
-
-; reads trainer's Y position to wTrainerScreenY and X position to wTrainerScreenX
-ReadTrainerScreenPosition:
-	ld a, [wTrainerSpriteOffset]
-	add $4
-	ld d, $0
-	ld e, a
-	ld hl, wSpriteStateData1
-	add hl, de
-	ld a, [hl] ; c1x4 (sprite Y pos)
-	ld [wTrainerScreenY], a
-	ld a, [wTrainerSpriteOffset]
-	add $6
-	ld d, $0
-	ld e, a
-	ld hl, wSpriteStateData1
-	add hl, de
-	ld a, [hl] ; c1x6 (sprite X pos)
-	ld [wTrainerScreenX], a
-	ret
-
-; checks if the sprite is properly lined up with the player with respect to the direction it's looking. Also checks the distance between player and sprite
-; note that this does not necessarily mean the sprite is seeing the player, he could be behind it's back
-; a: distance player to sprite
-CheckSpriteCanSeePlayer:
-	ld b, a
-	ld a, [wTrainerEngageDistance] ; how far the trainer can see
-	cp b
-	jr nc, .checkIfLinedUp
-	jr .notInLine         ; player too far away
-.checkIfLinedUp
-	ld a, [wTrainerFacingDirection]         ; sprite facing direction
-	cp SPRITE_FACING_DOWN
-	jr z, .checkXCoord
-	cp SPRITE_FACING_UP
-	jr z, .checkXCoord
-	cp SPRITE_FACING_LEFT
-	jr z, .checkYCoord
-	cp SPRITE_FACING_RIGHT
-	jr z, .checkYCoord
-	jr .notInLine
-.checkXCoord
-	ld a, [wTrainerScreenX]         ; sprite screen X position
-	ld b, a
-	cp $40
-	jr z, .inLine
-	jr .notInLine
-.checkYCoord
-	ld a, [wTrainerScreenY]         ; sprite screen Y position
-	ld b, a
-	cp $3c
-	jr nz, .notInLine
-.inLine
-	scf
-	ret
-.notInLine
-	and a
-	ret
-
-; tests if the player is in front of the sprite (rather than behind it)
-CheckPlayerIsInFrontOfSprite:
-	ld a, [wCurMap]
-	cp POWER_PLANT
-	jp z, .engage       ; bypass this for power plant to get voltorb fake items to work
-	ld a, [wTrainerSpriteOffset]
-	add $4
-	ld d, $0
-	ld e, a
-	ld hl, wSpriteStateData1
-	add hl, de
-	ld a, [hl]          ; c1x4 (sprite screen Y pos)
-	cp $fc
-	jr nz, .notOnTopmostTile ; special case if sprite is on topmost tile (Y = $fc (-4)), make it come down a block
-	ld a, $c
-.notOnTopmostTile
-	ld [wTrainerScreenY], a
-	ld a, [wTrainerSpriteOffset]
-	add $6
-	ld d, $0
-	ld e, a
-	ld hl, wSpriteStateData1
-	add hl, de
-	ld a, [hl]          ; c1x6 (sprite screen X pos)
-	ld [wTrainerScreenX], a
-	ld a, [wTrainerFacingDirection]       ; facing direction
-	cp SPRITE_FACING_DOWN
-	jr nz, .notFacingDown
-	ld a, [wTrainerScreenY]       ; sprite screen Y pos
-	cp $3c
-	jr c, .engage       ; sprite above player
-	jr .noEngage        ; sprite below player
-.notFacingDown
-	cp SPRITE_FACING_UP
-	jr nz, .notFacingUp
-	ld a, [wTrainerScreenY]       ; sprite screen Y pos
-	cp $3c
-	jr nc, .engage      ; sprite below player
-	jr .noEngage        ; sprite above player
-.notFacingUp
-	cp SPRITE_FACING_LEFT
-	jr nz, .notFacingLeft
-	ld a, [wTrainerScreenX]       ; sprite screen X pos
-	cp $40
-	jr nc, .engage      ; sprite right of player
-	jr .noEngage        ; sprite left of player
-.notFacingLeft
-	ld a, [wTrainerScreenX]       ; sprite screen X pos
-	cp $40
-	jr nc, .noEngage    ; sprite right of player
-.engage
-	ld a, $ff
-	jr .done
-.noEngage
-	xor a
-.done
-	ld [wTrainerSpriteOffset], a
-	ret
--- /dev/null
+++ b/engine/overworld/turn_sprite.asm
@@ -1,0 +1,25 @@
+UpdateSpriteFacingOffsetAndDelayMovement::
+	ld h, $c2
+	ld a, [H_CURRENTSPRITEOFFSET]
+	add $8
+	ld l, a
+	ld a, $7f ; maximum movement delay
+	ld [hl], a ; c2x8 (movement delay)
+	dec h
+	ld a, [H_CURRENTSPRITEOFFSET]
+	add $9
+	ld l, a
+	ld a, [hld] ; c1x9 (facing direction)
+	ld b, a
+	xor a
+	ld [hld], a
+	ld [hl], a ; c1x8 (walk animation frame)
+	ld a, [H_CURRENTSPRITEOFFSET]
+	add $2
+	ld l, a
+	ld a, [hl] ; c1x2 (facing and animation table offset)
+	or b ; or in the facing direction
+	ld [hld], a
+	ld a, $2 ; delayed movement status
+	ld [hl], a ; c1x1 (movement status)
+	ret
--- a/engine/palettes.asm
+++ /dev/null
@@ -1,641 +1,0 @@
-_RunPaletteCommand:
-	call GetPredefRegisters
-	ld a, b
-	cp $ff
-	jr nz, .next
-	ld a, [wDefaultPaletteCommand] ; use default command if command ID is $ff
-.next
-	cp UPDATE_PARTY_MENU_BLK_PACKET
-	jp z, UpdatePartyMenuBlkPacket
-	ld l, a
-	ld h, 0
-	add hl, hl
-	ld de, SetPalFunctions
-	add hl, de
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld de, SendSGBPackets
-	push de
-	jp hl
-
-SetPal_BattleBlack:
-	ld hl, PalPacket_Black
-	ld de, BlkPacket_Battle
-	ret
-
-; uses PalPacket_Empty to build a packet based on mon IDs and health color
-SetPal_Battle:
-	ld hl, PalPacket_Empty
-	ld de, wPalPacket
-	ld bc, $10
-	call CopyData
-	ld a, [wPlayerBattleStatus3]
-	ld hl, wBattleMonSpecies
-	call DeterminePaletteID
-	ld b, a
-	ld a, [wEnemyBattleStatus3]
-	ld hl, wEnemyMonSpecies2
-	call DeterminePaletteID
-	ld c, a
-	ld hl, wPalPacket + 1
-	ld a, [wPlayerHPBarColor]
-	add PAL_GREENBAR
-	ld [hli], a
-	inc hl
-	ld a, [wEnemyHPBarColor]
-	add PAL_GREENBAR
-	ld [hli], a
-	inc hl
-	ld a, b
-	ld [hli], a
-	inc hl
-	ld a, c
-	ld [hl], a
-	ld hl, wPalPacket
-	ld de, BlkPacket_Battle
-	ld a, SET_PAL_BATTLE
-	ld [wDefaultPaletteCommand], a
-	ret
-
-SetPal_TownMap:
-	ld hl, PalPacket_TownMap
-	ld de, BlkPacket_WholeScreen
-	ret
-
-; uses PalPacket_Empty to build a packet based the mon ID
-SetPal_StatusScreen:
-	ld hl, PalPacket_Empty
-	ld de, wPalPacket
-	ld bc, $10
-	call CopyData
-	ld a, [wcf91]
-	cp NUM_POKEMON_INDEXES + 1
-	jr c, .pokemon
-	ld a, $1 ; not pokemon
-.pokemon
-	call DeterminePaletteIDOutOfBattle
-	push af
-	ld hl, wPalPacket + 1
-	ld a, [wStatusScreenHPBarColor]
-	add PAL_GREENBAR
-	ld [hli], a
-	inc hl
-	pop af
-	ld [hl], a
-	ld hl, wPalPacket
-	ld de, BlkPacket_StatusScreen
-	ret
-
-SetPal_PartyMenu:
-	ld hl, PalPacket_PartyMenu
-	ld de, wPartyMenuBlkPacket
-	ret
-
-SetPal_Pokedex:
-	ld hl, PalPacket_Pokedex
-	ld de, wPalPacket
-	ld bc, $10
-	call CopyData
-	ld a, [wcf91]
-	call DeterminePaletteIDOutOfBattle
-	ld hl, wPalPacket + 3
-	ld [hl], a
-	ld hl, wPalPacket
-	ld de, BlkPacket_Pokedex
-	ret
-
-SetPal_Slots:
-	ld hl, PalPacket_Slots
-	ld de, BlkPacket_Slots
-	ret
-
-SetPal_TitleScreen:
-	ld hl, PalPacket_Titlescreen
-	ld de, BlkPacket_Titlescreen
-	ret
-
-; used mostly for menus and the Oak intro
-SetPal_Generic:
-	ld hl, PalPacket_Generic
-	ld de, BlkPacket_WholeScreen
-	ret
-
-SetPal_NidorinoIntro:
-	ld hl, PalPacket_NidorinoIntro
-	ld de, BlkPacket_NidorinoIntro
-	ret
-
-SetPal_GameFreakIntro:
-	ld hl, PalPacket_GameFreakIntro
-	ld de, BlkPacket_GameFreakIntro
-	ld a, SET_PAL_GENERIC
-	ld [wDefaultPaletteCommand], a
-	ret
-
-; uses PalPacket_Empty to build a packet based on the current map
-SetPal_Overworld:
-	ld hl, PalPacket_Empty
-	ld de, wPalPacket
-	ld bc, $10
-	call CopyData
-	ld a, [wCurMapTileset]
-	cp CEMETERY
-	jr z, .PokemonTowerOrAgatha
-	cp CAVERN
-	jr z, .caveOrBruno
-	ld a, [wCurMap]
-	cp REDS_HOUSE_1F
-	jr c, .townOrRoute
-	cp CERULEAN_CAVE_2F
-	jr c, .normalDungeonOrBuilding
-	cp NAME_RATERS_HOUSE
-	jr c, .caveOrBruno
-	cp LORELEIS_ROOM
-	jr z, .Lorelei
-	cp BRUNOS_ROOM
-	jr z, .caveOrBruno
-.normalDungeonOrBuilding
-	ld a, [wLastMap] ; town or route that current dungeon or building is located
-.townOrRoute
-	cp SAFFRON_CITY + 1
-	jr c, .town
-	ld a, PAL_ROUTE - 1
-.town
-	inc a ; a town's palette ID is its map ID + 1
-	ld hl, wPalPacket + 1
-	ld [hld], a
-	ld de, BlkPacket_WholeScreen
-	ld a, SET_PAL_OVERWORLD
-	ld [wDefaultPaletteCommand], a
-	ret
-.PokemonTowerOrAgatha
-	ld a, PAL_GREYMON - 1
-	jr .town
-.caveOrBruno
-	ld a, PAL_CAVE - 1
-	jr .town
-.Lorelei
-	xor a
-	jr .town
-
-; used when a Pokemon is the only thing on the screen
-; such as evolution, trading and the Hall of Fame
-SetPal_PokemonWholeScreen:
-	push bc
-	ld hl, PalPacket_Empty
-	ld de, wPalPacket
-	ld bc, $10
-	call CopyData
-	pop bc
-	ld a, c
-	and a
-	ld a, PAL_BLACK
-	jr nz, .next
-	ld a, [wWholeScreenPaletteMonSpecies]
-	call DeterminePaletteIDOutOfBattle
-.next
-	ld [wPalPacket + 1], a
-	ld hl, wPalPacket
-	ld de, BlkPacket_WholeScreen
-	ret
-
-SetPal_TrainerCard:
-	ld hl, BlkPacket_TrainerCard
-	ld de, wTrainerCardBlkPacket
-	ld bc, $40
-	call CopyData
-	ld de, BadgeBlkDataLengths
-	ld hl, wTrainerCardBlkPacket + 2
-	ld a, [wObtainedBadges]
-	ld c, 8
-.badgeLoop
-	srl a
-	push af
-	jr c, .haveBadge
-; The player doens't have the badge, so zero the badge's blk data.
-	push bc
-	ld a, [de]
-	ld c, a
-	xor a
-.zeroBadgeDataLoop
-	ld [hli], a
-	dec c
-	jr nz, .zeroBadgeDataLoop
-	pop bc
-	jr .nextBadge
-.haveBadge
-; The player does have the badge, so skip past the badge's blk data.
-	ld a, [de]
-.skipBadgeDataLoop
-	inc hl
-	dec a
-	jr nz, .skipBadgeDataLoop
-.nextBadge
-	pop af
-	inc de
-	dec c
-	jr nz, .badgeLoop
-	ld hl, PalPacket_TrainerCard
-	ld de, wTrainerCardBlkPacket
-	ret
-
-SetPalFunctions:
-	dw SetPal_BattleBlack
-	dw SetPal_Battle
-	dw SetPal_TownMap
-	dw SetPal_StatusScreen
-	dw SetPal_Pokedex
-	dw SetPal_Slots
-	dw SetPal_TitleScreen
-	dw SetPal_NidorinoIntro
-	dw SetPal_Generic
-	dw SetPal_Overworld
-	dw SetPal_PartyMenu
-	dw SetPal_PokemonWholeScreen
-	dw SetPal_GameFreakIntro
-	dw SetPal_TrainerCard
-
-; The length of the blk data of each badge on the Trainer Card.
-; The Rainbow Badge has 3 entries because of its many colors.
-BadgeBlkDataLengths:
-	db 6     ; Boulder Badge
-	db 6     ; Cascade Badge
-	db 6     ; Thunder Badge
-	db 6 * 3 ; Rainbow Badge
-	db 6     ; Soul Badge
-	db 6     ; Marsh Badge
-	db 6     ; Volcano Badge
-	db 6     ; Earth Badge
-
-DeterminePaletteID:
-	bit TRANSFORMED, a ; a is battle status 3
-	ld a, PAL_GREYMON  ; if the mon has used Transform, use Ditto's palette
-	ret nz
-	ld a, [hl]
-DeterminePaletteIDOutOfBattle:
-	ld [wd11e], a
-	and a ; is the mon index 0?
-	jr z, .skipDexNumConversion
-	push bc
-	predef IndexToPokedex
-	pop bc
-	ld a, [wd11e]
-.skipDexNumConversion
-	ld e, a
-	ld d, 0
-	ld hl, MonsterPalettes ; not just for Pokemon, Trainers use it too
-	add hl, de
-	ld a, [hl]
-	ret
-
-InitPartyMenuBlkPacket:
-	ld hl, BlkPacket_PartyMenu
-	ld de, wPartyMenuBlkPacket
-	ld bc, $30
-	jp CopyData
-
-UpdatePartyMenuBlkPacket:
-; Update the blk packet with the palette of the HP bar that is
-; specified in [wWhichPartyMenuHPBar].
-	ld hl, wPartyMenuHPBarColors
-	ld a, [wWhichPartyMenuHPBar]
-	ld e, a
-	ld d, 0
-	add hl, de
-	ld e, l
-	ld d, h
-	ld a, [de]
-	and a
-	ld e, (1 << 2) | 1 ; green
-	jr z, .next
-	dec a
-	ld e, (2 << 2) | 2 ; yellow
-	jr z, .next
-	ld e, (3 << 2) | 3 ; red
-.next
-	push de
-	ld hl, wPartyMenuBlkPacket + 8 + 1
-	ld bc, 6
-	ld a, [wWhichPartyMenuHPBar]
-	call AddNTimes
-	pop de
-	ld [hl], e
-	ret
-
-SendSGBPacket:
-;check number of packets
-	ld a, [hl]
-	and $07
-	ret z
-; store number of packets in B
-	ld b, a
-.loop2
-; save B for later use
-	push bc
-; disable ReadJoypad to prevent it from interfering with sending the packet
-	ld a, 1
-	ld [hDisableJoypadPolling], a
-; send RESET signal (P14=LOW, P15=LOW)
-	xor a
-	ld [rJOYP], a
-; set P14=HIGH, P15=HIGH
-	ld a, $30
-	ld [rJOYP], a
-;load length of packets (16 bytes)
-	ld b, $10
-.nextByte
-;set bit counter (8 bits per byte)
-	ld e, $08
-; get next byte in the packet
-	ld a, [hli]
-	ld d, a
-.nextBit0
-	bit 0, d
-; if 0th bit is not zero set P14=HIGH,P15=LOW (send bit 1)
-	ld a, $10
-	jr nz, .next0
-; else (if 0th bit is zero) set P14=LOW,P15=HIGH (send bit 0)
-	ld a, $20
-.next0
-	ld [rJOYP], a
-; must set P14=HIGH,P15=HIGH between each "pulse"
-	ld a, $30
-	ld [rJOYP], a
-; rotation will put next bit in 0th position (so  we can always use command
-; "bit 0,d" to fetch the bit that has to be sent)
-	rr d
-; decrease bit counter so we know when we have sent all 8 bits of current byte
-	dec e
-	jr nz, .nextBit0
-	dec b
-	jr nz, .nextByte
-; send bit 1 as a "stop bit" (end of parameter data)
-	ld a, $20
-	ld [rJOYP], a
-; set P14=HIGH,P15=HIGH
-	ld a, $30
-	ld [rJOYP], a
-	xor a
-	ld [hDisableJoypadPolling], a
-; wait for about 70000 cycles
-	call Wait7000
-; restore (previously pushed) number of packets
-	pop bc
-	dec b
-; return if there are no more packets
-	ret z
-; else send 16 more bytes
-	jr .loop2
-
-LoadSGB:
-	xor a
-	ld [wOnSGB], a
-	call CheckSGB
-	ret nc
-	ld a, 1
-	ld [wOnSGB], a
-	ld a, [wGBC]
-	and a
-	jr z, .notGBC
-	ret
-.notGBC
-	di
-	call PrepareSuperNintendoVRAMTransfer
-	ei
-	ld a, 1
-	ld [wCopyingSGBTileData], a
-	ld de, ChrTrnPacket
-	ld hl, SGBBorderGraphics
-	call CopyGfxToSuperNintendoVRAM
-	xor a
-	ld [wCopyingSGBTileData], a
-	ld de, PctTrnPacket
-	ld hl, BorderPalettes
-	call CopyGfxToSuperNintendoVRAM
-	xor a
-	ld [wCopyingSGBTileData], a
-	ld de, PalTrnPacket
-	ld hl, SuperPalettes
-	call CopyGfxToSuperNintendoVRAM
-	call ClearVram
-	ld hl, MaskEnCancelPacket
-	jp SendSGBPacket
-
-PrepareSuperNintendoVRAMTransfer:
-	ld hl, .packetPointers
-	ld c, 9
-.loop
-	push bc
-	ld a, [hli]
-	push hl
-	ld h, [hl]
-	ld l, a
-	call SendSGBPacket
-	pop hl
-	inc hl
-	pop bc
-	dec c
-	jr nz, .loop
-	ret
-
-.packetPointers
-; Only the first packet is needed.
-	dw MaskEnFreezePacket
-	dw DataSnd_72548
-	dw DataSnd_72558
-	dw DataSnd_72568
-	dw DataSnd_72578
-	dw DataSnd_72588
-	dw DataSnd_72598
-	dw DataSnd_725a8
-	dw DataSnd_725b8
-
-CheckSGB:
-; Returns whether the game is running on an SGB in carry.
-	ld hl, MltReq2Packet
-	di
-	call SendSGBPacket
-	ld a, 1
-	ld [hDisableJoypadPolling], a
-	ei
-	call Wait7000
-	ld a, [rJOYP]
-	and $3
-	cp $3
-	jr nz, .isSGB
-	ld a, $20
-	ld [rJOYP], a
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	call Wait7000
-	call Wait7000
-	ld a, $30
-	ld [rJOYP], a
-	call Wait7000
-	call Wait7000
-	ld a, $10
-	ld [rJOYP], a
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	call Wait7000
-	call Wait7000
-	ld a, $30
-	ld [rJOYP], a
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	ld a, [rJOYP]
-	call Wait7000
-	call Wait7000
-	ld a, [rJOYP]
-	and $3
-	cp $3
-	jr nz, .isSGB
-	call SendMltReq1Packet
-	and a
-	ret
-.isSGB
-	call SendMltReq1Packet
-	scf
-	ret
-
-SendMltReq1Packet:
-	ld hl, MltReq1Packet
-	call SendSGBPacket
-	jp Wait7000
-
-CopyGfxToSuperNintendoVRAM:
-	di
-	push de
-	call DisableLCD
-	ld a, $e4
-	ld [rBGP], a
-	ld de, vChars1
-	ld a, [wCopyingSGBTileData]
-	and a
-	jr z, .notCopyingTileData
-	call CopySGBBorderTiles
-	jr .next
-.notCopyingTileData
-	ld bc, $1000
-	call CopyData
-.next
-	ld hl, vBGMap0
-	ld de, $c
-	ld a, $80
-	ld c, $d
-.loop
-	ld b, $14
-.innerLoop
-	ld [hli], a
-	inc a
-	dec b
-	jr nz, .innerLoop
-	add hl, de
-	dec c
-	jr nz, .loop
-	ld a, $e3
-	ld [rLCDC], a
-	pop hl
-	call SendSGBPacket
-	xor a
-	ld [rBGP], a
-	ei
-	ret
-
-Wait7000:
-; Each loop takes 9 cycles so this routine actually waits 63000 cycles.
-	ld de, 7000
-.loop
-	nop
-	nop
-	nop
-	dec de
-	ld a, d
-	or e
-	jr nz, .loop
-	ret
-
-SendSGBPackets:
-	ld a, [wGBC]
-	and a
-	jr z, .notGBC
-	push de
-	call InitGBCPalettes
-	pop hl
-	call EmptyFunc5
-	ret
-.notGBC
-	push de
-	call SendSGBPacket
-	pop hl
-	jp SendSGBPacket
-
-InitGBCPalettes:
-	ld a, $80 ; index 0 with auto-increment
-	ld [rBGPI], a
-	inc hl
-	ld c, $20
-.loop
-	ld a, [hli]
-	inc hl
-	add a
-	add a
-	add a
-	ld de, SuperPalettes
-	add e
-	jr nc, .noCarry
-	inc d
-.noCarry
-	ld a, [de]
-	ld [rBGPD], a
-	dec c
-	jr nz, .loop
-	ret
-
-EmptyFunc5:
-	ret
-
-CopySGBBorderTiles:
-; SGB tile data is stored in a 4BPP planar format.
-; Each tile is 32 bytes. The first 16 bytes contain bit planes 1 and 2, while
-; the second 16 bytes contain bit planes 3 and 4.
-; This function converts 2BPP planar data into this format by mapping
-; 2BPP colors 0-3 to 4BPP colors 0-3. 4BPP colors 4-15 are not used.
-	ld b, 128
-
-.tileLoop
-
-; Copy bit planes 1 and 2 of the tile data.
-	ld c, 16
-.copyLoop
-	ld a, [hli]
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .copyLoop
-
-; Zero bit planes 3 and 4.
-	ld c, 16
-	xor a
-.zeroLoop
-	ld [de], a
-	inc de
-	dec c
-	jr nz, .zeroLoop
-
-	dec b
-	jr nz, .tileLoop
-	ret
-
-INCLUDE "data/sgb_packets.asm"
-
-INCLUDE "data/mon_palettes.asm"
-
-INCLUDE "data/super_palettes.asm"
-
-INCLUDE "data/sgb_border.asm"
--- a/engine/pathfinding.asm
+++ /dev/null
@@ -1,201 +1,0 @@
-FindPathToPlayer:
-	xor a
-	ld hl, hFindPathNumSteps
-	ld [hli], a ; hFindPathNumSteps
-	ld [hli], a ; hFindPathFlags
-	ld [hli], a ; hFindPathYProgress
-	ld [hl], a  ; hFindPathXProgress
-	ld hl, wNPCMovementDirections2
-	ld de, $0
-.loop
-	ld a, [hFindPathYProgress]
-	ld b, a
-	ld a, [hNPCPlayerYDistance] ; Y distance in steps
-	call CalcDifference
-	ld d, a
-	and a
-	jr nz, .asm_f8da
-	ld a, [hFindPathFlags]
-	set 0, a ; current end of path matches the player's Y coordinate
-	ld [hFindPathFlags], a
-.asm_f8da
-	ld a, [hFindPathXProgress]
-	ld b, a
-	ld a, [hNPCPlayerXDistance] ; X distance in steps
-	call CalcDifference
-	ld e, a
-	and a
-	jr nz, .asm_f8ec
-	ld a, [hFindPathFlags]
-	set 1, a ; current end of path matches the player's X coordinate
-	ld [hFindPathFlags], a
-.asm_f8ec
-	ld a, [hFindPathFlags]
-	cp $3 ; has the end of the path reached the player's position?
-	jr z, .done
-; Compare whether the X distance between the player and the current of the path
-; is greater or if the Y distance is. Then, try to reduce whichever is greater.
-	ld a, e
-	cp d
-	jr c, .yDistanceGreater
-; x distance is greater
-	ld a, [hNPCPlayerRelativePosFlags]
-	bit 1, a
-	jr nz, .playerIsLeftOfNPC
-	ld d, NPC_MOVEMENT_RIGHT
-	jr .next1
-.playerIsLeftOfNPC
-	ld d, NPC_MOVEMENT_LEFT
-.next1
-	ld a, [hFindPathXProgress]
-	add 1
-	ld [hFindPathXProgress], a
-	jr .storeDirection
-.yDistanceGreater
-	ld a, [hNPCPlayerRelativePosFlags]
-	bit 0, a
-	jr nz, .playerIsAboveNPC
-	ld d, NPC_MOVEMENT_DOWN
-	jr .next2
-.playerIsAboveNPC
-	ld d, NPC_MOVEMENT_UP
-.next2
-	ld a, [hFindPathYProgress]
-	add 1
-	ld [hFindPathYProgress], a
-.storeDirection
-	ld a, d
-	ld [hli], a
-	ld a, [hFindPathNumSteps]
-	inc a
-	ld [hFindPathNumSteps], a
-	jp .loop
-.done
-	ld [hl], $ff
-	ret
-
-CalcPositionOfPlayerRelativeToNPC:
-	xor a
-	ld [hNPCPlayerRelativePosFlags], a
-	ld a, [wSpriteStateData1 + 4] ; player's sprite screen Y position in pixels
-	ld d, a
-	ld a, [wSpriteStateData1 + 6] ; player's sprite screen X position in pixels
-	ld e, a
-	ld hl, wSpriteStateData1
-	ld a, [hNPCSpriteOffset]
-	add l
-	add $4
-	ld l, a
-	jr nc, .noCarry
-	inc h
-.noCarry
-	ld a, d
-	ld b, a
-	ld a, [hli] ; NPC sprite screen Y position in pixels
-	call CalcDifference
-	jr nc, .NPCSouthOfOrAlignedWithPlayer
-.NPCNorthOfPlayer
-	push hl
-	ld hl, hNPCPlayerRelativePosFlags
-	bit 0, [hl]
-	set 0, [hl]
-	pop hl
-	jr .divideYDistance
-.NPCSouthOfOrAlignedWithPlayer
-	push hl
-	ld hl, hNPCPlayerRelativePosFlags
-	bit 0, [hl]
-	res 0, [hl]
-	pop hl
-.divideYDistance
-	push hl
-	ld hl, hDividend2
-	ld [hli], a
-	ld a, 16
-	ld [hli], a
-	call DivideBytes ; divide Y absolute distance by 16
-	ld a, [hl] ; quotient
-	ld [hNPCPlayerYDistance], a
-	pop hl
-	inc hl
-	ld b, e
-	ld a, [hl] ; NPC sprite screen X position in pixels
-	call CalcDifference
-	jr nc, .NPCEastOfOrAlignedWithPlayer
-.NPCWestOfPlayer
-	push hl
-	ld hl, hNPCPlayerRelativePosFlags
-	bit 1, [hl]
-	set 1, [hl]
-	pop hl
-	jr .divideXDistance
-.NPCEastOfOrAlignedWithPlayer
-	push hl
-	ld hl, hNPCPlayerRelativePosFlags
-	bit 1, [hl]
-	res 1, [hl]
-	pop hl
-.divideXDistance
-	ld [hDividend2], a
-	ld a, 16
-	ld [hDivisor2], a
-	call DivideBytes ; divide X absolute distance by 16
-	ld a, [hQuotient2]
-	ld [hNPCPlayerXDistance], a
-	ld a, [hNPCPlayerRelativePosPerspective]
-	and a
-	ret z
-	ld a, [hNPCPlayerRelativePosFlags]
-	cpl
-	and $3
-	ld [hNPCPlayerRelativePosFlags], a
-	ret
-
-ConvertNPCMovementDirectionsToJoypadMasks:
-	ld a, [hNPCMovementDirections2Index]
-	ld [wNPCMovementDirections2Index], a
-	dec a
-	ld de, wSimulatedJoypadStatesEnd
-	ld hl, wNPCMovementDirections2
-	add l
-	ld l, a
-	jr nc, .loop
-	inc h
-.loop
-	ld a, [hld]
-	call ConvertNPCMovementDirectionToJoypadMask
-	ld [de], a
-	inc de
-	ld a, [hNPCMovementDirections2Index]
-	dec a
-	ld [hNPCMovementDirections2Index], a
-	jr nz, .loop
-	ret
-
-ConvertNPCMovementDirectionToJoypadMask:
-	push hl
-	ld b, a
-	ld hl, NPCMovementDirectionsToJoypadMasksTable
-.loop
-	ld a, [hli]
-	cp $ff
-	jr z, .done
-	cp b
-	jr z, .loadJoypadMask
-	inc hl
-	jr .loop
-.loadJoypadMask
-	ld a, [hl]
-.done
-	pop hl
-	ret
-
-NPCMovementDirectionsToJoypadMasksTable:
-	db NPC_MOVEMENT_UP, D_UP
-	db NPC_MOVEMENT_DOWN, D_DOWN
-	db NPC_MOVEMENT_LEFT, D_LEFT
-	db NPC_MOVEMENT_RIGHT, D_RIGHT
-	db $ff
-
-; unreferenced
-	ret
--- a/engine/pokedex_rating.asm
+++ /dev/null
@@ -1,154 +1,0 @@
-DisplayDexRating:
-	ld hl, wPokedexSeen
-	ld b, wPokedexSeenEnd - wPokedexSeen
-	call CountSetBits
-	ld a, [wNumSetBits]
-	ld [hDexRatingNumMonsSeen], a
-	ld hl, wPokedexOwned
-	ld b, wPokedexOwnedEnd - wPokedexOwned
-	call CountSetBits
-	ld a, [wNumSetBits]
-	ld [hDexRatingNumMonsOwned], a
-	ld hl, DexRatingsTable
-.findRating
-	ld a, [hli]
-	ld b, a
-	ld a, [hDexRatingNumMonsOwned]
-	cp b
-	jr c, .foundRating
-	inc hl
-	inc hl
-	jr .findRating
-.foundRating
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a ; load text pointer into hl
-	CheckAndResetEventA EVENT_HALL_OF_FAME_DEX_RATING
-	jr nz, .hallOfFame
-	push hl
-	ld hl, PokedexRatingText_441cc
-	call PrintText
-	pop hl
-	call PrintText
-	callba PlayPokedexRatingSfx
-	jp WaitForTextScrollButtonPress
-.hallOfFame
-	ld de, wDexRatingNumMonsSeen
-	ld a, [hDexRatingNumMonsSeen]
-	ld [de], a
-	inc de
-	ld a, [hDexRatingNumMonsOwned]
-	ld [de], a
-	inc de
-.copyRatingTextLoop
-	ld a, [hli]
-	cp "@"
-	jr z, .doneCopying
-	ld [de], a
-	inc de
-	jr .copyRatingTextLoop
-.doneCopying
-	ld [de], a
-	ret
-
-PokedexRatingText_441cc:
-	TX_FAR _OaksLabText_441cc
-	db "@"
-
-DexRatingsTable:
-	db 10
-	dw PokedexRatingText_44201
-	db 20
-	dw PokedexRatingText_44206
-	db 30
-	dw PokedexRatingText_4420b
-	db 40
-	dw PokedexRatingText_44210
-	db 50
-	dw PokedexRatingText_44215
-	db 60
-	dw PokedexRatingText_4421a
-	db 70
-	dw PokedexRatingText_4421f
-	db 80
-	dw PokedexRatingText_44224
-	db 90
-	dw PokedexRatingText_44229
-	db 100
-	dw PokedexRatingText_4422e
-	db 110
-	dw PokedexRatingText_44233
-	db 120
-	dw PokedexRatingText_44238
-	db 130
-	dw PokedexRatingText_4423d
-	db 140
-	dw PokedexRatingText_44242
-	db 150
-	dw PokedexRatingText_44247
-	db NUM_POKEMON + 1
-	dw PokedexRatingText_4424c
-
-PokedexRatingText_44201:
-	TX_FAR _OaksLabText_44201
-	db "@"
-
-PokedexRatingText_44206:
-	TX_FAR _OaksLabText_44206
-	db "@"
-
-PokedexRatingText_4420b:
-	TX_FAR _OaksLabText_4420b
-	db "@"
-
-PokedexRatingText_44210:
-	TX_FAR _OaksLabText_44210
-	db "@"
-
-PokedexRatingText_44215:
-	TX_FAR _OaksLabText_44215
-	db "@"
-
-PokedexRatingText_4421a:
-	TX_FAR _OaksLabText_4421a
-	db "@"
-
-PokedexRatingText_4421f:
-	TX_FAR _OaksLabText_4421f
-	db "@"
-
-PokedexRatingText_44224:
-	TX_FAR _OaksLabText_44224
-	db "@"
-
-PokedexRatingText_44229:
-	TX_FAR _OaksLabText_44229
-	db "@"
-
-PokedexRatingText_4422e:
-	TX_FAR _OaksLabText_4422e
-	db "@"
-
-PokedexRatingText_44233:
-	TX_FAR _OaksLabText_44233
-	db "@"
-
-PokedexRatingText_44238:
-	TX_FAR _OaksLabText_44238
-	db "@"
-
-PokedexRatingText_4423d:
-	TX_FAR _OaksLabText_4423d
-	db "@"
-
-PokedexRatingText_44242:
-	TX_FAR _OaksLabText_44242
-	db "@"
-
-PokedexRatingText_44247:
-	TX_FAR _OaksLabText_44247
-	db "@"
-
-PokedexRatingText_4424c:
-	TX_FAR _OaksLabText_4424c
-	db "@"
--- /dev/null
+++ b/engine/pokemon/add_mon.asm
@@ -1,0 +1,516 @@
+_AddPartyMon::
+; Adds a new mon to the player's or enemy's party.
+; [wMonDataLocation] is used in an unusual way in this function.
+; If the lower nybble is 0, the mon is added to the player's party, else the enemy's.
+; If the entire value is 0, then the player is allowed to name the mon.
+	ld de, wPartyCount
+	ld a, [wMonDataLocation]
+	and $f
+	jr z, .next
+	ld de, wEnemyPartyCount
+.next
+	ld a, [de]
+	inc a
+	cp PARTY_LENGTH + 1
+	ret nc ; return if the party is already full
+	ld [de], a
+	ld a, [de]
+	ld [hNewPartyLength], a
+	add e
+	ld e, a
+	jr nc, .noCarry
+	inc d
+.noCarry
+	ld a, [wcf91]
+	ld [de], a ; write species of new mon in party list
+	inc de
+	ld a, $ff ; terminator
+	ld [de], a
+	ld hl, wPartyMonOT
+	ld a, [wMonDataLocation]
+	and $f
+	jr z, .next2
+	ld hl, wEnemyMonOT
+.next2
+	ld a, [hNewPartyLength]
+	dec a
+	call SkipFixedLengthTextEntries
+	ld d, h
+	ld e, l
+	ld hl, wPlayerName
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld a, [wMonDataLocation]
+	and a
+	jr nz, .skipNaming
+	ld hl, wPartyMonNicks
+	ld a, [hNewPartyLength]
+	dec a
+	call SkipFixedLengthTextEntries
+	ld a, NAME_MON_SCREEN
+	ld [wNamingScreenType], a
+	predef AskName
+.skipNaming
+	ld hl, wPartyMons
+	ld a, [wMonDataLocation]
+	and $f
+	jr z, .next3
+	ld hl, wEnemyMons
+.next3
+	ld a, [hNewPartyLength]
+	dec a
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld e, l
+	ld d, h
+	push hl
+	ld a, [wcf91]
+	ld [wd0b5], a
+	call GetMonHeader
+	ld hl, wMonHeader
+	ld a, [hli]
+	ld [de], a ; species
+	inc de
+	pop hl
+	push hl
+	ld a, [wMonDataLocation]
+	and $f
+	ld a, $98     ; set enemy trainer mon IVs to fixed average values
+	ld b, $88
+	jr nz, .next4
+
+; If the mon is being added to the player's party, update the pokedex.
+	ld a, [wcf91]
+	ld [wd11e], a
+	push de
+	predef IndexToPokedex
+	pop de
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_TEST
+	ld hl, wPokedexOwned
+	call FlagAction
+	ld a, c ; whether the mon was already flagged as owned
+	ld [wUnusedD153], a ; not read
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_SET
+	push bc
+	call FlagAction
+	pop bc
+	ld hl, wPokedexSeen
+	call FlagAction
+
+	pop hl
+	push hl
+
+	ld a, [wIsInBattle]
+	and a ; is this a wild mon caught in battle?
+	jr nz, .copyEnemyMonData
+
+; Not wild.
+	call Random ; generate random IVs
+	ld b, a
+	call Random
+
+.next4
+	push bc
+	ld bc, wPartyMon1DVs - wPartyMon1
+	add hl, bc
+	pop bc
+	ld [hli], a
+	ld [hl], b         ; write IVs
+	ld bc, (wPartyMon1HPExp - 1) - (wPartyMon1DVs + 1)
+	add hl, bc
+	ld a, 1
+	ld c, a
+	xor a
+	ld b, a
+	call CalcStat      ; calc HP stat (set cur Hp to max HP)
+	ld a, [H_MULTIPLICAND+1]
+	ld [de], a
+	inc de
+	ld a, [H_MULTIPLICAND+2]
+	ld [de], a
+	inc de
+	xor a
+	ld [de], a         ; box level
+	inc de
+	ld [de], a         ; status ailments
+	inc de
+	jr .copyMonTypesAndMoves
+.copyEnemyMonData
+	ld bc, wEnemyMon1DVs - wEnemyMon1
+	add hl, bc
+	ld a, [wEnemyMonDVs] ; copy IVs from cur enemy mon
+	ld [hli], a
+	ld a, [wEnemyMonDVs + 1]
+	ld [hl], a
+	ld a, [wEnemyMonHP]    ; copy HP from cur enemy mon
+	ld [de], a
+	inc de
+	ld a, [wEnemyMonHP+1]
+	ld [de], a
+	inc de
+	xor a
+	ld [de], a                ; box level
+	inc de
+	ld a, [wEnemyMonStatus]   ; copy status ailments from cur enemy mon
+	ld [de], a
+	inc de
+.copyMonTypesAndMoves
+	ld hl, wMonHTypes
+	ld a, [hli]       ; type 1
+	ld [de], a
+	inc de
+	ld a, [hli]       ; type 2
+	ld [de], a
+	inc de
+	ld a, [hli]       ; catch rate (held item in gen 2)
+	ld [de], a
+	ld hl, wMonHMoves
+	ld a, [hli]
+	inc de
+	push de
+	ld [de], a
+	ld a, [hli]
+	inc de
+	ld [de], a
+	ld a, [hli]
+	inc de
+	ld [de], a
+	ld a, [hli]
+	inc de
+	ld [de], a
+	push de
+	dec de
+	dec de
+	dec de
+	xor a
+	ld [wLearningMovesFromDayCare], a
+	predef WriteMonMoves
+	pop de
+	ld a, [wPlayerID]  ; set trainer ID to player ID
+	inc de
+	ld [de], a
+	ld a, [wPlayerID + 1]
+	inc de
+	ld [de], a
+	push de
+	ld a, [wCurEnemyLVL]
+	ld d, a
+	callab CalcExperience
+	pop de
+	inc de
+	ld a, [hExperience] ; write experience
+	ld [de], a
+	inc de
+	ld a, [hExperience + 1]
+	ld [de], a
+	inc de
+	ld a, [hExperience + 2]
+	ld [de], a
+	xor a
+	ld b, NUM_STATS * 2
+.writeEVsLoop              ; set all EVs to 0
+	inc de
+	ld [de], a
+	dec b
+	jr nz, .writeEVsLoop
+	inc de
+	inc de
+	pop hl
+	call AddPartyMon_WriteMovePP
+	inc de
+	ld a, [wCurEnemyLVL]
+	ld [de], a
+	inc de
+	ld a, [wIsInBattle]
+	dec a
+	jr nz, .calcFreshStats
+	ld hl, wEnemyMonMaxHP
+	ld bc, $a
+	call CopyData          ; copy stats of cur enemy mon
+	pop hl
+	jr .done
+.calcFreshStats
+	pop hl
+	ld bc, wPartyMon1HPExp - 1 - wPartyMon1
+	add hl, bc
+	ld b, $0
+	call CalcStats         ; calculate fresh set of stats
+.done
+	scf
+	ret
+
+LoadMovePPs:
+	call GetPredefRegisters
+	; fallthrough
+AddPartyMon_WriteMovePP:
+	ld b, NUM_MOVES
+.pploop
+	ld a, [hli]     ; read move ID
+	and a
+	jr z, .empty
+	dec a
+	push hl
+	push de
+	push bc
+	ld hl, Moves
+	ld bc, MoveEnd - Moves
+	call AddNTimes
+	ld de, wcd6d
+	ld a, BANK(Moves)
+	call FarCopyData
+	pop bc
+	pop de
+	pop hl
+	ld a, [wcd6d + 5] ; PP is byte 5 of move data
+.empty
+	inc de
+	ld [de], a
+	dec b
+	jr nz, .pploop ; there are still moves to read
+	ret
+
+; adds enemy mon [wcf91] (at position [wWhichPokemon] in enemy list) to own party
+; used in the cable club trade center
+_AddEnemyMonToPlayerParty::
+	ld hl, wPartyCount
+	ld a, [hl]
+	cp PARTY_LENGTH
+	scf
+	ret z            ; party full, return failure
+	inc a
+	ld [hl], a       ; add 1 to party members
+	ld c, a
+	ld b, $0
+	add hl, bc
+	ld a, [wcf91]
+	ld [hli], a      ; add mon as last list entry
+	ld [hl], $ff     ; write new sentinel
+	ld hl, wPartyMons
+	ld a, [wPartyCount]
+	dec a
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld e, l
+	ld d, h
+	ld hl, wLoadedMon
+	call CopyData    ; write new mon's data (from wLoadedMon)
+	ld hl, wPartyMonOT
+	ld a, [wPartyCount]
+	dec a
+	call SkipFixedLengthTextEntries
+	ld d, h
+	ld e, l
+	ld hl, wEnemyMonOT
+	ld a, [wWhichPokemon]
+	call SkipFixedLengthTextEntries
+	ld bc, NAME_LENGTH
+	call CopyData    ; write new mon's OT name (from an enemy mon)
+	ld hl, wPartyMonNicks
+	ld a, [wPartyCount]
+	dec a
+	call SkipFixedLengthTextEntries
+	ld d, h
+	ld e, l
+	ld hl, wEnemyMonNicks
+	ld a, [wWhichPokemon]
+	call SkipFixedLengthTextEntries
+	ld bc, NAME_LENGTH
+	call CopyData    ; write new mon's nickname (from an enemy mon)
+	ld a, [wcf91]
+	ld [wd11e], a
+	predef IndexToPokedex
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_SET
+	ld hl, wPokedexOwned
+	push bc
+	call FlagAction ; add to owned pokemon
+	pop bc
+	ld hl, wPokedexSeen
+	call FlagAction ; add to seen pokemon
+	and a
+	ret                  ; return success
+
+_MoveMon::
+	ld a, [wMoveMonType]
+	and a   ; BOX_TO_PARTY
+	jr z, .checkPartyMonSlots
+	cp DAYCARE_TO_PARTY
+	jr z, .checkPartyMonSlots
+	cp PARTY_TO_DAYCARE
+	ld hl, wDayCareMon
+	jr z, .findMonDataSrc
+	; else it's PARTY_TO_BOX
+	ld hl, wNumInBox
+	ld a, [hl]
+	cp MONS_PER_BOX
+	jr nz, .partyOrBoxNotFull
+	jr .boxFull
+.checkPartyMonSlots
+	ld hl, wPartyCount
+	ld a, [hl]
+	cp PARTY_LENGTH
+	jr nz, .partyOrBoxNotFull
+.boxFull
+	scf
+	ret
+.partyOrBoxNotFull
+	inc a
+	ld [hl], a           ; increment number of mons in party/box
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [wMoveMonType]
+	cp DAYCARE_TO_PARTY
+	ld a, [wDayCareMon]
+	jr z, .copySpecies
+	ld a, [wcf91]
+.copySpecies
+	ld [hli], a          ; write new mon ID
+	ld [hl], $ff         ; write new sentinel
+.findMonDataDest
+	ld a, [wMoveMonType]
+	dec a
+	ld hl, wPartyMons
+	ld bc, wPartyMon2 - wPartyMon1 ; $2c
+	ld a, [wPartyCount]
+	jr nz, .addMonOffset
+	; if it's PARTY_TO_BOX
+	ld hl, wBoxMons
+	ld bc, wBoxMon2 - wBoxMon1 ; $21
+	ld a, [wNumInBox]
+.addMonOffset
+	dec a
+	call AddNTimes
+.findMonDataSrc
+	push hl
+	ld e, l
+	ld d, h
+	ld a, [wMoveMonType]
+	and a
+	ld hl, wBoxMons
+	ld bc, wBoxMon2 - wBoxMon1 ; $21
+	jr z, .addMonOffset2
+	cp DAYCARE_TO_PARTY
+	ld hl, wDayCareMon
+	jr z, .copyMonData
+	ld hl, wPartyMons
+	ld bc, wPartyMon2 - wPartyMon1 ; $2c
+.addMonOffset2
+	ld a, [wWhichPokemon]
+	call AddNTimes
+.copyMonData
+	push hl
+	push de
+	ld bc, wBoxMon2 - wBoxMon1
+	call CopyData
+	pop de
+	pop hl
+	ld a, [wMoveMonType]
+	and a ; BOX_TO_PARTY
+	jr z, .findOTdest
+	cp DAYCARE_TO_PARTY
+	jr z, .findOTdest
+	ld bc, wBoxMon2 - wBoxMon1
+	add hl, bc
+	ld a, [hl] ; hl = Level
+	inc de
+	inc de
+	inc de
+	ld [de], a ; de = BoxLevel
+.findOTdest
+	ld a, [wMoveMonType]
+	cp PARTY_TO_DAYCARE
+	ld de, wDayCareMonOT
+	jr z, .findOTsrc
+	dec a 
+	ld hl, wPartyMonOT
+	ld a, [wPartyCount]
+	jr nz, .addOToffset
+	ld hl, wBoxMonOT
+	ld a, [wNumInBox]
+.addOToffset
+	dec a
+	call SkipFixedLengthTextEntries
+	ld d, h
+	ld e, l
+.findOTsrc
+	ld hl, wBoxMonOT
+	ld a, [wMoveMonType]
+	and a
+	jr z, .addOToffset2
+	ld hl, wDayCareMonOT
+	cp DAYCARE_TO_PARTY
+	jr z, .copyOT
+	ld hl, wPartyMonOT
+.addOToffset2
+	ld a, [wWhichPokemon]
+	call SkipFixedLengthTextEntries
+.copyOT
+	ld bc, NAME_LENGTH
+	call CopyData
+	ld a, [wMoveMonType]
+.findNickDest
+	cp PARTY_TO_DAYCARE
+	ld de, wDayCareMonName
+	jr z, .findNickSrc
+	dec a
+	ld hl, wPartyMonNicks
+	ld a, [wPartyCount]
+	jr nz, .addNickOffset
+	ld hl, wBoxMonNicks
+	ld a, [wNumInBox]
+.addNickOffset
+	dec a
+	call SkipFixedLengthTextEntries
+	ld d, h
+	ld e, l
+.findNickSrc
+	ld hl, wBoxMonNicks
+	ld a, [wMoveMonType]
+	and a
+	jr z, .addNickOffset2
+	ld hl, wDayCareMonName
+	cp DAYCARE_TO_PARTY
+	jr z, .copyNick
+	ld hl, wPartyMonNicks
+.addNickOffset2
+	ld a, [wWhichPokemon]
+	call SkipFixedLengthTextEntries
+.copyNick
+	ld bc, NAME_LENGTH
+	call CopyData
+	pop hl
+	ld a, [wMoveMonType]
+	cp PARTY_TO_BOX
+	jr z, .done
+	cp PARTY_TO_DAYCARE
+	jr z, .done
+	push hl
+	srl a
+	add $2
+	ld [wMonDataLocation], a
+	call LoadMonData
+	callba CalcLevelFromExperience
+	ld a, d
+	ld [wCurEnemyLVL], a
+	pop hl
+	ld bc, wBoxMon2 - wBoxMon1
+	add hl, bc
+	ld [hli], a
+	ld d, h
+	ld e, l
+	ld bc, -18
+	add hl, bc
+	ld b, $1
+	call CalcStats
+.done
+	and a
+	ret
--- /dev/null
+++ b/engine/pokemon/bills_pc.asm
@@ -1,0 +1,548 @@
+DisplayPCMainMenu::
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call SaveScreenTilesToBuffer2
+	ld a, [wNumHoFTeams]
+	and a
+	jr nz, .leaguePCAvailable
+	CheckEvent EVENT_GOT_POKEDEX
+	jr z, .noOaksPC
+	ld a, [wNumHoFTeams]
+	and a
+	jr nz, .leaguePCAvailable
+	coord hl, 0, 0
+	ld b, 8
+	ld c, 14
+	jr .next
+.noOaksPC
+	coord hl, 0, 0
+	ld b, 6
+	ld c, 14
+	jr .next
+.leaguePCAvailable
+	coord hl, 0, 0
+	ld b, 10
+	ld c, 14
+.next
+	call TextBoxBorder
+	call UpdateSprites
+	ld a, 3
+	ld [wMaxMenuItem], a
+	CheckEvent EVENT_MET_BILL
+	jr nz, .metBill
+	coord hl, 2, 2
+	ld de, SomeonesPCText
+	jr .next2
+.metBill
+	coord hl, 2, 2
+	ld de, BillsPCText
+.next2
+	call PlaceString
+	coord hl, 2, 4
+	ld de, wPlayerName
+	call PlaceString
+	ld l, c
+	ld h, b
+	ld de, PlayersPCText
+	call PlaceString
+	CheckEvent EVENT_GOT_POKEDEX
+	jr z, .noOaksPC2
+	coord hl, 2, 6
+	ld de, OaksPCText
+	call PlaceString
+	ld a, [wNumHoFTeams]
+	and a
+	jr z, .noLeaguePC
+	ld a, 4
+	ld [wMaxMenuItem], a
+	coord hl, 2, 8
+	ld de, PKMNLeaguePCText
+	call PlaceString
+	coord hl, 2, 10
+	ld de, LogOffPCText
+	jr .next3
+.noLeaguePC
+	coord hl, 2, 8
+	ld de, LogOffPCText
+	jr .next3
+.noOaksPC2
+	ld a, $2
+	ld [wMaxMenuItem], a
+	coord hl, 2, 6
+	ld de, LogOffPCText
+.next3
+	call PlaceString
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 2
+	ld [wTopMenuItemY], a
+	ld a, 1
+	ld [wTopMenuItemX], a
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ret
+
+SomeonesPCText:   db "SOMEONE's PC@"
+BillsPCText:      db "BILL's PC@"
+PlayersPCText:    db "'s PC@"
+OaksPCText:       db "PROF.OAK's PC@"
+PKMNLeaguePCText: db "<pkmn>LEAGUE@"
+LogOffPCText:     db "LOG OFF@"
+
+BillsPC_::
+	ld hl, wd730
+	set 6, [hl]
+	xor a
+	ld [wParentMenuItem], a
+	inc a               ; MONSTER_NAME
+	ld [wNameListType], a
+	call LoadHpBarAndStatusTilePatterns
+	ld a, [wListScrollOffset]
+	push af
+	ld a, [wFlags_0xcd60]
+	bit 3, a ; accessing Bill's PC through another PC?
+	jr nz, BillsPCMenu
+; accessing it directly
+	ld a, SFX_TURN_ON_PC
+	call PlaySound
+	ld hl, SwitchOnText
+	call PrintText
+
+BillsPCMenu:
+	ld a, [wParentMenuItem]
+	ld [wCurrentMenuItem], a
+	ld hl, vChars2 + $780
+	ld de, PokeballTileGraphics
+	lb bc, BANK(PokeballTileGraphics), $01
+	call CopyVideoData
+	call LoadScreenTilesFromBuffer2DisableBGTransfer
+	coord hl, 0, 0
+	ld b, 10
+	ld c, 12
+	call TextBoxBorder
+	coord hl, 2, 2
+	ld de, BillsPCMenuText
+	call PlaceString
+	ld hl, wTopMenuItemY
+	ld a, 2
+	ld [hli], a ; wTopMenuItemY
+	dec a
+	ld [hli], a ; wTopMenuItemX
+	inc hl
+	inc hl
+	ld a, 4
+	ld [hli], a ; wMaxMenuItem
+	ld a, A_BUTTON | B_BUTTON
+	ld [hli], a ; wMenuWatchedKeys
+	xor a
+	ld [hli], a ; wLastMenuItem
+	ld [hli], a ; wPartyAndBillsPCSavedMenuItem
+	ld hl, wListScrollOffset
+	ld [hli], a ; wListScrollOffset
+	ld [hl], a ; wMenuWatchMovingOutOfBounds
+	ld [wPlayerMonNumber], a
+	ld hl, WhatText
+	call PrintText
+	coord hl, 9, 14
+	ld b, 2
+	ld c, 9
+	call TextBoxBorder
+	ld a, [wCurrentBoxNum]
+	and $7f
+	cp 9
+	jr c, .singleDigitBoxNum
+; two digit box num
+	sub 9
+	coord hl, 17, 16
+	ld [hl], "1"
+	add "0"
+	jr .next
+.singleDigitBoxNum
+	add "1"
+.next
+	Coorda 18, 16
+	coord hl, 10, 16
+	ld de, BoxNoPCText
+	call PlaceString
+	ld a, 1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Delay3
+	call HandleMenuInput
+	bit 1, a
+	jp nz, ExitBillsPC ; b button
+	call PlaceUnfilledArrowMenuCursor
+	ld a, [wCurrentMenuItem]
+	ld [wParentMenuItem], a
+	and a
+	jp z, BillsPCWithdraw ; withdraw
+	cp $1
+	jp z, BillsPCDeposit ; deposit
+	cp $2
+	jp z, BillsPCRelease ; release
+	cp $3
+	jp z, BillsPCChangeBox ; change box
+
+ExitBillsPC:
+	ld a, [wFlags_0xcd60]
+	bit 3, a ; accessing Bill's PC through another PC?
+	jr nz, .next
+; accessing it directly
+	call LoadTextBoxTilePatterns
+	ld a, SFX_TURN_OFF_PC
+	call PlaySound
+	call WaitForSoundToFinish
+.next
+	ld hl, wFlags_0xcd60
+	res 5, [hl]
+	call LoadScreenTilesFromBuffer2
+	pop af
+	ld [wListScrollOffset], a
+	ld hl, wd730
+	res 6, [hl]
+	ret
+
+BillsPCDeposit:
+	ld a, [wPartyCount]
+	dec a
+	jr nz, .partyLargeEnough
+	ld hl, CantDepositLastMonText
+	call PrintText
+	jp BillsPCMenu
+.partyLargeEnough
+	ld a, [wNumInBox]
+	cp MONS_PER_BOX
+	jr nz, .boxNotFull
+	ld hl, BoxFullText
+	call PrintText
+	jp BillsPCMenu
+.boxNotFull
+	ld hl, wPartyCount
+	call DisplayMonListMenu
+	jp c, BillsPCMenu
+	call DisplayDepositWithdrawMenu
+	jp nc, BillsPCMenu
+	ld a, [wcf91]
+	call GetCryData
+	call PlaySoundWaitForCurrent
+	ld a, PARTY_TO_BOX
+	ld [wMoveMonType], a
+	call MoveMon
+	xor a
+	ld [wRemoveMonFromBox], a
+	call RemovePokemon
+	call WaitForSoundToFinish
+	ld hl, wBoxNumString
+	ld a, [wCurrentBoxNum]
+	and $7f
+	cp 9
+	jr c, .singleDigitBoxNum
+	sub 9
+	ld [hl], "1"
+	inc hl
+	add "0"
+	jr .next
+.singleDigitBoxNum
+	add "1"
+.next
+	ld [hli], a
+	ld [hl], "@"
+	ld hl, MonWasStoredText
+	call PrintText
+	jp BillsPCMenu
+
+BillsPCWithdraw:
+	ld a, [wNumInBox]
+	and a
+	jr nz, .boxNotEmpty
+	ld hl, NoMonText
+	call PrintText
+	jp BillsPCMenu
+.boxNotEmpty
+	ld a, [wPartyCount]
+	cp PARTY_LENGTH
+	jr nz, .partyNotFull
+	ld hl, CantTakeMonText
+	call PrintText
+	jp BillsPCMenu
+.partyNotFull
+	ld hl, wNumInBox
+	call DisplayMonListMenu
+	jp c, BillsPCMenu
+	call DisplayDepositWithdrawMenu
+	jp nc, BillsPCMenu
+	ld a, [wWhichPokemon]
+	ld hl, wBoxMonNicks
+	call GetPartyMonName
+	ld a, [wcf91]
+	call GetCryData
+	call PlaySoundWaitForCurrent
+	xor a ; BOX_TO_PARTY
+	ld [wMoveMonType], a
+	call MoveMon
+	ld a, 1
+	ld [wRemoveMonFromBox], a
+	call RemovePokemon
+	call WaitForSoundToFinish
+	ld hl, MonIsTakenOutText
+	call PrintText
+	jp BillsPCMenu
+
+BillsPCRelease:
+	ld a, [wNumInBox]
+	and a
+	jr nz, .loop
+	ld hl, NoMonText
+	call PrintText
+	jp BillsPCMenu
+.loop
+	ld hl, wNumInBox
+	call DisplayMonListMenu
+	jp c, BillsPCMenu
+	ld hl, OnceReleasedText
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .loop
+	inc a
+	ld [wRemoveMonFromBox], a
+	call RemovePokemon
+	call WaitForSoundToFinish
+	ld a, [wcf91]
+	call PlayCry
+	ld hl, MonWasReleasedText
+	call PrintText
+	jp BillsPCMenu
+
+BillsPCChangeBox:
+	callba ChangeBox
+	jp BillsPCMenu
+
+DisplayMonListMenu:
+	ld a, l
+	ld [wListPointer], a
+	ld a, h
+	ld [wListPointer + 1], a
+	xor a
+	ld [wPrintItemPrices], a
+	ld [wListMenuID], a
+	inc a                ; MONSTER_NAME
+	ld [wNameListType], a
+	ld a, [wPartyAndBillsPCSavedMenuItem]
+	ld [wCurrentMenuItem], a
+	call DisplayListMenuID
+	ld a, [wCurrentMenuItem]
+	ld [wPartyAndBillsPCSavedMenuItem], a
+	ret
+
+BillsPCMenuText:
+	db   "WITHDRAW <pkmn>"
+	next "DEPOSIT <pkmn>"
+	next "RELEASE <pkmn>"
+	next "CHANGE BOX"
+	next "SEE YA!"
+	db "@"
+
+BoxNoPCText:
+	db "BOX No.@"
+
+KnowsHMMove::
+; returns whether mon with party index [wWhichPokemon] knows an HM move
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2 - wPartyMon1
+	jr .next
+; unreachable
+	ld hl, wBoxMon1Moves
+	ld bc, wBoxMon2 - wBoxMon1
+.next
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld b, NUM_MOVES
+.loop
+	ld a, [hli]
+	push hl
+	push bc
+	ld hl, HMMoveArray
+	ld de, 1
+	call IsInArray
+	pop bc
+	pop hl
+	ret c
+	dec b
+	jr nz, .loop
+	and a
+	ret
+
+INCLUDE "data/hm_moves.asm"
+
+DisplayDepositWithdrawMenu:
+	coord hl, 9, 10
+	ld b, 6
+	ld c, 9
+	call TextBoxBorder
+	ld a, [wParentMenuItem]
+	and a ; was the Deposit or Withdraw item selected in the parent menu?
+	ld de, DepositPCText
+	jr nz, .next
+	ld de, WithdrawPCText
+.next
+	coord hl, 11, 12
+	call PlaceString
+	coord hl, 11, 14
+	ld de, StatsCancelPCText
+	call PlaceString
+	ld hl, wTopMenuItemY
+	ld a, 12
+	ld [hli], a ; wTopMenuItemY
+	ld a, 10
+	ld [hli], a ; wTopMenuItemX
+	xor a
+	ld [hli], a ; wCurrentMenuItem
+	inc hl
+	ld a, 2
+	ld [hli], a ; wMaxMenuItem
+	ld a, A_BUTTON | B_BUTTON
+	ld [hli], a ; wMenuWatchedKeys
+	xor a
+	ld [hl], a ; wLastMenuItem
+	ld hl, wListScrollOffset
+	ld [hli], a ; wListScrollOffset
+	ld [hl], a ; wMenuWatchMovingOutOfBounds
+	ld [wPlayerMonNumber], a
+	ld [wPartyAndBillsPCSavedMenuItem], a
+.loop
+	call HandleMenuInput
+	bit 1, a ; pressed B?
+	jr nz, .exit
+	ld a, [wCurrentMenuItem]
+	and a
+	jr z, .choseDepositWithdraw
+	dec a
+	jr z, .viewStats
+.exit
+	and a
+	ret
+.choseDepositWithdraw
+	scf
+	ret
+.viewStats
+	call SaveScreenTilesToBuffer1
+	ld a, [wParentMenuItem]
+	and a
+	ld a, PLAYER_PARTY_DATA
+	jr nz, .next2
+	ld a, BOX_DATA
+.next2
+	ld [wMonDataLocation], a
+	predef StatusScreen
+	predef StatusScreen2
+	call LoadScreenTilesFromBuffer1
+	call ReloadTilesetTilePatterns
+	call RunDefaultPaletteCommand
+	call LoadGBPal
+	jr .loop
+
+DepositPCText:  db "DEPOSIT@"
+WithdrawPCText: db "WITHDRAW@"
+StatsCancelPCText:
+	db   "STATS"
+	next "CANCEL@"
+
+SwitchOnText:
+	TX_FAR _SwitchOnText
+	db "@"
+
+WhatText:
+	TX_FAR _WhatText
+	db "@"
+
+DepositWhichMonText:
+	TX_FAR _DepositWhichMonText
+	db "@"
+
+MonWasStoredText:
+	TX_FAR _MonWasStoredText
+	db "@"
+
+CantDepositLastMonText:
+	TX_FAR _CantDepositLastMonText
+	db "@"
+
+BoxFullText:
+	TX_FAR _BoxFullText
+	db "@"
+
+MonIsTakenOutText:
+	TX_FAR _MonIsTakenOutText
+	db "@"
+
+NoMonText:
+	TX_FAR _NoMonText
+	db "@"
+
+CantTakeMonText:
+	TX_FAR _CantTakeMonText
+	db "@"
+
+ReleaseWhichMonText:
+	TX_FAR _ReleaseWhichMonText
+	db "@"
+
+OnceReleasedText:
+	TX_FAR _OnceReleasedText
+	db "@"
+
+MonWasReleasedText:
+	TX_FAR _MonWasReleasedText
+	db "@"
+
+CableClubLeftGameboy::
+	ld a, [hSerialConnectionStatus]
+	cp USING_EXTERNAL_CLOCK
+	ret z
+	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+	cp SPRITE_FACING_RIGHT
+	ret nz
+	ld a, [wCurMap]
+	cp TRADE_CENTER
+	ld a, LINK_STATE_START_TRADE
+	jr z, .next
+	inc a ; LINK_STATE_START_BATTLE
+.next
+	ld [wLinkState], a
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump JustAMomentText
+
+CableClubRightGameboy::
+	ld a, [hSerialConnectionStatus]
+	cp USING_INTERNAL_CLOCK
+	ret z
+	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+	cp SPRITE_FACING_LEFT
+	ret nz
+	ld a, [wCurMap]
+	cp TRADE_CENTER
+	ld a, LINK_STATE_START_TRADE
+	jr z, .next
+	inc a ; LINK_STATE_START_BATTLE
+.next
+	ld [wLinkState], a
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump JustAMomentText
+
+JustAMomentText::
+	TX_FAR _JustAMomentText
+	db "@"
+
+	ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+	cp SPRITE_FACING_UP
+	ret nz
+	call EnableAutoTextBoxDrawing
+	tx_pre_jump OpenBillsPCText
+
+OpenBillsPCText::
+	TX_BILLS_PC
+
--- /dev/null
+++ b/engine/pokemon/evos_moves.asm
@@ -1,0 +1,513 @@
+; try to evolve the mon in [wWhichPokemon]
+TryEvolvingMon:
+	ld hl, wCanEvolveFlags
+	xor a
+	ld [hl], a
+	ld a, [wWhichPokemon]
+	ld c, a
+	ld b, FLAG_SET
+	call Evolution_FlagAction
+
+; this is only called after battle
+; it is supposed to do level up evolutions, though there is a bug that allows item evolutions to occur
+EvolutionAfterBattle:
+	ld a, [hTilesetType]
+	push af
+	xor a
+	ld [wEvolutionOccurred], a
+	dec a
+	ld [wWhichPokemon], a
+	push hl
+	push bc
+	push de
+	ld hl, wPartyCount
+	push hl
+
+Evolution_PartyMonLoop: ; loop over party mons
+	ld hl, wWhichPokemon
+	inc [hl]
+	pop hl
+	inc hl
+	ld a, [hl]
+	cp $ff ; have we reached the end of the party?
+	jp z, .done
+	ld [wEvoOldSpecies], a
+	push hl
+	ld a, [wWhichPokemon]
+	ld c, a
+	ld hl, wCanEvolveFlags
+	ld b, FLAG_TEST
+	call Evolution_FlagAction
+	ld a, c
+	and a ; is the mon's bit set?
+	jp z, Evolution_PartyMonLoop ; if not, go to the next mon
+	ld a, [wEvoOldSpecies]
+	dec a
+	ld b, 0
+	ld hl, EvosMovesPointerTable
+	add a
+	rl b
+	ld c, a
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	push hl
+	ld a, [wcf91]
+	push af
+	xor a ; PLAYER_PARTY_DATA
+	ld [wMonDataLocation], a
+	call LoadMonData
+	pop af
+	ld [wcf91], a
+	pop hl
+
+.evoEntryLoop ; loop over evolution entries
+	ld a, [hli]
+	and a ; have we reached the end of the evolution data?
+	jr z, Evolution_PartyMonLoop
+	ld b, a ; evolution type
+	cp EV_TRADE
+	jr z, .checkTradeEvo
+; not trade evolution
+	ld a, [wLinkState]
+	cp LINK_STATE_TRADING
+	jr z, Evolution_PartyMonLoop ; if trading, go the next mon
+	ld a, b
+	cp EV_ITEM
+	jr z, .checkItemEvo
+	ld a, [wForceEvolution]
+	and a
+	jr nz, Evolution_PartyMonLoop
+	ld a, b
+	cp EV_LEVEL
+	jr z, .checkLevel
+.checkTradeEvo
+	ld a, [wLinkState]
+	cp LINK_STATE_TRADING
+	jp nz, .nextEvoEntry1 ; if not trading, go to the next evolution entry
+	ld a, [hli] ; level requirement
+	ld b, a
+	ld a, [wLoadedMonLevel]
+	cp b ; is the mon's level greater than the evolution requirement?
+	jp c, Evolution_PartyMonLoop ; if so, go the next mon
+	jr .doEvolution
+.checkItemEvo
+	ld a, [hli]
+	ld b, a ; evolution item
+	ld a, [wcf91] ; this is supposed to be the last item used, but it is also used to hold species numbers
+	cp b ; was the evolution item in this entry used?
+	jp nz, .nextEvoEntry1 ; if not, go to the next evolution entry
+.checkLevel
+	ld a, [hli] ; level requirement
+	ld b, a
+	ld a, [wLoadedMonLevel]
+	cp b ; is the mon's level greater than the evolution requirement?
+	jp c, .nextEvoEntry2 ; if so, go the next evolution entry
+.doEvolution
+	ld [wCurEnemyLVL], a
+	ld a, 1
+	ld [wEvolutionOccurred], a
+	push hl
+	ld a, [hl]
+	ld [wEvoNewSpecies], a
+	ld a, [wWhichPokemon]
+	ld hl, wPartyMonNicks
+	call GetPartyMonName
+	call CopyStringToCF4B
+	ld hl, IsEvolvingText
+	call PrintText
+	ld c, 50
+	call DelayFrames
+	xor a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	coord hl, 0, 0
+	lb bc, 12, 20
+	call ClearScreenArea
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, $ff
+	ld [wUpdateSpritesEnabled], a
+	call ClearSprites
+	callab EvolveMon
+	jp c, CancelledEvolution
+	ld hl, EvolvedText
+	call PrintText
+	pop hl
+	ld a, [hl]
+	ld [wd0b5], a
+	ld [wLoadedMonSpecies], a
+	ld [wEvoNewSpecies], a
+	ld a, MONSTER_NAME
+	ld [wNameListType], a
+	ld a, BANK(TrainerNames) ; bank is not used for monster names
+	ld [wPredefBank], a
+	call GetName
+	push hl
+	ld hl, IntoText
+	call PrintText_NoCreatingTextBox
+	ld a, SFX_GET_ITEM_2
+	call PlaySoundWaitForCurrent
+	call WaitForSoundToFinish
+	ld c, 40
+	call DelayFrames
+	call ClearScreen
+	call RenameEvolvedMon
+	ld a, [wd11e]
+	push af
+	ld a, [wd0b5]
+	ld [wd11e], a
+	predef IndexToPokedex
+	ld a, [wd11e]
+	dec a
+	ld hl, BaseStats
+	ld bc, MonBaseStatsEnd - MonBaseStats
+	call AddNTimes
+	ld de, wMonHeader
+	call CopyData
+	ld a, [wd0b5]
+	ld [wMonHIndex], a
+	pop af
+	ld [wd11e], a
+	ld hl, wLoadedMonHPExp - 1
+	ld de, wLoadedMonStats
+	ld b, $1
+	call CalcStats
+	ld a, [wWhichPokemon]
+	ld hl, wPartyMon1
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+	ld e, l
+	ld d, h
+	push hl
+	push bc
+	ld bc, wPartyMon1MaxHP - wPartyMon1
+	add hl, bc
+	ld a, [hli]
+	ld b, a
+	ld c, [hl]
+	ld hl, wLoadedMonMaxHP + 1
+	ld a, [hld]
+	sub c
+	ld c, a
+	ld a, [hl]
+	sbc b
+	ld b, a
+	ld hl, wLoadedMonHP + 1
+	ld a, [hl]
+	add c
+	ld [hld], a
+	ld a, [hl]
+	adc b
+	ld [hl], a
+	dec hl
+	pop bc
+	call CopyData
+	ld a, [wd0b5]
+	ld [wd11e], a
+	xor a
+	ld [wMonDataLocation], a
+	call LearnMoveFromLevelUp
+	pop hl
+	predef SetPartyMonTypes
+	ld a, [wIsInBattle]
+	and a
+	call z, Evolution_ReloadTilesetTilePatterns
+	predef IndexToPokedex
+	ld a, [wd11e]
+	dec a
+	ld c, a
+	ld b, FLAG_SET
+	ld hl, wPokedexOwned
+	push bc
+	call Evolution_FlagAction
+	pop bc
+	ld hl, wPokedexSeen
+	call Evolution_FlagAction
+	pop de
+	pop hl
+	ld a, [wLoadedMonSpecies]
+	ld [hl], a
+	push hl
+	ld l, e
+	ld h, d
+	jr .nextEvoEntry2
+
+.nextEvoEntry1
+	inc hl
+
+.nextEvoEntry2
+	inc hl
+	jp .evoEntryLoop
+
+.done
+	pop de
+	pop bc
+	pop hl
+	pop af
+	ld [hTilesetType], a
+	ld a, [wLinkState]
+	cp LINK_STATE_TRADING
+	ret z
+	ld a, [wIsInBattle]
+	and a
+	ret nz
+	ld a, [wEvolutionOccurred]
+	and a
+	call nz, PlayDefaultMusic
+	ret
+
+RenameEvolvedMon:
+; Renames the mon to its new, evolved form's standard name unless it had a
+; nickname, in which case the nickname is kept.
+	ld a, [wd0b5]
+	push af
+	ld a, [wMonHIndex]
+	ld [wd0b5], a
+	call GetName
+	pop af
+	ld [wd0b5], a
+	ld hl, wcd6d
+	ld de, wcf4b
+.compareNamesLoop
+	ld a, [de]
+	inc de
+	cp [hl]
+	inc hl
+	ret nz
+	cp "@"
+	jr nz, .compareNamesLoop
+	ld a, [wWhichPokemon]
+	ld bc, NAME_LENGTH
+	ld hl, wPartyMonNicks
+	call AddNTimes
+	push hl
+	call GetName
+	ld hl, wcd6d
+	pop de
+	jp CopyData
+
+CancelledEvolution:
+	ld hl, StoppedEvolvingText
+	call PrintText
+	call ClearScreen
+	pop hl
+	call Evolution_ReloadTilesetTilePatterns
+	jp Evolution_PartyMonLoop
+
+EvolvedText:
+	TX_FAR _EvolvedText
+	db "@"
+
+IntoText:
+	TX_FAR _IntoText
+	db "@"
+
+StoppedEvolvingText:
+	TX_FAR _StoppedEvolvingText
+	db "@"
+
+IsEvolvingText:
+	TX_FAR _IsEvolvingText
+	db "@"
+
+Evolution_ReloadTilesetTilePatterns:
+	ld a, [wLinkState]
+	cp LINK_STATE_TRADING
+	ret z
+	jp ReloadTilesetTilePatterns
+
+LearnMoveFromLevelUp:
+	ld hl, EvosMovesPointerTable
+	ld a, [wd11e] ; species
+	ld [wcf91], a
+	dec a
+	ld bc, 0
+	ld hl, EvosMovesPointerTable
+	add a
+	rl b
+	ld c, a
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+.skipEvolutionDataLoop ; loop to skip past the evolution data, which comes before the move data
+	ld a, [hli]
+	and a ; have we reached the end of the evolution data?
+	jr nz, .skipEvolutionDataLoop ; if not, jump back up
+.learnSetLoop ; loop over the learn set until we reach a move that is learnt at the current level or the end of the list
+	ld a, [hli]
+	and a ; have we reached the end of the learn set?
+	jr z, .done ; if we've reached the end of the learn set, jump
+	ld b, a ; level the move is learnt at
+	ld a, [wCurEnemyLVL]
+	cp b ; is the move learnt at the mon's current level?
+	ld a, [hli] ; move ID
+	jr nz, .learnSetLoop
+	ld d, a ; ID of move to learn
+	ld a, [wMonDataLocation]
+	and a
+	jr nz, .next
+; If [wMonDataLocation] is 0 (PLAYER_PARTY_DATA), get the address of the mon's
+; current moves in party data. Every call to this function sets
+; [wMonDataLocation] to 0 because other data locations are not supported.
+; If it is not 0, this function will not work properly.
+	ld hl, wPartyMon1Moves
+	ld a, [wWhichPokemon]
+	ld bc, wPartyMon2 - wPartyMon1
+	call AddNTimes
+.next
+	ld b, NUM_MOVES
+.checkCurrentMovesLoop ; check if the move to learn is already known
+	ld a, [hli]
+	cp d
+	jr z, .done ; if already known, jump
+	dec b
+	jr nz, .checkCurrentMovesLoop
+	ld a, d
+	ld [wMoveNum], a
+	ld [wd11e], a
+	call GetMoveName
+	call CopyStringToCF4B
+	predef LearnMove
+.done
+	ld a, [wcf91]
+	ld [wd11e], a
+	ret
+
+; writes the moves a mon has at level [wCurEnemyLVL] to [de]
+; move slots are being filled up sequentially and shifted if all slots are full
+WriteMonMoves:
+	call GetPredefRegisters
+	push hl
+	push de
+	push bc
+	ld hl, EvosMovesPointerTable
+	ld b, 0
+	ld a, [wcf91]  ; cur mon ID
+	dec a
+	add a
+	rl b
+	ld c, a
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+.skipEvoEntriesLoop
+	ld a, [hli]
+	and a
+	jr nz, .skipEvoEntriesLoop
+	jr .firstMove
+.nextMove
+	pop de
+.nextMove2
+	inc hl
+.firstMove
+	ld a, [hli]       ; read level of next move in learnset
+	and a
+	jp z, .done       ; end of list
+	ld b, a
+	ld a, [wCurEnemyLVL]
+	cp b
+	jp c, .done       ; mon level < move level (assumption: learnset is sorted by level)
+	ld a, [wLearningMovesFromDayCare]
+	and a
+	jr z, .skipMinLevelCheck
+	ld a, [wDayCareStartLevel]
+	cp b
+	jr nc, .nextMove2 ; min level >= move level
+
+.skipMinLevelCheck
+
+; check if the move is already known
+	push de
+	ld c, NUM_MOVES
+.alreadyKnowsCheckLoop
+	ld a, [de]
+	inc de
+	cp [hl]
+	jr z, .nextMove
+	dec c
+	jr nz, .alreadyKnowsCheckLoop
+
+; try to find an empty move slot
+	pop de
+	push de
+	ld c, NUM_MOVES
+.findEmptySlotLoop
+	ld a, [de]
+	and a
+	jr z, .writeMoveToSlot2
+	inc de
+	dec c
+	jr nz, .findEmptySlotLoop
+
+; no empty move slots found
+	pop de
+	push de
+	push hl
+	ld h, d
+	ld l, e
+	call WriteMonMoves_ShiftMoveData ; shift all moves one up (deleting move 1)
+	ld a, [wLearningMovesFromDayCare]
+	and a
+	jr z, .writeMoveToSlot
+
+; shift PP as well if learning moves from day care
+	push de
+	ld bc, wPartyMon1PP - (wPartyMon1Moves + 3)
+	add hl, bc
+	ld d, h
+	ld e, l
+	call WriteMonMoves_ShiftMoveData ; shift all move PP data one up
+	pop de
+
+.writeMoveToSlot
+	pop hl
+.writeMoveToSlot2
+	ld a, [hl]
+	ld [de], a
+	ld a, [wLearningMovesFromDayCare]
+	and a
+	jr z, .nextMove
+
+; write move PP value if learning moves from day care
+	push hl
+	ld a, [hl]
+	ld hl, wPartyMon1PP - wPartyMon1Moves
+	add hl, de
+	push hl
+	dec a
+	ld hl, Moves
+	ld bc, MoveEnd - Moves
+	call AddNTimes
+	ld de, wBuffer
+	ld a, BANK(Moves)
+	call FarCopyData
+	ld a, [wBuffer + 5]
+	pop hl
+	ld [hl], a
+	pop hl
+	jr .nextMove
+
+.done
+	pop bc
+	pop de
+	pop hl
+	ret
+
+; shifts all move data one up (freeing 4th move slot)
+WriteMonMoves_ShiftMoveData:
+	ld c, NUM_MOVES - 1
+.loop
+	inc de
+	ld a, [de]
+	ld [hli], a
+	dec c
+	jr nz, .loop
+	ret
+
+Evolution_FlagAction:
+	predef_jump FlagActionPredef
+
+INCLUDE "data/evos_moves.asm"
--- /dev/null
+++ b/engine/pokemon/experience.asm
@@ -1,0 +1,149 @@
+; calculates the level a mon should be based on its current exp
+CalcLevelFromExperience::
+	ld a, [wLoadedMonSpecies]
+	ld [wd0b5], a
+	call GetMonHeader
+	ld d, $1 ; init level to 1
+.loop
+	inc d ; increment level
+	call CalcExperience
+	push hl
+	ld hl, wLoadedMonExp + 2 ; current exp
+; compare exp needed for level d with current exp
+	ld a, [hExperience + 2]
+	ld c, a
+	ld a, [hld]
+	sub c
+	ld a, [hExperience + 1]
+	ld c, a
+	ld a, [hld]
+	sbc c
+	ld a, [hExperience]
+	ld c, a
+	ld a, [hl]
+	sbc c
+	pop hl
+	jr nc, .loop ; if exp needed for level d is not greater than exp, try the next level
+	dec d ; since the exp was too high on the last loop iteration, go back to the previous value and return
+	ret
+
+; calculates the amount of experience needed for level d
+CalcExperience::
+	ld a, [wMonHGrowthRate]
+	add a
+	add a
+	ld c, a
+	ld b, 0
+	ld hl, GrowthRateTable
+	add hl, bc
+	call CalcDSquared
+	ld a, d
+	ld [H_MULTIPLIER], a
+	call Multiply
+	ld a, [hl]
+	and $f0
+	swap a
+	ld [H_MULTIPLIER], a
+	call Multiply
+	ld a, [hli]
+	and $f
+	ld [H_DIVISOR], a
+	ld b, $4
+	call Divide
+	ld a, [H_QUOTIENT + 1]
+	push af
+	ld a, [H_QUOTIENT + 2]
+	push af
+	ld a, [H_QUOTIENT + 3]
+	push af
+	call CalcDSquared
+	ld a, [hl]
+	and $7f
+	ld [H_MULTIPLIER], a
+	call Multiply
+	ld a, [H_PRODUCT + 1]
+	push af
+	ld a, [H_PRODUCT + 2]
+	push af
+	ld a, [H_PRODUCT + 3]
+	push af
+	ld a, [hli]
+	push af
+	xor a
+	ld [H_MULTIPLICAND], a
+	ld [H_MULTIPLICAND + 1], a
+	ld a, d
+	ld [H_MULTIPLICAND + 2], a
+	ld a, [hli]
+	ld [H_MULTIPLIER], a
+	call Multiply
+	ld b, [hl]
+	ld a, [H_PRODUCT + 3]
+	sub b
+	ld [H_PRODUCT + 3], a
+	ld b, $0
+	ld a, [H_PRODUCT + 2]
+	sbc b
+	ld [H_PRODUCT + 2], a
+	ld a, [H_PRODUCT + 1]
+	sbc b
+	ld [H_PRODUCT + 1], a
+; The difference of the linear term and the constant term consists of 3 bytes
+; starting at H_PRODUCT + 1. Below, hExperience (an alias of that address) will
+; be used instead for the further work of adding or subtracting the squared
+; term and adding the cubed term.
+	pop af
+	and $80
+	jr nz, .subtractSquaredTerm ; check sign
+	pop bc
+	ld a, [hExperience + 2]
+	add b
+	ld [hExperience + 2], a
+	pop bc
+	ld a, [hExperience + 1]
+	adc b
+	ld [hExperience + 1], a
+	pop bc
+	ld a, [hExperience]
+	adc b
+	ld [hExperience], a
+	jr .addCubedTerm
+.subtractSquaredTerm
+	pop bc
+	ld a, [hExperience + 2]
+	sub b
+	ld [hExperience + 2], a
+	pop bc
+	ld a, [hExperience + 1]
+	sbc b
+	ld [hExperience + 1], a
+	pop bc
+	ld a, [hExperience]
+	sbc b
+	ld [hExperience], a
+.addCubedTerm
+	pop bc
+	ld a, [hExperience + 2]
+	add b
+	ld [hExperience + 2], a
+	pop bc
+	ld a, [hExperience + 1]
+	adc b
+	ld [hExperience + 1], a
+	pop bc
+	ld a, [hExperience]
+	adc b
+	ld [hExperience], a
+	ret
+
+; calculates d*d
+CalcDSquared:
+	xor a
+	ld [H_MULTIPLICAND], a
+	ld [H_MULTIPLICAND + 1], a
+	ld a, d
+	ld [H_MULTIPLICAND + 2], a
+	ld [H_MULTIPLIER], a
+	jp Multiply
+
+INCLUDE "data/growth_rates.asm"
--- /dev/null
+++ b/engine/pokemon/learn_move.asm
@@ -1,0 +1,226 @@
+LearnMove:
+	call SaveScreenTilesToBuffer1
+	ld a, [wWhichPokemon]
+	ld hl, wPartyMonNicks
+	call GetPartyMonName
+	ld hl, wcd6d
+	ld de, wLearnMoveMonName
+	ld bc, NAME_LENGTH
+	call CopyData
+
+DontAbandonLearning:
+	ld hl, wPartyMon1Moves
+	ld bc, wPartyMon2Moves - wPartyMon1Moves
+	ld a, [wWhichPokemon]
+	call AddNTimes
+	ld d, h
+	ld e, l
+	ld b, NUM_MOVES
+.findEmptyMoveSlotLoop
+	ld a, [hl]
+	and a
+	jr z, .next
+	inc hl
+	dec b
+	jr nz, .findEmptyMoveSlotLoop
+	push de
+	call TryingToLearn
+	pop de
+	jp c, AbandonLearning
+	push hl
+	push de
+	ld [wd11e], a
+	call GetMoveName
+	ld hl, OneTwoAndText
+	call PrintText
+	pop de
+	pop hl
+.next
+	ld a, [wMoveNum]
+	ld [hl], a
+	ld bc, wPartyMon1PP - wPartyMon1Moves
+	add hl, bc
+	push hl
+	push de
+	dec a
+	ld hl, Moves
+	ld bc, MoveEnd - Moves
+	call AddNTimes
+	ld de, wBuffer
+	ld a, BANK(Moves)
+	call FarCopyData
+	ld a, [wBuffer + 5] ; a = move's max PP
+	pop de
+	pop hl
+	ld [hl], a
+	ld a, [wIsInBattle]
+	and a
+	jp z, PrintLearnedMove
+	ld a, [wWhichPokemon]
+	ld b, a
+	ld a, [wPlayerMonNumber]
+	cp b
+	jp nz, PrintLearnedMove
+	ld h, d
+	ld l, e
+	ld de, wBattleMonMoves
+	ld bc, NUM_MOVES
+	call CopyData
+	ld bc, wPartyMon1PP - wPartyMon1OTID
+	add hl, bc
+	ld de, wBattleMonPP
+	ld bc, NUM_MOVES
+	call CopyData
+	jp PrintLearnedMove
+
+AbandonLearning:
+	ld hl, AbandonLearningText
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; yes/no menu
+	ld a, [wCurrentMenuItem]
+	and a
+	jp nz, DontAbandonLearning
+	ld hl, DidNotLearnText
+	call PrintText
+	ld b, 0
+	ret
+
+PrintLearnedMove:
+	ld hl, LearnedMove1Text
+	call PrintText
+	ld b, 1
+	ret
+
+TryingToLearn:
+	push hl
+	ld hl, TryingToLearnText
+	call PrintText
+	coord hl, 14, 7
+	lb bc, 8, 15
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID ; yes/no menu
+	pop hl
+	ld a, [wCurrentMenuItem]
+	rra
+	ret c
+	ld bc, -NUM_MOVES
+	add hl, bc
+	push hl
+	ld de, wMoves
+	ld bc, NUM_MOVES
+	call CopyData
+	callab FormatMovesString
+	pop hl
+.loop
+	push hl
+	ld hl, WhichMoveToForgetText
+	call PrintText
+	coord hl, 4, 7
+	ld b, 4
+	ld c, 14
+	call TextBoxBorder
+	coord hl, 6, 8
+	ld de, wMovesString
+	ld a, [hFlags_0xFFF6]
+	set 2, a
+	ld [hFlags_0xFFF6], a
+	call PlaceString
+	ld a, [hFlags_0xFFF6]
+	res 2, a
+	ld [hFlags_0xFFF6], a
+	ld hl, wTopMenuItemY
+	ld a, 8
+	ld [hli], a ; wTopMenuItemY
+	ld a, 5
+	ld [hli], a ; wTopMenuItemX
+	xor a
+	ld [hli], a ; wCurrentMenuItem
+	inc hl
+	ld a, [wNumMovesMinusOne]
+	ld [hli], a ; wMaxMenuItem
+	ld a, A_BUTTON | B_BUTTON
+	ld [hli], a ; wMenuWatchedKeys
+	ld [hl], 0 ; wLastMenuItem
+	ld hl, hFlags_0xFFF6
+	set 1, [hl]
+	call HandleMenuInput
+	ld hl, hFlags_0xFFF6
+	res 1, [hl]
+	push af
+	call LoadScreenTilesFromBuffer1
+	pop af
+	pop hl
+	bit 1, a ; pressed b
+	jr nz, .cancel
+	push hl
+	ld a, [wCurrentMenuItem]
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hl]
+	push af
+	push bc
+	call IsMoveHM
+	pop bc
+	pop de
+	ld a, d
+	jr c, .hm
+	pop hl
+	add hl, bc
+	and a
+	ret
+.hm
+	ld hl, HMCantDeleteText
+	call PrintText
+	pop hl
+	jr .loop
+.cancel
+	scf
+	ret
+
+LearnedMove1Text:
+	TX_FAR _LearnedMove1Text
+	TX_SFX_ITEM_1 ; plays SFX_GET_ITEM_1 in the party menu (rare candy) and plays SFX_LEVEL_UP in battle
+	TX_BLINK
+	db "@"
+
+WhichMoveToForgetText:
+	TX_FAR _WhichMoveToForgetText
+	db "@"
+
+AbandonLearningText:
+	TX_FAR _AbandonLearningText
+	db "@"
+
+DidNotLearnText:
+	TX_FAR _DidNotLearnText
+	db "@"
+
+TryingToLearnText:
+	TX_FAR _TryingToLearnText
+	db "@"
+
+OneTwoAndText:
+	TX_FAR _OneTwoAndText
+	TX_DELAY
+	TX_ASM
+	ld a, SFX_SWAP
+	call PlaySoundWaitForCurrent
+	ld hl, PoofText
+	ret
+
+PoofText:
+	TX_FAR _PoofText
+	TX_DELAY
+ForgotAndText:
+	TX_FAR _ForgotAndText
+	db "@"
+
+HMCantDeleteText:
+	TX_FAR _HMCantDeleteText
+	db "@"
--- /dev/null
+++ b/engine/pokemon/load_mon_data.asm
@@ -1,0 +1,49 @@
+LoadMonData_::
+; Load monster [wWhichPokemon] from list [wMonDataLocation]:
+;  0: partymon
+;  1: enemymon
+;  2: boxmon
+;  3: daycaremon
+; Return monster id at wcf91 and its data at wLoadedMon.
+; Also load base stats at wMonHeader for convenience.
+
+	ld a, [wDayCareMonSpecies]
+	ld [wcf91], a
+	ld a, [wMonDataLocation]
+	cp DAYCARE_DATA
+	jr z, .GetMonHeader
+
+	ld a, [wWhichPokemon]
+	ld e, a
+	callab GetMonSpecies
+
+.GetMonHeader
+	ld a, [wcf91]
+	ld [wd0b5], a ; input for GetMonHeader
+	call GetMonHeader
+
+	ld hl, wPartyMons
+	ld bc, wPartyMon2 - wPartyMon1
+	ld a, [wMonDataLocation]
+	cp ENEMY_PARTY_DATA
+	jr c, .getMonEntry
+
+	ld hl, wEnemyMons
+	jr z, .getMonEntry
+
+	cp 2
+	ld hl, wBoxMons
+	ld bc, wBoxMon2 - wBoxMon1
+	jr z, .getMonEntry
+
+	ld hl, wDayCareMon
+	jr .copyMonData
+
+.getMonEntry
+	ld a, [wWhichPokemon]
+	call AddNTimes
+
+.copyMonData
+	ld de, wLoadedMon
+	ld bc, wPartyMon2 - wPartyMon1
+	jp CopyData
--- /dev/null
+++ b/engine/pokemon/remove_mon.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/pokemon/set_types.asm
@@ -1,0 +1,15 @@
+; updates the types of a party mon (pointed to in hl) to the ones of the mon specified in wd11e
+SetPartyMonTypes:
+	call GetPredefRegisters
+	ld bc, wPartyMon1Type - wPartyMon1 ; $5
+	add hl, bc
+	ld a, [wd11e]
+	ld [wd0b5], a
+	push hl
+	call GetMonHeader
+	pop hl
+	ld a, [wMonHType1]
+	ld [hli], a
+	ld a, [wMonHType2]
+	ld [hl], a
+	ret
--- /dev/null
+++ b/engine/pokemon/status_ailments.asm
@@ -1,0 +1,46 @@
+PrintStatusAilment::
+	ld a, [de]
+	bit PSN, a
+	jr nz, .psn
+	bit BRN, a
+	jr nz, .brn
+	bit FRZ, a
+	jr nz, .frz
+	bit PAR, a
+	jr nz, .par
+	and SLP
+	ret z
+	ld a, "S"
+	ld [hli], a
+	ld a, "L"
+	ld [hli], a
+	ld [hl], "P"
+	ret
+.psn
+	ld a, "P"
+	ld [hli], a
+	ld a, "S"
+	ld [hli], a
+	ld [hl], "N"
+	ret
+.brn
+	ld a, "B"
+	ld [hli], a
+	ld a, "R"
+	ld [hli], a
+	ld [hl], "N"
+	ret
+.frz
+	ld a, "F"
+	ld [hli], a
+	ld a, "R"
+	ld [hli], a
+	ld [hl], "Z"
+	ret
+.par
+	ld a, "P"
+	ld [hli], a
+	ld a, "A"
+	ld [hli], a
+	ld [hl], "R"
+	ret
--- /dev/null
+++ b/engine/pokemon/status_screen.asm
@@ -1,0 +1,481 @@
+DrawHP:
+; Draws the HP bar in the stats screen
+	call GetPredefRegisters
+	ld a, $1
+	jr DrawHP_
+
+DrawHP2:
+; Draws the HP bar in the party screen
+	call GetPredefRegisters
+	ld a, $2
+
+DrawHP_:
+	ld [wHPBarType], a
+	push hl
+	ld a, [wLoadedMonHP]
+	ld b, a
+	ld a, [wLoadedMonHP + 1]
+	ld c, a
+	or b
+	jr nz, .nonzeroHP
+	xor a
+	ld c, a
+	ld e, a
+	ld a, $6
+	ld d, a
+	jp .drawHPBarAndPrintFraction
+.nonzeroHP
+	ld a, [wLoadedMonMaxHP]
+	ld d, a
+	ld a, [wLoadedMonMaxHP + 1]
+	ld e, a
+	predef HPBarLength
+	ld a, $6
+	ld d, a
+	ld c, a
+.drawHPBarAndPrintFraction
+	pop hl
+	push de
+	push hl
+	push hl
+	call DrawHPBar
+	pop hl
+	ld a, [hFlags_0xFFF6]
+	bit 0, a
+	jr z, .printFractionBelowBar
+	ld bc, $9 ; right of bar
+	jr .printFraction
+.printFractionBelowBar
+	ld bc, SCREEN_WIDTH + 1 ; below bar
+.printFraction
+	add hl, bc
+	ld de, wLoadedMonHP
+	lb bc, 2, 3
+	call PrintNumber
+	ld a, "/"
+	ld [hli], a
+	ld de, wLoadedMonMaxHP
+	lb bc, 2, 3
+	call PrintNumber
+	pop hl
+	pop de
+	ret
+
+
+; Predef 0x37
+StatusScreen:
+	call LoadMonData
+	ld a, [wMonDataLocation]
+	cp BOX_DATA
+	jr c, .DontRecalculate
+; mon is in a box or daycare
+	ld a, [wLoadedMonBoxLevel]
+	ld [wLoadedMonLevel], a
+	ld [wCurEnemyLVL], a
+	ld hl, wLoadedMonHPExp - 1
+	ld de, wLoadedMonStats
+	ld b, $1
+	call CalcStats ; Recalculate stats
+.DontRecalculate
+	ld hl, wd72c
+	set 1, [hl]
+	ld a, $33
+	ld [rNR50], a ; Reduce the volume
+	call GBPalWhiteOutWithDelay3
+	call ClearScreen
+	call UpdateSprites
+	call LoadHpBarAndStatusTilePatterns
+	ld de, BattleHudTiles1  ; source
+	ld hl, vChars2 + $6d0 ; dest
+	lb bc, BANK(BattleHudTiles1), $03
+	call CopyVideoDataDouble ; ·│ :L and halfarrow line end
+	ld de, BattleHudTiles2
+	ld hl, vChars2 + $780
+	lb bc, BANK(BattleHudTiles2), $01
+	call CopyVideoDataDouble ; │
+	ld de, BattleHudTiles3
+	ld hl, vChars2 + $760
+	lb bc, BANK(BattleHudTiles3), $02
+	call CopyVideoDataDouble ; ─┘
+	ld de, PTile
+	ld hl, vChars2 + $720
+	lb bc, BANK(PTile), (PTileEnd - PTile) / $8
+	call CopyVideoDataDouble ; P (for PP), inline
+	ld a, [hTilesetType]
+	push af
+	xor a
+	ld [hTilesetType], a
+	coord hl, 19, 1
+	lb bc, 6, 10
+	call DrawLineBox ; Draws the box around name, HP and status
+	ld de, -6
+	add hl, de
+	ld [hl], "⠄" ; . after No ("." is a different one)
+	dec hl
+	ld [hl], "№"
+	coord hl, 19, 9
+	lb bc, 8, 6
+	call DrawLineBox ; Draws the box around types, ID No. and OT
+	coord hl, 10, 9
+	ld de, Type1Text
+	call PlaceString ; "TYPE1/"
+	coord hl, 11, 3
+	predef DrawHP
+	ld hl, wStatusScreenHPBarColor
+	call GetHealthBarColor
+	ld b, SET_PAL_STATUS_SCREEN
+	call RunPaletteCommand
+	coord hl, 16, 6
+	ld de, wLoadedMonStatus
+	call PrintStatusCondition
+	jr nz, .StatusWritten
+	coord hl, 16, 6
+	ld de, OKText
+	call PlaceString ; "OK"
+.StatusWritten
+	coord hl, 9, 6
+	ld de, StatusText
+	call PlaceString ; "STATUS/"
+	coord hl, 14, 2
+	call PrintLevel ; Pokémon level
+	ld a, [wMonHIndex]
+	ld [wd11e], a
+	ld [wd0b5], a
+	predef IndexToPokedex
+	coord hl, 3, 7
+	ld de, wd11e
+	lb bc, LEADING_ZEROES | 1, 3
+	call PrintNumber ; Pokémon no.
+	coord hl, 11, 10
+	predef PrintMonType
+	ld hl, NamePointers2
+	call .GetStringPointer
+	ld d, h
+	ld e, l
+	coord hl, 9, 1
+	call PlaceString ; Pokémon name
+	ld hl, OTPointers
+	call .GetStringPointer
+	ld d, h
+	ld e, l
+	coord hl, 12, 16
+	call PlaceString ; OT
+	coord hl, 12, 14
+	ld de, wLoadedMonOTID
+	lb bc, LEADING_ZEROES | 2, 5
+	call PrintNumber ; ID Number
+	ld d, $0
+	call PrintStatsBox
+	call Delay3
+	call GBPalNormal
+	coord hl, 1, 0
+	call LoadFlippedFrontSpriteByMonIndex ; draw Pokémon picture
+	ld a, [wcf91]
+	call PlayCry ; play Pokémon cry
+	call WaitForTextScrollButtonPress ; wait for button
+	pop af
+	ld [hTilesetType], a
+	ret
+
+.GetStringPointer
+	ld a, [wMonDataLocation]
+	add a
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld a, [wMonDataLocation]
+	cp DAYCARE_DATA
+	ret z
+	ld a, [wWhichPokemon]
+	jp SkipFixedLengthTextEntries
+
+OTPointers:
+	dw wPartyMonOT
+	dw wEnemyMonOT
+	dw wBoxMonOT
+	dw wDayCareMonOT
+
+NamePointers2:
+	dw wPartyMonNicks
+	dw wEnemyMonNicks
+	dw wBoxMonNicks
+	dw wDayCareMonName
+
+Type1Text:
+	db "TYPE1/", $4e
+
+Type2Text:
+	db "TYPE2/", $4e
+
+IDNoText:
+	db $73, "№/", $4e
+
+OTText:
+	db   "OT/"
+	next "@"
+
+StatusText:
+	db "STATUS/@"
+
+OKText:
+	db "OK@"
+
+; Draws a line starting from hl high b and wide c
+DrawLineBox:
+	ld de, SCREEN_WIDTH ; New line
+.PrintVerticalLine
+	ld [hl], $78 ; │
+	add hl, de
+	dec b
+	jr nz, .PrintVerticalLine
+	ld [hl], $77 ; ┘
+	dec hl
+.PrintHorizLine
+	ld [hl], $76 ; ─
+	dec hl
+	dec c
+	jr nz, .PrintHorizLine
+	ld [hl], $6f ; ← (halfarrow ending)
+	ret
+
+PTile:
+	INCBIN "gfx/font/P.1bpp"
+PTileEnd:
+
+PrintStatsBox:
+	ld a, d
+	and a ; a is 0 from the status screen
+	jr nz, .DifferentBox
+	coord hl, 0, 8
+	ld b, 8
+	ld c, 8
+	call TextBoxBorder ; Draws the box
+	coord hl, 1, 9 ; Start printing stats from here
+	ld bc, $0019 ; Number offset
+	jr .PrintStats
+.DifferentBox
+	coord hl, 9, 2
+	ld b, 8
+	ld c, 9
+	call TextBoxBorder
+	coord hl, 11, 3
+	ld bc, $0018
+.PrintStats
+	push bc
+	push hl
+	ld de, StatsText
+	call PlaceString
+	pop hl
+	pop bc
+	add hl, bc
+	ld de, wLoadedMonAttack
+	lb bc, 2, 3
+	call PrintStat
+	ld de, wLoadedMonDefense
+	call PrintStat
+	ld de, wLoadedMonSpeed
+	call PrintStat
+	ld de, wLoadedMonSpecial
+	jp PrintNumber
+PrintStat:
+	push hl
+	call PrintNumber
+	pop hl
+	ld de, SCREEN_WIDTH * 2
+	add hl, de
+	ret
+
+StatsText:
+	db   "ATTACK"
+	next "DEFENSE"
+	next "SPEED"
+	next "SPECIAL@"
+
+StatusScreen2:
+	ld a, [hTilesetType]
+	push af
+	xor a
+	ld [hTilesetType], a
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld bc, NUM_MOVES + 1
+	ld hl, wMoves
+	call FillMemory
+	ld hl, wLoadedMonMoves
+	ld de, wMoves
+	ld bc, NUM_MOVES
+	call CopyData
+	callab FormatMovesString
+	coord hl, 9, 2
+	lb bc, 5, 10
+	call ClearScreenArea ; Clear under name
+	coord hl, 19, 3
+	ld [hl], $78
+	coord hl, 0, 8
+	ld b, 8
+	ld c, 18
+	call TextBoxBorder ; Draw move container
+	coord hl, 2, 9
+	ld de, wMovesString
+	call PlaceString ; Print moves
+	ld a, [wNumMovesMinusOne]
+	inc a
+	ld c, a
+	ld a, $4
+	sub c
+	ld b, a ; Number of moves ?
+	coord hl, 11, 10
+	ld de, SCREEN_WIDTH * 2
+	ld a, $72 ; special P tile id
+	call StatusScreen_PrintPP ; Print "PP"
+	ld a, b
+	and a
+	jr z, .InitPP
+	ld c, a
+	ld a, "-"
+	call StatusScreen_PrintPP ; Fill the rest with --
+.InitPP
+	ld hl, wLoadedMonMoves
+	coord de, 14, 10
+	ld b, 0
+.PrintPP
+	ld a, [hli]
+	and a
+	jr z, .PPDone
+	push bc
+	push hl
+	push de
+	ld hl, wCurrentMenuItem
+	ld a, [hl]
+	push af
+	ld a, b
+	ld [hl], a
+	push hl
+	callab GetMaxPP
+	pop hl
+	pop af
+	ld [hl], a
+	pop de
+	pop hl
+	push hl
+	ld bc, wPartyMon1PP - wPartyMon1Moves - 1
+	add hl, bc
+	ld a, [hl]
+	and $3f
+	ld [wStatusScreenCurrentPP], a
+	ld h, d
+	ld l, e
+	push hl
+	ld de, wStatusScreenCurrentPP
+	lb bc, 1, 2
+	call PrintNumber
+	ld a, "/"
+	ld [hli], a
+	ld de, wMaxPP
+	lb bc, 1, 2
+	call PrintNumber
+	pop hl
+	ld de, SCREEN_WIDTH * 2
+	add hl, de
+	ld d, h
+	ld e, l
+	pop hl
+	pop bc
+	inc b
+	ld a, b
+	cp $4
+	jr nz, .PrintPP
+.PPDone
+	coord hl, 9, 3
+	ld de, StatusScreenExpText
+	call PlaceString
+	ld a, [wLoadedMonLevel]
+	push af
+	cp MAX_LEVEL
+	jr z, .Level100
+	inc a
+	ld [wLoadedMonLevel], a ; Increase temporarily if not 100
+.Level100
+	coord hl, 14, 6
+	ld [hl], $70 ; 1-tile "to"
+	inc hl
+	inc hl
+	call PrintLevel
+	pop af
+	ld [wLoadedMonLevel], a
+	ld de, wLoadedMonExp
+	coord hl, 12, 4
+	lb bc, 3, 7
+	call PrintNumber ; exp
+	call CalcExpToLevelUp
+	ld de, wLoadedMonExp
+	coord hl, 7, 6
+	lb bc, 3, 7
+	call PrintNumber ; exp needed to level up
+	coord hl, 9, 0
+	call StatusScreen_ClearName
+	coord hl, 9, 1
+	call StatusScreen_ClearName
+	ld a, [wMonHIndex]
+	ld [wd11e], a
+	call GetMonName
+	coord hl, 9, 1
+	call PlaceString
+	ld a, $1
+	ld [H_AUTOBGTRANSFERENABLED], a
+	call Delay3
+	call WaitForTextScrollButtonPress ; wait for button
+	pop af
+	ld [hTilesetType], a
+	ld hl, wd72c
+	res 1, [hl]
+	ld a, $77
+	ld [rNR50], a
+	call GBPalWhiteOut
+	jp ClearScreen
+
+CalcExpToLevelUp:
+	ld a, [wLoadedMonLevel]
+	cp MAX_LEVEL
+	jr z, .atMaxLevel
+	inc a
+	ld d, a
+	callab CalcExperience
+	ld hl, wLoadedMonExp + 2
+	ld a, [hExperience + 2]
+	sub [hl]
+	ld [hld], a
+	ld a, [hExperience + 1]
+	sbc [hl]
+	ld [hld], a
+	ld a, [hExperience]
+	sbc [hl]
+	ld [hld], a
+	ret
+.atMaxLevel
+	ld hl, wLoadedMonExp
+	xor a
+	ld [hli], a
+	ld [hli], a
+	ld [hl], a
+	ret
+
+StatusScreenExpText:
+	db   "EXP POINTS"
+	next "LEVEL UP@"
+
+StatusScreen_ClearName:
+	ld bc, 10
+	ld a, " "
+	jp FillMemory
+
+StatusScreen_PrintPP:
+; print PP or -- c times, going down two rows each time
+	ld [hli], a
+	ld [hld], a
+	add hl, de
+	dec c
+	jr nz, StatusScreen_PrintPP
+	ret
--- a/engine/predefs12.asm
+++ /dev/null
@@ -1,71 +1,0 @@
-; b = new colour for BG colour 0 (usually white) for 4 frames
-ChangeBGPalColor0_4Frames:
-	call GetPredefRegisters
-	ld a, [rBGP]
-	or b
-	ld [rBGP], a
-	ld c, 4
-	call DelayFrames
-	ld a, [rBGP]
-	and %11111100
-	ld [rBGP], a
-	ret
-
-PredefShakeScreenVertically:
-; Moves the window down and then back in a sequence of progressively smaller
-; numbers of pixels, starting at b.
-	call GetPredefRegisters
-	ld a, 1
-	ld [wDisableVBlankWYUpdate], a
-	xor a
-.loop
-	ld [$ff96], a
-	call .MutateWY
-	call .MutateWY
-	dec b
-	ld a, b
-	jr nz, .loop
-	xor a
-	ld [wDisableVBlankWYUpdate], a
-	ret
-
-.MutateWY
-	ld a, [$ff96]
-	xor b
-	ld [$ff96], a
-	ld [rWY], a
-	ld c, 3
-	jp DelayFrames
-
-PredefShakeScreenHorizontally:
-; Moves the window right and then back in a sequence of progressively smaller
-; numbers of pixels, starting at b.
-	call GetPredefRegisters
-	xor a
-.loop
-	ld [$ff97], a
-	call .MutateWX
-	ld c, 1
-	call DelayFrames
-	call .MutateWX
-	dec b
-	ld a, b
-	jr nz, .loop
-
-; restore normal WX
-	ld a, 7
-	ld [rWX], a
-	ret
-
-.MutateWX
-	ld a, [$ff97]
-	xor b
-	ld [$ff97], a
-	bit 7, a
-	jr z, .skipZeroing
-	xor a ; zero a if it's negative
-.skipZeroing
-	add 7
-	ld [rWX], a
-	ld c, 4
-	jp DelayFrames
--- a/engine/predefs17.asm
+++ /dev/null
@@ -1,9 +1,0 @@
-; this function temporarily makes the starters (and Ivysaur) seen
-; so that the full Pokedex information gets displayed in Oak's lab
-StarterDex:
-	ld a, %01001011 ; set starter flags
-	ld [wPokedexOwned], a
-	predef ShowPokedexData
-	xor a ; unset starter flags
-	ld [wPokedexOwned], a
-	ret
--- a/engine/predefs17_2.asm
+++ /dev/null
@@ -1,15 +1,0 @@
-; updates the types of a party mon (pointed to in hl) to the ones of the mon specified in wd11e
-SetPartyMonTypes:
-	call GetPredefRegisters
-	ld bc, wPartyMon1Type - wPartyMon1 ; $5
-	add hl, bc
-	ld a, [wd11e]
-	ld [wd0b5], a
-	push hl
-	call GetMonHeader
-	pop hl
-	ld a, [wMonHType1]
-	ld [hli], a
-	ld a, [wMonHType2]
-	ld [hl], a
-	ret
--- a/engine/predefs7.asm
+++ /dev/null
@@ -1,48 +1,0 @@
-DisplayElevatorFloorMenu:
-	ld hl, WhichFloorText
-	call PrintText
-	ld hl, wItemList
-	ld a, l
-	ld [wListPointer], a
-	ld a, h
-	ld [wListPointer + 1], a
-	ld a, [wListScrollOffset]
-	push af
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wListScrollOffset], a
-	ld [wPrintItemPrices], a
-	ld a, SPECIALLISTMENU
-	ld [wListMenuID], a
-	call DisplayListMenuID
-	pop bc
-	ld a, b
-	ld [wListScrollOffset], a
-	ret c
-	ld hl, wCurrentMapScriptFlags
-	set 7, [hl]
-	ld hl, wElevatorWarpMaps
-	ld a, [wWhichPokemon]
-	add a
-	ld d, 0
-	ld e, a
-	add hl, de
-	ld a, [hli]
-	ld b, a
-	ld a, [hl]
-	ld c, a
-	ld hl, wWarpEntries
-	call .UpdateWarp
-
-.UpdateWarp
-	inc hl
-	inc hl
-	ld a, b
-	ld [hli], a ; destination warp ID
-	ld a, c
-	ld [hli], a ; destination map ID
-	ret
-
-WhichFloorText:
-	TX_FAR _WhichFloorText
-	db "@"
--- a/engine/print_waiting_text.asm
+++ /dev/null
@@ -1,20 +1,0 @@
-PrintWaitingText::
-	coord hl, 3, 10
-	ld b, $1
-	ld c, $b
-	ld a, [wIsInBattle]
-	and a
-	jr z, .asm_4c17
-	call TextBoxBorder
-	jr .asm_4c1a
-.asm_4c17
-	call CableClub_TextBoxBorder
-.asm_4c1a
-	coord hl, 4, 11
-	ld de, WaitingText
-	call PlaceString
-	ld c, 50
-	jp DelayFrames
-
-WaitingText:
-	db "Waiting...!@"
--- a/engine/random.asm
+++ /dev/null
@@ -1,13 +1,0 @@
-Random_::
-; Generate a random 16-bit value.
-	ld a, [rDIV]
-	ld b, a
-	ld a, [hRandomAdd]
-	adc b
-	ld [hRandomAdd], a
-	ld a, [rDIV]
-	ld b, a
-	ld a, [hRandomSub]
-	sbc b
-	ld [hRandomSub], a
-	ret
--- a/engine/remove_pokemon.asm
+++ /dev/null
@@ -1,95 +1,0 @@
-_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
--- a/engine/save.asm
+++ /dev/null
@@ -1,708 +1,0 @@
-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
--- a/engine/slot_machine.asm
+++ /dev/null
@@ -1,892 +1,0 @@
-PromptUserToPlaySlots:
-	call SaveScreenTilesToBuffer2
-	ld a, BANK(DisplayTextIDInit)
-	ld [wAutoTextBoxDrawingControl], a
-	ld b, a
-	ld hl, DisplayTextIDInit
-	call Bankswitch
-	ld hl, PlaySlotMachineText
-	call PrintText
-	call YesNoChoice
-	ld a, [wCurrentMenuItem]
-	and a
-	jr nz, .done ; if player chose No
-	dec a
-	ld [wUpdateSpritesEnabled], a
-	ld hl, wSlotMachineRerollCounter
-	xor a
-	ld [hli], a
-	ld [hl], SMILE_BUBBLE
-	predef EmotionBubble
-	call GBPalWhiteOutWithDelay3
-	call LoadSlotMachineTiles
-	call LoadFontTilePatterns
-	ld b, SET_PAL_SLOTS
-	call RunPaletteCommand
-	call GBPalNormal
-	ld a, $e4
-	ld [rOBP0], a
-	ld hl, wd730
-	set 6, [hl]
-	xor a
-	ld [wSlotMachineAllowMatchesCounter], a
-	ld hl, wStoppingWhichSlotMachineWheel
-	ld bc, $0014
-	call FillMemory
-	call MainSlotMachineLoop
-	ld hl, wd730
-	res 6, [hl]
-	xor a
-	ld [wSlotMachineAllowMatchesCounter], a
-	call GBPalWhiteOutWithDelay3
-	ld a, $1
-	ld [wUpdateSpritesEnabled], a
-	call RunDefaultPaletteCommand
-	call ReloadMapSpriteTilePatterns
-	call ReloadTilesetTilePatterns
-.done
-	call LoadScreenTilesFromBuffer2
-	call Delay3
-	call GBPalNormal
-	ld a, [wSlotMachineSavedROMBank]
-	push af
-	jp CloseTextDisplay
-
-PlaySlotMachineText:
-	TX_FAR _PlaySlotMachineText
-	db "@"
-
-MainSlotMachineLoop:
-	call SlotMachine_PrintCreditCoins
-	xor a
-	ld hl, wPayoutCoins
-	ld [hli], a
-	ld [hl], a
-	call SlotMachine_PrintPayoutCoins
-	ld hl, BetHowManySlotMachineText
-	call PrintText
-	call SaveScreenTilesToBuffer1
-.loop
-	ld a, A_BUTTON | B_BUTTON
-	ld [wMenuWatchedKeys], a
-	ld a, 2
-	ld [wMaxMenuItem], a
-	ld a, 12
-	ld [wTopMenuItemY], a
-	ld a, 15
-	ld [wTopMenuItemX], a
-	xor a
-	ld [wCurrentMenuItem], a
-	ld [wLastMenuItem], a
-	ld [wMenuWatchMovingOutOfBounds], a
-	coord hl, 14, 11
-	ld b, 5
-	ld c, 4
-	call TextBoxBorder
-	coord hl, 16, 12
-	ld de, CoinMultiplierSlotMachineText
-	call PlaceString
-	call HandleMenuInput
-	and B_BUTTON
-	jp nz, LoadScreenTilesFromBuffer1
-	ld a, [wCurrentMenuItem]
-	ld b, a
-	ld a, 3
-	sub b
-	ld [wSlotMachineBet], a
-	ld hl, wPlayerCoins
-	ld c, a
-	ld a, [hli]
-	and a
-	jr nz, .skip1
-	ld a, [hl]
-	cp c
-	jr nc, .skip1
-	ld hl, NotEnoughCoinsSlotMachineText
-	call PrintText
-	jr .loop
-.skip1
-	call LoadScreenTilesFromBuffer1
-	call SlotMachine_SubtractBetFromPlayerCoins
-	call SlotMachine_LightBalls
-	call SlotMachine_SetFlags
-	ld a, 4
-	ld hl, wSlotMachineWheel1SlipCounter
-	ld [hli], a
-	ld [hli], a
-	ld [hl], a
-	call WaitForSoundToFinish
-	ld a, SFX_SLOTS_NEW_SPIN
-	call PlaySound
-	ld hl, StartSlotMachineText
-	call PrintText
-	call SlotMachine_SpinWheels
-	call SlotMachine_CheckForMatches
-	ld hl, wPlayerCoins
-	ld a, [hli]
-	or [hl]
-	jr nz, .skip2
-	ld hl, OutOfCoinsSlotMachineText
-	call PrintText
-	ld c, 60
-	jp DelayFrames
-.skip2
-	ld hl, OneMoreGoSlotMachineText
-	call PrintText
-	coord hl, 14, 12
-	lb bc, 13, 15
-	xor a ; YES_NO_MENU
-	ld [wTwoOptionMenuID], a
-	ld a, TWO_OPTION_MENU
-	ld [wTextBoxID], a
-	call DisplayTextBoxID
-	ld a, [wCurrentMenuItem]
-	and a
-	ret nz
-	call SlotMachine_PutOutLitBalls
-	jp MainSlotMachineLoop
-
-CoinMultiplierSlotMachineText:
-	db   "×3"
-	next "×2"
-	next "×1@"
-
-OutOfCoinsSlotMachineText:
-	TX_FAR _OutOfCoinsSlotMachineText
-	db "@"
-
-BetHowManySlotMachineText:
-	TX_FAR _BetHowManySlotMachineText
-	db "@"
-
-StartSlotMachineText:
-	TX_FAR _StartSlotMachineText
-	db "@"
-
-NotEnoughCoinsSlotMachineText:
-	TX_FAR _NotEnoughCoinsSlotMachineText
-	db "@"
-
-OneMoreGoSlotMachineText:
-	TX_FAR _OneMoreGoSlotMachineText
-	db "@"
-
-SlotMachine_SetFlags:
-	ld hl, wSlotMachineFlags
-	bit 7, [hl]
-	ret nz
-	ld a, [wSlotMachineAllowMatchesCounter]
-	and a
-	jr nz, .allowMatches
-	call Random
-	and a
-	jr z, .setAllowMatchesCounter ; 1/256 (~0.4%) chance
-	ld b, a
-	ld a, [wSlotMachineSevenAndBarModeChance]
-	cp b
-	jr c, .allowSevenAndBarMatches
-	ld a, 210
-	cp b
-	jr c, .allowMatches ; 55/256 (~21.5%) chance
-	ld [hl], 0
-	ret
-.allowMatches
-	set 6, [hl]
-	ret
-.setAllowMatchesCounter
-	ld a, 60
-	ld [wSlotMachineAllowMatchesCounter], a
-	ret
-.allowSevenAndBarMatches
-	set 7, [hl]
-	ret
-
-SlotMachine_SpinWheels:
-	ld c, 20
-.loop1
-	push bc
-	call SlotMachine_AnimWheel1
-	call SlotMachine_AnimWheel2
-	call SlotMachine_AnimWheel3
-	ld c, 2
-	call DelayFrames
-	pop bc
-	dec c
-	jr nz, .loop1
-	xor a
-	ld [wStoppingWhichSlotMachineWheel], a
-.loop2
-	call SlotMachine_HandleInputWhileWheelsSpin
-	call SlotMachine_StopOrAnimWheel1
-	call SlotMachine_StopOrAnimWheel2
-	call SlotMachine_StopOrAnimWheel3
-	ret c
-	ld a, [wOnSGB]
-	xor $1
-	inc a
-	ld c, a
-	call DelayFrames
-	jr .loop2
-
-; Note that the wheels can only stop when a symbol is centred in the wheel
-; and thus 3 full symbols rather than 2 full symbols and 2 half symbols are
-; visible. The 3 functions below ensure this by checking if the wheel offset
-; is even before stopping the wheel.
-
-SlotMachine_StopOrAnimWheel1:
-	ld a, [wStoppingWhichSlotMachineWheel]
-	cp 1
-	jr c, .animWheel
-	ld de, wSlotMachineWheel1Offset
-	ld a, [de]
-	rra
-	jr nc, .animWheel ; check that a symbol is centred in the wheel
-	ld hl, wSlotMachineWheel1SlipCounter
-	ld a, [hl]
-	and a
-	ret z
-	dec [hl]
-	call SlotMachine_StopWheel1Early
-	ret nz
-.animWheel
-	jp SlotMachine_AnimWheel1
-
-SlotMachine_StopOrAnimWheel2:
-	ld a, [wStoppingWhichSlotMachineWheel]
-	cp 2
-	jr c, .animWheel
-	ld de, wSlotMachineWheel2Offset
-	ld a, [de]
-	rra
-	jr nc, .animWheel ; check that a symbol is centred in the wheel
-	ld hl, wSlotMachineWheel2SlipCounter
-	ld a, [hl]
-	and a
-	ret z
-	dec [hl]
-	call SlotMachine_StopWheel2Early
-	ret z
-.animWheel
-	jp SlotMachine_AnimWheel2
-
-SlotMachine_StopOrAnimWheel3:
-	ld a, [wStoppingWhichSlotMachineWheel]
-	cp 3
-	jr c, .animWheel
-	ld de, wSlotMachineWheel3Offset
-	ld a, [de]
-	rra
-	jr nc, .animWheel ; check that a symbol is centred in the wheel
-; wheel 3 stops as soon as possible
-	scf
-	ret
-.animWheel
-	call SlotMachine_AnimWheel3
-	and a
-	ret
-
-SlotMachine_StopWheel1Early:
-	call SlotMachine_GetWheel1Tiles
-	ld hl, wSlotMachineWheel1BottomTile
-	ld a, [wSlotMachineFlags]
-	and $80
-	jr nz, .sevenAndBarMode
-; Stop early if the middle symbol is not a cherry.
-	inc hl
-	ld a, [hl]
-	cp SLOTSCHERRY >> 8
-	jr nz, .stopWheel
-	ret
-; It looks like this was intended to make the wheel stop when a 7 symbol was
-; visible, but it has a bug and so the wheel stops randomly.
-.sevenAndBarMode
-	ld c, $3
-.loop
-	ld a, [hli]
-	cp SLOTS7 >> 8
-	jr c, .stopWheel ; condition never true
-	dec c
-	jr nz, .loop
-	ret
-.stopWheel
-	inc a
-	ld hl, wSlotMachineWheel1SlipCounter
-	ld [hl], 0
-	ret
-
-SlotMachine_StopWheel2Early:
-	call SlotMachine_GetWheel2Tiles
-	ld a, [wSlotMachineFlags]
-	and $80
-	jr nz, .sevenAndBarMode
-; Stop early if any symbols are lined up in the first two wheels.
-	call SlotMachine_FindWheel1Wheel2Matches
-	ret nz
-	jr .stopWheel
-; Stop early if two 7 symbols or two bar symbols are lined up in the first two
-; wheels OR if no symbols are lined up and the bottom symbol in wheel 2 is a
-; 7 symbol or bar symbol. The second part could be a bug or a way to reduce the
-; player's odds.
-.sevenAndBarMode
-	call SlotMachine_FindWheel1Wheel2Matches
-	ld a, [de]
-	cp (SLOTSBAR >> 8) + 1
-	ret nc
-.stopWheel
-	xor a
-	ld [wSlotMachineWheel2SlipCounter], a
-	ret
-
-SlotMachine_FindWheel1Wheel2Matches:
-; return whether wheel 1 and wheel 2's current positions allow a match (given
-; that wheel 3 stops in a good position) in Z
-	ld hl, wSlotMachineWheel1BottomTile
-	ld de, wSlotMachineWheel2BottomTile
-	ld a, [de]
-	cp [hl] ; wheel 1 bottom, wheel 2 bottom
-	ret z
-	inc de
-	ld a, [de]
-	cp [hl] ; wheel 1 bottom, wheel 2 middle
-	ret z
-	inc hl
-	cp [hl] ; wheel 1 middle, wheel 2 middle
-	ret z
-	inc hl
-	cp [hl] ; wheel 1 top, wheel 2 middle
-	ret z
-	inc de
-	ld a, [de]
-	cp [hl] ; wheel 1 top, wheel 2 top
-	ret z
-	dec de
-	dec de
-	ret
-
-SlotMachine_CheckForMatches:
-	call SlotMachine_GetWheel3Tiles
-	ld a, [wSlotMachineBet]
-	cp 2
-	jr z, .checkMatchesFor2CoinBet
-	cp 1
-	jr z, .checkMatchFor1CoinBet
-; 3 coin bet allows diagonal matches (plus the matches for 1/2 coin bets)
-	ld hl, wSlotMachineWheel1BottomTile
-	ld de, wSlotMachineWheel2MiddleTile
-	ld bc, wSlotMachineWheel3TopTile
-	call SlotMachine_CheckForMatch
-	jp z, .foundMatch
-	ld hl, wSlotMachineWheel1TopTile
-	ld de, wSlotMachineWheel2MiddleTile
-	ld bc, wSlotMachineWheel3BottomTile
-	call SlotMachine_CheckForMatch
-	jr z, .foundMatch
-; 2 coin bet allows top/bottom horizontal matches (plus the match for a 1 coin bet)
-.checkMatchesFor2CoinBet
-	ld hl, wSlotMachineWheel1TopTile
-	ld de, wSlotMachineWheel2TopTile
-	ld bc, wSlotMachineWheel3TopTile
-	call SlotMachine_CheckForMatch
-	jr z, .foundMatch
-	ld hl, wSlotMachineWheel1BottomTile
-	ld de, wSlotMachineWheel2BottomTile
-	ld bc, wSlotMachineWheel3BottomTile
-	call SlotMachine_CheckForMatch
-	jr z, .foundMatch
-; 1 coin bet only allows a middle horizontal match
-.checkMatchFor1CoinBet
-	ld hl, wSlotMachineWheel1MiddleTile
-	ld de, wSlotMachineWheel2MiddleTile
-	ld bc, wSlotMachineWheel3MiddleTile
-	call SlotMachine_CheckForMatch
-	jr z, .foundMatch
-	ld a, [wSlotMachineFlags]
-	and $c0
-	jr z, .noMatch
-	ld hl, wSlotMachineRerollCounter
-	dec [hl]
-	jr nz, .rollWheel3DownByOneSymbol
-.noMatch
-	ld hl, NotThisTimeText
-	call PrintText
-.done
-	xor a
-	ld [wMuteAudioAndPauseMusic], a
-	ret
-.rollWheel3DownByOneSymbol
-	call SlotMachine_AnimWheel3
-	call DelayFrame
-	call SlotMachine_AnimWheel3
-	call DelayFrame
-	jp SlotMachine_CheckForMatches
-.foundMatch
-	ld a, [wSlotMachineFlags]
-	and $c0
-	jr z, .rollWheel3DownByOneSymbol ; roll wheel if player isn't allowed to win
-	and $80
-	jr nz, .acceptMatch
-; if 7/bar matches aren't enabled and the match was a 7/bar symbol, roll wheel
-	ld a, [hl]
-	cp (SLOTSBAR >> 8) + 1
-	jr c, .rollWheel3DownByOneSymbol
-.acceptMatch
-	ld a, [hl]
-	sub $2
-	ld [wSlotMachineWinningSymbol], a
-	ld hl, SlotRewardPointers
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hli]
-	ld e, a
-	ld a, [hli]
-	ld d, a
-	push de
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld de, wcf4b
-	ld bc, 4
-	call CopyData
-	pop hl
-	ld de, .flashScreenLoop
-	push de
-	jp hl
-
-.flashScreenLoop
-	ld a, [rBGP]
-	xor $40
-	ld [rBGP], a
-	ld c, 5
-	call DelayFrames
-	dec b
-	jr nz, .flashScreenLoop
-	ld hl, wPayoutCoins
-	ld [hl], d
-	inc hl
-	ld [hl], e
-	call SlotMachine_PrintPayoutCoins
-	ld hl, SymbolLinedUpSlotMachineText
-	call PrintText
-	call WaitForTextScrollButtonPress
-	call SlotMachine_PayCoinsToPlayer
-	call SlotMachine_PrintPayoutCoins
-	ld a, $e4
-	ld [rOBP0], a
-	jp .done
-
-SymbolLinedUpSlotMachineText:
-	TX_ASM
-	push bc
-	call SlotMachine_PrintWinningSymbol
-	ld hl, LinedUpText
-	pop bc
-	inc bc
-	inc bc
-	inc bc
-	inc bc
-	ret
-
-LinedUpText:
-	TX_FAR _LinedUpText
-	db "@"
-
-SlotRewardPointers:
-	dw SlotReward300Func
-	dw SlotReward300Text
-	dw SlotReward100Func
-	dw SlotReward100Text
-	dw SlotReward8Func
-	dw SlotReward8Text
-	dw SlotReward15Func
-	dw SlotReward15Text
-	dw SlotReward15Func
-	dw SlotReward15Text
-	dw SlotReward15Func
-	dw SlotReward15Text
-
-SlotReward300Text:
-	db "300@"
-
-SlotReward100Text:
-	db "100@"
-
-SlotReward8Text:
-	db "8@"
-
-SlotReward15Text:
-	db "15@"
-
-NotThisTimeText:
-	TX_FAR _NotThisTimeText
-	db "@"
-
-; compares the slot machine tiles at bc, de, and hl
-SlotMachine_CheckForMatch:
-	ld a, [de]
-	cp [hl]
-	ret nz
-	ld a, [bc]
-	cp [hl]
-	ret
-
-SlotMachine_GetWheel3Tiles:
-	ld de, wSlotMachineWheel3BottomTile
-	ld hl, SlotMachineWheel3
-	ld a, [wSlotMachineWheel3Offset]
-	call SlotMachine_GetWheelTiles
-
-SlotMachine_GetWheel2Tiles:
-	ld de, wSlotMachineWheel2BottomTile
-	ld hl, SlotMachineWheel2
-	ld a, [wSlotMachineWheel2Offset]
-	call SlotMachine_GetWheelTiles
-
-SlotMachine_GetWheel1Tiles:
-	ld de, wSlotMachineWheel1BottomTile
-	ld hl, SlotMachineWheel1
-	ld a, [wSlotMachineWheel1Offset]
-
-SlotMachine_GetWheelTiles:
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld c, 3
-.loop
-	ld a, [hli]
-	ld [de], a
-	inc de
-	inc hl
-	dec c
-	jr nz, .loop
-	ret
-
-SlotReward8Func:
-	ld hl, wSlotMachineAllowMatchesCounter
-	ld a, [hl]
-	and a
-	jr z, .skip
-	dec [hl]
-.skip
-	ld b, $2
-	ld de, 8
-	ret
-
-SlotReward15Func:
-	ld hl, wSlotMachineAllowMatchesCounter
-	ld a, [hl]
-	and a
-	jr z, .skip
-	dec [hl]
-.skip
-	ld b, $4
-	ld de, 15
-	ret
-
-SlotReward100Func:
-	ld a, SFX_GET_KEY_ITEM
-	call PlaySound
-	xor a
-	ld [wSlotMachineFlags], a
-	ld b, $8
-	ld de, 100
-	ret
-
-SlotReward300Func:
-	ld hl, YeahText
-	call PrintText
-	ld a, SFX_GET_ITEM_2
-	call PlaySound
-	call Random
-	cp $80
-	ld a, $0
-	jr c, .skip
-	ld [wSlotMachineFlags], a
-.skip
-	ld [wSlotMachineAllowMatchesCounter], a
-	ld b, $14
-	ld de, 300
-	ret
-
-YeahText:
-	TX_FAR _YeahText
-	TX_DELAY
-	db "@"
-
-SlotMachine_PrintWinningSymbol:
-; prints winning symbol and down arrow in text box
-	coord hl, 2, 14
-	ld a, [wSlotMachineWinningSymbol]
-	add $25
-	ld [hli], a
-	inc a
-	ld [hld], a
-	inc a
-	ld de, -SCREEN_WIDTH
-	add hl, de
-	ld [hli], a
-	inc a
-	ld [hl], a
-	coord hl, 18, 16
-	ld [hl], "▼"
-	ret
-
-SlotMachine_SubtractBetFromPlayerCoins:
-	ld hl, wTempCoins2 + 1
-	ld a, [wSlotMachineBet]
-	ld [hld], a
-	xor a
-	ld [hli], a
-	ld de, wPlayerCoins + 1
-	ld c, $2
-	predef SubBCDPredef
-
-SlotMachine_PrintCreditCoins:
-	coord hl, 5, 1
-	ld de, wPlayerCoins
-	ld c, $2
-	jp PrintBCDNumber
-
-SlotMachine_PrintPayoutCoins:
-	coord hl, 11, 1
-	ld de, wPayoutCoins
-	lb bc, LEADING_ZEROES | 2, 4 ; 2 bytes, 4 digits
-	jp PrintNumber
-
-SlotMachine_PayCoinsToPlayer:
-	ld a, $1
-	ld [wMuteAudioAndPauseMusic], a
-	call WaitForSoundToFinish
-
-; Put 1 in the temp coins variable. This value is added to the player's coins
-; repeatedly so the player can watch the value go up 1 coin at a time.
-	ld hl, wTempCoins1
-	xor a
-	ld [hli], a
-	inc a
-	ld [hl], a
-
-	ld a, 5
-	ld [wAnimCounter], a
-
-; Subtract 1 from the payout amount and add 1 to the player's coins each
-; iteration until the payout amount reaches 0.
-.loop
-	ld a, [wPayoutCoins + 1]
-	ld l, a
-	ld a, [wPayoutCoins]
-	ld h, a
-	or l
-	ret z
-	ld de, -1
-	add hl, de
-	ld a, l
-	ld [wPayoutCoins + 1], a
-	ld a, h
-	ld [wPayoutCoins], a
-	ld hl, wTempCoins1 + 1
-	ld de, wPlayerCoins + 1
-	ld c, $2
-	predef AddBCDPredef
-	call SlotMachine_PrintCreditCoins
-	call SlotMachine_PrintPayoutCoins
-	ld a, SFX_SLOTS_REWARD
-	call PlaySound
-	ld a, [wAnimCounter]
-	dec a
-	jr nz, .skip1
-	ld a, [rOBP0]
-	xor $40 ; make the slot wheel symbols flash
-	ld [rOBP0], a
-	ld a, 5
-.skip1
-	ld [wAnimCounter], a
-	ld a, [wSlotMachineWinningSymbol]
-	cp (SLOTSBAR >> 8) + 1
-	ld c, 8
-	jr nc, .skip2
-	srl c ; c = 4 (make the the coins transfer faster if the symbol was 7 or bar)
-.skip2
-	call DelayFrames
-	jr .loop
-
-SlotMachine_PutOutLitBalls:
-	ld a, $23
-	ld [wNewSlotMachineBallTile], a
-	jr SlotMachine_UpdateThreeCoinBallTiles
-
-SlotMachine_LightBalls:
-	ld a, $14
-	ld [wNewSlotMachineBallTile], a
-	ld a, [wSlotMachineBet]
-	dec a
-	jr z, SlotMachine_UpdateOneCoinBallTiles
-	dec a
-	jr z, SlotMachine_UpdateTwoCoinBallTiles
-
-SlotMachine_UpdateThreeCoinBallTiles:
-	coord hl, 3, 2
-	call SlotMachine_UpdateBallTiles
-	coord hl, 3, 10
-	call SlotMachine_UpdateBallTiles
-
-SlotMachine_UpdateTwoCoinBallTiles:
-	coord hl, 3, 4
-	call SlotMachine_UpdateBallTiles
-	coord hl, 3, 8
-	call SlotMachine_UpdateBallTiles
-
-SlotMachine_UpdateOneCoinBallTiles:
-	coord hl, 3, 6
-
-SlotMachine_UpdateBallTiles:
-	ld a, [wNewSlotMachineBallTile]
-	ld [hl], a
-	ld bc, 13
-	add hl, bc
-	ld [hl], a
-	ld bc, 7
-	add hl, bc
-	inc a
-	ld [hl], a
-	ld bc, 13
-	add hl, bc
-	ld [hl], a
-	ret
-
-SlotMachine_AnimWheel1:
-	ld bc, SlotMachineWheel1
-	ld de, wSlotMachineWheel1Offset
-	ld hl, wOAMBuffer
-	ld a, $30
-	ld [wBaseCoordX], a
-	jr SlotMachine_AnimWheel
-
-SlotMachine_AnimWheel2:
-	ld bc, SlotMachineWheel2
-	ld de, wSlotMachineWheel2Offset
-	ld hl, wOAMBuffer + $30
-	ld a, $50
-	ld [wBaseCoordX], a
-	jr SlotMachine_AnimWheel
-
-SlotMachine_AnimWheel3:
-	ld bc, SlotMachineWheel3
-	ld de, wSlotMachineWheel3Offset
-	ld hl, wOAMBuffer + $60
-	ld a, $70
-	ld [wBaseCoordX], a
-
-SlotMachine_AnimWheel:
-	ld a, $58
-	ld [wBaseCoordY], a
-	push de
-	ld a, [de]
-	ld d, b
-	add c
-	ld e, a
-	jr nc, .loop
-	inc d
-.loop
-	ld a, [wBaseCoordY]
-	ld [hli], a
-	ld a, [wBaseCoordX]
-	ld [hli], a
-	ld a, [de]
-	ld [hli], a
-	ld a, $80
-	ld [hli], a
-	ld a, [wBaseCoordY]
-	ld [hli], a
-	ld a, [wBaseCoordX]
-	add $8
-	ld [hli], a
-	ld a, [de]
-	inc a
-	ld [hli], a
-	ld a, $80
-	ld [hli], a
-	inc de
-	ld a, [wBaseCoordY]
-	sub $8
-	ld [wBaseCoordY], a
-	cp $28
-	jr nz, .loop
-	pop de
-	ld a, [de]
-	inc a ; advance the offset so that the wheel animates
-	cp 30
-	jr nz, .skip
-	xor a ; wrap around to 0 when the offset reaches 30
-.skip
-	ld [de], a
-	ret
-
-SlotMachine_HandleInputWhileWheelsSpin:
-	call DelayFrame
-	call JoypadLowSensitivity
-	ld a, [hJoy5]
-	and A_BUTTON
-	ret z
-	ld hl, wStoppingWhichSlotMachineWheel
-	ld a, [hl]
-	dec a
-	ld de, wSlotMachineWheel1SlipCounter
-	jr z, .skip
-	dec a
-	ld de, wSlotMachineWheel2SlipCounter
-	jr z, .skip
-.loop
-	inc [hl]
-	ld a, SFX_SLOTS_STOP_WHEEL
-	jp PlaySound
-.skip
-	ld a, [de]
-	and a
-	ret nz
-	jr .loop
-
-LoadSlotMachineTiles:
-	call DisableLCD
-	ld hl, SlotMachineTiles2
-	ld de, vChars0
-	ld bc, $1c0
-	ld a, BANK(SlotMachineTiles2)
-	call FarCopyData2
-	ld hl, SlotMachineTiles1
-	ld de, vChars2
-	ld bc, $250
-	ld a, BANK(SlotMachineTiles1)
-	call FarCopyData2
-	ld hl, SlotMachineTiles2
-	ld de, vChars2 + $250
-	ld bc, $1c0
-	ld a, BANK(SlotMachineTiles2)
-	call FarCopyData2
-	ld hl, SlotMachineMap
-	coord de, 0, 0
-	ld bc, SlotMachineMapEnd - SlotMachineMap
-	call CopyData
-	call EnableLCD
-	ld hl, wSlotMachineWheel1Offset
-	ld a, $1c
-	ld [hli], a
-	ld [hli], a
-	ld [hl], a
-	call SlotMachine_AnimWheel1
-	call SlotMachine_AnimWheel2
-	jp SlotMachine_AnimWheel3
-
-SlotMachineMap:
-	INCBIN "gfx/slots/slots.tilemap"
-SlotMachineMapEnd:
-
-INCLUDE "data/slot_machine_wheels.asm"
-
-SlotMachineTiles1:
-IF DEF(_RED)
-	INCBIN "gfx/slots/red_slots_1.2bpp"
-ENDC
-IF DEF(_BLUE)
-	INCBIN "gfx/slots/blue_slots_1.2bpp"
-ENDC
--- /dev/null
+++ b/engine/slots/game_corner_slots.asm
@@ -1,0 +1,54 @@
+StartSlotMachine:
+	ld a, [wHiddenObjectFunctionArgument]
+	cp $fd
+	jr z, .printOutOfOrder
+	cp $fe
+	jr z, .printOutToLunch
+	cp $ff
+	jr z, .printSomeonesKeys
+	callba AbleToPlaySlotsCheck
+	ld a, [wCanPlaySlots]
+	and a
+	ret z
+	ld a, [wLuckySlotHiddenObjectIndex]
+	ld b, a
+	ld a, [wHiddenObjectIndex]
+	inc a
+	cp b
+	jr z, .match
+	ld a, 253
+	jr .next
+.match
+	ld a, 250
+.next
+	ld [wSlotMachineSevenAndBarModeChance], a
+	ld a, [H_LOADEDROMBANK]
+	ld [wSlotMachineSavedROMBank], a
+	call PromptUserToPlaySlots
+	ret
+.printOutOfOrder
+	tx_pre_id GameCornerOutOfOrderText
+	jr .printText
+.printOutToLunch
+	tx_pre_id GameCornerOutToLunchText
+	jr .printText
+.printSomeonesKeys
+	tx_pre_id GameCornerSomeonesKeysText
+.printText
+	push af
+	call EnableAutoTextBoxDrawing
+	pop af
+	call PrintPredefTextID
+	ret
+
+GameCornerOutOfOrderText::
+	TX_FAR _GameCornerOutOfOrderText
+	db "@"
+
+GameCornerOutToLunchText::
+	TX_FAR _GameCornerOutToLunchText
+	db "@"
+
+GameCornerSomeonesKeysText::
+	TX_FAR _GameCornerSomeonesKeysText
+	db "@"
--- /dev/null
+++ b/engine/slots/game_corner_slots2.asm
@@ -1,0 +1,31 @@
+AbleToPlaySlotsCheck:
+	ld a, [wSpriteStateData1 + 2]
+	and $8
+	jr z, .done ; not able
+	ld b, COIN_CASE
+	predef GetQuantityOfItemInBag
+	ld a, b
+	and a
+	ld b, (GameCornerCoinCaseText_id - TextPredefs) / 2 + 1
+	jr z, .printCoinCaseRequired
+	ld hl, wPlayerCoins
+	ld a, [hli]
+	or [hl]
+	jr nz, .done ; able to play
+	ld b, (GameCornerNoCoinsText_id - TextPredefs) / 2 + 1
+.printCoinCaseRequired
+	call EnableAutoTextBoxDrawing
+	ld a, b
+	call PrintPredefTextID
+	xor a
+.done
+	ld [wCanPlaySlots], a
+	ret
+
+GameCornerCoinCaseText::
+	TX_FAR _GameCornerCoinCaseText
+	db "@"
+
+GameCornerNoCoinsText::
+	TX_FAR _GameCornerNoCoinsText
+	db "@"
--- /dev/null
+++ b/engine/slots/slot_machine.asm
@@ -1,0 +1,892 @@
+PromptUserToPlaySlots:
+	call SaveScreenTilesToBuffer2
+	ld a, BANK(DisplayTextIDInit)
+	ld [wAutoTextBoxDrawingControl], a
+	ld b, a
+	ld hl, DisplayTextIDInit
+	call Bankswitch
+	ld hl, PlaySlotMachineText
+	call PrintText
+	call YesNoChoice
+	ld a, [wCurrentMenuItem]
+	and a
+	jr nz, .done ; if player chose No
+	dec a
+	ld [wUpdateSpritesEnabled], a
+	ld hl, wSlotMachineRerollCounter
+	xor a
+	ld [hli], a
+	ld [hl], SMILE_BUBBLE
+	predef EmotionBubble
+	call GBPalWhiteOutWithDelay3
+	call LoadSlotMachineTiles
+	call LoadFontTilePatterns
+	ld b, SET_PAL_SLOTS
+	call RunPaletteCommand
+	call GBPalNormal
+	ld a, $e4
+	ld [rOBP0], a
+	ld hl, wd730
+	set 6, [hl]
+	xor a
+	ld [wSlotMachineAllowMatchesCounter], a
+	ld hl, wStoppingWhichSlotMachineWheel
+	ld bc, $0014
+	call FillMemory
+	call MainSlotMachineLoop
+	ld hl, wd730
+	res 6, [hl]
+	xor a
+	ld [wSlotMachineAllowMatchesCounter], a
+	call GBPalWhiteOutWithDelay3
+	ld a, $1
+	ld [wUpdateSpritesEnabled], a
+	call RunDefaultPaletteCommand
+	call ReloadMapSpriteTilePatterns
+	call ReloadTilesetTilePatterns
+.done
+	call LoadScreenTilesFromBuffer2
+	call Delay3
+	call GBPalNormal
+	ld a, [wSlotMachineSavedROMBank]
+	push af
+	jp CloseTextDisplay
+
+PlaySlotMachineText:
+	TX_FAR _PlaySlotMachineText
+	db "@"
+
+MainSlotMachineLoop:
+	call SlotMachine_PrintCreditCoins
+	xor a
+	ld hl, wPayoutCoins
+	ld [hli], a
+	ld [hl], a
+	call SlotMachine_PrintPayoutCoins
+	ld hl, BetHowManySlotMachineText
+	call PrintText
+	call SaveScreenTilesToBuffer1
+.loop
+	ld a, A_BUTTON | B_BUTTON
+	ld [wMenuWatchedKeys], a
+	ld a, 2
+	ld [wMaxMenuItem], a
+	ld a, 12
+	ld [wTopMenuItemY], a
+	ld a, 15
+	ld [wTopMenuItemX], a
+	xor a
+	ld [wCurrentMenuItem], a
+	ld [wLastMenuItem], a
+	ld [wMenuWatchMovingOutOfBounds], a
+	coord hl, 14, 11
+	ld b, 5
+	ld c, 4
+	call TextBoxBorder
+	coord hl, 16, 12
+	ld de, CoinMultiplierSlotMachineText
+	call PlaceString
+	call HandleMenuInput
+	and B_BUTTON
+	jp nz, LoadScreenTilesFromBuffer1
+	ld a, [wCurrentMenuItem]
+	ld b, a
+	ld a, 3
+	sub b
+	ld [wSlotMachineBet], a
+	ld hl, wPlayerCoins
+	ld c, a
+	ld a, [hli]
+	and a
+	jr nz, .skip1
+	ld a, [hl]
+	cp c
+	jr nc, .skip1
+	ld hl, NotEnoughCoinsSlotMachineText
+	call PrintText
+	jr .loop
+.skip1
+	call LoadScreenTilesFromBuffer1
+	call SlotMachine_SubtractBetFromPlayerCoins
+	call SlotMachine_LightBalls
+	call SlotMachine_SetFlags
+	ld a, 4
+	ld hl, wSlotMachineWheel1SlipCounter
+	ld [hli], a
+	ld [hli], a
+	ld [hl], a
+	call WaitForSoundToFinish
+	ld a, SFX_SLOTS_NEW_SPIN
+	call PlaySound
+	ld hl, StartSlotMachineText
+	call PrintText
+	call SlotMachine_SpinWheels
+	call SlotMachine_CheckForMatches
+	ld hl, wPlayerCoins
+	ld a, [hli]
+	or [hl]
+	jr nz, .skip2
+	ld hl, OutOfCoinsSlotMachineText
+	call PrintText
+	ld c, 60
+	jp DelayFrames
+.skip2
+	ld hl, OneMoreGoSlotMachineText
+	call PrintText
+	coord hl, 14, 12
+	lb bc, 13, 15
+	xor a ; YES_NO_MENU
+	ld [wTwoOptionMenuID], a
+	ld a, TWO_OPTION_MENU
+	ld [wTextBoxID], a
+	call DisplayTextBoxID
+	ld a, [wCurrentMenuItem]
+	and a
+	ret nz
+	call SlotMachine_PutOutLitBalls
+	jp MainSlotMachineLoop
+
+CoinMultiplierSlotMachineText:
+	db   "×3"
+	next "×2"
+	next "×1@"
+
+OutOfCoinsSlotMachineText:
+	TX_FAR _OutOfCoinsSlotMachineText
+	db "@"
+
+BetHowManySlotMachineText:
+	TX_FAR _BetHowManySlotMachineText
+	db "@"
+
+StartSlotMachineText:
+	TX_FAR _StartSlotMachineText
+	db "@"
+
+NotEnoughCoinsSlotMachineText:
+	TX_FAR _NotEnoughCoinsSlotMachineText
+	db "@"
+
+OneMoreGoSlotMachineText:
+	TX_FAR _OneMoreGoSlotMachineText
+	db "@"
+
+SlotMachine_SetFlags:
+	ld hl, wSlotMachineFlags
+	bit 7, [hl]
+	ret nz
+	ld a, [wSlotMachineAllowMatchesCounter]
+	and a
+	jr nz, .allowMatches
+	call Random
+	and a
+	jr z, .setAllowMatchesCounter ; 1/256 (~0.4%) chance
+	ld b, a
+	ld a, [wSlotMachineSevenAndBarModeChance]
+	cp b
+	jr c, .allowSevenAndBarMatches
+	ld a, 210
+	cp b
+	jr c, .allowMatches ; 55/256 (~21.5%) chance
+	ld [hl], 0
+	ret
+.allowMatches
+	set 6, [hl]
+	ret
+.setAllowMatchesCounter
+	ld a, 60
+	ld [wSlotMachineAllowMatchesCounter], a
+	ret
+.allowSevenAndBarMatches
+	set 7, [hl]
+	ret
+
+SlotMachine_SpinWheels:
+	ld c, 20
+.loop1
+	push bc
+	call SlotMachine_AnimWheel1
+	call SlotMachine_AnimWheel2
+	call SlotMachine_AnimWheel3
+	ld c, 2
+	call DelayFrames
+	pop bc
+	dec c
+	jr nz, .loop1
+	xor a
+	ld [wStoppingWhichSlotMachineWheel], a
+.loop2
+	call SlotMachine_HandleInputWhileWheelsSpin
+	call SlotMachine_StopOrAnimWheel1
+	call SlotMachine_StopOrAnimWheel2
+	call SlotMachine_StopOrAnimWheel3
+	ret c
+	ld a, [wOnSGB]
+	xor $1
+	inc a
+	ld c, a
+	call DelayFrames
+	jr .loop2
+
+; Note that the wheels can only stop when a symbol is centred in the wheel
+; and thus 3 full symbols rather than 2 full symbols and 2 half symbols are
+; visible. The 3 functions below ensure this by checking if the wheel offset
+; is even before stopping the wheel.
+
+SlotMachine_StopOrAnimWheel1:
+	ld a, [wStoppingWhichSlotMachineWheel]
+	cp 1
+	jr c, .animWheel
+	ld de, wSlotMachineWheel1Offset
+	ld a, [de]
+	rra
+	jr nc, .animWheel ; check that a symbol is centred in the wheel
+	ld hl, wSlotMachineWheel1SlipCounter
+	ld a, [hl]
+	and a
+	ret z
+	dec [hl]
+	call SlotMachine_StopWheel1Early
+	ret nz
+.animWheel
+	jp SlotMachine_AnimWheel1
+
+SlotMachine_StopOrAnimWheel2:
+	ld a, [wStoppingWhichSlotMachineWheel]
+	cp 2
+	jr c, .animWheel
+	ld de, wSlotMachineWheel2Offset
+	ld a, [de]
+	rra
+	jr nc, .animWheel ; check that a symbol is centred in the wheel
+	ld hl, wSlotMachineWheel2SlipCounter
+	ld a, [hl]
+	and a
+	ret z
+	dec [hl]
+	call SlotMachine_StopWheel2Early
+	ret z
+.animWheel
+	jp SlotMachine_AnimWheel2
+
+SlotMachine_StopOrAnimWheel3:
+	ld a, [wStoppingWhichSlotMachineWheel]
+	cp 3
+	jr c, .animWheel
+	ld de, wSlotMachineWheel3Offset
+	ld a, [de]
+	rra
+	jr nc, .animWheel ; check that a symbol is centred in the wheel
+; wheel 3 stops as soon as possible
+	scf
+	ret
+.animWheel
+	call SlotMachine_AnimWheel3
+	and a
+	ret
+
+SlotMachine_StopWheel1Early:
+	call SlotMachine_GetWheel1Tiles
+	ld hl, wSlotMachineWheel1BottomTile
+	ld a, [wSlotMachineFlags]
+	and $80
+	jr nz, .sevenAndBarMode
+; Stop early if the middle symbol is not a cherry.
+	inc hl
+	ld a, [hl]
+	cp SLOTSCHERRY >> 8
+	jr nz, .stopWheel
+	ret
+; It looks like this was intended to make the wheel stop when a 7 symbol was
+; visible, but it has a bug and so the wheel stops randomly.
+.sevenAndBarMode
+	ld c, $3
+.loop
+	ld a, [hli]
+	cp SLOTS7 >> 8
+	jr c, .stopWheel ; condition never true
+	dec c
+	jr nz, .loop
+	ret
+.stopWheel
+	inc a
+	ld hl, wSlotMachineWheel1SlipCounter
+	ld [hl], 0
+	ret
+
+SlotMachine_StopWheel2Early:
+	call SlotMachine_GetWheel2Tiles
+	ld a, [wSlotMachineFlags]
+	and $80
+	jr nz, .sevenAndBarMode
+; Stop early if any symbols are lined up in the first two wheels.
+	call SlotMachine_FindWheel1Wheel2Matches
+	ret nz
+	jr .stopWheel
+; Stop early if two 7 symbols or two bar symbols are lined up in the first two
+; wheels OR if no symbols are lined up and the bottom symbol in wheel 2 is a
+; 7 symbol or bar symbol. The second part could be a bug or a way to reduce the
+; player's odds.
+.sevenAndBarMode
+	call SlotMachine_FindWheel1Wheel2Matches
+	ld a, [de]
+	cp (SLOTSBAR >> 8) + 1
+	ret nc
+.stopWheel
+	xor a
+	ld [wSlotMachineWheel2SlipCounter], a
+	ret
+
+SlotMachine_FindWheel1Wheel2Matches:
+; return whether wheel 1 and wheel 2's current positions allow a match (given
+; that wheel 3 stops in a good position) in Z
+	ld hl, wSlotMachineWheel1BottomTile
+	ld de, wSlotMachineWheel2BottomTile
+	ld a, [de]
+	cp [hl] ; wheel 1 bottom, wheel 2 bottom
+	ret z
+	inc de
+	ld a, [de]
+	cp [hl] ; wheel 1 bottom, wheel 2 middle
+	ret z
+	inc hl
+	cp [hl] ; wheel 1 middle, wheel 2 middle
+	ret z
+	inc hl
+	cp [hl] ; wheel 1 top, wheel 2 middle
+	ret z
+	inc de
+	ld a, [de]
+	cp [hl] ; wheel 1 top, wheel 2 top
+	ret z
+	dec de
+	dec de
+	ret
+
+SlotMachine_CheckForMatches:
+	call SlotMachine_GetWheel3Tiles
+	ld a, [wSlotMachineBet]
+	cp 2
+	jr z, .checkMatchesFor2CoinBet
+	cp 1
+	jr z, .checkMatchFor1CoinBet
+; 3 coin bet allows diagonal matches (plus the matches for 1/2 coin bets)
+	ld hl, wSlotMachineWheel1BottomTile
+	ld de, wSlotMachineWheel2MiddleTile
+	ld bc, wSlotMachineWheel3TopTile
+	call SlotMachine_CheckForMatch
+	jp z, .foundMatch
+	ld hl, wSlotMachineWheel1TopTile
+	ld de, wSlotMachineWheel2MiddleTile
+	ld bc, wSlotMachineWheel3BottomTile
+	call SlotMachine_CheckForMatch
+	jr z, .foundMatch
+; 2 coin bet allows top/bottom horizontal matches (plus the match for a 1 coin bet)
+.checkMatchesFor2CoinBet
+	ld hl, wSlotMachineWheel1TopTile
+	ld de, wSlotMachineWheel2TopTile
+	ld bc, wSlotMachineWheel3TopTile
+	call SlotMachine_CheckForMatch
+	jr z, .foundMatch
+	ld hl, wSlotMachineWheel1BottomTile
+	ld de, wSlotMachineWheel2BottomTile
+	ld bc, wSlotMachineWheel3BottomTile
+	call SlotMachine_CheckForMatch
+	jr z, .foundMatch
+; 1 coin bet only allows a middle horizontal match
+.checkMatchFor1CoinBet
+	ld hl, wSlotMachineWheel1MiddleTile
+	ld de, wSlotMachineWheel2MiddleTile
+	ld bc, wSlotMachineWheel3MiddleTile
+	call SlotMachine_CheckForMatch
+	jr z, .foundMatch
+	ld a, [wSlotMachineFlags]
+	and $c0
+	jr z, .noMatch
+	ld hl, wSlotMachineRerollCounter
+	dec [hl]
+	jr nz, .rollWheel3DownByOneSymbol
+.noMatch
+	ld hl, NotThisTimeText
+	call PrintText
+.done
+	xor a
+	ld [wMuteAudioAndPauseMusic], a
+	ret
+.rollWheel3DownByOneSymbol
+	call SlotMachine_AnimWheel3
+	call DelayFrame
+	call SlotMachine_AnimWheel3
+	call DelayFrame
+	jp SlotMachine_CheckForMatches
+.foundMatch
+	ld a, [wSlotMachineFlags]
+	and $c0
+	jr z, .rollWheel3DownByOneSymbol ; roll wheel if player isn't allowed to win
+	and $80
+	jr nz, .acceptMatch
+; if 7/bar matches aren't enabled and the match was a 7/bar symbol, roll wheel
+	ld a, [hl]
+	cp (SLOTSBAR >> 8) + 1
+	jr c, .rollWheel3DownByOneSymbol
+.acceptMatch
+	ld a, [hl]
+	sub $2
+	ld [wSlotMachineWinningSymbol], a
+	ld hl, SlotRewardPointers
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld a, [hli]
+	ld e, a
+	ld a, [hli]
+	ld d, a
+	push de
+	ld a, [hli]
+	ld h, [hl]
+	ld l, a
+	ld de, wcf4b
+	ld bc, 4
+	call CopyData
+	pop hl
+	ld de, .flashScreenLoop
+	push de
+	jp hl
+
+.flashScreenLoop
+	ld a, [rBGP]
+	xor $40
+	ld [rBGP], a
+	ld c, 5
+	call DelayFrames
+	dec b
+	jr nz, .flashScreenLoop
+	ld hl, wPayoutCoins
+	ld [hl], d
+	inc hl
+	ld [hl], e
+	call SlotMachine_PrintPayoutCoins
+	ld hl, SymbolLinedUpSlotMachineText
+	call PrintText
+	call WaitForTextScrollButtonPress
+	call SlotMachine_PayCoinsToPlayer
+	call SlotMachine_PrintPayoutCoins
+	ld a, $e4
+	ld [rOBP0], a
+	jp .done
+
+SymbolLinedUpSlotMachineText:
+	TX_ASM
+	push bc
+	call SlotMachine_PrintWinningSymbol
+	ld hl, LinedUpText
+	pop bc
+	inc bc
+	inc bc
+	inc bc
+	inc bc
+	ret
+
+LinedUpText:
+	TX_FAR _LinedUpText
+	db "@"
+
+SlotRewardPointers:
+	dw SlotReward300Func
+	dw SlotReward300Text
+	dw SlotReward100Func
+	dw SlotReward100Text
+	dw SlotReward8Func
+	dw SlotReward8Text
+	dw SlotReward15Func
+	dw SlotReward15Text
+	dw SlotReward15Func
+	dw SlotReward15Text
+	dw SlotReward15Func
+	dw SlotReward15Text
+
+SlotReward300Text:
+	db "300@"
+
+SlotReward100Text:
+	db "100@"
+
+SlotReward8Text:
+	db "8@"
+
+SlotReward15Text:
+	db "15@"
+
+NotThisTimeText:
+	TX_FAR _NotThisTimeText
+	db "@"
+
+; compares the slot machine tiles at bc, de, and hl
+SlotMachine_CheckForMatch:
+	ld a, [de]
+	cp [hl]
+	ret nz
+	ld a, [bc]
+	cp [hl]
+	ret
+
+SlotMachine_GetWheel3Tiles:
+	ld de, wSlotMachineWheel3BottomTile
+	ld hl, SlotMachineWheel3
+	ld a, [wSlotMachineWheel3Offset]
+	call SlotMachine_GetWheelTiles
+
+SlotMachine_GetWheel2Tiles:
+	ld de, wSlotMachineWheel2BottomTile
+	ld hl, SlotMachineWheel2
+	ld a, [wSlotMachineWheel2Offset]
+	call SlotMachine_GetWheelTiles
+
+SlotMachine_GetWheel1Tiles:
+	ld de, wSlotMachineWheel1BottomTile
+	ld hl, SlotMachineWheel1
+	ld a, [wSlotMachineWheel1Offset]
+
+SlotMachine_GetWheelTiles:
+	ld c, a
+	ld b, 0
+	add hl, bc
+	ld c, 3
+.loop
+	ld a, [hli]
+	ld [de], a
+	inc de
+	inc hl
+	dec c
+	jr nz, .loop
+	ret
+
+SlotReward8Func:
+	ld hl, wSlotMachineAllowMatchesCounter
+	ld a, [hl]
+	and a
+	jr z, .skip
+	dec [hl]
+.skip
+	ld b, $2
+	ld de, 8
+	ret
+
+SlotReward15Func:
+	ld hl, wSlotMachineAllowMatchesCounter
+	ld a, [hl]
+	and a
+	jr z, .skip
+	dec [hl]
+.skip
+	ld b, $4
+	ld de, 15
+	ret
+
+SlotReward100Func:
+	ld a, SFX_GET_KEY_ITEM
+	call PlaySound
+	xor a
+	ld [wSlotMachineFlags], a
+	ld b, $8
+	ld de, 100
+	ret
+
+SlotReward300Func:
+	ld hl, YeahText
+	call PrintText
+	ld a, SFX_GET_ITEM_2
+	call PlaySound
+	call Random
+	cp $80
+	ld a, $0
+	jr c, .skip
+	ld [wSlotMachineFlags], a
+.skip
+	ld [wSlotMachineAllowMatchesCounter], a
+	ld b, $14
+	ld de, 300
+	ret
+
+YeahText:
+	TX_FAR _YeahText
+	TX_DELAY
+	db "@"
+
+SlotMachine_PrintWinningSymbol:
+; prints winning symbol and down arrow in text box
+	coord hl, 2, 14
+	ld a, [wSlotMachineWinningSymbol]
+	add $25
+	ld [hli], a
+	inc a
+	ld [hld], a
+	inc a
+	ld de, -SCREEN_WIDTH
+	add hl, de
+	ld [hli], a
+	inc a
+	ld [hl], a
+	coord hl, 18, 16
+	ld [hl], "▼"
+	ret
+
+SlotMachine_SubtractBetFromPlayerCoins:
+	ld hl, wTempCoins2 + 1
+	ld a, [wSlotMachineBet]
+	ld [hld], a
+	xor a
+	ld [hli], a
+	ld de, wPlayerCoins + 1
+	ld c, $2
+	predef SubBCDPredef
+
+SlotMachine_PrintCreditCoins:
+	coord hl, 5, 1
+	ld de, wPlayerCoins
+	ld c, $2
+	jp PrintBCDNumber
+
+SlotMachine_PrintPayoutCoins:
+	coord hl, 11, 1
+	ld de, wPayoutCoins
+	lb bc, LEADING_ZEROES | 2, 4 ; 2 bytes, 4 digits
+	jp PrintNumber
+
+SlotMachine_PayCoinsToPlayer:
+	ld a, $1
+	ld [wMuteAudioAndPauseMusic], a
+	call WaitForSoundToFinish
+
+; Put 1 in the temp coins variable. This value is added to the player's coins
+; repeatedly so the player can watch the value go up 1 coin at a time.
+	ld hl, wTempCoins1
+	xor a
+	ld [hli], a
+	inc a
+	ld [hl], a
+
+	ld a, 5
+	ld [wAnimCounter], a
+
+; Subtract 1 from the payout amount and add 1 to the player's coins each
+; iteration until the payout amount reaches 0.
+.loop
+	ld a, [wPayoutCoins + 1]
+	ld l, a
+	ld a, [wPayoutCoins]
+	ld h, a
+	or l
+	ret z
+	ld de, -1
+	add hl, de
+	ld a, l
+	ld [wPayoutCoins + 1], a
+	ld a, h
+	ld [wPayoutCoins], a
+	ld hl, wTempCoins1 + 1
+	ld de, wPlayerCoins + 1
+	ld c, $2
+	predef AddBCDPredef
+	call SlotMachine_PrintCreditCoins
+	call SlotMachine_PrintPayoutCoins
+	ld a, SFX_SLOTS_REWARD
+	call PlaySound
+	ld a, [wAnimCounter]
+	dec a
+	jr nz, .skip1
+	ld a, [rOBP0]
+	xor $40 ; make the slot wheel symbols flash
+	ld [rOBP0], a
+	ld a, 5
+.skip1
+	ld [wAnimCounter], a
+	ld a, [wSlotMachineWinningSymbol]
+	cp (SLOTSBAR >> 8) + 1
+	ld c, 8
+	jr nc, .skip2
+	srl c ; c = 4 (make the the coins transfer faster if the symbol was 7 or bar)
+.skip2
+	call DelayFrames
+	jr .loop
+
+SlotMachine_PutOutLitBalls:
+	ld a, $23
+	ld [wNewSlotMachineBallTile], a
+	jr SlotMachine_UpdateThreeCoinBallTiles
+
+SlotMachine_LightBalls:
+	ld a, $14
+	ld [wNewSlotMachineBallTile], a
+	ld a, [wSlotMachineBet]
+	dec a
+	jr z, SlotMachine_UpdateOneCoinBallTiles
+	dec a
+	jr z, SlotMachine_UpdateTwoCoinBallTiles
+
+SlotMachine_UpdateThreeCoinBallTiles:
+	coord hl, 3, 2
+	call SlotMachine_UpdateBallTiles
+	coord hl, 3, 10
+	call SlotMachine_UpdateBallTiles
+
+SlotMachine_UpdateTwoCoinBallTiles:
+	coord hl, 3, 4
+	call SlotMachine_UpdateBallTiles
+	coord hl, 3, 8
+	call SlotMachine_UpdateBallTiles
+
+SlotMachine_UpdateOneCoinBallTiles:
+	coord hl, 3, 6
+
+SlotMachine_UpdateBallTiles:
+	ld a, [wNewSlotMachineBallTile]
+	ld [hl], a
+	ld bc, 13
+	add hl, bc
+	ld [hl], a
+	ld bc, 7
+	add hl, bc
+	inc a
+	ld [hl], a
+	ld bc, 13
+	add hl, bc
+	ld [hl], a
+	ret
+
+SlotMachine_AnimWheel1:
+	ld bc, SlotMachineWheel1
+	ld de, wSlotMachineWheel1Offset
+	ld hl, wOAMBuffer
+	ld a, $30
+	ld [wBaseCoordX], a
+	jr SlotMachine_AnimWheel
+
+SlotMachine_AnimWheel2:
+	ld bc, SlotMachineWheel2
+	ld de, wSlotMachineWheel2Offset
+	ld hl, wOAMBuffer + $30
+	ld a, $50
+	ld [wBaseCoordX], a
+	jr SlotMachine_AnimWheel
+
+SlotMachine_AnimWheel3:
+	ld bc, SlotMachineWheel3
+	ld de, wSlotMachineWheel3Offset
+	ld hl, wOAMBuffer + $60
+	ld a, $70
+	ld [wBaseCoordX], a
+
+SlotMachine_AnimWheel:
+	ld a, $58
+	ld [wBaseCoordY], a
+	push de
+	ld a, [de]
+	ld d, b
+	add c
+	ld e, a
+	jr nc, .loop
+	inc d
+.loop
+	ld a, [wBaseCoordY]
+	ld [hli], a
+	ld a, [wBaseCoordX]
+	ld [hli], a
+	ld a, [de]
+	ld [hli], a
+	ld a, $80
+	ld [hli], a
+	ld a, [wBaseCoordY]
+	ld [hli], a
+	ld a, [wBaseCoordX]
+	add $8
+	ld [hli], a
+	ld a, [de]
+	inc a
+	ld [hli], a
+	ld a, $80
+	ld [hli], a
+	inc de
+	ld a, [wBaseCoordY]
+	sub $8
+	ld [wBaseCoordY], a
+	cp $28
+	jr nz, .loop
+	pop de
+	ld a, [de]
+	inc a ; advance the offset so that the wheel animates
+	cp 30
+	jr nz, .skip
+	xor a ; wrap around to 0 when the offset reaches 30
+.skip
+	ld [de], a
+	ret
+
+SlotMachine_HandleInputWhileWheelsSpin:
+	call DelayFrame
+	call JoypadLowSensitivity
+	ld a, [hJoy5]
+	and A_BUTTON
+	ret z
+	ld hl, wStoppingWhichSlotMachineWheel
+	ld a, [hl]
+	dec a
+	ld de, wSlotMachineWheel1SlipCounter
+	jr z, .skip
+	dec a
+	ld de, wSlotMachineWheel2SlipCounter
+	jr z, .skip
+.loop
+	inc [hl]
+	ld a, SFX_SLOTS_STOP_WHEEL
+	jp PlaySound
+.skip
+	ld a, [de]
+	and a
+	ret nz
+	jr .loop
+
+LoadSlotMachineTiles:
+	call DisableLCD
+	ld hl, SlotMachineTiles2
+	ld de, vChars0
+	ld bc, $1c0
+	ld a, BANK(SlotMachineTiles2)
+	call FarCopyData2
+	ld hl, SlotMachineTiles1
+	ld de, vChars2
+	ld bc, $250
+	ld a, BANK(SlotMachineTiles1)
+	call FarCopyData2
+	ld hl, SlotMachineTiles2
+	ld de, vChars2 + $250
+	ld bc, $1c0
+	ld a, BANK(SlotMachineTiles2)
+	call FarCopyData2
+	ld hl, SlotMachineMap
+	coord de, 0, 0
+	ld bc, SlotMachineMapEnd - SlotMachineMap
+	call CopyData
+	call EnableLCD
+	ld hl, wSlotMachineWheel1Offset
+	ld a, $1c
+	ld [hli], a
+	ld [hli], a
+	ld [hl], a
+	call SlotMachine_AnimWheel1
+	call SlotMachine_AnimWheel2
+	jp SlotMachine_AnimWheel3
+
+SlotMachineMap:
+	INCBIN "gfx/slots/slots.tilemap"
+SlotMachineMapEnd:
+
+INCLUDE "data/slot_machine_wheels.asm"
+
+SlotMachineTiles1:
+IF DEF(_RED)
+	INCBIN "gfx/slots/red_slots_1.2bpp"
+ENDC
+IF DEF(_BLUE)
+	INCBIN "gfx/slots/blue_slots_1.2bpp"
+ENDC
--- a/engine/special_warps.asm
+++ /dev/null
@@ -1,149 +1,0 @@
-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"
--- a/engine/status_ailments.asm
+++ /dev/null
@@ -1,46 +1,0 @@
-PrintStatusAilment::
-	ld a, [de]
-	bit PSN, a
-	jr nz, .psn
-	bit BRN, a
-	jr nz, .brn
-	bit FRZ, a
-	jr nz, .frz
-	bit PAR, a
-	jr nz, .par
-	and SLP
-	ret z
-	ld a, "S"
-	ld [hli], a
-	ld a, "L"
-	ld [hli], a
-	ld [hl], "P"
-	ret
-.psn
-	ld a, "P"
-	ld [hli], a
-	ld a, "S"
-	ld [hli], a
-	ld [hl], "N"
-	ret
-.brn
-	ld a, "B"
-	ld [hli], a
-	ld a, "R"
-	ld [hli], a
-	ld [hl], "N"
-	ret
-.frz
-	ld a, "F"
-	ld [hli], a
-	ld a, "R"
-	ld [hli], a
-	ld [hl], "Z"
-	ret
-.par
-	ld a, "P"
-	ld [hli], a
-	ld a, "A"
-	ld [hli], a
-	ld [hl], "R"
-	ret
--- a/engine/subtract_paid_money.asm
+++ /dev/null
@@ -1,17 +1,0 @@
-; subtracts the amount the player paid from their money
-; OUTPUT: carry = 0(success) or 1(fail because there is not enough money)
-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/engine/test_battle.asm
+++ /dev/null
@@ -1,45 +1,0 @@
-TestBattle:
-	ret
-
-.loop
-	call GBPalNormal
-
-	; Don't mess around
-	; with obedience.
-	ld a, %10000000 ; EARTHBADGE
-	ld [wObtainedBadges], a
-
-	ld hl, wFlags_D733
-	set BIT_TEST_BATTLE, [hl]
-
-	; Reset the party.
-	ld hl, wPartyCount
-	xor a
-	ld [hli], a
-	dec a
-	ld [hl], a
-
-	; Give the player a
-	; level 20 Rhydon.
-	ld a, RHYDON
-	ld [wcf91], a
-	ld a, 20
-	ld [wCurEnemyLVL], a
-	xor a
-	ld [wMonDataLocation], a
-	ld [wCurMap], a
-	call AddPartyMon
-
-	; Fight against a
-	; level 20 Rhydon.
-	ld a, RHYDON
-	ld [wCurOpponent], a
-
-	predef InitOpponent
-
-	; When the battle ends,
-	; do it all again.
-	ld a, 1
-	ld [wUpdateSpritesEnabled], a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	jr .loop
--- a/engine/titlescreen.asm
+++ /dev/null
@@ -1,403 +1,0 @@
-; copy text of fixed length NAME_LENGTH (like player name, rival name, mon names, ...)
-CopyFixedLengthText:
-	ld bc, NAME_LENGTH
-	jp CopyData
-
-SetDefaultNamesBeforeTitlescreen::
-	ld hl, NintenText
-	ld de, wPlayerName
-	call CopyFixedLengthText
-	ld hl, SonyText
-	ld de, wRivalName
-	call CopyFixedLengthText
-	xor a
-	ld [hWY], a
-	ld [wLetterPrintingDelayFlags], a
-	ld hl, wd732
-	ld [hli], a
-	ld [hli], a
-	ld [hl], a
-	ld a, BANK(Music_TitleScreen)
-	ld [wAudioROMBank], a
-	ld [wAudioSavedROMBank], a
-
-DisplayTitleScreen:
-	call GBPalWhiteOut
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	xor a
-	ld [hTilesetType], a
-	ld [hSCX], a
-	ld a, $40
-	ld [hSCY], a
-	ld a, $90
-	ld [hWY], a
-	call ClearScreen
-	call DisableLCD
-	call LoadFontTilePatterns
-	ld hl, NintendoCopyrightLogoGraphics
-	ld de, vTitleLogo2 + $100
-	ld bc, $50
-	ld a, BANK(NintendoCopyrightLogoGraphics)
-	call FarCopyData2
-	ld hl, GamefreakLogoGraphics
-	ld de, vTitleLogo2 + $100 + $50
-	ld bc, $90
-	ld a, BANK(GamefreakLogoGraphics)
-	call FarCopyData2
-	ld hl, PokemonLogoGraphics
-	ld de, vTitleLogo
-	ld bc, $600
-	ld a, BANK(PokemonLogoGraphics)
-	call FarCopyData2          ; first chunk
-	ld hl, PokemonLogoGraphics+$600
-	ld de, vTitleLogo2
-	ld bc, $100
-	ld a, BANK(PokemonLogoGraphics)
-	call FarCopyData2          ; second chunk
-	ld hl, Version_GFX
-	ld de, vChars2 + $600 - (Version_GFXEnd - Version_GFX - $50)
-	ld bc, Version_GFXEnd - Version_GFX
-	ld a, BANK(Version_GFX)
-	call FarCopyDataDouble
-	call ClearBothBGMaps
-
-; place tiles for pokemon logo (except for the last row)
-	coord hl, 2, 1
-	ld a, $80
-	ld de, SCREEN_WIDTH
-	ld c, 6
-.pokemonLogoTileLoop
-	ld b, $10
-	push hl
-.pokemonLogoTileRowLoop ; place tiles for one row
-	ld [hli], a
-	inc a
-	dec b
-	jr nz, .pokemonLogoTileRowLoop
-	pop hl
-	add hl, de
-	dec c
-	jr nz, .pokemonLogoTileLoop
-
-; place tiles for the last row of the pokemon logo
-	coord hl, 2, 7
-	ld a, $31
-	ld b, $10
-.pokemonLogoLastTileRowLoop
-	ld [hli], a
-	inc a
-	dec b
-	jr nz, .pokemonLogoLastTileRowLoop
-
-	call DrawPlayerCharacter
-
-; put a pokeball in the player's hand
-	ld hl, wOAMBuffer + $28
-	ld a, $74
-	ld [hl], a
-
-; place tiles for title screen copyright
-	coord hl, 2, 17
-	ld de, .tileScreenCopyrightTiles
-	ld b, $10
-.tileScreenCopyrightTilesLoop
-	ld a, [de]
-	ld [hli], a
-	inc de
-	dec b
-	jr nz, .tileScreenCopyrightTilesLoop
-
-	jr .next
-
-.tileScreenCopyrightTiles
-	db $41,$42,$43,$42,$44,$42,$45,$46,$47,$48,$49,$4A,$4B,$4C,$4D,$4E ; ©'95.'96.'98 GAME FREAK inc.
-
-.next
-	call SaveScreenTilesToBuffer2
-	call LoadScreenTilesFromBuffer2
-	call EnableLCD
-IF DEF(_RED)
-	ld a, CHARMANDER ; which Pokemon to show first on the title screen
-ENDC
-IF DEF(_BLUE)
-	ld a, SQUIRTLE ; which Pokemon to show first on the title screen
-ENDC
-
-	ld [wTitleMonSpecies], a
-	call LoadTitleMonSprite
-	ld a, (vBGMap0 + $300) / $100
-	call TitleScreenCopyTileMapToVRAM
-	call SaveScreenTilesToBuffer1
-	ld a, $40
-	ld [hWY], a
-	call LoadScreenTilesFromBuffer2
-	ld a, vBGMap0 / $100
-	call TitleScreenCopyTileMapToVRAM
-	ld b, SET_PAL_TITLE_SCREEN
-	call RunPaletteCommand
-	call GBPalNormal
-	ld a, %11100100
-	ld [rOBP0], a
-
-; make pokemon logo bounce up and down
-	ld bc, hSCY ; background scroll Y
-	ld hl, .TitleScreenPokemonLogoYScrolls
-.bouncePokemonLogoLoop
-	ld a, [hli]
-	and a
-	jr z, .finishedBouncingPokemonLogo
-	ld d, a
-	cp -3
-	jr nz, .skipPlayingSound
-	ld a, SFX_INTRO_CRASH
-	call PlaySound
-.skipPlayingSound
-	ld a, [hli]
-	ld e, a
-	call .ScrollTitleScreenPokemonLogo
-	jr .bouncePokemonLogoLoop
-
-.TitleScreenPokemonLogoYScrolls:
-; Controls the bouncing effect of the Pokemon logo on the title screen
-	db -4,16  ; y scroll amount, number of times to scroll
-	db 3,4
-	db -3,4
-	db 2,2
-	db -2,2
-	db 1,2
-	db -1,2
-	db 0      ; terminate list with 0
-
-.ScrollTitleScreenPokemonLogo:
-; Scrolls the Pokemon logo on the title screen to create the bouncing effect
-; Scrolls d pixels e times
-	call DelayFrame
-	ld a, [bc] ; background scroll Y
-	add d
-	ld [bc], a
-	dec e
-	jr nz, .ScrollTitleScreenPokemonLogo
-	ret
-
-.finishedBouncingPokemonLogo
-	call LoadScreenTilesFromBuffer1
-	ld c, 36
-	call DelayFrames
-	ld a, SFX_INTRO_WHOOSH
-	call PlaySound
-
-; scroll game version in from the right
-	call PrintGameVersionOnTitleScreen
-	ld a, SCREEN_HEIGHT_PIXELS
-	ld [hWY], a
-	ld d, 144
-.scrollTitleScreenGameVersionLoop
-	ld h, d
-	ld l, 64
-	call ScrollTitleScreenGameVersion
-	ld h, 0
-	ld l, 80
-	call ScrollTitleScreenGameVersion
-	ld a, d
-	add 4
-	ld d, a
-	and a
-	jr nz, .scrollTitleScreenGameVersionLoop
-
-	ld a, vBGMap1 / $100
-	call TitleScreenCopyTileMapToVRAM
-	call LoadScreenTilesFromBuffer2
-	call PrintGameVersionOnTitleScreen
-	call Delay3
-	call WaitForSoundToFinish
-	ld a, MUSIC_TITLE_SCREEN
-	ld [wNewSoundID], a
-	call PlaySound
-	xor a
-	ld [wUnusedCC5B], a
-
-; Keep scrolling in new mons indefinitely until the user performs input.
-.awaitUserInterruptionLoop
-	ld c, 200
-	call CheckForUserInterruption
-	jr c, .finishedWaiting
-	call TitleScreenScrollInMon
-	ld c, 1
-	call CheckForUserInterruption
-	jr c, .finishedWaiting
-	callba TitleScreenAnimateBallIfStarterOut
-	call TitleScreenPickNewMon
-	jr .awaitUserInterruptionLoop
-
-.finishedWaiting
-	ld a, [wTitleMonSpecies]
-	call PlayCry
-	call WaitForSoundToFinish
-	call GBPalWhiteOutWithDelay3
-	call ClearSprites
-	xor a
-	ld [hWY], a
-	inc a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call ClearScreen
-	ld a, vBGMap0 / $100
-	call TitleScreenCopyTileMapToVRAM
-	ld a, vBGMap1 / $100
-	call TitleScreenCopyTileMapToVRAM
-	call Delay3
-	call LoadGBPal
-	ld a, [hJoyHeld]
-	ld b, a
-	and D_UP | SELECT | B_BUTTON
-	cp D_UP | SELECT | B_BUTTON
-	jp z, .doClearSaveDialogue
-	jp MainMenu
-
-.doClearSaveDialogue
-	jpba DoClearSaveDialogue
-
-TitleScreenPickNewMon:
-	ld a, vBGMap0 / $100
-	call TitleScreenCopyTileMapToVRAM
-
-.loop
-; Keep looping until a mon different from the current one is picked.
-	call Random
-	and $f
-	ld c, a
-	ld b, 0
-	ld hl, TitleMons
-	add hl, bc
-	ld a, [hl]
-	ld hl, wTitleMonSpecies
-
-; Can't be the same as before.
-	cp [hl]
-	jr z, .loop
-
-	ld [hl], a
-	call LoadTitleMonSprite
-
-	ld a, $90
-	ld [hWY], a
-	ld d, 1 ; scroll out
-	callba TitleScroll
-	ret
-
-TitleScreenScrollInMon:
-	ld d, 0 ; scroll in
-	callba TitleScroll
-	xor a
-	ld [hWY], a
-	ret
-
-ScrollTitleScreenGameVersion:
-.wait
-	ld a, [rLY]
-	cp l
-	jr nz, .wait
-
-	ld a, h
-	ld [rSCX], a
-
-.wait2
-	ld a, [rLY]
-	cp h
-	jr z, .wait2
-	ret
-
-DrawPlayerCharacter:
-	ld hl, PlayerCharacterTitleGraphics
-	ld de, vSprites
-	ld bc, PlayerCharacterTitleGraphicsEnd - PlayerCharacterTitleGraphics
-	ld a, BANK(PlayerCharacterTitleGraphics)
-	call FarCopyData2
-	call ClearSprites
-	xor a
-	ld [wPlayerCharacterOAMTile], a
-	ld hl, wOAMBuffer
-	ld de, $605a
-	ld b, 7
-.loop
-	push de
-	ld c, 5
-.innerLoop
-	ld a, d
-	ld [hli], a ; Y
-	ld a, e
-	ld [hli], a ; X
-	add 8
-	ld e, a
-	ld a, [wPlayerCharacterOAMTile]
-	ld [hli], a ; tile
-	inc a
-	ld [wPlayerCharacterOAMTile], a
-	inc hl
-	dec c
-	jr nz, .innerLoop
-	pop de
-	ld a, 8
-	add d
-	ld d, a
-	dec b
-	jr nz, .loop
-	ret
-
-ClearBothBGMaps:
-	ld hl, vBGMap0
-	ld bc, $400 * 2
-	ld a, " "
-	jp FillMemory
-
-LoadTitleMonSprite:
-	ld [wcf91], a
-	ld [wd0b5], a
-	coord hl, 5, 10
-	call GetMonHeader
-	jp LoadFrontSpriteByMonIndex
-
-TitleScreenCopyTileMapToVRAM:
-	ld [H_AUTOBGTRANSFERDEST + 1], a
-	jp Delay3
-
-LoadCopyrightAndTextBoxTiles:
-	xor a
-	ld [hWY], a
-	call ClearScreen
-	call LoadTextBoxTilePatterns
-
-LoadCopyrightTiles:
-	ld de, NintendoCopyrightLogoGraphics
-	ld hl, vChars2 + $600
-	lb bc, BANK(NintendoCopyrightLogoGraphics), (GamefreakLogoGraphicsEnd - NintendoCopyrightLogoGraphics) / $10
-	call CopyVideoData
-	coord hl, 2, 7
-	ld de, CopyrightTextString
-	jp PlaceString
-
-CopyrightTextString:
-	db   $60,$61,$62,$61,$63,$61,$64,$7F,$65,$66,$67,$68,$69,$6A             ; ©'95.'96.'98 Nintendo
-	next $60,$61,$62,$61,$63,$61,$64,$7F,$6B,$6C,$6D,$6E,$6F,$70,$71,$72     ; ©'95.'96.'98 Creatures inc.
-	next $60,$61,$62,$61,$63,$61,$64,$7F,$73,$74,$75,$76,$77,$78,$79,$7A,$7B ; ©'95.'96.'98 GAME FREAK inc.
-	db   "@"
-
-INCLUDE "data/title_mons.asm"
-
-; prints version text (red, blue)
-PrintGameVersionOnTitleScreen:
-	coord hl, 7, 8
-	ld de, VersionOnTitleScreenText
-	jp PlaceString
-
-; these point to special tiles specifically loaded for that purpose and are not usual text
-VersionOnTitleScreenText:
-IF DEF(_RED)
-	db $60,$61,$7F,$65,$66,$67,$68,$69,"@" ; "Red Version"
-ENDC
-IF DEF(_BLUE)
-	db $61,$62,$63,$64,$65,$66,$67,$68,"@" ; "Blue Version"
-ENDC
-
-NintenText: db "NINTEN@"
-SonyText:   db "SONY@"
--- a/engine/titlescreen2.asm
+++ /dev/null
@@ -1,120 +1,0 @@
-TitleScroll_WaitBall:
-; Wait around for the TitleBall animation to play out.
-; hi: speed
-; lo: duration
-	db $05, $05, 0
-
-TitleScroll_In:
-; Scroll a TitleMon in from the right.
-; hi: speed
-; lo: duration
-	db $a2, $94, $84, $63, $52, $31, $11, 0
-
-TitleScroll_Out:
-; Scroll a TitleMon out to the left.
-; hi: speed
-; lo: duration
-	db $12, $22, $32, $42, $52, $62, $83, $93, 0
-
-TitleScroll:
-	ld a, d
-
-	ld bc, TitleScroll_In
-	ld d, $88
-	ld e, 0 ; don't animate titleball
-
-	and a
-	jr nz, .ok
-
-	ld bc, TitleScroll_Out
-	ld d, $00
-	ld e, 0 ; don't animate titleball
-.ok
-
-_TitleScroll:
-	ld a, [bc]
-	and a
-	ret z
-
-	inc bc
-	push bc
-
-	ld b, a
-	and $f
-	ld c, a
-	ld a, b
-	and $f0
-	swap a
-	ld b, a
-
-.loop
-	ld h, d
-	ld l, $48
-	call .ScrollBetween
-
-	ld h, $00
-	ld l, $88
-	call .ScrollBetween
-
-	ld a, d
-	add b
-	ld d, a
-
-	call GetTitleBallY
-	dec c
-	jr nz, .loop
-
-	pop bc
-	jr _TitleScroll
-
-.ScrollBetween:
-.wait
-	ld a, [rLY] ; rLY
-	cp l
-	jr nz, .wait
-
-	ld a, h
-	ld [rSCX], a
-
-.wait2
-	ld a, [rLY] ; rLY
-	cp h
-	jr z, .wait2
-	ret
-
-TitleBallYTable:
-; OBJ y-positions for the Poke Ball held by Red in the title screen.
-; This is really two 0-terminated lists. Initiated with an index of 1.
-	db 0, $71, $6f, $6e, $6d, $6c, $6d, $6e, $6f, $71, $74, 0
-
-TitleScreenAnimateBallIfStarterOut:
-; Animate the TitleBall if a starter just got scrolled out.
-	ld a, [wTitleMonSpecies]
-	cp STARTER1
-	jr z, .ok
-	cp STARTER2
-	jr z, .ok
-	cp STARTER3
-	ret nz
-.ok
-	ld e, 1 ; animate titleball
-	ld bc, TitleScroll_WaitBall
-	ld d, 0
-	jp _TitleScroll
-
-GetTitleBallY:
-; Get position e from TitleBallYTable
-	push de
-	push hl
-	xor a
-	ld d, a
-	ld hl, TitleBallYTable
-	add hl, de
-	ld a, [hl]
-	pop hl
-	pop de
-	and a
-	ret z
-	ld [wOAMBuffer + $28], a
-	inc e
-	ret
--- a/engine/town_map.asm
+++ /dev/null
@@ -1,618 +1,0 @@
-DisplayTownMap:
-	call LoadTownMap
-	ld hl, wUpdateSpritesEnabled
-	ld a, [hl]
-	push af
-	ld [hl], $ff
-	push hl
-	ld a, $1
-	ld [hJoy7], a
-	ld a, [wCurMap]
-	push af
-	ld b, $0
-	call DrawPlayerOrBirdSprite ; player sprite
-	coord hl, 1, 0
-	ld de, wcd6d
-	call PlaceString
-	ld hl, wOAMBuffer
-	ld de, wTileMapBackup
-	ld bc, $10
-	call CopyData
-	ld hl, vSprites + $40
-	ld de, TownMapCursor
-	lb bc, BANK(TownMapCursor), (TownMapCursorEnd - TownMapCursor) / $8
-	call CopyVideoDataDouble
-	xor a
-	ld [wWhichTownMapLocation], a
-	pop af
-	jr .enterLoop
-
-.townMapLoop
-	coord hl, 0, 0
-	lb bc, 1, 20
-	call ClearScreenArea
-	ld hl, TownMapOrder
-	ld a, [wWhichTownMapLocation]
-	ld c, a
-	ld b, 0
-	add hl, bc
-	ld a, [hl]
-.enterLoop
-	ld de, wTownMapCoords
-	call LoadTownMapEntry
-	ld a, [de]
-	push hl
-	call TownMapCoordsToOAMCoords
-	ld a, $4
-	ld [wOAMBaseTile], a
-	ld hl, wOAMBuffer + $10
-	call WriteTownMapSpriteOAM ; town map cursor sprite
-	pop hl
-	ld de, wcd6d
-.copyMapName
-	ld a, [hli]
-	ld [de], a
-	inc de
-	cp $50
-	jr nz, .copyMapName
-	coord hl, 1, 0
-	ld de, wcd6d
-	call PlaceString
-	ld hl, wOAMBuffer + $10
-	ld de, wTileMapBackup + 16
-	ld bc, $10
-	call CopyData
-.inputLoop
-	call TownMapSpriteBlinkingAnimation
-	call JoypadLowSensitivity
-	ld a, [hJoy5]
-	ld b, a
-	and A_BUTTON | B_BUTTON | D_UP | D_DOWN
-	jr z, .inputLoop
-	ld a, SFX_TINK
-	call PlaySound
-	bit 6, b
-	jr nz, .pressedUp
-	bit 7, b
-	jr nz, .pressedDown
-	xor a
-	ld [wTownMapSpriteBlinkingEnabled], a
-	ld [hJoy7], a
-	ld [wAnimCounter], a
-	call ExitTownMap
-	pop hl
-	pop af
-	ld [hl], a
-	ret
-.pressedUp
-	ld a, [wWhichTownMapLocation]
-	inc a
-	cp TownMapOrderEnd - TownMapOrder ; number of list items + 1
-	jr nz, .noOverflow
-	xor a
-.noOverflow
-	ld [wWhichTownMapLocation], a
-	jp .townMapLoop
-.pressedDown
-	ld a, [wWhichTownMapLocation]
-	dec a
-	cp -1
-	jr nz, .noUnderflow
-	ld a, TownMapOrderEnd - TownMapOrder - 1 ; number of list items
-.noUnderflow
-	ld [wWhichTownMapLocation], a
-	jp .townMapLoop
-
-INCLUDE "data/town_map_order.asm"
-
-TownMapCursor:
-	INCBIN "gfx/town_map/town_map_cursor.1bpp"
-TownMapCursorEnd:
-
-LoadTownMap_Nest:
-	call LoadTownMap
-	ld hl, wUpdateSpritesEnabled
-	ld a, [hl]
-	push af
-	ld [hl], $ff
-	push hl
-	call DisplayWildLocations
-	call GetMonName
-	coord hl, 1, 0
-	call PlaceString
-	ld h, b
-	ld l, c
-	ld de, MonsNestText
-	call PlaceString
-	call WaitForTextScrollButtonPress
-	call ExitTownMap
-	pop hl
-	pop af
-	ld [hl], a
-	ret
-
-MonsNestText:
-	db "'s NEST@"
-
-LoadTownMap_Fly::
-	call ClearSprites
-	call LoadTownMap
-	call LoadPlayerSpriteGraphics
-	call LoadFontTilePatterns
-	ld de, BirdSprite
-	ld hl, vSprites + $40
-	lb bc, BANK(BirdSprite), $c
-	call CopyVideoData
-	ld de, TownMapUpArrow
-	ld hl, vChars1 + $6d0
-	lb bc, BANK(TownMapUpArrow), (TownMapUpArrowEnd - TownMapUpArrow) / $8
-	call CopyVideoDataDouble
-	call BuildFlyLocationsList
-	ld hl, wUpdateSpritesEnabled
-	ld a, [hl]
-	push af
-	ld [hl], $ff
-	push hl
-	coord hl, 0, 0
-	ld de, ToText
-	call PlaceString
-	ld a, [wCurMap]
-	ld b, $0
-	call DrawPlayerOrBirdSprite
-	ld hl, wFlyLocationsList
-	coord de, 18, 0
-.townMapFlyLoop
-	ld a, " "
-	ld [de], a
-	push hl
-	push hl
-	coord hl, 3, 0
-	lb bc, 1, 15
-	call ClearScreenArea
-	pop hl
-	ld a, [hl]
-	ld b, $4
-	call DrawPlayerOrBirdSprite ; draw bird sprite
-	coord hl, 3, 0
-	ld de, wcd6d
-	call PlaceString
-	ld c, 15
-	call DelayFrames
-	coord hl, 18, 0
-	ld [hl], "▲"
-	coord hl, 19, 0
-	ld [hl], "▼"
-	pop hl
-.inputLoop
-	push hl
-	call DelayFrame
-	call JoypadLowSensitivity
-	ld a, [hJoy5]
-	ld b, a
-	pop hl
-	and A_BUTTON | B_BUTTON | D_UP | D_DOWN
-	jr z, .inputLoop
-	bit 0, b
-	jr nz, .pressedA
-	ld a, SFX_TINK
-	call PlaySound
-	bit 6, b
-	jr nz, .pressedUp
-	bit 7, b
-	jr nz, .pressedDown
-	jr .pressedB
-.pressedA
-	ld a, SFX_HEAL_AILMENT
-	call PlaySound
-	ld a, [hl]
-	ld [wDestinationMap], a
-	ld hl, wd732
-	set 3, [hl]
-	inc hl
-	set 7, [hl]
-.pressedB
-	xor a
-	ld [wTownMapSpriteBlinkingEnabled], a
-	call GBPalWhiteOutWithDelay3
-	pop hl
-	pop af
-	ld [hl], a
-	ret
-.pressedUp
-	coord de, 18, 0
-	inc hl
-	ld a, [hl]
-	cp $ff
-	jr z, .wrapToStartOfList
-	cp $fe
-	jr z, .pressedUp ; skip past unvisited towns
-	jp .townMapFlyLoop
-.wrapToStartOfList
-	ld hl, wFlyLocationsList
-	jp .townMapFlyLoop
-.pressedDown
-	coord de, 19, 0
-	dec hl
-	ld a, [hl]
-	cp $ff
-	jr z, .wrapToEndOfList
-	cp $fe
-	jr z, .pressedDown ; skip past unvisited towns
-	jp .townMapFlyLoop
-.wrapToEndOfList
-	ld hl, wFlyLocationsList + 11
-	jr .pressedDown
-
-ToText:
-	db "To@"
-
-BuildFlyLocationsList:
-	ld hl, wFlyLocationsList - 1
-	ld [hl], $ff
-	inc hl
-	ld a, [wTownVisitedFlag]
-	ld e, a
-	ld a, [wTownVisitedFlag + 1]
-	ld d, a
-	ld bc, SAFFRON_CITY + 1
-.loop
-	srl d
-	rr e
-	ld a, $fe ; store $fe if the town hasn't been visited
-	jr nc, .notVisited
-	ld a, b ; store the map number of the town if it has been visited
-.notVisited
-	ld [hl], a
-	inc hl
-	inc b
-	dec c
-	jr nz, .loop
-	ld [hl], $ff
-	ret
-
-TownMapUpArrow:
-	INCBIN "gfx/town_map/up_arrow.1bpp"
-TownMapUpArrowEnd:
-
-LoadTownMap:
-	call GBPalWhiteOutWithDelay3
-	call ClearScreen
-	call UpdateSprites
-	coord hl, 0, 0
-	ld b, $12
-	ld c, $12
-	call TextBoxBorder
-	call DisableLCD
-	ld hl, WorldMapTileGraphics
-	ld de, vChars2 + $600
-	ld bc, WorldMapTileGraphicsEnd - WorldMapTileGraphics
-	ld a, BANK(WorldMapTileGraphics)
-	call FarCopyData2
-	ld hl, MonNestIcon
-	ld de, vSprites + $40
-	ld bc, MonNestIconEnd - MonNestIcon
-	ld a, BANK(MonNestIcon)
-	call FarCopyDataDouble
-	coord hl, 0, 0
-	ld de, CompressedMap
-.nextTile
-	ld a, [de]
-	and a
-	jr z, .done
-	ld b, a
-	and $f
-	ld c, a
-	ld a, b
-	swap a
-	and $f
-	add $60
-.writeRunLoop
-	ld [hli], a
-	dec c
-	jr nz, .writeRunLoop
-	inc de
-	jr .nextTile
-.done
-	call EnableLCD
-	ld b, SET_PAL_TOWN_MAP
-	call RunPaletteCommand
-	call Delay3
-	call GBPalNormal
-	xor a
-	ld [wAnimCounter], a
-	inc a
-	ld [wTownMapSpriteBlinkingEnabled], a
-	ret
-
-CompressedMap:
-	INCBIN "gfx/town_map/town_map.rle"
-
-ExitTownMap:
-; clear town map graphics data and load usual graphics data
-	xor a
-	ld [wTownMapSpriteBlinkingEnabled], a
-	call GBPalWhiteOut
-	call ClearScreen
-	call ClearSprites
-	call LoadPlayerSpriteGraphics
-	call LoadFontTilePatterns
-	call UpdateSprites
-	jp RunDefaultPaletteCommand
-
-DrawPlayerOrBirdSprite:
-; a = map number
-; b = OAM base tile
-	push af
-	ld a, b
-	ld [wOAMBaseTile], a
-	pop af
-	ld de, wTownMapCoords
-	call LoadTownMapEntry
-	ld a, [de]
-	push hl
-	call TownMapCoordsToOAMCoords
-	call WritePlayerOrBirdSpriteOAM
-	pop hl
-	ld de, wcd6d
-.loop
-	ld a, [hli]
-	ld [de], a
-	inc de
-	cp "@"
-	jr nz, .loop
-	ld hl, wOAMBuffer
-	ld de, wTileMapBackup
-	ld bc, $a0
-	jp CopyData
-
-DisplayWildLocations:
-	callba FindWildLocationsOfMon
-	call ZeroOutDuplicatesInList
-	ld hl, wOAMBuffer
-	ld de, wTownMapCoords
-.loop
-	ld a, [de]
-	cp $ff
-	jr z, .exitLoop
-	and a
-	jr z, .nextEntry
-	push hl
-	call LoadTownMapEntry
-	pop hl
-	ld a, [de]
-	cp $19 ; Cerulean Cave's coordinates
-	jr z, .nextEntry ; skip Cerulean Cave
-	call TownMapCoordsToOAMCoords
-	ld a, $4 ; nest icon tile no.
-	ld [hli], a
-	xor a
-	ld [hli], a
-.nextEntry
-	inc de
-	jr .loop
-.exitLoop
-	ld a, l
-	and a ; were any OAM entries written?
-	jr nz, .drawPlayerSprite
-; if no OAM entries were written, print area unknown text
-	coord hl, 1, 7
-	ld b, 2
-	ld c, 15
-	call TextBoxBorder
-	coord hl, 2, 9
-	ld de, AreaUnknownText
-	call PlaceString
-	jr .done
-.drawPlayerSprite
-	ld a, [wCurMap]
-	ld b, $0
-	call DrawPlayerOrBirdSprite
-.done
-	ld hl, wOAMBuffer
-	ld de, wTileMapBackup
-	ld bc, $a0
-	jp CopyData
-
-AreaUnknownText:
-	db " AREA UNKNOWN@"
-
-TownMapCoordsToOAMCoords:
-; in: lower nybble of a = x, upper nybble of a = y
-; out: b and [hl] = (y * 8) + 24, c and [hl+1] = (x * 8) + 24
-	push af
-	and $f0
-	srl a
-	add 24
-	ld b, a
-	ld [hli], a
-	pop af
-	and $f
-	swap a
-	srl a
-	add 24
-	ld c, a
-	ld [hli], a
-	ret
-
-WritePlayerOrBirdSpriteOAM:
-	ld a, [wOAMBaseTile]
-	and a
-	ld hl, wOAMBuffer + $90 ; for player sprite
-	jr z, WriteTownMapSpriteOAM
-	ld hl, wOAMBuffer + $80 ; for bird sprite
-
-WriteTownMapSpriteOAM:
-	push hl
-
-; Subtract 4 from c (X coord) and 4 from b (Y coord). However, the carry from c
-; is added to b, so the net result is that only 3 is subtracted from b.
-	lb hl, -4, -4
-	add hl, bc
-
-	ld b, h
-	ld c, l
-	pop hl
-
-WriteAsymmetricMonPartySpriteOAM:
-; Writes 4 OAM blocks for a helix mon party sprite, since it does not have
-; a vertical line of symmetry.
-	lb de, 2, 2
-.loop
-	push de
-	push bc
-.innerLoop
-	ld a, b
-	ld [hli], a
-	ld a, c
-	ld [hli], a
-	ld a, [wOAMBaseTile]
-	ld [hli], a
-	inc a
-	ld [wOAMBaseTile], a
-	xor a
-	ld [hli], a
-	inc d
-	ld a, 8
-	add c
-	ld c, a
-	dec e
-	jr nz, .innerLoop
-	pop bc
-	pop de
-	ld a, 8
-	add b
-	ld b, a
-	dec d
-	jr nz, .loop
-	ret
-
-WriteSymmetricMonPartySpriteOAM:
-; Writes 4 OAM blocks for a mon party sprite other than a helix. All the
-; sprites other than the helix one have a vertical line of symmetry which allows
-; the X-flip OAM bit to be used so that only 2 rather than 4 tile patterns are
-; needed.
-	xor a
-	ld [wSymmetricSpriteOAMAttributes], a
-	lb de, 2, 2
-.loop
-	push de
-	push bc
-.innerLoop
-	ld a, b
-	ld [hli], a ; Y
-	ld a, c
-	ld [hli], a ; X
-	ld a, [wOAMBaseTile]
-	ld [hli], a ; tile
-	ld a, [wSymmetricSpriteOAMAttributes]
-	ld [hli], a ; attributes
-	xor (1 << OAM_X_FLIP)
-	ld [wSymmetricSpriteOAMAttributes], a
-	inc d
-	ld a, 8
-	add c
-	ld c, a
-	dec e
-	jr nz, .innerLoop
-	pop bc
-	pop de
-	push hl
-	ld hl, wOAMBaseTile
-	inc [hl]
-	inc [hl]
-	pop hl
-	ld a, 8
-	add b
-	ld b, a
-	dec d
-	jr nz, .loop
-	ret
-
-ZeroOutDuplicatesInList:
-; replace duplicate bytes in the list of wild pokemon locations with 0
-	ld de, wBuffer
-.loop
-	ld a, [de]
-	inc de
-	cp $ff
-	ret z
-	ld c, a
-	ld l, e
-	ld h, d
-.zeroDuplicatesLoop
-	ld a, [hl]
-	cp $ff
-	jr z, .loop
-	cp c
-	jr nz, .skipZeroing
-	xor a
-	ld [hl], a
-.skipZeroing
-	inc hl
-	jr .zeroDuplicatesLoop
-
-LoadTownMapEntry:
-; in: a = map number
-; out: lower nybble of [de] = x, upper nybble of [de] = y, hl = address of name
-	cp REDS_HOUSE_1F
-	jr c, .external
-	ld bc, 4
-	ld hl, InternalMapEntries
-.loop
-	cp [hl]
-	jr c, .foundEntry
-	add hl, bc
-	jr .loop
-.foundEntry
-	inc hl
-	jr .readEntry
-.external
-	ld hl, ExternalMapEntries
-	ld c, a
-	ld b, 0
-	add hl, bc
-	add hl, bc
-	add hl, bc
-.readEntry
-	ld a, [hli]
-	ld [de], a
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ret
-
-INCLUDE "data/town_map_entries.asm"
-
-INCLUDE "text/map_names.asm"
-
-MonNestIcon:
-	INCBIN "gfx/town_map/mon_nest_icon.1bpp"
-MonNestIconEnd:
-
-TownMapSpriteBlinkingAnimation::
-	ld a, [wAnimCounter]
-	inc a
-	cp 25
-	jr z, .hideSprites
-	cp 50
-	jr nz, .done
-; show sprites when the counter reaches 50
-	ld hl, wTileMapBackup
-	ld de, wOAMBuffer
-	ld bc, $90
-	call CopyData
-	xor a
-	jr .done
-.hideSprites
-	ld hl, wOAMBuffer
-	ld b, $24
-	ld de, $4
-.hideSpritesLoop
-	ld [hl], $a0
-	add hl, de
-	dec b
-	jr nz, .hideSpritesLoop
-	ld a, 25
-.done
-	ld [wAnimCounter], a
-	jp DelayFrame
--- a/engine/trade.asm
+++ /dev/null
@@ -1,853 +1,0 @@
-InternalClockTradeAnim:
-; Do the trading animation with the player's gameboy on the left.
-; In-game trades and internally clocked link cable trades use this.
-	ld a, [wTradedPlayerMonSpecies]
-	ld [wLeftGBMonSpecies], a
-	ld a, [wTradedEnemyMonSpecies]
-	ld [wRightGBMonSpecies], a
-	ld de, InternalClockTradeFuncSequence
-	jr TradeAnimCommon
-
-ExternalClockTradeAnim:
-; Do the trading animation with the player's gameboy on the right.
-; Externally clocked link cable trades use this.
-	ld a, [wTradedEnemyMonSpecies]
-	ld [wLeftGBMonSpecies], a
-	ld a, [wTradedPlayerMonSpecies]
-	ld [wRightGBMonSpecies], a
-	ld de, ExternalClockTradeFuncSequence
-
-TradeAnimCommon:
-	ld a, [wOptions]
-	push af
-	ld a, [hSCY]
-	push af
-	ld a, [hSCX]
-	push af
-	xor a
-	ld [wOptions], a
-	ld [hSCY], a
-	ld [hSCX], a
-	push de
-.loop
-	pop de
-	ld a, [de]
-	cp $ff
-	jr z, .done
-	inc de
-	push de
-	ld hl, TradeFuncPointerTable
-	add a
-	ld c, a
-	ld b, $0
-	add hl, bc
-	ld a, [hli]
-	ld h, [hl]
-	ld l, a
-	ld de, .loop
-	push de
-	jp hl ; call trade func, which will return to the top of the loop
-.done
-	pop af
-	ld [hSCX], a
-	pop af
-	ld [hSCY], a
-	pop af
-	ld [wOptions], a
-	ret
-
-addtradefunc: MACRO
-\1TradeFunc::
-	dw \1
-	ENDM
-
-tradefunc: MACRO
-	db (\1TradeFunc - TradeFuncPointerTable) / 2
-	ENDM
-
-; The functions in the sequences below are executed in order by TradeFuncCommon.
-; They are from opposite perspectives. The external clock one makes use of
-; Trade_SwapNames to swap the player and enemy names for some functions.
-
-InternalClockTradeFuncSequence:
-	tradefunc LoadTradingGFXAndMonNames
-	tradefunc Trade_ShowPlayerMon
-	tradefunc Trade_DrawOpenEndOfLinkCable
-	tradefunc Trade_AnimateBallEnteringLinkCable
-	tradefunc Trade_AnimLeftToRight
-	tradefunc Trade_Delay100
-	tradefunc Trade_ShowClearedWindow
-	tradefunc PrintTradeWentToText
-	tradefunc PrintTradeForSendsText
-	tradefunc PrintTradeFarewellText
-	tradefunc Trade_AnimRightToLeft
-	tradefunc Trade_ShowClearedWindow
-	tradefunc Trade_DrawOpenEndOfLinkCable
-	tradefunc Trade_ShowEnemyMon
-	tradefunc Trade_Delay100
-	tradefunc Trade_Cleanup
-	db $FF
-
-ExternalClockTradeFuncSequence:
-	tradefunc LoadTradingGFXAndMonNames
-	tradefunc Trade_ShowClearedWindow
-	tradefunc PrintTradeWillTradeText
-	tradefunc PrintTradeFarewellText
-	tradefunc Trade_SwapNames
-	tradefunc Trade_AnimLeftToRight
-	tradefunc Trade_SwapNames
-	tradefunc Trade_ShowClearedWindow
-	tradefunc Trade_DrawOpenEndOfLinkCable
-	tradefunc Trade_ShowEnemyMon
-	tradefunc Trade_SlideTextBoxOffScreen
-	tradefunc Trade_ShowPlayerMon
-	tradefunc Trade_DrawOpenEndOfLinkCable
-	tradefunc Trade_AnimateBallEnteringLinkCable
-	tradefunc Trade_SwapNames
-	tradefunc Trade_AnimRightToLeft
-	tradefunc Trade_SwapNames
-	tradefunc Trade_Delay100
-	tradefunc Trade_ShowClearedWindow
-	tradefunc PrintTradeWentToText
-	tradefunc Trade_Cleanup
-	db $FF
-
-TradeFuncPointerTable:
-	addtradefunc LoadTradingGFXAndMonNames
-	addtradefunc Trade_ShowPlayerMon
-	addtradefunc Trade_DrawOpenEndOfLinkCable
-	addtradefunc Trade_AnimateBallEnteringLinkCable
-	addtradefunc Trade_ShowEnemyMon
-	addtradefunc Trade_AnimLeftToRight
-	addtradefunc Trade_AnimRightToLeft
-	addtradefunc Trade_Delay100
-	addtradefunc Trade_ShowClearedWindow
-	addtradefunc PrintTradeWentToText
-	addtradefunc PrintTradeForSendsText
-	addtradefunc PrintTradeFarewellText
-	addtradefunc PrintTradeTakeCareText
-	addtradefunc PrintTradeWillTradeText
-	addtradefunc Trade_Cleanup
-	addtradefunc Trade_SlideTextBoxOffScreen
-	addtradefunc Trade_SwapNames
-
-Trade_Delay100:
-	ld c, 100
-	jp DelayFrames
-
-Trade_CopyTileMapToVRAM:
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Delay3
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ret
-
-Trade_Delay80:
-	ld c, 80
-	jp DelayFrames
-
-Trade_ClearTileMap:
-	coord hl, 0, 0
-	ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
-	ld a, " "
-	jp FillMemory
-
-LoadTradingGFXAndMonNames:
-	call Trade_ClearTileMap
-	call DisableLCD
-	ld hl, TradingAnimationGraphics
-	ld de, vChars2 + $310
-	ld bc, TradingAnimationGraphicsEnd - TradingAnimationGraphics
-	ld a, BANK(TradingAnimationGraphics)
-	call FarCopyData2
-	ld hl, TradingAnimationGraphics2
-	ld de, vSprites + $7c0
-	ld bc, TradingAnimationGraphics2End - TradingAnimationGraphics2
-	ld a, BANK(TradingAnimationGraphics2)
-	call FarCopyData2
-	ld hl, vBGMap0
-	ld bc, $800
-	ld a, " "
-	call FillMemory
-	call ClearSprites
-	ld a, $ff
-	ld [wUpdateSpritesEnabled], a
-	ld hl, wd730
-	set 6, [hl] ; turn on instant text printing
-	ld a, [wOnSGB]
-	and a
-	ld a, $e4 ; non-SGB OBP0
-	jr z, .next
-	ld a, $f0 ; SGB OBP0
-.next
-	ld [rOBP0], a
-	call EnableLCD
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, [wTradedPlayerMonSpecies]
-	ld [wd11e], a
-	call GetMonName
-	ld hl, wcd6d
-	ld de, wcf4b
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld a, [wTradedEnemyMonSpecies]
-	ld [wd11e], a
-	jp GetMonName
-
-Trade_LoadMonPartySpriteGfx:
-	ld a, %11010000
-	ld [rOBP1], a
-	jpba LoadMonPartySpriteGfx
-
-Trade_SwapNames:
-	ld hl, wPlayerName
-	ld de, wBuffer
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, wLinkEnemyTrainerName
-	ld de, wPlayerName
-	ld bc, NAME_LENGTH
-	call CopyData
-	ld hl, wBuffer
-	ld de, wLinkEnemyTrainerName
-	ld bc, NAME_LENGTH
-	jp CopyData
-
-Trade_Cleanup:
-	xor a
-	call LoadGBPal
-	ld hl, wd730
-	res 6, [hl] ; turn off instant text printing
-	ret
-
-Trade_ShowPlayerMon:
-	ld a, %10101011
-	ld [rLCDC], a
-	ld a, $50
-	ld [hWY], a
-	ld a, $86
-	ld [rWX], a
-	ld [hSCX], a
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	coord hl, 4, 0
-	ld b, 6
-	ld c, 10
-	call TextBoxBorder
-	call Trade_PrintPlayerMonInfoText
-	ld b, vBGMap0 / $100
-	call CopyScreenTileBufferToVRAM
-	call ClearScreen
-	ld a, [wTradedPlayerMonSpecies]
-	call Trade_LoadMonSprite
-	ld a, $7e
-.slideScreenLoop
-	push af
-	call DelayFrame
-	pop af
-	ld [rWX], a
-	ld [hSCX], a
-	dec a
-	dec a
-	and a
-	jr nz, .slideScreenLoop
-	call Trade_Delay80
-	ld a, TRADE_BALL_POOF_ANIM
-	call Trade_ShowAnimation
-	ld a, TRADE_BALL_DROP_ANIM
-	call Trade_ShowAnimation ; clears mon pic
-	ld a, [wTradedPlayerMonSpecies]
-	call PlayCry
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ret
-
-Trade_DrawOpenEndOfLinkCable:
-	call Trade_ClearTileMap
-	ld b, vBGMap0 / $100
-	call CopyScreenTileBufferToVRAM
-	ld b, SET_PAL_GENERIC
-	call RunPaletteCommand
-
-; This function call is pointless. It just copies blank tiles to VRAM that was
-; already filled with blank tiles.
-	ld hl, vBGMap1 + $8c
-	call Trade_CopyCableTilesOffScreen
-
-	ld a, $a0
-	ld [hSCX], a
-	call DelayFrame
-	ld a, %10001011
-	ld [rLCDC], a
-	coord hl, 6, 2
-	ld b, $7 ; open end of link cable tile ID list index
-	call CopyTileIDsFromList_ZeroBaseTileID
-	call Trade_CopyTileMapToVRAM
-	ld a, SFX_HEAL_HP
-	call PlaySound
-	ld c, 20
-.loop
-	ld a, [hSCX]
-	add 4
-	ld [hSCX], a
-	dec c
-	jr nz, .loop
-	ret
-
-Trade_AnimateBallEnteringLinkCable:
-	ld a, TRADE_BALL_SHAKE_ANIM
-	call Trade_ShowAnimation
-	ld c, 10
-	call DelayFrames
-	ld a, %11100100
-	ld [rOBP0], a
-	xor a
-	ld [wLinkCableAnimBulgeToggle], a
-	lb bc, $20, $60
-.moveBallInsideLinkCableLoop
-	push bc
-	xor a
-	ld de, Trade_BallInsideLinkCableOAM
-	call WriteOAMBlock
-	ld a, [wLinkCableAnimBulgeToggle]
-	xor $1
-	ld [wLinkCableAnimBulgeToggle], a
-	add $7e
-	ld hl, wOAMBuffer + $02
-	ld de, 4
-	ld c, e
-.cycleLinkCableBulgeTile
-	ld [hl], a
-	add hl, de
-	dec c
-	jr nz, .cycleLinkCableBulgeTile
-	call Delay3
-	pop bc
-	ld a, c
-	add $4
-	ld c, a
-	cp $a0
-	jr nc, .ballSpriteReachedEdgeOfScreen
-	ld a, SFX_TINK
-	call PlaySound
-	jr .moveBallInsideLinkCableLoop
-.ballSpriteReachedEdgeOfScreen
-	call ClearSprites
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call ClearScreen
-	ld b, $98
-	call CopyScreenTileBufferToVRAM
-	call Delay3
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ret
-
-Trade_BallInsideLinkCableOAM:
-	db $7E,$00,$7E,$20
-	db $7E,$40,$7E,$60
-
-Trade_ShowEnemyMon:
-	ld a, TRADE_BALL_TILT_ANIM
-	call Trade_ShowAnimation
-	call Trade_ShowClearedWindow
-	coord hl, 4, 10
-	ld b, 6
-	ld c, 10
-	call TextBoxBorder
-	call Trade_PrintEnemyMonInfoText
-	call Trade_CopyTileMapToVRAM
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, [wTradedEnemyMonSpecies]
-	call Trade_LoadMonSprite
-	ld a, TRADE_BALL_POOF_ANIM
-	call Trade_ShowAnimation
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	ld a, [wTradedEnemyMonSpecies]
-	call PlayCry
-	call Trade_Delay100
-	coord hl, 4, 10
-	lb bc, 8, 12
-	call ClearScreenArea
-	jp PrintTradeTakeCareText
-
-Trade_AnimLeftToRight:
-; Animates the mon moving from the left GB to the right one.
-	call Trade_InitGameboyTransferGfx
-	ld a, $1
-	ld [wTradedMonMovingRight], a
-	ld a, %11100100
-	ld [rOBP0], a
-	ld a, $54
-	ld [wBaseCoordX], a
-	ld a, $1c
-	ld [wBaseCoordY], a
-	ld a, [wLeftGBMonSpecies]
-	ld [wMonPartySpriteSpecies], a
-	call Trade_WriteCircledMonOAM
-	call Trade_DrawLeftGameboy
-	call Trade_CopyTileMapToVRAM
-	call Trade_DrawCableAcrossScreen
-	ld hl, vBGMap1 + $8c
-	call Trade_CopyCableTilesOffScreen
-	ld b, $6
-	call Trade_AnimMonMoveHorizontal
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Trade_DrawCableAcrossScreen
-	ld b, $4
-	call Trade_AnimMonMoveHorizontal
-	call Trade_DrawRightGameboy
-	ld b, $6
-	call Trade_AnimMonMoveHorizontal
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Trade_AnimMonMoveVertical
-	jp ClearSprites
-
-Trade_AnimRightToLeft:
-; Animates the mon moving from the right GB to the left one.
-	call Trade_InitGameboyTransferGfx
-	xor a
-	ld [wTradedMonMovingRight], a
-	ld a, $64
-	ld [wBaseCoordX], a
-	ld a, $44
-	ld [wBaseCoordY], a
-	ld a, [wRightGBMonSpecies]
-	ld [wMonPartySpriteSpecies], a
-	call Trade_WriteCircledMonOAM
-	call Trade_DrawRightGameboy
-	call Trade_CopyTileMapToVRAM
-	call Trade_DrawCableAcrossScreen
-	ld hl, vBGMap1 + $94
-	call Trade_CopyCableTilesOffScreen
-	call Trade_AnimMonMoveVertical
-	ld b, $6
-	call Trade_AnimMonMoveHorizontal
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Trade_DrawCableAcrossScreen
-	ld b, $4
-	call Trade_AnimMonMoveHorizontal
-	call Trade_DrawLeftGameboy
-	ld b, $6
-	call Trade_AnimMonMoveHorizontal
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	jp ClearSprites
-
-Trade_InitGameboyTransferGfx:
-; Initialises the graphics for showing a mon moving between gameboys.
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call ClearScreen
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call Trade_LoadMonPartySpriteGfx
-	call DelayFrame
-	ld a, %10101011
-	ld [rLCDC], a
-	xor a
-	ld [hSCX], a
-	ld a, $90
-	ld [hWY], a
-	ret
-
-Trade_DrawLeftGameboy:
-	call Trade_ClearTileMap
-
-; draw link cable
-	coord hl, 11, 4
-	ld a, $5d
-	ld [hli], a
-	ld a, $5e
-	ld c, 8
-.loop
-	ld [hli], a
-	dec c
-	jr nz, .loop
-
-; draw gameboy pic
-	coord hl, 5, 3
-	ld b, $6
-	call CopyTileIDsFromList_ZeroBaseTileID
-
-; draw text box with player name below gameboy pic
-	coord hl, 4, 12
-	ld b, 2
-	ld c, 7
-	call TextBoxBorder
-	coord hl, 5, 14
-	ld de, wPlayerName
-	call PlaceString
-
-	jp DelayFrame
-
-Trade_DrawRightGameboy:
-	call Trade_ClearTileMap
-
-; draw horizontal segment of link cable
-	coord hl, 0, 4
-	ld a, $5e
-	ld c, $e
-.loop
-	ld [hli], a
-	dec c
-	jr nz, .loop
-
-; draw vertical segment of link cable
-	ld a, $5f
-	ld [hl], a
-	ld de, SCREEN_WIDTH
-	add hl, de
-	ld a, $61
-	ld [hl], a
-	add hl, de
-	ld [hl], a
-	add hl, de
-	ld [hl], a
-	add hl, de
-	ld [hl], a
-	add hl, de
-	ld a, $60
-	ld [hld], a
-	ld a, $5d
-	ld [hl], a
-
-; draw gameboy pic
-	coord hl, 7, 8
-	ld b, $6
-	call CopyTileIDsFromList_ZeroBaseTileID
-
-; draw text box with enemy name above link cable
-	coord hl, 6, 0
-	ld b, 2
-	ld c, 7
-	call TextBoxBorder
-	coord hl, 7, 2
-	ld de, wLinkEnemyTrainerName
-	call PlaceString
-
-	jp DelayFrame
-
-Trade_DrawCableAcrossScreen:
-; Draws the link cable across the screen.
-	call Trade_ClearTileMap
-	coord hl, 0, 4
-	ld a, $5e
-	ld c, SCREEN_WIDTH
-.loop
-	ld [hli], a
-	dec c
-	jr nz, .loop
-	ret
-
-Trade_CopyCableTilesOffScreen:
-; This is used to copy the link cable tiles off screen so that the cable
-; continues when the screen is scrolled.
-	push hl
-	coord hl, 0, 4
-	call CopyToRedrawRowOrColumnSrcTiles
-	pop hl
-	ld a, h
-	ld [hRedrawRowOrColumnDest + 1], a
-	ld a, l
-	ld [hRedrawRowOrColumnDest], a
-	ld a, REDRAW_ROW
-	ld [hRedrawRowOrColumnMode], a
-	ld c, 10
-	jp DelayFrames
-
-Trade_AnimMonMoveHorizontal:
-; Animates the mon going through the link cable horizontally over a distance of
-; b 16-pixel units.
-	ld a, [wTradedMonMovingRight]
-	ld e, a
-	ld d, $8
-.scrollLoop
-	ld a, e
-	dec a
-	jr z, .movingRight
-; moving left
-	ld a, [hSCX]
-	sub $2
-	jr .next
-.movingRight
-	ld a, [hSCX]
-	add $2
-.next
-	ld [hSCX], a
-	call DelayFrame
-	dec d
-	jr nz, .scrollLoop
-	call Trade_AnimCircledMon
-	dec b
-	jr nz, Trade_AnimMonMoveHorizontal
-	ret
-
-Trade_AnimCircledMon:
-; Cycles between the two animation frames of the mon party sprite, cycles
-; between a circle and an oval around the mon sprite, and makes the cable flash.
-	push de
-	push bc
-	push hl
-	ld a, [rBGP]
-	xor $3c ; make link cable flash
-	ld [rBGP], a
-	ld hl, wOAMBuffer + $02
-	ld de, $4
-	ld c, $14
-.loop
-	ld a, [hl]
-	xor $40
-	ld [hl], a
-	add hl, de
-	dec c
-	jr nz, .loop
-	pop hl
-	pop bc
-	pop de
-	ret
-
-Trade_WriteCircledMonOAM:
-	callba WriteMonPartySpriteOAMBySpecies
-	call Trade_WriteCircleOAM
-
-Trade_AddOffsetsToOAMCoords:
-	ld hl, wOAMBuffer
-	ld c, $14
-.loop
-	ld a, [wBaseCoordY]
-	add [hl]
-	ld [hli], a
-	ld a, [wBaseCoordX]
-	add [hl]
-	ld [hli], a
-	inc hl
-	inc hl
-	dec c
-	jr nz, .loop
-	ret
-
-Trade_AnimMonMoveVertical:
-; Animates the mon going through the link cable vertically as well as
-; horizontally for a bit. The last bit of horizontal movement (when moving
-; right) or the first bit of horizontal movement (when moving left) are done
-; here instead of Trade_AnimMonMoveHorizontal because this function moves the
-; sprite itself rather than scrolling the screen around the sprite. Moving the
-; sprite itself is necessary because the vertical segment of the link cable is
-; to the right of the screen position that the mon sprite has when
-; Trade_AnimMonMoveHorizontal is executing.
-	ld a, [wTradedMonMovingRight]
-	and a
-	jr z, .movingLeft
-; moving right
-	lb bc, 4, 0 ; move right
-	call .doAnim
-	lb bc, 0, 10 ; move down
-	jr .doAnim
-.movingLeft
-	lb bc, 0, -10 ; move up
-	call .doAnim
-	lb bc, -4, 0 ; move left
-.doAnim
-	ld a, b
-	ld [wBaseCoordX], a
-	ld a, c
-	ld [wBaseCoordY], a
-	ld d, $4
-.loop
-	call Trade_AddOffsetsToOAMCoords
-	call Trade_AnimCircledMon
-	ld c, 8
-	call DelayFrames
-	dec d
-	jr nz, .loop
-	ret
-
-Trade_WriteCircleOAM:
-; Writes the OAM blocks for the circle around the traded mon as it passes
-; the link cable.
-	ld hl, Trade_CircleOAMPointers
-	ld c, $4
-	xor a
-.loop
-	push bc
-	ld e, [hl]
-	inc hl
-	ld d, [hl]
-	inc hl
-	ld c, [hl]
-	inc hl
-	ld b, [hl]
-	inc hl
-	push hl
-	inc a
-	push af
-	call WriteOAMBlock
-	pop af
-	pop hl
-	pop bc
-	dec c
-	jr nz, .loop
-	ret
-
-Trade_CircleOAMPointers:
-	dw Trade_CircleOAM0
-	db $08,$08
-	dw Trade_CircleOAM1
-	db $18,$08
-	dw Trade_CircleOAM2
-	db $08,$18
-	dw Trade_CircleOAM3
-	db $18,$18
-
-Trade_CircleOAM0:
-	db $38,$10,$39,$10
-	db $3A,$10,$3B,$10
-
-Trade_CircleOAM1:
-	db $39,$30,$38,$30
-	db $3B,$30,$3A,$30
-
-Trade_CircleOAM2:
-	db $3A,$50,$3B,$50
-	db $38,$50,$39,$50
-
-Trade_CircleOAM3:
-	db $3B,$70,$3A,$70
-	db $39,$70,$38,$70
-
-; a = species
-Trade_LoadMonSprite:
-	ld [wcf91], a
-	ld [wd0b5], a
-	ld [wWholeScreenPaletteMonSpecies], a
-	ld b, SET_PAL_POKEMON_WHOLE_SCREEN
-	ld c, 0
-	call RunPaletteCommand
-	ld a, [H_AUTOBGTRANSFERENABLED]
-	xor $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call GetMonHeader
-	coord hl, 7, 2
-	call LoadFlippedFrontSpriteByMonIndex
-	ld c, 10
-	jp DelayFrames
-
-Trade_ShowClearedWindow:
-; clears the window and covers the BG entirely with the window
-	ld a, $1
-	ld [H_AUTOBGTRANSFERENABLED], a
-	call ClearScreen
-	ld a, %11100011
-	ld [rLCDC], a
-	ld a, $7
-	ld [rWX], a
-	xor a
-	ld [hWY], a
-	ld a, $90
-	ld [hSCX], a
-	ret
-
-Trade_SlideTextBoxOffScreen:
-; Slides the window right until it's off screen. The window usually just has
-; a text box at the bottom when this is called. However, when this is called
-; after Trade_ShowEnemyMon in the external clock sequence, there is a mon pic
-; above the text box and it is also scrolled off the screen.
-	ld c, 50
-	call DelayFrames
-.loop
-	call DelayFrame
-	ld a, [rWX]
-	inc a
-	inc a
-	ld [rWX], a
-	cp $a1
-	jr nz, .loop
-	call Trade_ClearTileMap
-	ld c, 10
-	call DelayFrames
-	ld a, $7
-	ld [rWX], a
-	ret
-
-PrintTradeWentToText:
-	ld hl, TradeWentToText
-	call PrintText
-	ld c, 200
-	call DelayFrames
-	jp Trade_SlideTextBoxOffScreen
-
-TradeWentToText:
-	TX_FAR _TradeWentToText
-	db "@"
-
-PrintTradeForSendsText:
-	ld hl, TradeForText
-	call PrintText
-	call Trade_Delay80
-	ld hl, TradeSendsText
-	call PrintText
-	jp Trade_Delay80
-
-TradeForText:
-	TX_FAR _TradeForText
-	db "@"
-
-TradeSendsText:
-	TX_FAR _TradeSendsText
-	db "@"
-
-PrintTradeFarewellText:
-	ld hl, TradeWavesFarewellText
-	call PrintText
-	call Trade_Delay80
-	ld hl, TradeTransferredText
-	call PrintText
-	call Trade_Delay80
-	jp Trade_SlideTextBoxOffScreen
-
-TradeWavesFarewellText:
-	TX_FAR _TradeWavesFarewellText
-	db "@"
-
-TradeTransferredText:
-	TX_FAR _TradeTransferredText
-	db "@"
-
-PrintTradeTakeCareText:
-	ld hl, TradeTakeCareText
-	call PrintText
-	jp Trade_Delay80
-
-TradeTakeCareText:
-	TX_FAR _TradeTakeCareText
-	db "@"
-
-PrintTradeWillTradeText:
-	ld hl, TradeWillTradeText
-	call PrintText
-	call Trade_Delay80
-	ld hl, TradeforText
-	call PrintText
-	jp Trade_Delay80
-
-TradeWillTradeText:
-	TX_FAR _TradeWillTradeText
-	db "@"
-
-TradeforText:
-	TX_FAR _TradeforText
-	db "@"
-
-Trade_ShowAnimation:
-	ld [wAnimationID], a
-	xor a
-	ld [wAnimationType], a
-	predef_jump MoveAnimation
--- a/engine/trade2.asm
+++ /dev/null
@@ -1,48 +1,0 @@
-Trade_PrintPlayerMonInfoText:
-	coord hl, 5, 0
-	ld de, Trade_MonInfoText
-	call PlaceString
-	ld a, [wTradedPlayerMonSpecies]
-	ld [wd11e], a
-	predef IndexToPokedex
-	coord hl, 9, 0
-	ld de, wd11e
-	lb bc, LEADING_ZEROES | 1, 3
-	call PrintNumber
-	coord hl, 5, 2
-	ld de, wcf4b
-	call PlaceString
-	coord hl, 8, 4
-	ld de, wTradedPlayerMonOT
-	call PlaceString
-	coord hl, 8, 6
-	ld de, wTradedPlayerMonOTID
-	lb bc, LEADING_ZEROES | 2, 5
-	jp PrintNumber
-
-Trade_PrintEnemyMonInfoText:
-	coord hl, 5, 10
-	ld de, Trade_MonInfoText
-	call PlaceString
-	ld a, [wTradedEnemyMonSpecies]
-	ld [wd11e], a
-	predef IndexToPokedex
-	coord hl, 9, 10
-	ld de, wd11e
-	lb bc, LEADING_ZEROES | 1, 3
-	call PrintNumber
-	coord hl, 5, 12
-	ld de, wcd6d
-	call PlaceString
-	coord hl, 8, 14
-	ld de, wTradedEnemyMonOT
-	call PlaceString
-	coord hl, 8, 16
-	ld de, wTradedEnemyMonOTID
-	lb bc, LEADING_ZEROES | 2, 5
-	jp PrintNumber
-
-Trade_MonInfoText:
-	db "──№⠄",$4E
-	next "OT/"
-	next $73,"№⠄","@"
--- a/engine/turn_sprite.asm
+++ /dev/null
@@ -1,25 +1,0 @@
-UpdateSpriteFacingOffsetAndDelayMovement::
-	ld h, $c2
-	ld a, [H_CURRENTSPRITEOFFSET]
-	add $8
-	ld l, a
-	ld a, $7f ; maximum movement delay
-	ld [hl], a ; c2x8 (movement delay)
-	dec h
-	ld a, [H_CURRENTSPRITEOFFSET]
-	add $9
-	ld l, a
-	ld a, [hld] ; c1x9 (facing direction)
-	ld b, a
-	xor a
-	ld [hld], a
-	ld [hl], a ; c1x8 (walk animation frame)
-	ld a, [H_CURRENTSPRITEOFFSET]
-	add $2
-	ld l, a
-	ld a, [hl] ; c1x2 (facing and animation table offset)
-	or b ; or in the facing direction
-	ld [hld], a
-	ld a, $2 ; delayed movement status
-	ld [hl], a ; c1x1 (movement status)
-	ret
--- a/home.asm
+++ b/home.asm
@@ -1282,7 +1282,7 @@
 	TX_FAR _RepelWoreOffText
 	db "@"
 
-INCLUDE "engine/menu/start_menu.asm"
+INCLUDE "home/start_menu.asm"
 
 ; function to count how many bits are set in a string of bytes
 ; INPUT:
--- /dev/null
+++ b/home/start_menu.asm
@@ -1,0 +1,85 @@
+DisplayStartMenu::
+	ld a, BANK(StartMenu_Pokedex)
+	ld [H_LOADEDROMBANK], a
+	ld [MBC1RomBank], a
+	ld a, [wWalkBikeSurfState] ; walking/biking/surfing
+	ld [wWalkBikeSurfStateCopy], a
+	ld a, SFX_START_MENU
+	call PlaySound
+
+RedisplayStartMenu::
+	callba DrawStartMenu
+	callba PrintSafariZoneSteps ; print Safari Zone info, if in Safari Zone
+	call UpdateSprites
+.loop
+	call HandleMenuInput
+	ld b, a
+.checkIfUpPressed
+	bit 6, a ; was Up pressed?
+	jr z, .checkIfDownPressed
+	ld a, [wCurrentMenuItem] ; menu selection
+	and a
+	jr nz, .loop
+	ld a, [wLastMenuItem]
+	and a
+	jr nz, .loop
+; if the player pressed tried to go past the top item, wrap around to the bottom
+	CheckEvent EVENT_GOT_POKEDEX
+	ld a, 6 ; there are 7 menu items with the pokedex, so the max index is 6
+	jr nz, .wrapMenuItemId
+	dec a ; there are only 6 menu items without the pokedex
+.wrapMenuItemId
+	ld [wCurrentMenuItem], a
+	call EraseMenuCursor
+	jr .loop
+.checkIfDownPressed
+	bit 7, a
+	jr z, .buttonPressed
+; if the player pressed tried to go past the bottom item, wrap around to the top
+	CheckEvent EVENT_GOT_POKEDEX
+	ld a, [wCurrentMenuItem]
+	ld c, 7 ; there are 7 menu items with the pokedex
+	jr nz, .checkIfPastBottom
+	dec c ; there are only 6 menu items without the pokedex
+.checkIfPastBottom
+	cp c
+	jr nz, .loop
+; the player went past the bottom, so wrap to the top
+	xor a
+	ld [wCurrentMenuItem], a
+	call EraseMenuCursor
+	jr .loop
+.buttonPressed ; A, B, or Start button pressed
+	call PlaceUnfilledArrowMenuCursor
+	ld a, [wCurrentMenuItem]
+	ld [wBattleAndStartSavedMenuItem], a ; save current menu selection
+	ld a, b
+	and %00001010 ; was the Start button or B button pressed?
+	jp nz, CloseStartMenu
+	call SaveScreenTilesToBuffer2 ; copy background from wTileMap to wTileMapBackup2
+	CheckEvent EVENT_GOT_POKEDEX
+	ld a, [wCurrentMenuItem]
+	jr nz, .displayMenuItem
+	inc a ; adjust position to account for missing pokedex menu item
+.displayMenuItem
+	cp 0
+	jp z, StartMenu_Pokedex
+	cp 1
+	jp z, StartMenu_Pokemon
+	cp 2
+	jp z, StartMenu_Item
+	cp 3
+	jp z, StartMenu_TrainerInfo
+	cp 4
+	jp z, StartMenu_SaveReset
+	cp 5
+	jp z, StartMenu_Option
+
+; EXIT falls through to here
+CloseStartMenu::
+	call Joypad
+	ld a, [hJoyPressed]
+	bit 0, a ; was A button newly pressed?
+	jr nz, CloseStartMenu
+	call LoadTextBoxTilePatterns
+	jp CloseTextDisplay
--- a/layout.link
+++ b/layout.link
@@ -94,6 +94,8 @@
 	"Sound Effect Headers 2"
 	"Music Headers 2"
 	"Sound Effects 2"
+	"Low Health Alarm (Audio Engine 2)"
+	"Bill's PC"
 	"Audio Engine 2"
 	"Music 2"
 
--- a/main.asm
+++ b/main.asm
@@ -4,7 +4,7 @@
 SECTION "bank1", ROMX
 
 INCLUDE "data/facing.asm"
-INCLUDE "engine/black_out.asm"
+INCLUDE "engine/events/black_out.asm"
 
 MewPicFront:: INCBIN "gfx/pokemon/front/mew.pic"
 MewPicBack::  INCBIN "gfx/pokemon/back/mewb.pic"
@@ -11,39 +11,39 @@
 INCLUDE "data/baseStats/mew.asm"
 
 INCLUDE "engine/battle/safari_zone.asm"
-INCLUDE "engine/titlescreen.asm"
-INCLUDE "engine/load_mon_data.asm"
+INCLUDE "engine/movie/titlescreen.asm"
+INCLUDE "engine/pokemon/load_mon_data.asm"
 INCLUDE "data/item_prices.asm"
 INCLUDE "text/item_names.asm"
 INCLUDE "text/unused_names.asm"
-INCLUDE "engine/overworld/oam.asm"
-INCLUDE "engine/oam_dma.asm"
-INCLUDE "engine/print_waiting_text.asm"
-INCLUDE "engine/overworld/map_sprite_functions1.asm"
-INCLUDE "engine/test_battle.asm"
-INCLUDE "engine/overworld/item.asm"
+INCLUDE "engine/gfx/sprite_oam.asm"
+INCLUDE "engine/gfx/oam_dma.asm"
+INCLUDE "engine/link/print_waiting_text.asm"
+INCLUDE "engine/overworld/sprite_collisions.asm"
+INCLUDE "engine/debug/test_battle.asm"
+INCLUDE "engine/events/pick_up_item.asm"
 INCLUDE "engine/overworld/movement.asm"
-INCLUDE "engine/cable_club.asm"
-INCLUDE "engine/menu/main_menu.asm"
-INCLUDE "engine/oak_speech.asm"
-INCLUDE "engine/special_warps.asm"
-INCLUDE "engine/debug1.asm"
-INCLUDE "engine/menu/naming_screen.asm"
-INCLUDE "engine/oak_speech2.asm"
-INCLUDE "engine/subtract_paid_money.asm"
-INCLUDE "engine/menu/swap_items.asm"
-INCLUDE "engine/overworld/pokemart.asm"
-INCLUDE "engine/learn_move.asm"
-INCLUDE "engine/overworld/pokecenter.asm"
-INCLUDE "engine/overworld/set_blackout_map.asm"
-INCLUDE "engine/display_text_id_init.asm"
-INCLUDE "engine/menu/draw_start_menu.asm"
-INCLUDE "engine/overworld/cable_club_npc.asm"
-INCLUDE "engine/menu/text_box.asm"
-INCLUDE "engine/battle/moveEffects/drain_hp_effect.asm"
-INCLUDE "engine/menu/players_pc.asm"
-INCLUDE "engine/remove_pokemon.asm"
-INCLUDE "engine/display_pokedex.asm"
+INCLUDE "engine/link/cable_club.asm"
+INCLUDE "engine/menus/main_menu.asm"
+INCLUDE "engine/movie/oak_speech/oak_speech.asm"
+INCLUDE "engine/overworld/special_warps.asm"
+INCLUDE "engine/debug/debug_party.asm"
+INCLUDE "engine/menus/naming_screen.asm"
+INCLUDE "engine/movie/oak_speech/oak_speech2.asm"
+INCLUDE "engine/items/subtract_paid_money.asm"
+INCLUDE "engine/menus/swap_items.asm"
+INCLUDE "engine/events/pokemart.asm"
+INCLUDE "engine/pokemon/learn_move.asm"
+INCLUDE "engine/events/pokecenter.asm"
+INCLUDE "engine/events/set_blackout_map.asm"
+INCLUDE "engine/menus/display_text_id_init.asm"
+INCLUDE "engine/menus/draw_start_menu.asm"
+INCLUDE "engine/link/cable_club_npc.asm"
+INCLUDE "engine/menus/text_box.asm"
+INCLUDE "engine/battle/move_effects/drain_hp.asm"
+INCLUDE "engine/menus/players_pc.asm"
+INCLUDE "engine/pokemon/remove_mon.asm"
+INCLUDE "engine/events/display_pokedex.asm"
 
 
 SECTION "bank3", ROMX
@@ -53,28 +53,28 @@
 INCLUDE "data/map_header_banks.asm"
 INCLUDE "engine/overworld/clear_variables.asm"
 INCLUDE "engine/overworld/player_state.asm"
-INCLUDE "engine/overworld/poison.asm"
-INCLUDE "engine/overworld/tileset_header.asm"
+INCLUDE "engine/events/poison.asm"
+INCLUDE "engine/overworld/tilesets.asm"
 INCLUDE "engine/overworld/daycare_exp.asm"
 INCLUDE "data/hide_show_data.asm"
 INCLUDE "engine/overworld/field_move_messages.asm"
 INCLUDE "engine/items/inventory.asm"
 INCLUDE "engine/overworld/wild_mons.asm"
-INCLUDE "engine/items/items.asm"
-INCLUDE "engine/menu/draw_badges.asm"
+INCLUDE "engine/items/item_effects.asm"
+INCLUDE "engine/menus/draw_badges.asm"
 INCLUDE "engine/overworld/update_map.asm"
 INCLUDE "engine/overworld/cut.asm"
 INCLUDE "engine/overworld/missable_objects.asm"
 INCLUDE "engine/overworld/push_boulder.asm"
-INCLUDE "engine/add_mon.asm"
+INCLUDE "engine/pokemon/add_mon.asm"
 INCLUDE "engine/flag_action.asm"
-INCLUDE "engine/heal_party.asm"
-INCLUDE "engine/bcd.asm"
-INCLUDE "engine/init_player_data.asm"
-INCLUDE "engine/get_bag_item_quantity.asm"
-INCLUDE "engine/pathfinding.asm"
-INCLUDE "engine/hp_bar.asm"
-INCLUDE "engine/hidden_object_functions3.asm"
+INCLUDE "engine/events/heal_party.asm"
+INCLUDE "engine/math/bcd.asm"
+INCLUDE "engine/movie/oak_speech/init_player_data.asm"
+INCLUDE "engine/items/get_bag_item_quantity.asm"
+INCLUDE "engine/overworld/pathfinding.asm"
+INCLUDE "engine/gfx/hp_bar.asm"
+INCLUDE "engine/events/hidden_object_functions3.asm"
 
 
 SECTION "Graphics (BANK 4)", ROMX
@@ -106,33 +106,33 @@
 SECTION "Battle (BANK 4)", ROMX
 
 INCLUDE "engine/overworld/is_player_just_outside_map.asm"
-INCLUDE "engine/menu/status_screen.asm"
-INCLUDE "engine/menu/party_menu.asm"
+INCLUDE "engine/pokemon/status_screen.asm"
+INCLUDE "engine/menus/party_menu.asm"
 
 RedPicFront:: INCBIN "gfx/player/red.pic"
 ShrinkPic1::  INCBIN "gfx/player/shrink1.pic"
 ShrinkPic2::  INCBIN "gfx/player/shrink2.pic"
 
-INCLUDE "engine/turn_sprite.asm"
-INCLUDE "engine/menu/start_sub_menus.asm"
+INCLUDE "engine/overworld/turn_sprite.asm"
+INCLUDE "engine/menus/start_sub_menus.asm"
 INCLUDE "engine/items/tms.asm"
 INCLUDE "engine/battle/end_of_battle.asm"
 INCLUDE "engine/battle/wild_encounters.asm"
-INCLUDE "engine/battle/moveEffects/recoil_effect.asm"
-INCLUDE "engine/battle/moveEffects/conversion_effect.asm"
-INCLUDE "engine/battle/moveEffects/haze_effect.asm"
+INCLUDE "engine/battle/move_effects/recoil.asm"
+INCLUDE "engine/battle/move_effects/conversion.asm"
+INCLUDE "engine/battle/move_effects/haze.asm"
 INCLUDE "engine/battle/get_trainer_name.asm"
-INCLUDE "engine/random.asm"
+INCLUDE "engine/math/random.asm"
 
 
 SECTION "Battle (BANK 5)", ROMX
 
-INCLUDE "engine/load_pokedex_tiles.asm"
+INCLUDE "engine/gfx/load_pokedex_tiles.asm"
 INCLUDE "engine/overworld/map_sprites.asm"
 INCLUDE "engine/overworld/emotion_bubbles.asm"
-INCLUDE "engine/evolve_trade.asm"
-INCLUDE "engine/battle/moveEffects/substitute_effect.asm"
-INCLUDE "engine/menu/pc.asm"
+INCLUDE "engine/events/evolve_trade.asm"
+INCLUDE "engine/battle/move_effects/substitute.asm"
+INCLUDE "engine/menus/pc.asm"
 
 
 SECTION "bank6_1", ROMX
@@ -142,7 +142,7 @@
 
 SECTION "bank6_2", ROMX
 
-INCLUDE "engine/overworld/npc_movement.asm"
+INCLUDE "engine/overworld/auto_movement.asm"
 INCLUDE "engine/overworld/doors.asm"
 INCLUDE "engine/overworld/ledges.asm"
 
@@ -150,26 +150,31 @@
 SECTION "bank7_1", ROMX
 
 INCLUDE "text/monster_names.asm"
-INCLUDE "engine/clear_save.asm"
-INCLUDE "engine/predefs7.asm"
+INCLUDE "engine/movie/oak_speech/clear_save.asm"
+INCLUDE "engine/events/elevator.asm"
 
 
 SECTION "bank7_2", ROMX
 
-INCLUDE "engine/menu/oaks_pc.asm"
-INCLUDE "engine/hidden_object_functions7.asm"
+INCLUDE "engine/menus/oaks_pc.asm"
+INCLUDE "engine/events/hidden_object_functions7.asm"
 
 
+SECTION "Bill's PC", ROMX
+
+INCLUDE "engine/pokemon/bills_pc.asm"
+
+
 SECTION "Battle (BANK 9)", ROMX
 
 INCLUDE "engine/battle/print_type.asm"
 INCLUDE "engine/battle/save_trainer_name.asm"
-INCLUDE "engine/battle/moveEffects/focus_energy_effect.asm"
+INCLUDE "engine/battle/move_effects/focus_energy.asm"
 
 
 SECTION "Battle (BANK A)", ROMX
 
-INCLUDE "engine/battle/moveEffects/leech_seed_effect.asm"
+INCLUDE "engine/battle/move_effects/leech_seed.asm"
 
 
 SECTION "Battle (BANK B)", ROMX
@@ -184,24 +189,24 @@
 
 INCLUDE "engine/items/tmhm.asm"
 INCLUDE "engine/battle/scale_sprites.asm"
-INCLUDE "engine/battle/moveEffects/pay_day_effect.asm"
-INCLUDE "engine/game_corner_slots2.asm"
+INCLUDE "engine/battle/move_effects/pay_day.asm"
+INCLUDE "engine/slots/game_corner_slots2.asm"
 
 
 SECTION "Battle (BANK C)", ROMX
 
-INCLUDE "engine/battle/moveEffects/mist_effect.asm"
-INCLUDE "engine/battle/moveEffects/one_hit_ko_effect.asm"
+INCLUDE "engine/battle/move_effects/mist.asm"
+INCLUDE "engine/battle/move_effects/one_hit_ko.asm"
 
 
 SECTION "Battle (BANK D)", ROMX
 
-INCLUDE "engine/titlescreen2.asm"
+INCLUDE "engine/movie/titlescreen2.asm"
 INCLUDE "engine/battle/link_battle_versus_text.asm"
-INCLUDE "engine/slot_machine.asm"
-INCLUDE "engine/overworld/pewter_guys.asm"
-INCLUDE "engine/multiply_divide.asm"
-INCLUDE "engine/game_corner_slots.asm"
+INCLUDE "engine/slots/slot_machine.asm"
+INCLUDE "engine/events/pewter_guys.asm"
+INCLUDE "engine/math/multiply_divide.asm"
+INCLUDE "engine/slots/game_corner_slots.asm"
 
 
 SECTION "bankE", ROMX
@@ -223,10 +228,10 @@
 TradingAnimationGraphics2: INCBIN "gfx/trade/cable_ball.2bpp"
 TradingAnimationGraphics2End:
 
-INCLUDE "engine/evos_moves.asm"
-INCLUDE "engine/battle/moveEffects/heal_effect.asm"
-INCLUDE "engine/battle/moveEffects/transform_effect.asm"
-INCLUDE "engine/battle/moveEffects/reflect_light_screen_effect.asm"
+INCLUDE "engine/pokemon/evos_moves.asm"
+INCLUDE "engine/battle/move_effects/heal.asm"
+INCLUDE "engine/battle/move_effects/transform.asm"
+INCLUDE "engine/battle/move_effects/reflect_light_screen.asm"
 
 
 SECTION "bankF", ROMX
@@ -237,15 +242,15 @@
 
 SECTION "bank10", ROMX
 
-INCLUDE "engine/menu/pokedex.asm"
-INCLUDE "engine/trade.asm"
-INCLUDE "engine/intro.asm"
-INCLUDE "engine/trade2.asm"
+INCLUDE "engine/menus/pokedex.asm"
+INCLUDE "engine/movie/trade.asm"
+INCLUDE "engine/movie/intro.asm"
+INCLUDE "engine/movie/trade2.asm"
 
 
 SECTION "bank11_1", ROMX
 
-INCLUDE "engine/pokedex_rating.asm"
+INCLUDE "engine/events/pokedex_rating.asm"
 
 
 SECTION "bank11_2", ROMX
@@ -255,12 +260,12 @@
 
 SECTION "bank12", ROMX
 
-INCLUDE "engine/predefs12.asm"
+INCLUDE "engine/gfx/screen_effects.asm"
 
 
 SECTION "bank13", ROMX
 
-INCLUDE "engine/give_pokemon.asm"
+INCLUDE "engine/events/give_pokemon.asm"
 INCLUDE "engine/predefs.asm"
 
 
@@ -267,10 +272,10 @@
 SECTION "bank14", ROMX
 
 INCLUDE "engine/battle/init_battle_variables.asm"
-INCLUDE "engine/battle/moveEffects/paralyze_effect.asm"
-INCLUDE "engine/overworld/card_key.asm"
-INCLUDE "engine/menu/prize_menu.asm"
-INCLUDE "engine/hidden_object_functions14.asm"
+INCLUDE "engine/battle/move_effects/paralyze.asm"
+INCLUDE "engine/events/card_key.asm"
+INCLUDE "engine/events/prize_menu.asm"
+INCLUDE "engine/events/hidden_object_functions14.asm"
 
 
 SECTION "bank15_1", ROMX
@@ -280,41 +285,41 @@
 
 SECTION "bank15_2", ROMX
 
-INCLUDE "engine/menu/diploma.asm"
-INCLUDE "engine/overworld/trainers.asm"
+INCLUDE "engine/events/diploma.asm"
+INCLUDE "engine/overworld/trainer_sight.asm"
 
 
 SECTION "bank16_1", ROMX
 
 INCLUDE "engine/battle/common_text.asm"
-INCLUDE "engine/experience.asm"
-INCLUDE "engine/overworld/oaks_aide.asm"
+INCLUDE "engine/pokemon/experience.asm"
+INCLUDE "engine/events/oaks_aide.asm"
 
 
 SECTION "bank16_2", ROMX
 
-INCLUDE "engine/overworld/saffron_guards.asm"
+INCLUDE "engine/events/saffron_guards.asm"
 
 
 SECTION "bank17_1", ROMX
 
-INCLUDE "engine/predefs17.asm"
+INCLUDE "engine/events/starter_dex.asm"
 
 
 SECTION "bank17_2", ROMX
 
-INCLUDE "engine/predefs17_2.asm"
-INCLUDE "engine/hidden_object_functions17.asm"
+INCLUDE "engine/pokemon/set_types.asm"
+INCLUDE "engine/events/hidden_object_functions17.asm"
 
 
 SECTION "bank18_1", ROMX
 
-INCLUDE "engine/overworld/cinnabar_lab.asm"
+INCLUDE "engine/events/cinnabar_lab.asm"
 
 
 SECTION "bank18_2", ROMX
 
-INCLUDE "engine/hidden_object_functions18.asm"
+INCLUDE "engine/events/hidden_object_functions18.asm"
 
 
 SECTION "bank1A", ROMX
@@ -333,35 +338,35 @@
 
 SECTION "bank1C", ROMX
 
-INCLUDE "engine/gamefreak.asm"
-INCLUDE "engine/hall_of_fame.asm"
+INCLUDE "engine/movie/gamefreak.asm"
+INCLUDE "engine/movie/hall_of_fame.asm"
 INCLUDE "engine/overworld/healing_machine.asm"
 INCLUDE "engine/overworld/player_animations.asm"
 INCLUDE "engine/battle/ghost_marowak_anim.asm"
 INCLUDE "engine/battle/battle_transitions.asm"
-INCLUDE "engine/town_map.asm"
-INCLUDE "engine/mon_party_sprites.asm"
-INCLUDE "engine/in_game_trades.asm"
-INCLUDE "engine/palettes.asm"
-INCLUDE "engine/save.asm"
+INCLUDE "engine/items/town_map.asm"
+INCLUDE "engine/gfx/mon_icons.asm"
+INCLUDE "engine/events/in_game_trades.asm"
+INCLUDE "engine/gfx/palettes.asm"
+INCLUDE "engine/menus/save.asm"
 
 
 SECTION "bank1D_1", ROMX
 
-INCLUDE "engine/HoF_room_pc.asm"
-INCLUDE "engine/status_ailments.asm"
+INCLUDE "engine/movie/credits.asm"
+INCLUDE "engine/pokemon/status_ailments.asm"
 INCLUDE "engine/items/itemfinder.asm"
 
 
 SECTION "bank1D_2", ROMX
 
-INCLUDE "engine/menu/vending_machine.asm"
+INCLUDE "engine/events/vending_machine.asm"
 
 
 SECTION "bank1D_3", ROMX
 
-INCLUDE "engine/menu/league_pc.asm"
-INCLUDE "engine/overworld/hidden_items.asm"
+INCLUDE "engine/menus/league_pc.asm"
+INCLUDE "engine/events/hidden_items.asm"
 
 
 SECTION "bank1E", ROMX
@@ -368,7 +373,7 @@
 
 INCLUDE "engine/battle/animations.asm"
 INCLUDE "engine/overworld/cut2.asm"
-INCLUDE "engine/overworld/ssanne.asm"
+INCLUDE "engine/overworld/dust_smoke.asm"
 
 RedFishingTilesFront: INCBIN "gfx/overworld/red_fish_front.2bpp"
 RedFishingTilesBack:  INCBIN "gfx/overworld/red_fish_back.2bpp"
@@ -376,6 +381,6 @@
 RedFishingRodTiles:   INCBIN "gfx/overworld/fishing_rod.2bpp"
 
 INCLUDE "data/animations.asm"
-INCLUDE "engine/evolution.asm"
+INCLUDE "engine/movie/evolution.asm"
 INCLUDE "engine/overworld/elevator.asm"
 INCLUDE "engine/items/tm_prices.asm"