summaryrefslogtreecommitdiff
path: root/engine/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'engine/gfx')
-rw-r--r--engine/gfx/cgb_layouts.asm1033
-rw-r--r--engine/gfx/color.asm1356
-rw-r--r--engine/gfx/crystal_layouts.asm325
-rw-r--r--engine/gfx/dma_transfer.asm626
-rw-r--r--engine/gfx/load_font.asm156
-rw-r--r--engine/gfx/load_overworld_font.asm17
-rw-r--r--engine/gfx/load_pics.asm491
-rw-r--r--engine/gfx/load_push_oam.asm21
-rw-r--r--engine/gfx/mon_icons.asm471
-rw-r--r--engine/gfx/pic_animation.asm1141
-rw-r--r--engine/gfx/place_graphic.asm55
-rw-r--r--engine/gfx/player_gfx.asm224
-rw-r--r--engine/gfx/sgb_layouts.asm605
-rw-r--r--engine/gfx/sprite_anims.asm889
-rw-r--r--engine/gfx/sprites.asm677
-rw-r--r--engine/gfx/trademon_frontpic.asm38
16 files changed, 8125 insertions, 0 deletions
diff --git a/engine/gfx/cgb_layouts.asm b/engine/gfx/cgb_layouts.asm
new file mode 100644
index 000000000..b36dceeb6
--- /dev/null
+++ b/engine/gfx/cgb_layouts.asm
@@ -0,0 +1,1033 @@
+; Replaces the functionality of sgb.asm to work with CGB hardware.
+
+CheckCGB: ; 8d55
+ ld a, [hCGB]
+ and a
+ ret
+; 8d59
+
+LoadSGBLayoutCGB: ; 8d59
+ ld a, b
+ cp SCGB_RAM
+ jr nz, .not_ram
+ ld a, [wSGBPredef]
+.not_ram
+ cp SCGB_PARTY_MENU_HP_PALS
+ jp z, CGB_ApplyPartyMenuHPPals
+ call ResetBGPals
+ ld l, a
+ ld h, 0
+ add hl, hl
+ ld de, .dw
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, .ReturnFromJumpTable
+ push de
+ jp hl
+; 8d79
+
+.ReturnFromJumpTable: ; 8d79
+ ret
+; 8d7a
+
+.dw ; 8d7a
+ dw _CGB_BattleGrayscale
+ dw _CGB_BattleColors
+ dw _CGB_PokegearPals
+ dw _CGB_StatsScreenHPPals
+ dw _CGB_Pokedex
+ dw _CGB_SlotMachine
+ dw _CGB06
+ dw _CGB_GSIntro
+ dw _CGB_Diploma
+ dw _CGB_MapPals
+ dw _CGB_PartyMenu
+ dw _CGB_Evolution
+ dw _CGB_GSTitleScreen
+ dw _CGB0d
+ dw _CGB_MoveList
+ dw _CGB0f
+ dw _CGB_PokedexSearchOption
+ dw _CGB11
+ dw _CGB_Pokepic
+ dw _CGB13
+ dw _CGB_PackPals
+ dw _CGB_TrainerCard
+ dw _CGB_PokedexUnownMode
+ dw _CGB_BillsPC
+ dw _CGB_UnownPuzzle
+ dw _CGB_GamefreakLogo
+ dw _CGB_PlayerOrMonFrontpicPals
+ dw _CGB_TradeTube
+ dw _CGB_TrainerOrMonFrontpicPals
+ dw _CGB_MysteryGift
+ dw _CGB1e
+; 8db8
+
+_CGB_BattleGrayscale: ; 8db8
+ ld hl, PalPacket_BattleGrayscale + 1
+ ld de, wBGPals1
+ ld c, 4
+ call CopyPalettes
+ ld hl, PalPacket_BattleGrayscale + 1
+ ld de, wBGPals1 palette PAL_BATTLE_BG_EXP
+ ld c, 4
+ call CopyPalettes
+ ld hl, PalPacket_BattleGrayscale + 1
+ ld de, wOBPals1
+ ld c, 2
+ call CopyPalettes
+ jr _CGB_FinishBattleScreenLayout
+
+_CGB_BattleColors: ; 8ddb
+ ld de, wBGPals1
+ call GetBattlemonBackpicPalettePointer
+ push hl
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_BG_PLAYER
+ call GetEnemyFrontpicPalettePointer
+ push hl
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_BG_ENEMY
+ ld a, [wEnemyHPPal]
+ ld l, a
+ ld h, $0
+ add hl, hl
+ add hl, hl
+ ld bc, HPBarPals
+ add hl, bc
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_BG_ENEMY_HP
+ ld a, [wPlayerHPPal]
+ ld l, a
+ ld h, $0
+ add hl, hl
+ add hl, hl
+ ld bc, HPBarPals
+ add hl, bc
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_BG_PLAYER_HP
+ ld hl, ExpBarPalette
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_BG_EXP
+ ld de, wOBPals1
+ pop hl
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_OB_ENEMY
+ pop hl
+ call LoadPalette_White_Col1_Col2_Black ; PAL_BATTLE_OB_PLAYER
+ ld a, SCGB_BATTLE_COLORS
+ ld [wSGBPredef], a
+ call ApplyPals
+_CGB_FinishBattleScreenLayout: ; 8e23
+ call InitPartyMenuBGPal7
+ hlcoord 0, 0, wAttrMap
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ ld a, PAL_BATTLE_BG_ENEMY_HP
+ call ByteFill
+ hlcoord 0, 4, wAttrMap
+ lb bc, 8, 10
+ ld a, PAL_BATTLE_BG_PLAYER
+ call FillBoxCGB
+ hlcoord 10, 0, wAttrMap
+ lb bc, 7, 10
+ ld a, PAL_BATTLE_BG_ENEMY
+ call FillBoxCGB
+ hlcoord 0, 0, wAttrMap
+ lb bc, 4, 10
+ ld a, PAL_BATTLE_BG_ENEMY_HP
+ call FillBoxCGB
+ hlcoord 10, 7, wAttrMap
+ lb bc, 5, 10
+ ld a, PAL_BATTLE_BG_PLAYER_HP
+ call FillBoxCGB
+ hlcoord 10, 11, wAttrMap
+ lb bc, 1, 9
+ ld a, PAL_BATTLE_BG_EXP
+ call FillBoxCGB
+ hlcoord 0, 12, wAttrMap
+ ld bc, 6 * SCREEN_WIDTH
+ ld a, PAL_BATTLE_BG_TEXT
+ call ByteFill
+ ld hl, BattleObjectPals
+ ld de, wOBPals1 palette PAL_BATTLE_OB_GRAY
+ ld bc, 6 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ call ApplyAttrMap
+ ret
+; 8e85
+
+
+InitPartyMenuBGPal7: ; 8e85
+ farcall Function100dc0
+Mobile_InitPartyMenuBGPal7: ; 8e8b
+ ld hl, PartyMenuBGPalette
+ jr nc, .not_mobile
+ ld hl, PartyMenuBGMobilePalette
+.not_mobile
+ ld de, wBGPals1 palette 7
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+; 8e9f
+
+InitPartyMenuBGPal0: ; 8e9f
+ farcall Function100dc0
+ ld hl, PartyMenuBGPalette
+ jr nc, .not_mobile
+ ld hl, PartyMenuBGMobilePalette
+.not_mobile
+ ld de, wBGPals1 palette 0
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+; 8eb9
+
+_CGB_PokegearPals: ; 8eb9
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .male
+ ld hl, FemalePokegearPals
+ jr .got_pals
+
+.male
+ ld hl, MalePokegearPals
+.got_pals
+ ld de, wBGPals1
+ ld bc, 6 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 8edb
+
+_CGB_StatsScreenHPPals: ; 8edb
+ ld de, wBGPals1
+ ld a, [wCurHPPal]
+ ld l, a
+ ld h, $0
+ add hl, hl
+ add hl, hl
+ ld bc, HPBarPals
+ add hl, bc
+ call LoadPalette_White_Col1_Col2_Black ; hp palette
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ call LoadPalette_White_Col1_Col2_Black ; mon palette
+ ld hl, ExpBarPalette
+ call LoadPalette_White_Col1_Col2_Black ; exp palette
+ ld hl, StatsScreenPagePals
+ ld de, wBGPals1 palette 3
+ ld bc, 3 palettes ; pink, green, and blue page palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call WipeAttrMap
+
+ hlcoord 0, 0, wAttrMap
+ lb bc, 8, SCREEN_WIDTH
+ ld a, $1 ; mon palette
+ call FillBoxCGB
+
+ hlcoord 10, 16, wAttrMap
+ ld bc, 10
+ ld a, $2 ; exp palette
+ call ByteFill
+
+ hlcoord 13, 5, wAttrMap
+ lb bc, 2, 2
+ ld a, $3 ; pink page palette
+ call FillBoxCGB
+
+ hlcoord 15, 5, wAttrMap
+ lb bc, 2, 2
+ ld a, $4 ; green page palette
+ call FillBoxCGB
+
+ hlcoord 17, 5, wAttrMap
+ lb bc, 2, 2
+ ld a, $5 ; blue page palette
+ call FillBoxCGB
+
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 8f52
+
+StatsScreenPagePals: ; 8f52
+INCLUDE "gfx/stats/pages.pal"
+; 8f6a
+
+StatsScreenPals: ; 8f6a
+INCLUDE "gfx/stats/stats.pal"
+; 8f70
+
+_CGB_Pokedex: ; 8f70
+ ld de, wBGPals1
+ ld a, PREDEFPAL_POKEDEX
+ call GetPredefPal
+ call LoadHLPaletteIntoDE ; dex interface palette
+ ld a, [wCurPartySpecies]
+ cp $ff
+ jr nz, .is_pokemon
+ ld hl, .PokedexQuestionMarkPalette
+ call LoadHLPaletteIntoDE ; green question mark palette
+ jr .got_palette
+
+.is_pokemon
+ call GetMonPalettePointer_
+ call LoadPalette_White_Col1_Col2_Black ; mon palette
+.got_palette
+ call WipeAttrMap
+ hlcoord 1, 1, wAttrMap
+ lb bc, 7, 7
+ ld a, $1 ; green question mark palette
+ call FillBoxCGB
+ call InitPartyMenuOBPals
+ ld hl, .PokedexCursorPalette
+ ld de, wOBPals1 palette 7 ; green cursor palette
+ ld bc, 1 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 8fba
+
+.PokedexQuestionMarkPalette: ; 8fba
+INCLUDE "gfx/pokedex/question_mark.pal"
+; 8fc2
+
+.PokedexCursorPalette: ; 8fc2
+INCLUDE "gfx/pokedex/cursor.pal"
+; 8fca
+
+_CGB_BillsPC: ; 8fca
+ ld de, wBGPals1
+ ld a, PREDEFPAL_POKEDEX
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ ld a, [wCurPartySpecies]
+ cp $ff
+ jr nz, .GetMonPalette
+ ld hl, .BillsPCOrangePalette
+ call LoadHLPaletteIntoDE
+ jr .Resume
+
+.GetMonPalette:
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+.Resume:
+ call WipeAttrMap
+ hlcoord 1, 4, wAttrMap
+ lb bc, 7, 7
+ ld a, $1
+ call FillBoxCGB
+ call InitPartyMenuOBPals
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9009
+
+.Function9009: ; 9009
+ ld hl, .BillsPCOrangePalette
+ call LoadHLPaletteIntoDE
+ jr .asm_901a
+
+.unused
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+.asm_901a
+ call WipeAttrMap
+ hlcoord 1, 1, wAttrMap
+ lb bc, 7, 7
+ ld a, $1
+ call FillBoxCGB
+ call InitPartyMenuOBPals
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9036
+
+.BillsPCOrangePalette: ; 9036
+INCLUDE "gfx/pc/orange.pal"
+; 903e
+
+_CGB_PokedexUnownMode: ; 903e
+ ld de, wBGPals1
+ ld a, PREDEFPAL_POKEDEX
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ ld a, [wCurPartySpecies]
+ call GetMonPalettePointer_
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ hlcoord 7, 5, wAttrMap
+ lb bc, 7, 7
+ ld a, $1
+ call FillBoxCGB
+ call InitPartyMenuOBPals
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 906e
+
+_CGB_SlotMachine: ; 906e
+ ld hl, SlotMachinePals
+ ld de, wBGPals1
+ ld bc, 16 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call WipeAttrMap
+ hlcoord 0, 2, wAttrMap
+ lb bc, 10, 3
+ ld a, $2
+ call FillBoxCGB
+ hlcoord 17, 2, wAttrMap
+ lb bc, 10, 3
+ ld a, $2
+ call FillBoxCGB
+ hlcoord 0, 4, wAttrMap
+ lb bc, 6, 3
+ ld a, $3
+ call FillBoxCGB
+ hlcoord 17, 4, wAttrMap
+ lb bc, 6, 3
+ ld a, $3
+ call FillBoxCGB
+ hlcoord 0, 6, wAttrMap
+ lb bc, 2, 3
+ ld a, $4
+ call FillBoxCGB
+ hlcoord 17, 6, wAttrMap
+ lb bc, 2, 3
+ ld a, $4
+ call FillBoxCGB
+ hlcoord 4, 2, wAttrMap
+ lb bc, 2, 12
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 3, 2, wAttrMap
+ lb bc, 10, 1
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 16, 2, wAttrMap
+ lb bc, 10, 1
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 0, 12, wAttrMap
+ ld bc, $78
+ ld a, $7
+ call ByteFill
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 90f8
+
+_CGB06: ; 90f8
+ ld hl, PalPacket_SCGB_06 + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ ld de, wOBPals1
+ ld a, PREDEFPAL_PACK
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ hlcoord 0, 6, wAttrMap
+ lb bc, 12, SCREEN_WIDTH
+ ld a, $1
+ call FillBoxCGB
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9122
+
+_CGB_GSIntro: ; 9122
+ ld b, 0
+ ld hl, .Jumptable
+ add hl, bc
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+; 912d
+
+.Jumptable: ; 912d
+ dw .ShellderLaprasScene
+ dw .JigglypuffPikachuScene
+ dw .StartersCharizardScene
+; 9133
+
+.ShellderLaprasScene: ; 9133
+ ld hl, .ShellderLaprasBGPalette
+ ld de, wBGPals1
+ call LoadHLPaletteIntoDE
+ ld hl, .ShellderLaprasOBPals
+ ld de, wOBPals1
+ ld bc, 2 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ call WipeAttrMap
+ ret
+; 914e
+
+.ShellderLaprasBGPalette: ; 914e
+ RGB 19, 31, 19
+ RGB 18, 23, 31
+ RGB 11, 21, 28
+ RGB 04, 16, 24
+
+.ShellderLaprasOBPals: ; 9156
+ RGB 29, 29, 29
+ RGB 20, 19, 20
+ RGB 19, 06, 04
+ RGB 03, 04, 06
+
+ RGB 31, 31, 31
+ RGB 31, 31, 31
+ RGB 31, 00, 00
+ RGB 03, 04, 06
+; 9166
+
+.JigglypuffPikachuScene: ; 9166
+ ld de, wBGPals1
+ ld a, PREDEFPAL_GS_INTRO_JIGGLYPUFF_PIKACHU_BG
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+
+ ld de, wOBPals1
+ ld a, PREDEFPAL_GS_INTRO_JIGGLYPUFF_PIKACHU_OB
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ ret
+; 9180
+
+.StartersCharizardScene: ; 9180
+ ld hl, PalPacket_Pack + 1
+ call CopyFourPalettes
+ ld de, wOBPals1
+ ld a, PREDEFPAL_GS_INTRO_STARTERS_TRANSITION
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ ret
+; 9195
+
+_CGB11: ; 9195
+ ld hl, Palettes_SCGB_11
+ ld de, wBGPals1
+ ld bc, 5 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call ApplyPals
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+; 91ad
+
+_CGB_Diploma: ; 91ad
+ ld hl, DiplomaPalettes
+ ld de, wBGPals1
+ ld bc, 16 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+
+ ld hl, PalPacket_Diploma + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+; 91c8
+
+_CGB_MapPals: ; 91c8
+ call LoadMapPals
+ ld a, SCGB_MAPPALS
+ ld [wSGBPredef], a
+ ret
+; 91d1
+
+_CGB_PartyMenu: ; 91d1
+ ld hl, PalPacket_PartyMenu + 1
+ call CopyFourPalettes
+ call InitPartyMenuBGPal0
+ call InitPartyMenuBGPal7
+ call InitPartyMenuOBPals
+ call ApplyAttrMap
+ ret
+; 91e4
+
+_CGB_Evolution: ; 91e4
+ ld de, wBGPals1
+ ld a, c
+ and a
+ jr z, .pokemon
+ ld a, PREDEFPAL_BLACKOUT
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ jr .got_palette
+
+.pokemon
+ ld hl, wPartyMon1DVs
+ ld bc, PARTYMON_STRUCT_LENGTH
+ ld a, [wCurPartyMon]
+ call AddNTimes
+ ld c, l
+ ld b, h
+ ld a, [wPlayerHPPal]
+ call GetPlayerOrMonPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld hl, BattleObjectPals
+ ld de, wOBPals1 palette PAL_BATTLE_OB_GRAY
+ ld bc, 6 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+
+.got_palette
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9228
+
+_CGB_GSTitleScreen: ; 9228
+ ld hl, UnusedGSTitleBGPals
+ ld de, wBGPals1
+ ld bc, 5 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ld hl, UnusedGSTitleOBPals
+ ld de, wOBPals1
+ ld bc, 2 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ ld a, SCGB_DIPLOMA
+ ld [wSGBPredef], a
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9251
+
+_CGB0d: ; 9251
+ ld hl, PalPacket_Diploma + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+; 925e
+
+_CGB_UnownPuzzle: ; 925e
+ ld hl, PalPacket_UnownPuzzle + 1
+ call CopyFourPalettes
+ ld de, wOBPals1
+ ld a, PREDEFPAL_UNOWN_PUZZLE
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wOBPals1)
+ ld [rSVBK], a
+ ld hl, wOBPals1
+ ld a, LOW(palred 31 + palgreen 0 + palblue 0)
+ ld [hli], a
+ ld a, HIGH(palred 31 + palgreen 0 + palblue 0)
+ ld [hl], a
+ pop af
+ ld [rSVBK], a
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+; 9289
+
+_CGB_TrainerCard: ; 9289
+ ld de, wBGPals1
+ xor a ; CHRIS
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, FALKNER ; KRIS
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, BUGSY
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, WHITNEY
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, MORTY
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, CHUCK
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, JASMINE
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, PRYCE
+ call GetTrainerPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ ld a, PREDEFPAL_CGB_BADGE
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+
+ ; fill screen with opposite-gender palette for the card border
+ hlcoord 0, 0, wAttrMap
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ ld a, [wPlayerGender]
+ and a
+ ld a, $1 ; kris
+ jr z, .got_gender
+ ld a, $0 ; chris
+.got_gender
+ call ByteFill
+ ; fill trainer sprite area with same-gender palette
+ hlcoord 14, 1, wAttrMap
+ lb bc, 7, 5
+ ld a, [wPlayerGender]
+ and a
+ ld a, $0 ; chris
+ jr z, .got_gender2
+ ld a, $1 ; kris
+.got_gender2
+ call FillBoxCGB
+ ; top-right corner still uses the border's palette
+ hlcoord 18, 1, wAttrMap
+ ld [hl], $1
+ hlcoord 2, 11, wAttrMap
+ lb bc, 2, 4
+ ld a, $1 ; falkner
+ call FillBoxCGB
+ hlcoord 6, 11, wAttrMap
+ lb bc, 2, 4
+ ld a, $2 ; bugsy
+ call FillBoxCGB
+ hlcoord 10, 11, wAttrMap
+ lb bc, 2, 4
+ ld a, $3 ; whitney
+ call FillBoxCGB
+ hlcoord 14, 11, wAttrMap
+ lb bc, 2, 4
+ ld a, $4 ; morty
+ call FillBoxCGB
+ hlcoord 2, 14, wAttrMap
+ lb bc, 2, 4
+ ld a, $5 ; chuck
+ call FillBoxCGB
+ hlcoord 6, 14, wAttrMap
+ lb bc, 2, 4
+ ld a, $6 ; jasmine
+ call FillBoxCGB
+ hlcoord 10, 14, wAttrMap
+ lb bc, 2, 4
+ ld a, $7 ; pryce
+ call FillBoxCGB
+ ; clair uses kris's palette
+ ld a, [wPlayerGender]
+ and a
+ push af
+ jr z, .got_gender3
+ hlcoord 14, 14, wAttrMap
+ lb bc, 2, 4
+ ld a, $1
+ call FillBoxCGB
+.got_gender3
+ pop af
+ ld c, $0
+ jr nz, .got_gender4
+ inc c
+.got_gender4
+ ld a, c
+ hlcoord 18, 1, wAttrMap
+ ld [hl], a
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9373
+
+_CGB_MoveList: ; 9373
+ ld de, wBGPals1
+ ld a, PREDEFPAL_GOLDENROD
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ ld a, [wPlayerHPPal]
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ ld bc, HPBarPals
+ add hl, bc
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ hlcoord 11, 1, wAttrMap
+ lb bc, 2, 9
+ ld a, $1
+ call FillBoxCGB
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 93a6
+
+_CGB0f: ; 93a6
+ ld hl, PalPacket_SCGB_0F + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 93ba
+
+_CGB_PokedexSearchOption: ; 93ba
+ ld de, wBGPals1
+ ld a, PREDEFPAL_POKEDEX
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 93d3
+
+_CGB_PackPals: ; 93d3
+; pack pals
+ ld a, [wBattleType]
+ cp BATTLETYPE_TUTORIAL
+ jr z, .tutorial_male
+
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .tutorial_male
+
+ ld hl, .KrisPackPals
+ jr .got_gender
+
+.tutorial_male
+ ld hl, .ChrisPackPals
+
+.got_gender
+ ld de, wBGPals1
+ ld bc, 8 palettes ; 6 palettes?
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call WipeAttrMap
+ hlcoord 0, 0, wAttrMap
+ lb bc, 1, 10
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 10, 0, wAttrMap
+ lb bc, 1, 10
+ ld a, $2
+ call FillBoxCGB
+ hlcoord 7, 2, wAttrMap
+ lb bc, 9, 1
+ ld a, $3
+ call FillBoxCGB
+ hlcoord 0, 7, wAttrMap
+ lb bc, 3, 5
+ ld a, $4
+ call FillBoxCGB
+ hlcoord 0, 3, wAttrMap
+ lb bc, 3, 5
+ ld a, $5
+ call FillBoxCGB
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 9439
+
+.ChrisPackPals: ; 9439
+INCLUDE "gfx/pack/pack.pal"
+; 9469
+
+.KrisPackPals: ; 9469
+INCLUDE "gfx/pack/pack_f.pal"
+; 9499
+
+_CGB_Pokepic: ; 9499
+ call _CGB_MapPals
+ ld de, SCREEN_WIDTH
+ hlcoord 0, 0, wAttrMap
+ ld a, [wMenuBorderTopCoord]
+.loop
+ and a
+ jr z, .found_top
+ dec a
+ add hl, de
+ jr .loop
+
+.found_top
+ ld a, [wMenuBorderLeftCoord]
+ ld e, a
+ ld d, $0
+ add hl, de
+ ld a, [wMenuBorderTopCoord]
+ ld b, a
+ ld a, [wMenuBorderBottomCoord]
+ inc a
+ sub b
+ ld b, a
+ ld a, [wMenuBorderLeftCoord]
+ ld c, a
+ ld a, [wMenuBorderRightCoord]
+ sub c
+ inc a
+ ld c, a
+ ld a, $0
+ call FillBoxCGB
+ call ApplyAttrMap
+ ret
+; 94d0
+
+_CGB13: ; 94d0
+ ld hl, PalPacket_SCGB_13 + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ hlcoord 0, 4, wAttrMap
+ lb bc, 10, SCREEN_WIDTH
+ ld a, $2
+ call FillBoxCGB
+ hlcoord 0, 6, wAttrMap
+ lb bc, 6, SCREEN_WIDTH
+ ld a, $1
+ call FillBoxCGB
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 94fa
+
+_CGB_GamefreakLogo: ; 94fa
+ ld de, wBGPals1
+ ld a, PREDEFPAL_GAMEFREAK_LOGO
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ ld hl, .Palette
+ ld de, wOBPals1
+ call LoadHLPaletteIntoDE
+ ld hl, .Palette
+ ld de, wOBPals1 palette 1
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ret
+; 9521
+
+.Palette: ; 9521
+INCLUDE "gfx/splash/logo.pal"
+; 9529
+
+_CGB_PlayerOrMonFrontpicPals: ; 9529
+ ld de, wBGPals1
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ret
+; 9542
+
+_CGB1e: ; 9542
+ ld de, wBGPals1
+ ld a, [wCurPartySpecies]
+ call GetMonPalettePointer_
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+; 9555
+
+_CGB_TradeTube: ; 9555
+ ld hl, PalPacket_TradeTube + 1
+ call CopyFourPalettes
+ ld hl, PartyMenuOBPals
+ ld de, wOBPals1
+ ld bc, 1 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ ld de, wOBPals1 palette 7
+ ld a, PREDEFPAL_TRADE_TUBE
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ ret
+; 9578
+
+_CGB_TrainerOrMonFrontpicPals: ; 9578
+ ld de, wBGPals1
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetFrontpicPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ret
+; 9591
+
+_CGB_MysteryGift: ; 9591
+ ld hl, .Palettes
+ ld de, wBGPals1
+ ld bc, 2 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call ApplyPals
+ call WipeAttrMap
+ hlcoord 3, 7, wAttrMap
+ lb bc, 8, 14
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 1, 5, wAttrMap
+ lb bc, 1, 18
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 1, 16, wAttrMap
+ lb bc, 1, 18
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 0, 0, wAttrMap
+ lb bc, 17, 2
+ ld a, $1
+ call FillBoxCGB
+ hlcoord 18, 5, wAttrMap
+ lb bc, 12, 1
+ ld a, $1
+ call FillBoxCGB
+ call ApplyAttrMap
+ ret
+; 95e0
+
+.Palettes: ; 95e0
+INCLUDE "gfx/mystery_gift/mystery_gift.pal"
+; 95f0
diff --git a/engine/gfx/color.asm b/engine/gfx/color.asm
new file mode 100644
index 000000000..6cf896ff4
--- /dev/null
+++ b/engine/gfx/color.asm
@@ -0,0 +1,1356 @@
+INCLUDE "engine/gfx/sgb_layouts.asm"
+
+SHINY_ATK_BIT EQU 5
+SHINY_DEF_VAL EQU 10
+SHINY_SPD_VAL EQU 10
+SHINY_SPC_VAL EQU 10
+
+CheckShininess:
+; Check if a mon is shiny by DVs at bc.
+; Return carry if shiny.
+
+ ld l, c
+ ld h, b
+
+; Attack
+ ld a, [hl]
+ and 1 << SHINY_ATK_BIT
+ jr z, .NotShiny
+
+; Defense
+ ld a, [hli]
+ and $f
+ cp SHINY_DEF_VAL
+ jr nz, .NotShiny
+
+; Speed
+ ld a, [hl]
+ and $f0
+ cp SHINY_SPD_VAL << 4
+ jr nz, .NotShiny
+
+; Special
+ ld a, [hl]
+ and $f
+ cp SHINY_SPC_VAL
+ jr nz, .NotShiny
+
+.Shiny:
+ scf
+ ret
+
+.NotShiny:
+ and a
+ ret
+
+Unused_CheckContestMon:
+; Check a mon's DVs at hl in the bug catching contest.
+; Return carry if its DVs are good enough to place in the contest.
+
+; Attack
+ ld a, [hl]
+ cp 10 << 4
+ jr c, .Bad
+
+; Defense
+ ld a, [hli]
+ and $f
+ cp 10
+ jr c, .Bad
+
+; Speed
+ ld a, [hl]
+ cp 10 << 4
+ jr c, .Bad
+
+; Special
+ ld a, [hl]
+ and $f
+ cp 10
+ jr c, .Bad
+
+.Good:
+ scf
+ ret
+
+.Bad:
+ and a
+ ret
+
+Unreferenced_Function8aa4:
+ push de
+ push bc
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ pop bc
+ pop de
+ ld a, c
+ ld [wSGBPals + 3], a
+ ld a, b
+ ld [wSGBPals + 4], a
+ ld a, e
+ ld [wSGBPals + 5], a
+ ld a, d
+ ld [wSGBPals + 6], a
+ ld hl, wSGBPals
+ call PushSGBPals_
+ ld hl, BlkPacket_9a86
+ call PushSGBPals_
+ ret
+
+InitPartyMenuPalettes:
+ ld hl, PalPacket_PartyMenu + 1
+ call CopyFourPalettes
+ call InitPartyMenuOBPals
+ call WipeAttrMap
+ ret
+
+; SGB layout for SCGB_PARTY_MENU_HP_PALS
+SGB_ApplyPartyMenuHPPals: ; 8ade
+ ld hl, wHPPals
+ ld a, [wSGBPals]
+ ld e, a
+ ld d, $0
+ add hl, de
+ ld e, l
+ ld d, h
+ ld a, [de]
+ and a
+ ld e, $5
+ jr z, .okay
+ dec a
+ ld e, $a
+ jr z, .okay
+ ld e, $f
+.okay
+ push de
+ ld hl, wSGBPals + 10
+ ld bc, $6
+ ld a, [wSGBPals]
+ call AddNTimes
+ pop de
+ ld [hl], e
+ ret
+
+Unreferenced_Function8b07:
+ call CheckCGB
+ ret z
+; CGB only
+ ld hl, .BGPal
+ ld de, wBGPals1
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+
+ ld hl, .OBPal
+ ld de, wOBPals1
+ ld bc, 1 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+
+.BGPal:
+ RGB 31, 31, 31
+ RGB 18, 23, 31
+ RGB 15, 20, 31
+ RGB 00, 00, 00
+
+.OBPal:
+ RGB 31, 31, 31
+ RGB 31, 31, 12
+ RGB 08, 16, 28
+ RGB 00, 00, 00
+
+Unreferenced_Function8b3f:
+ call CheckCGB
+ ret nz
+ ld a, [hSGB]
+ and a
+ ret z
+ ld hl, BlkPacket_9a86
+ jp PushSGBPals_
+
+Unreferenced_Function8b4d:
+ call CheckCGB
+ jr nz, .cgb
+ ld a, [hSGB]
+ and a
+ ret z
+ ld hl, PalPacket_Function8b4d
+ jp PushSGBPals_
+
+.cgb
+ ld de, wOBPals1
+ ld a, PREDEFPAL_3B
+ call GetPredefPal
+ jp LoadHLPaletteIntoDE
+
+Unreferenced_Function8b67:
+ call CheckCGB
+ jr nz, .cgb
+ ld a, [hSGB]
+ and a
+ ret z
+ ld hl, PalPacket_Pack
+ jp PushSGBPals_
+
+.cgb
+ ld de, wOBPals1
+ ld a, PREDEFPAL_PACK
+ call GetPredefPal
+ jp LoadHLPaletteIntoDE
+
+Unreferenced_Function8b81:
+ call CheckCGB
+ jr nz, .cgb
+ ld a, [hSGB]
+ and a
+ ret z
+ ld a, c
+ push af
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ pop af
+ call GetMonPalettePointer_
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+ ld hl, wSGBPals
+ jp PushSGBPals_
+
+.cgb
+ ld de, wOBPals1
+ ld a, c
+ call GetMonPalettePointer_
+ call LoadPalette_White_Col1_Col2_Black
+ ret
+
+LoadTrainerClassPaletteAsNthBGPal:
+ ld a, [wTrainerClass]
+ call GetTrainerPalettePointer
+ ld a, e
+ jr got_palette_pointer_8bd7
+
+LoadMonPaletteAsNthBGPal:
+ ld a, [wCurPartySpecies]
+ call GetMonPalettePointer
+ ld a, e
+ bit 7, a
+ jr z, got_palette_pointer_8bd7
+ and $7f
+ inc hl
+ inc hl
+ inc hl
+ inc hl
+
+got_palette_pointer_8bd7
+ push hl
+ ld hl, wBGPals1
+ ld de, 1 palettes
+.loop
+ and a
+ jr z, .got_addr
+ add hl, de
+ dec a
+ jr .loop
+
+.got_addr
+ ld e, l
+ ld d, h
+ pop hl
+ call LoadPalette_White_Col1_Col2_Black
+ ret
+
+Unreferenced_Function8bec:
+ ld a, [hCGB]
+ and a
+ jr nz, .cgb
+ ld hl, wPlayerLightScreenCount
+ jp PushSGBPals_
+
+.cgb
+ ld a, [wEnemyLightScreenCount] ; col
+ ld c, a
+ ld a, [wEnemyReflectCount] ; row
+ hlcoord 0, 0, wAttrMap
+ ld de, SCREEN_WIDTH
+.loop
+ and a
+ jr z, .done
+ add hl, de
+ dec a
+ jr .loop
+
+.done
+ ld b, $0
+ add hl, bc
+ lb bc, 6, 4
+ ld a, [wEnemySafeguardCount] ; value
+ and $3
+ call FillBoxCGB
+ call CopyTilemapAtOnce
+ ret
+
+ApplyMonOrTrainerPals:
+ call CheckCGB
+ ret z
+ ld a, e
+ and a
+ jr z, .get_trainer
+ ld a, [wCurPartySpecies]
+ call GetMonPalettePointer_
+ jr .load_palettes
+
+.get_trainer
+ ld a, [wTrainerClass]
+ call GetTrainerPalettePointer
+
+.load_palettes
+ ld de, wBGPals1
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ret
+
+ApplyHPBarPals:
+ ld a, [wWhichHPBar]
+ and a
+ jr z, .Enemy
+ cp $1
+ jr z, .Player
+ cp $2
+ jr z, .PartyMenu
+ ret
+
+.Enemy:
+ ld de, wBGPals2 palette PAL_BATTLE_BG_ENEMY_HP color 1
+ jr .okay
+
+.Player:
+ ld de, wBGPals2 palette PAL_BATTLE_BG_PLAYER_HP color 1
+
+.okay
+ ld l, c
+ ld h, $0
+ add hl, hl
+ add hl, hl
+ ld bc, HPBarPals
+ add hl, bc
+ ld bc, 4
+ ld a, BANK(wBGPals2)
+ call FarCopyWRAM
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+
+.PartyMenu:
+ ld e, c
+ inc e
+ hlcoord 11, 1, wAttrMap
+ ld bc, 2 * SCREEN_WIDTH
+ ld a, [wCurPartyMon]
+.loop
+ and a
+ jr z, .done
+ add hl, bc
+ dec a
+ jr .loop
+
+.done
+ lb bc, 2, 8
+ ld a, e
+ call FillBoxCGB
+ ret
+
+LoadStatsScreenPals:
+ call CheckCGB
+ ret z
+ ld hl, StatsScreenPals
+ ld b, 0
+ dec c
+ add hl, bc
+ add hl, bc
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wBGPals1)
+ ld [rSVBK], a
+ ld a, [hli]
+ ld [wBGPals1 palette 0], a
+ ld [wBGPals1 palette 2], a
+ ld a, [hl]
+ ld [wBGPals1 palette 0 + 1], a
+ ld [wBGPals1 palette 2 + 1], a
+ pop af
+ ld [rSVBK], a
+ call ApplyPals
+ ld a, $1
+ ret
+
+LoadMailPalettes:
+ ld l, e
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld de, .MailPals
+ add hl, de
+ call CheckCGB
+ jr nz, .cgb
+ push hl
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ pop hl
+ inc hl
+ inc hl
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hli]
+ ld [wSGBPals + 6], a
+ ld hl, wSGBPals
+ call PushSGBPals_
+ ld hl, BlkPacket_9a86
+ call PushSGBPals_
+ ret
+
+.cgb
+ ld de, wBGPals1
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call ApplyPals
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+
+.MailPals:
+INCLUDE "gfx/mail/mail.pal"
+
+INCLUDE "engine/gfx/cgb_layouts.asm"
+
+Unreferenced_Function95f0:
+ ld hl, .Palette
+ ld de, wBGPals1
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call ApplyPals
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+
+.Palette:
+ RGB 31, 31, 31
+ RGB 09, 31, 31
+ RGB 10, 12, 31
+ RGB 00, 03, 19
+
+CopyFourPalettes:
+ ld de, wBGPals1
+ ld c, 4
+
+CopyPalettes:
+.loop
+ push bc
+ ld a, [hli]
+ push hl
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ pop hl
+ inc hl
+ pop bc
+ dec c
+ jr nz, .loop
+ ret
+
+GetPredefPal:
+ ld l, a
+ ld h, $0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld bc, PredefPals
+ add hl, bc
+ ret
+
+LoadHLPaletteIntoDE:
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wOBPals1)
+ ld [rSVBK], a
+ ld c, 1 palettes
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+ pop af
+ ld [rSVBK], a
+ ret
+
+LoadPalette_White_Col1_Col2_Black:
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wBGPals1)
+ ld [rSVBK], a
+
+ ld a, LOW(PALRGB_WHITE)
+ ld [de], a
+ inc de
+ ld a, HIGH(PALRGB_WHITE)
+ ld [de], a
+ inc de
+
+ ld c, 2 * PAL_COLOR_SIZE
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+
+ xor a
+ ld [de], a
+ inc de
+ ld [de], a
+ inc de
+
+ pop af
+ ld [rSVBK], a
+ ret
+
+FillBoxCGB:
+.row
+ push bc
+ push hl
+.col
+ ld [hli], a
+ dec c
+ jr nz, .col
+ pop hl
+ ld bc, SCREEN_WIDTH
+ add hl, bc
+ pop bc
+ dec b
+ jr nz, .row
+ ret
+
+ResetBGPals:
+ push af
+ push bc
+ push de
+ push hl
+
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wBGPals1)
+ ld [rSVBK], a
+
+ ld hl, wBGPals1
+ ld c, 1 palettes
+.loop
+ ld a, $ff
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ xor a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ dec c
+ jr nz, .loop
+
+ pop af
+ ld [rSVBK], a
+
+ pop hl
+ pop de
+ pop bc
+ pop af
+ ret
+
+WipeAttrMap:
+ hlcoord 0, 0, wAttrMap
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ xor a
+ call ByteFill
+ ret
+
+ApplyPals:
+ ld hl, wBGPals1
+ ld de, wBGPals2
+ ld bc, 16 palettes
+ ld a, BANK(wGBCPalettes)
+ call FarCopyWRAM
+ ret
+
+ApplyAttrMap:
+ ld a, [rLCDC]
+ bit rLCDC_ENABLE, a
+ jr z, .UpdateVBank1
+ ld a, [hBGMapMode]
+ push af
+ ld a, $2
+ ld [hBGMapMode], a
+ call DelayFrame
+ call DelayFrame
+ call DelayFrame
+ call DelayFrame
+ pop af
+ ld [hBGMapMode], a
+ ret
+
+.UpdateVBank1:
+ hlcoord 0, 0, wAttrMap
+ debgcoord 0, 0
+ ld b, SCREEN_HEIGHT
+ ld a, $1
+ ld [rVBK], a
+.row
+ ld c, SCREEN_WIDTH
+.col
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .col
+ ld a, BG_MAP_WIDTH - SCREEN_WIDTH
+ add e
+ jr nc, .okay
+ inc d
+.okay
+ ld e, a
+ dec b
+ jr nz, .row
+ ld a, $0
+ ld [rVBK], a
+ ret
+
+; CGB layout for SCGB_PARTY_MENU_HP_PALS
+CGB_ApplyPartyMenuHPPals: ; 96f3
+ ld hl, wHPPals
+ ld a, [wSGBPals]
+ ld e, a
+ ld d, $0
+ add hl, de
+ ld e, l
+ ld d, h
+ ld a, [de]
+ inc a
+ ld e, a
+ hlcoord 11, 2, wAttrMap
+ ld bc, 2 * SCREEN_WIDTH
+ ld a, [wSGBPals]
+.loop
+ and a
+ jr z, .done
+ add hl, bc
+ dec a
+ jr .loop
+.done
+ lb bc, 2, 8
+ ld a, e
+ call FillBoxCGB
+ ret
+
+InitPartyMenuOBPals:
+ ld hl, PartyMenuOBPals
+ ld de, wOBPals1
+ ld bc, 2 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ ret
+
+GetBattlemonBackpicPalettePointer:
+ push de
+ farcall GetPartyMonDVs
+ ld c, l
+ ld b, h
+ ld a, [wTempBattleMonSpecies]
+ call GetPlayerOrMonPalettePointer
+ pop de
+ ret
+
+GetEnemyFrontpicPalettePointer:
+ push de
+ farcall GetEnemyMonDVs
+ ld c, l
+ ld b, h
+ ld a, [wTempEnemyMonSpecies]
+ call GetFrontpicPalettePointer
+ pop de
+ ret
+
+GetPlayerOrMonPalettePointer:
+ and a
+ jp nz, GetMonNormalOrShinyPalettePointer
+ ld a, [wPlayerSpriteSetupFlags]
+ bit PLAYERSPRITESETUP_FEMALE_TO_MALE_F, a
+ jr nz, .male
+ ld a, [wPlayerGender]
+ and a
+ jr z, .male
+ ld hl, KrisPalette
+ ret
+
+.male
+ ld hl, PlayerPalette
+ ret
+
+GetFrontpicPalettePointer:
+ and a
+ jp nz, GetMonNormalOrShinyPalettePointer
+ ld a, [wTrainerClass]
+
+GetTrainerPalettePointer:
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ ld bc, TrainerPalettes
+ add hl, bc
+ ret
+
+GetMonPalettePointer_:
+ call GetMonPalettePointer
+ ret
+
+Unreferenced_Function9779:
+ ret
+ call CheckCGB
+ ret z
+ ld hl, BattleObjectPals
+ ld a, $90
+ ld [rOBPI], a
+ ld c, 6 palettes
+.loop
+ ld a, [hli]
+ ld [rOBPD], a
+ dec c
+ jr nz, .loop
+ ld hl, BattleObjectPals
+ ld de, wOBPals1 palette 2
+ ld bc, 2 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ ret
+
+BattleObjectPals:
+INCLUDE "gfx/battle_anims/battle_anims.pal"
+
+Unreferenced_Function97cc:
+ call CheckCGB
+ ret z
+ ld a, $90
+ ld [rOBPI], a
+ ld a, PREDEFPAL_TRADE_TUBE
+ call GetPredefPal
+ call .PushPalette
+ ld a, PREDEFPAL_RB_GREENMON
+ call GetPredefPal
+ call .PushPalette
+ ret
+
+.PushPalette:
+ ld c, 1 palettes
+.loop
+ ld a, [hli]
+ ld [rOBPD], a
+ dec c
+ jr nz, .loop
+ ret
+
+GetMonPalettePointer:
+ ld l, a
+ ld h, $0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld bc, PokemonPalettes
+ add hl, bc
+ ret
+
+GetMonNormalOrShinyPalettePointer:
+ push bc
+ call GetMonPalettePointer
+ pop bc
+ push hl
+ call CheckShininess
+ pop hl
+ ret nc
+rept 4
+ inc hl
+endr
+ ret
+
+PushSGBPals_:
+ ld a, [wcfbe]
+ push af
+ set 7, a
+ ld [wcfbe], a
+ call PushSGBPals
+ pop af
+ ld [wcfbe], a
+ ret
+
+PushSGBPals:
+ ld a, [hl]
+ and $7
+ ret z
+ ld b, a
+.loop
+ push bc
+ xor a
+ ld [rJOYP], a
+ ld a, $30
+ ld [rJOYP], a
+ ld b, $10
+.loop2
+ ld e, $8
+ ld a, [hli]
+ ld d, a
+.loop3
+ bit 0, d
+ ld a, $10
+ jr nz, .okay
+ ld a, $20
+.okay
+ ld [rJOYP], a
+ ld a, $30
+ ld [rJOYP], a
+ rr d
+ dec e
+ jr nz, .loop3
+ dec b
+ jr nz, .loop2
+ ld a, $20
+ ld [rJOYP], a
+ ld a, $30
+ ld [rJOYP], a
+ call SGBDelayCycles
+ pop bc
+ dec b
+ jr nz, .loop
+ ret
+
+InitSGBBorder:
+ call CheckCGB
+ ret nz
+; SGB/DMG only
+ di
+ ld a, [wcfbe]
+ push af
+ set 7, a
+ ld [wcfbe], a
+ xor a
+ ld [rJOYP], a
+ ld [hSGB], a
+ call PushSGBBorderPalsAndWait
+ jr nc, .skip
+ ld a, $1
+ ld [hSGB], a
+ call _InitSGBBorderPals
+ call SGBBorder_PushBGPals
+ call SGBDelayCycles
+ call SGB_ClearVRAM
+ call PushSGBBorder
+ call SGBDelayCycles
+ call SGB_ClearVRAM
+ ld hl, MaskEnCancelPacket
+ call PushSGBPals
+
+.skip
+ pop af
+ ld [wcfbe], a
+ ei
+ ret
+
+InitCGBPals::
+ call CheckCGB
+ ret z
+; CGB only
+ ld a, BANK(vTiles3)
+ ld [rVBK], a
+ ld hl, vTiles3
+ ld bc, $200 tiles
+ xor a
+ call ByteFill
+ ld a, BANK(vTiles0)
+ ld [rVBK], a
+ ld a, 1 << rBGPI_AUTO_INCREMENT
+ ld [rBGPI], a
+ ld c, 4 * 8
+.bgpals_loop
+ ld a, LOW(PALRGB_WHITE)
+ ld [rBGPD], a
+ ld a, HIGH(PALRGB_WHITE)
+ ld [rBGPD], a
+ dec c
+ jr nz, .bgpals_loop
+ ld a, 1 << rOBPI_AUTO_INCREMENT
+ ld [rOBPI], a
+ ld c, 4 * 8
+.obpals_loop
+ ld a, LOW(PALRGB_WHITE)
+ ld [rOBPD], a
+ ld a, HIGH(PALRGB_WHITE)
+ ld [rOBPD], a
+ dec c
+ jr nz, .obpals_loop
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wBGPals1)
+ ld [rSVBK], a
+ ld hl, wBGPals1
+ call .LoadWhitePals
+ ld hl, wBGPals2
+ call .LoadWhitePals
+ pop af
+ ld [rSVBK], a
+ ret
+
+.LoadWhitePals:
+ ld c, 4 * 16
+.loop
+ ld a, LOW(PALRGB_WHITE)
+ ld [hli], a
+ ld a, HIGH(PALRGB_WHITE)
+ ld [hli], a
+ dec c
+ jr nz, .loop
+ ret
+
+_InitSGBBorderPals:
+ ld hl, .PacketPointerTable
+ ld c, 9
+.loop
+ push bc
+ ld a, [hli]
+ push hl
+ ld h, [hl]
+ ld l, a
+ call PushSGBPals
+ pop hl
+ inc hl
+ pop bc
+ dec c
+ jr nz, .loop
+ ret
+
+.PacketPointerTable:
+ dw MaskEnFreezePacket
+ dw DataSndPacket1
+ dw DataSndPacket2
+ dw DataSndPacket3
+ dw DataSndPacket4
+ dw DataSndPacket5
+ dw DataSndPacket6
+ dw DataSndPacket7
+ dw DataSndPacket8
+
+Unreferenced_Function9911:
+ di
+ xor a
+ ld [rJOYP], a
+ ld hl, MaskEnFreezePacket
+ call PushSGBPals
+ call PushSGBBorder
+ call SGBDelayCycles
+ call SGB_ClearVRAM
+ ld hl, MaskEnCancelPacket
+ call PushSGBPals
+ ei
+ ret
+
+PushSGBBorder:
+ call .LoadSGBBorderPointers
+ push de
+ call SGBBorder_YetMorePalPushing
+ pop hl
+ call SGBBorder_MorePalPushing
+ ret
+
+.LoadSGBBorderPointers:
+ ld hl, SGBBorder
+ ld de, SGBBorderMap
+ ret
+
+SGB_ClearVRAM:
+ ld hl, VRAM_Begin
+ ld bc, VRAM_End - VRAM_Begin
+ xor a
+ call ByteFill
+ ret
+
+PushSGBBorderPalsAndWait:
+ ld hl, MltReq2Packet
+ call PushSGBPals
+ call SGBDelayCycles
+ ld a, [rJOYP]
+ and $3
+ cp $3
+ jr nz, .carry
+ ld a, $20
+ ld [rJOYP], a
+ ld a, [rJOYP]
+ ld a, [rJOYP]
+ call SGBDelayCycles
+ call SGBDelayCycles
+ ld a, $30
+ ld [rJOYP], a
+ call SGBDelayCycles
+ call SGBDelayCycles
+ ld a, $10
+ ld [rJOYP], a
+rept 6
+ ld a, [rJOYP]
+endr
+ call SGBDelayCycles
+ call SGBDelayCycles
+ ld a, $30
+ ld [rJOYP], a
+ ld a, [rJOYP]
+ ld a, [rJOYP]
+ ld a, [rJOYP]
+ call SGBDelayCycles
+ call SGBDelayCycles
+ ld a, [rJOYP]
+ and $3
+ cp $3
+ jr nz, .carry
+ call .FinalPush
+ and a
+ ret
+
+.carry
+ call .FinalPush
+ scf
+ ret
+
+.FinalPush:
+ ld hl, MltReq1Packet
+ call PushSGBPals
+ jp SGBDelayCycles
+
+SGBBorder_PushBGPals:
+ call DisableLCD
+ ld a, %11100100
+ ld [rBGP], a
+ ld hl, PredefPals
+ ld de, vTiles1
+ ld bc, $100 tiles
+ call CopyData
+ call DrawDefaultTiles
+ ld a, LCDC_DEFAULT
+ ld [rLCDC], a
+ ld hl, PalTrnPacket
+ call PushSGBPals
+ xor a
+ ld [rBGP], a
+ ret
+
+SGBBorder_MorePalPushing:
+ call DisableLCD
+ ld a, $e4
+ ld [rBGP], a
+ ld de, vTiles1
+ ld bc, 20 tiles
+ call CopyData
+ ld b, 18
+.loop
+ push bc
+ ld bc, $c
+ call CopyData
+ ld bc, $28
+ call ClearBytes
+ ld bc, $c
+ call CopyData
+ pop bc
+ dec b
+ jr nz, .loop
+ ld bc, $140
+ call CopyData
+ ld bc, Start
+ call ClearBytes
+ ld bc, 16 palettes
+ call CopyData
+ call DrawDefaultTiles
+ ld a, LCDC_DEFAULT
+ ld [rLCDC], a
+ ld hl, PctTrnPacket
+ call PushSGBPals
+ xor a
+ ld [rBGP], a
+ ret
+
+SGBBorder_YetMorePalPushing:
+ call DisableLCD
+ ld a, %11100100
+ ld [rBGP], a
+ ld de, vTiles1
+ ld b, $80
+.loop
+ push bc
+ ld bc, 1 tiles
+ call CopyData
+ ld bc, 1 tiles
+ call ClearBytes
+ pop bc
+ dec b
+ jr nz, .loop
+ call DrawDefaultTiles
+ ld a, LCDC_DEFAULT
+ ld [rLCDC], a
+ ld hl, ChrTrnPacket
+ call PushSGBPals
+ xor a
+ ld [rBGP], a
+ ret
+
+CopyData: ; 0x9a52
+; copy bc bytes of data from hl to de
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec bc
+ ld a, c
+ or b
+ jr nz, .loop
+ ret
+; 0x9a5b
+
+ClearBytes: ; 0x9a5b
+; clear bc bytes of data starting from de
+.loop
+ xor a
+ ld [de], a
+ inc de
+ dec bc
+ ld a, c
+ or b
+ jr nz, .loop
+ ret
+; 0x9a64
+
+DrawDefaultTiles: ; 0x9a64
+; Draw 240 tiles (2/3 of the screen) from tiles in VRAM
+ hlbgcoord 0, 0 ; BG Map 0
+ ld de, BG_MAP_WIDTH - SCREEN_WIDTH
+ ld a, $80 ; starting tile
+ ld c, 12 + 1
+.line
+ ld b, 20
+.tile
+ ld [hli], a
+ inc a
+ dec b
+ jr nz, .tile
+; next line
+ add hl, de
+ dec c
+ jr nz, .line
+ ret
+; 0x9a7a
+
+SGBDelayCycles:
+ ld de, 7000
+.wait
+ nop
+ nop
+ nop
+ dec de
+ ld a, d
+ or e
+ jr nz, .wait
+ ret
+
+INCLUDE "gfx/sgb/blk_packets.asm"
+INCLUDE "gfx/sgb/pal_packets.asm"
+INCLUDE "data/sgb_ctrl_packets.asm"
+
+PredefPals:
+INCLUDE "gfx/sgb/predef.pal"
+
+SGBBorderMap:
+; interleaved tile ids and palette ids
+INCBIN "gfx/sgb/sgb_border.bin"
+
+SGBBorderPalettes:
+INCLUDE "gfx/sgb/sgb_border.pal"
+
+SGBBorder:
+INCBIN "gfx/sgb/sgb_border.2bpp"
+
+HPBarPals:
+INCLUDE "gfx/battle/hp_bar.pal"
+
+ExpBarPalette:
+INCLUDE "gfx/battle/exp_bar.pal"
+
+INCLUDE "data/pokemon/palettes.asm"
+
+INCLUDE "data/trainers/palettes.asm"
+
+LoadMapPals:
+ farcall LoadSpecialMapPalette
+ jr c, .got_pals
+
+ ; Which palette group is based on whether we're outside or inside
+ ld a, [wEnvironment]
+ and 7
+ ld e, a
+ ld d, 0
+ ld hl, EnvironmentColorsPointers
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ; Futher refine by time of day
+ ld a, [wTimeOfDayPal]
+ maskbits NUM_DAYTIMES
+ add a
+ add a
+ add a
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld e, l
+ ld d, h
+ ; Switch to palettes WRAM bank
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wBGPals1)
+ ld [rSVBK], a
+ ld hl, wBGPals1
+ ld b, 8
+.outer_loop
+ ld a, [de] ; lookup index for TilesetBGPalette
+ push de
+ push hl
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld de, TilesetBGPalette
+ add hl, de
+ ld e, l
+ ld d, h
+ pop hl
+ ld c, 1 palettes
+.inner_loop
+ ld a, [de]
+ inc de
+ ld [hli], a
+ dec c
+ jr nz, .inner_loop
+ pop de
+ inc de
+ dec b
+ jr nz, .outer_loop
+ pop af
+ ld [rSVBK], a
+
+.got_pals
+ ld a, [wTimeOfDayPal]
+ maskbits NUM_DAYTIMES
+ ld bc, 8 palettes
+ ld hl, MapObjectPals
+ call AddNTimes
+ ld de, wOBPals1
+ ld bc, 8 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+
+ ld a, [wEnvironment]
+ cp TOWN
+ jr z, .outside
+ cp ROUTE
+ ret nz
+.outside
+ ld a, [wMapGroup]
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld de, RoofPals
+ add hl, de
+ ld a, [wTimeOfDayPal]
+ maskbits NUM_DAYTIMES
+ cp NITE_F
+ jr c, .morn_day
+rept 4
+ inc hl
+endr
+.morn_day
+ ld de, wBGPals1 palette PAL_BG_ROOF color 1
+ ld bc, 4
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+
+INCLUDE "data/maps/environment_colors.asm"
+
+PartyMenuBGMobilePalette:
+INCLUDE "gfx/stats/party_menu_bg_mobile.pal"
+
+PartyMenuBGPalette:
+INCLUDE "gfx/stats/party_menu_bg.pal"
+
+TilesetBGPalette:
+INCLUDE "gfx/tilesets/bg_tiles.pal"
+
+MapObjectPals::
+INCLUDE "gfx/overworld/npc_sprites.pal"
+
+RoofPals:
+INCLUDE "gfx/tilesets/roofs.pal"
+
+DiplomaPalettes:
+INCLUDE "gfx/diploma/diploma.pal"
+
+PartyMenuOBPals:
+INCLUDE "gfx/stats/party_menu_ob.pal"
+
+UnusedGSTitleBGPals:
+INCLUDE "gfx/title/unused_gs_bg.pal"
+
+UnusedGSTitleOBPals:
+INCLUDE "gfx/title/unused_gs_fg.pal"
+
+MalePokegearPals:
+INCLUDE "gfx/pokegear/pokegear.pal"
+
+FemalePokegearPals:
+INCLUDE "gfx/pokegear/pokegear_f.pal"
+
+Palettes_SCGB_11:
+INCLUDE "gfx/unknown/b789.pal"
+
+SlotMachinePals:
+INCLUDE "gfx/slots/slots.pal"
diff --git a/engine/gfx/crystal_layouts.asm b/engine/gfx/crystal_layouts.asm
new file mode 100644
index 000000000..71e2e4f56
--- /dev/null
+++ b/engine/gfx/crystal_layouts.asm
@@ -0,0 +1,325 @@
+GetMysteryGift_MobileAdapterLayout: ; 4930f (mobile)
+ ld a, b
+ cp SCGB_RAM
+ jr nz, .not_ram
+ ld a, [wSGBPredef]
+.not_ram
+ push af
+ farcall ResetBGPals
+ pop af
+ ld l, a
+ ld h, 0
+ add hl, hl
+ ld de, .dw
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, .done
+ push de
+ jp hl
+.done
+ ret
+; 49330 (12:5330)
+
+.dw ; 49330
+ dw MG_Mobile_Layout00
+ dw MG_Mobile_Layout01
+ dw MG_Mobile_Layout02
+; 49336
+
+MG_Mobile_Layout_FillBox: ; 49336
+.row
+ push bc
+ push hl
+.col
+ ld [hli], a
+ dec c
+ jr nz, .col
+ pop hl
+ ld bc, SCREEN_WIDTH
+ add hl, bc
+ pop bc
+ dec b
+ jr nz, .row
+ ret
+; 49346
+
+MG_Mobile_Layout_WipeAttrMap: ; 49346 (12:5346)
+ hlcoord 0, 0, wAttrMap
+ ld bc, SCREEN_HEIGHT * SCREEN_WIDTH
+ xor a
+ call ByteFill
+ ret
+
+MG_Mobile_Layout_LoadPals: ; 49351 (12:5351)
+ ld de, wBGPals1
+ ld hl, Palette_MysteryGiftMobile
+ ld bc, 5 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ld de, wBGPals1 palette PAL_BG_TEXT
+ ld hl, Palette_TextBG7
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+
+MG_Mobile_Layout00: ; 4936e (12:536e)
+ call MG_Mobile_Layout_LoadPals
+ call MG_Mobile_Layout_WipeAttrMap
+ call MG_Mobile_Layout_CreatePalBoxes
+ farcall ApplyAttrMap
+ farcall ApplyPals
+ ret
+
+MG_Mobile_Layout_CreatePalBoxes: ; 49384 (12:5384)
+ hlcoord 0, 0, wAttrMap
+ lb bc, 4, 1
+ ld a, $1
+ call MG_Mobile_Layout_FillBox
+ lb bc, 2, 1
+ ld a, $2
+ call MG_Mobile_Layout_FillBox
+ lb bc, 6, 1
+ ld a, $3
+ call MG_Mobile_Layout_FillBox
+ hlcoord 1, 0, wAttrMap
+ ld a, $1
+ lb bc, 3, 18
+ call MG_Mobile_Layout_FillBox
+ lb bc, 2, 18
+ ld a, $2
+ call MG_Mobile_Layout_FillBox
+ lb bc, 12, 18
+ ld a, $3
+ call MG_Mobile_Layout_FillBox
+ hlcoord 19, 0, wAttrMap
+ lb bc, 4, 1
+ ld a, $1
+ call MG_Mobile_Layout_FillBox
+ lb bc, 2, 1
+ ld a, $2
+ call MG_Mobile_Layout_FillBox
+ lb bc, 6, 1
+ ld a, $3
+ call MG_Mobile_Layout_FillBox
+ hlcoord 0, 12, wAttrMap
+ ld bc, 6 * SCREEN_WIDTH
+ ld a, $7
+ call ByteFill
+ ret
+; 493e1 (12:53e1)
+
+Palette_MysteryGiftMobile: ; 493e1
+INCLUDE "gfx/mystery_gift/mg_mobile.pal"
+; 49409
+
+LoadOW_BGPal7:: ; 49409
+ ld hl, Palette_TextBG7
+ ld de, wBGPals1 palette PAL_BG_TEXT
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+; 49418
+
+Palette_TextBG7: ; 49418
+INCLUDE "gfx/font/bg_text.pal"
+; 49420
+
+Function49420:: ; 49420 (12:5420)
+ ld hl, MansionPalette1 + 8 palettes
+ ld de, wBGPals1 palette PAL_BG_ROOF
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+; 4942f (12:542f)
+
+MG_Mobile_Layout01: ; 4942f
+ call MG_Mobile_Layout_LoadPals
+ ld de, wBGPals1 palette PAL_BG_TEXT
+ ld hl, .Palette_49478
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call MG_Mobile_Layout_WipeAttrMap
+ hlcoord 0, 0, wAttrMap
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ xor a
+ call ByteFill
+ hlcoord 0, 14, wAttrMap
+ ld bc, 4 * SCREEN_WIDTH
+ ld a, $7
+ call ByteFill
+ ld a, [wd002]
+ bit 6, a
+ jr z, .asm_49464
+ call Function49480
+ jr .asm_49467
+
+.asm_49464
+ call Function49496
+
+.asm_49467
+ farcall ApplyAttrMap
+ farcall ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+; 49478
+
+.Palette_49478: ; 49478
+ RGB 31, 31, 31
+ RGB 26, 31, 00
+ RGB 20, 16, 03
+ RGB 00, 00, 00
+; 49480
+
+Function49480: ; 49480
+ hlcoord 0, 0, wAttrMap
+ lb bc, 4, SCREEN_WIDTH
+ ld a, $7
+ call MG_Mobile_Layout_FillBox
+ hlcoord 0, 2, wAttrMap
+ ld a, $4
+ ld [hl], a
+ hlcoord 19, 2, wAttrMap
+ ld [hl], a
+ ret
+; 49496
+
+Function49496: ; 49496
+ hlcoord 0, 0, wAttrMap
+ lb bc, 2, SCREEN_WIDTH
+ ld a, $7
+ call MG_Mobile_Layout_FillBox
+ hlcoord 0, 1, wAttrMap
+ ld a, $4
+ ld [hl], a
+ hlcoord 19, 1, wAttrMap
+ ld [hl], a
+ ret
+; 494ac
+
+INCLUDE "engine/tilesets/tileset_palettes.asm"
+
+MG_Mobile_Layout02: ; 49706
+ ld hl, .Palette_49732
+ ld de, wBGPals1
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ farcall ApplyPals
+ call MG_Mobile_Layout_WipeAttrMap
+ farcall ApplyAttrMap
+ ld hl, .Palette_4973a
+ ld de, wOBPals1
+ ld bc, 1 palettes
+ ld a, BANK(wOBPals1)
+ call FarCopyWRAM
+ ret
+; 49732
+
+.Palette_49732: ; 49732
+ RGB 31, 31, 31
+ RGB 23, 16, 07
+ RGB 23, 07, 07
+ RGB 03, 07, 20
+; 4973a
+
+.Palette_4973a: ; 4973a
+ RGB 00, 00, 00
+ RGB 07, 05, 31
+ RGB 14, 18, 31
+ RGB 31, 31, 31
+; 49742
+
+Function49742: ; 49742
+ ld hl, .Palette_49757
+ ld de, wBGPals1
+ ld bc, 8 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ farcall ApplyPals
+ ret
+; 49757
+
+.Palette_49757: ; 49757
+INCLUDE "gfx/unknown/49757.pal"
+; 49797
+
+_InitMG_Mobile_LinkTradePalMap: ; 49797
+ hlcoord 0, 0, wAttrMap
+ lb bc, 16, 2
+ ld a, $4
+ call MG_Mobile_Layout_FillBox
+ ld a, $3
+ ldcoord_a 0, 1, wAttrMap
+ ldcoord_a 0, 14, wAttrMap
+ hlcoord 2, 0, wAttrMap
+ lb bc, 8, 18
+ ld a, $5
+ call MG_Mobile_Layout_FillBox
+ hlcoord 2, 8, wAttrMap
+ lb bc, 8, 18
+ ld a, $6
+ call MG_Mobile_Layout_FillBox
+ hlcoord 0, 16, wAttrMap
+ lb bc, 2, SCREEN_WIDTH
+ ld a, $4
+ call MG_Mobile_Layout_FillBox
+ ld a, $3
+ lb bc, 6, 1
+ hlcoord 6, 1, wAttrMap
+ call MG_Mobile_Layout_FillBox
+ ld a, $3
+ lb bc, 6, 1
+ hlcoord 17, 1, wAttrMap
+ call MG_Mobile_Layout_FillBox
+ ld a, $3
+ lb bc, 6, 1
+ hlcoord 6, 9, wAttrMap
+ call MG_Mobile_Layout_FillBox
+ ld a, $3
+ lb bc, 6, 1
+ hlcoord 17, 9, wAttrMap
+ call MG_Mobile_Layout_FillBox
+ ld a, $2
+ hlcoord 2, 16, wAttrMap
+ ld [hli], a
+ ld a, $7
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld a, $2
+ ld [hl], a
+ hlcoord 2, 17, wAttrMap
+ ld a, $3
+ ld bc, 6
+ call ByteFill
+ ret
+; 49811
+
+LoadTradeRoomBGPals: ; 49811
+ ld hl, TradeRoomPalette
+ ld de, wBGPals1 palette PAL_BG_GREEN
+ ld bc, 6 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ farcall ApplyPals
+ ret
+; 49826
+
+TradeRoomPalette: ; 49826
+INCLUDE "gfx/trade/border.pal"
+; 49856
+
+InitMG_Mobile_LinkTradePalMap: ; 49856
+ call _InitMG_Mobile_LinkTradePalMap
+ ret
+; 4985a
+
+; unused
+INCLUDE "gfx/unknown/4985a.asm"
diff --git a/engine/gfx/dma_transfer.asm b/engine/gfx/dma_transfer.asm
new file mode 100644
index 000000000..e22adf69a
--- /dev/null
+++ b/engine/gfx/dma_transfer.asm
@@ -0,0 +1,626 @@
+HDMATransferAttrMapAndTileMapToWRAMBank3:: ; 104000
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ decoord 0, 0, wAttrMap
+ ld hl, wScratchAttrMap
+ call PadAttrMapForHDMATransfer
+ decoord 0, 0
+ ld hl, wScratchTileMap
+ call PadTilemapForHDMATransfer
+ ld a, $0
+ ld [rVBK], a
+ ld hl, wScratchTileMap
+ call HDMATransferToWRAMBank3
+ ld a, $1
+ ld [rVBK], a
+ ld hl, wScratchAttrMap
+ call HDMATransferToWRAMBank3
+ ret
+; 10402d
+
+HDMATransferTileMapToWRAMBank3:: ; 10402d
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ decoord 0, 0
+ ld hl, wScratchTileMap
+ call PadTilemapForHDMATransfer
+ ld a, $0
+ ld [rVBK], a
+ ld hl, wScratchTileMap
+ call HDMATransferToWRAMBank3
+ ret
+; 104047
+
+HDMATransferAttrMapToWRAMBank3: ; 104047
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ decoord 0, 0, wAttrMap
+ ld hl, wScratchAttrMap
+ call PadAttrMapForHDMATransfer
+ ld a, $1
+ ld [rVBK], a
+ ld hl, wScratchAttrMap
+ call HDMATransferToWRAMBank3
+ ret
+; 104061
+
+ReloadMapPart:: ; 104061
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ decoord 0, 0, wAttrMap
+ ld hl, wScratchAttrMap
+ call PadAttrMapForHDMATransfer
+ decoord 0, 0
+ ld hl, wScratchTileMap
+ call PadTilemapForHDMATransfer
+ call DelayFrame
+
+ di
+ ld a, [rVBK]
+ push af
+ ld a, $1
+ ld [rVBK], a
+ ld hl, wScratchAttrMap
+ call HDMATransfer_Wait127Scanlines_toBGMap
+ ld a, $0
+ ld [rVBK], a
+ ld hl, wScratchTileMap
+ call HDMATransfer_Wait127Scanlines_toBGMap
+ pop af
+ ld [rVBK], a
+ ei
+
+ ret
+
+Mobile_ReloadMapPart: ; 104099
+ ld hl, ReloadMapPart ; useless
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ decoord 0, 0, wAttrMap
+ ld hl, wScratchAttrMap
+ call PadAttrMapForHDMATransfer
+ decoord 0, 0
+ ld hl, wScratchTileMap
+ call PadTilemapForHDMATransfer
+ call DelayFrame
+
+ di
+ ld a, [rVBK]
+ push af
+ ld a, $1
+ ld [rVBK], a
+ ld hl, wScratchAttrMap
+ call HDMATransfer_NoDI
+ ld a, $0
+ ld [rVBK], a
+ ld hl, wScratchTileMap
+ call HDMATransfer_NoDI
+ pop af
+ ld [rVBK], a
+ ei
+
+ ret
+; 1040d4
+
+; unused
+ ld hl, .unreferenced_1040da
+ jp CallInSafeGFXMode
+
+.unreferenced_1040da
+ ld a, $1
+ ld [rVBK], a
+ ld a, BANK(w3_d800)
+ ld [rSVBK], a
+ ld de, w3_d800
+ ld a, [hBGMapAddress + 1]
+ ld [rHDMA1], a
+ ld a, [hBGMapAddress]
+ ld [rHDMA2], a
+ ld a, d
+ ld [rHDMA3], a
+ ld a, e
+ ld [rHDMA4], a
+ ld a, $23
+ ld [hDMATransfer], a
+ call WaitDMATransfer
+ ret
+; 1040fb
+
+; unused
+ ld hl, .unreferenced_104101
+ jp CallInSafeGFXMode
+
+.unreferenced_104101
+ ld a, $1
+ ld [rVBK], a
+ ld a, BANK(w3_d800)
+ ld [rSVBK], a
+ ld hl, w3_d800
+ call HDMATransferToWRAMBank3
+ ret
+; 104110
+
+OpenAndCloseMenu_HDMATransferTileMapAndAttrMap:: ; 104110
+; OpenText
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ ; Transfer wAttrMap and Tilemap to BGMap
+ ; Fill vBGAttrs with $00
+ ; Fill vBGTiles with " "
+ decoord 0, 0, wAttrMap
+ ld hl, wScratchAttrMap
+ call PadAttrMapForHDMATransfer
+ decoord 0, 0
+ ld hl, wScratchTileMap
+ call PadTilemapForHDMATransfer
+ call DelayFrame
+
+ di
+ ld a, [rVBK]
+ push af
+ ld a, $1
+ ld [rVBK], a
+ ld hl, wScratchAttrMap
+ call HDMATransfer_Wait123Scanlines_toBGMap
+ ld a, $0
+ ld [rVBK], a
+ ld hl, wScratchTileMap
+ call HDMATransfer_Wait123Scanlines_toBGMap
+ pop af
+ ld [rVBK], a
+ ei
+ ret
+; 104148
+
+Mobile_OpenAndCloseMenu_HDMATransferTileMapAndAttrMap: ; 104148 (41:4148)
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.Function:
+ ; Transfer wAttrMap and Tilemap to BGMap
+ ; Fill vBGAttrs with $00
+ ; Fill vBGTiles with $ff
+ decoord 0, 0, wAttrMap
+ ld hl, wScratchAttrMap
+ call PadAttrMapForHDMATransfer
+ ld c, $ff
+ decoord 0, 0
+ ld hl, wScratchTileMap
+ call PadMapForHDMATransfer
+
+ ld a, $1
+ ld [rVBK], a
+ ld hl, wScratchAttrMap
+ call HDMATransfer_Wait127Scanlines_toBGMap
+ ld a, $0
+ ld [rVBK], a
+ ld hl, wScratchTileMap
+ call HDMATransfer_Wait127Scanlines_toBGMap
+ ret
+; 104177
+
+CallInSafeGFXMode: ; 104177
+ ld a, [hBGMapMode]
+ push af
+ ld a, [hMapAnims]
+ push af
+ xor a
+ ld [hBGMapMode], a
+ ld [hMapAnims], a
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wScratchTileMap)
+ ld [rSVBK], a
+ ld a, [rVBK]
+ push af
+
+ call ._hl_
+
+ pop af
+ ld [rVBK], a
+ pop af
+ ld [rSVBK], a
+ pop af
+ ld [hMapAnims], a
+ pop af
+ ld [hBGMapMode], a
+ ret
+; 10419c
+
+._hl_ ; 10419c
+ jp hl
+; 10419d
+
+
+HDMATransferToWRAMBank3: ; 10419d (41:419d)
+ call _LoadHDMAParameters
+ ld a, $23
+ ld [hDMATransfer], a
+
+WaitDMATransfer: ; 104a14
+.loop
+ call DelayFrame
+ ld a, [hDMATransfer]
+ and a
+ jr nz, .loop
+ ret
+
+HDMATransfer_Wait127Scanlines_toBGMap: ; 1041ad (41:41ad)
+; HDMA transfer from hl to [hBGMapAddress]
+; hBGMapAddress -> de
+; 2 * SCREEN_HEIGHT -> c
+ ld a, [hBGMapAddress + 1]
+ ld d, a
+ ld a, [hBGMapAddress]
+ ld e, a
+ ld c, 2 * SCREEN_HEIGHT
+ jr HDMATransfer_Wait127Scanlines
+
+HDMATransfer_Wait123Scanlines_toBGMap: ; 1041b7 (41:41b7)
+; HDMA transfer from hl to [hBGMapAddress]
+; hBGMapAddress -> de
+; 2 * SCREEN_HEIGHT -> c
+; $7b --> b
+ ld a, [hBGMapAddress + 1]
+ ld d, a
+ ld a, [hBGMapAddress]
+ ld e, a
+ ld c, 2 * SCREEN_HEIGHT
+ jr HDMATransfer_Wait123Scanlines
+; 1041c1 (41:41c1)
+
+HDMATransfer_NoDI: ; 1041c1
+; HDMA transfer from hl to [hBGMapAddress]
+; [hBGMapAddress] --> de
+; 2 * SCREEN_HEIGHT --> c
+ ld a, [hBGMapAddress + 1]
+ ld d, a
+ ld a, [hBGMapAddress]
+ ld e, a
+ ld c, 2 * SCREEN_HEIGHT
+
+ ; [rHDMA1, rHDMA2] = hl & $fff0
+ ld a, h
+ ld [rHDMA1], a
+ ld a, l
+ and $f0
+ ld [rHDMA2], a
+ ; [rHDMA3, rHDMA4] = de & $1ff0
+ ld a, d
+ and $1f
+ ld [rHDMA3], a
+ ld a, e
+ and $f0
+ ld [rHDMA4], a
+ ; b = c | %10000000
+ ld a, c
+ dec c
+ or $80
+ ld b, a
+ ; d = $7f - c + 1
+ ld a, $7f
+ sub c
+ ld d, a
+ ; while [rLY] >= d: pass
+.loop1
+ ld a, [rLY]
+ cp d
+ jr nc, .loop1
+ ; while not [rSTAT] & 3: pass
+.loop2
+ ld a, [rSTAT]
+ and $3
+ jr z, .loop2
+ ; load the 5th byte of HDMA
+ ld a, b
+ ld [rHDMA5], a
+ ; wait until rLY advances (c + 1) times
+ ld a, [rLY]
+ inc c
+ ld hl, rLY
+.loop3
+ cp [hl]
+ jr z, .loop3
+ ld a, [hl]
+ dec c
+ jr nz, .loop3
+ ld hl, rHDMA5
+ res 7, [hl]
+ ret
+; 104205
+
+HDMATransfer_Wait123Scanlines:
+ ld b, $7b
+ jr _continue_HDMATransfer
+
+
+HDMATransfer_Wait127Scanlines:
+ ld b, $7f
+_continue_HDMATransfer:
+; a lot of waiting around for hardware registers
+ ; [rHDMA1, rHDMA2] = hl & $fff0
+ ld a, h
+ ld [rHDMA1], a
+ ld a, l
+ and $f0 ; high nybble
+ ld [rHDMA2], a
+ ; [rHDMA3, rHDMA4] = de & $1ff0
+ ld a, d
+ and $1f ; lower 5 bits
+ ld [rHDMA3], a
+ ld a, e
+ and $f0 ; high nybble
+ ld [rHDMA4], a
+ ; e = c | %10000000
+ ld a, c
+ dec c
+ or $80
+ ld e, a
+ ; d = b - c + 1
+ ld a, b
+ sub c
+ ld d, a
+ ; while [rLY] >= d: pass
+.ly_loop
+ ld a, [rLY]
+ cp d
+ jr nc, .ly_loop
+
+ di
+ ; while [rSTAT] & 3: pass
+.rstat_loop_1
+ ld a, [rSTAT]
+ and $3
+ jr nz, .rstat_loop_1
+ ; while not [rSTAT] & 3: pass
+.rstat_loop_2
+ ld a, [rSTAT]
+ and $3
+ jr z, .rstat_loop_2
+ ; load the 5th byte of HDMA
+ ld a, e
+ ld [rHDMA5], a
+ ; wait until rLY advances (c + 1) times
+ ld a, [rLY]
+ inc c
+ ld hl, rLY
+.final_ly_loop
+ cp [hl]
+ jr z, .final_ly_loop
+ ld a, [hl]
+ dec c
+ jr nz, .final_ly_loop
+ ld hl, rHDMA5
+ res 7, [hl]
+ ei
+
+ ret
+; 10424e
+
+
+_LoadHDMAParameters: ; 10424e (41:424e)
+ ld a, h
+ ld [rHDMA1], a
+ ld a, l
+ ld [rHDMA2], a
+ ld a, [hBGMapAddress + 1]
+ and $1f
+ ld [rHDMA3], a
+ ld a, [hBGMapAddress]
+ ld [rHDMA4], a
+ ret
+
+PadTilemapForHDMATransfer: ; 10425f (41:425f)
+ ld c, " "
+ jr PadMapForHDMATransfer
+
+PadAttrMapForHDMATransfer: ; 104263 (41:4263)
+ ld c, $0
+
+PadMapForHDMATransfer: ; 104265 (41:4265)
+; pad a 20x18 map to 32x18 for HDMA transfer
+; back up the padding value in c to hMapObjectIndexBuffer
+ ld a, [hMapObjectIndexBuffer]
+ push af
+ ld a, c
+ ld [hMapObjectIndexBuffer], a
+
+; for each row on the screen
+ ld c, SCREEN_HEIGHT
+.loop1
+; for each tile in the row
+ ld b, SCREEN_WIDTH
+.loop2
+; copy from de to hl
+ ld a, [de]
+ inc de
+ ld [hli], a
+ dec b
+ jr nz, .loop2
+
+; load the original padding value of c into hl for 32 - 20 = 12 rows
+ ld a, [hMapObjectIndexBuffer]
+ ld b, BG_MAP_WIDTH - SCREEN_WIDTH
+.loop3
+ ld [hli], a
+ dec b
+ jr nz, .loop3
+
+ dec c
+ jr nz, .loop1
+
+; restore the original value of hMapObjectIndexBuffer
+ pop af
+ ld [hMapObjectIndexBuffer], a
+ ret
+
+
+_Get2bpp:: ; 104284
+ ; 2bpp when [rLCDC] & $80
+ ; switch to WRAM bank 6
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wScratchTileMap)
+ ld [rSVBK], a
+
+ push bc
+ push hl
+
+ ; Copy c tiles of the 2bpp from b:de to wScratchTileMap
+ ld a, b ; bank
+ ld l, c ; number of tiles
+ ld h, $0
+ ; multiply by 16 (16 bytes of a 2bpp = 8 x 8 tile)
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld b, h
+ ld c, l
+ ld h, d ; address
+ ld l, e
+ ld de, wScratchTileMap
+ call FarCopyBytes
+
+ pop hl
+ pop bc
+
+ push bc
+ call DelayFrame
+ pop bc
+
+ ld d, h
+ ld e, l
+ ld hl, wScratchTileMap
+ call HDMATransfer_Wait127Scanlines
+
+ ; restore the previous bank
+ pop af
+ ld [rSVBK], a
+ ret
+; 1042b2
+
+_Get1bpp:: ; 1042b2
+ ; 1bpp when [rLCDC] & $80
+.loop
+ ld a, c
+ cp $10
+ jp c, .bankswitch
+ jp z, .bankswitch
+ push bc
+ push hl
+ push de
+ ld c, $10
+ call .bankswitch
+ pop de
+ ld hl, $80
+ add hl, de
+ ld d, h
+ ld e, l
+ pop hl
+ lb bc, 1, 0
+ add hl, bc
+ pop bc
+ ld a, c
+ sub $10
+ ld c, a
+ jr .loop
+; 1042d6
+
+.bankswitch ; 1042d6
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wScratchTileMap)
+ ld [rSVBK], a
+
+ push bc
+ push hl
+
+ ld a, b
+ ld l, c
+ ld h, $0
+ add hl, hl ; multiply by 8
+ add hl, hl ; multiply by 8
+ add hl, hl ; multiply by 8
+ ld c, l
+ ld b, h
+ ld h, d
+ ld l, e
+ ld de, wScratchTileMap
+ call FarCopyBytesDouble_DoubleBankSwitch
+
+ pop hl
+ pop bc
+
+ push bc
+ call DelayFrame
+ pop bc
+
+ ld d, h
+ ld e, l
+ ld hl, wScratchTileMap
+ call HDMATransfer_Wait127Scanlines
+
+ pop af
+ ld [rSVBK], a
+ ret
+; 104303
+
+HDMATransfer_OnlyTopFourRows: ; 104303
+ ld hl, .Function
+ jp CallInSafeGFXMode
+; 104309
+
+.Function:
+ ld hl, wScratchTileMap
+ decoord 0, 0
+ call .Copy
+ ld hl, wScratchTileMap + $80
+ decoord 0, 0, wAttrMap
+ call .Copy
+ ld a, $1
+ ld [rVBK], a
+ ld c, $8
+ ld hl, wScratchTileMap + $80
+ debgcoord 0, 0, vBGMap1
+ call HDMATransfer_Wait127Scanlines
+ ld a, $0
+ ld [rVBK], a
+ ld c, $8
+ ld hl, wScratchTileMap
+ debgcoord 0, 0, vBGMap1
+ call HDMATransfer_Wait127Scanlines
+ ret
+
+.Copy: ; 10433a (41:433a)
+ ld b, 4
+.outer_loop
+ ld c, SCREEN_WIDTH
+.inner_loop
+ ld a, [de]
+ ld [hli], a
+ inc de
+ dec c
+ jr nz, .inner_loop
+ ld a, l
+ add BG_MAP_WIDTH - SCREEN_WIDTH
+ ld l, a
+ ld a, h
+ adc 0
+ ld h, a
+ dec b
+ jr nz, .outer_loop
+ ret
+; 104350
diff --git a/engine/gfx/load_font.asm b/engine/gfx/load_font.asm
new file mode 100644
index 000000000..40dbb9c10
--- /dev/null
+++ b/engine/gfx/load_font.asm
@@ -0,0 +1,156 @@
+INCLUDE "gfx/font.asm"
+
+; This and the following two functions are unreferenced.
+; Debug, perhaps?
+Unreferenced_fb434:
+ db 0
+
+Unreferenced_Functionfb435: ; 4b435
+ ld a, [Unreferenced_fb434]
+ and a
+ jp nz, Get1bpp_2
+ jp Get1bpp
+; fb43f
+
+Unreferenced_Functionfb43f: ; fb43f
+ ld a, [Unreferenced_fb434]
+ and a
+ jp nz, Get2bpp_2
+ jp Get2bpp
+; End unreferenced block
+; fb449
+
+_LoadStandardFont:: ; fb449
+ ld de, Font
+ ld hl, vTiles1
+ lb bc, BANK(Font), 128 ; "A" to "9"
+ ld a, [rLCDC]
+ bit rLCDC_ENABLE, a
+ jp z, Copy1bpp
+
+ ld de, Font
+ ld hl, vTiles1
+ lb bc, BANK(Font), 32 ; "A" to "]"
+ call Get1bpp_2
+ ld de, Font + 32 * LEN_1BPP_TILE
+ ld hl, vTiles1 tile $20
+ lb bc, BANK(Font), 32 ; "a" to $bf
+ call Get1bpp_2
+ ld de, Font + 64 * LEN_1BPP_TILE
+ ld hl, vTiles1 tile $40
+ lb bc, BANK(Font), 32 ; "Ä" to "←"
+ call Get1bpp_2
+ ld de, Font + 96 * LEN_1BPP_TILE
+ ld hl, vTiles1 tile $60
+ lb bc, BANK(Font), 32 ; "'" to "9"
+ call Get1bpp_2
+ ret
+; fb48a
+
+_LoadFontsExtra1:: ; fb48a
+ ld de, FontsExtra_SolidBlackGFX
+ ld hl, vTiles2 tile "■" ; $60
+ lb bc, BANK(FontsExtra_SolidBlackGFX), 1
+ call Get1bpp_2
+ ld de, PokegearPhoneIconGFX
+ ld hl, vTiles2 tile "☎" ; $62
+ lb bc, BANK(PokegearPhoneIconGFX), 1
+ call Get2bpp_2
+ ld de, FontExtra + 3 tiles ; "<BOLD_D>"
+ ld hl, vTiles2 tile "<BOLD_D>"
+ lb bc, BANK(FontExtra), 22 ; "<BOLD_D>" to "ぉ"
+ call Get2bpp_2
+ jr LoadFrame
+; fb4b0
+
+_LoadFontsExtra2:: ; fb4b0
+ ld de, FontsExtra2_UpArrowGFX
+ ld hl, vTiles2 tile "▲" ; $61
+ ld b, BANK(FontsExtra2_UpArrowGFX)
+ ld c, 1
+ call Get2bpp_2
+ ret
+; fb4be
+
+_LoadFontsBattleExtra:: ; fb4be
+ ld de, FontBattleExtra
+ ld hl, vTiles2 tile $60
+ lb bc, BANK(FontBattleExtra), 25
+ call Get2bpp_2
+ jr LoadFrame
+; fb4cc
+
+LoadFrame: ; fb4cc
+ ld a, [wTextBoxFrame]
+ maskbits NUM_FRAMES
+ ld bc, 6 * LEN_1BPP_TILE
+ ld hl, Frames
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld hl, vTiles2 tile "┌" ; $79
+ lb bc, BANK(Frames), 6 ; "┌" to "┘"
+ call Get1bpp_2
+ ld hl, vTiles2 tile " " ; $7f
+ ld de, TextBoxSpaceGFX
+ lb bc, BANK(TextBoxSpaceGFX), 1
+ call Get1bpp_2
+ ret
+; fb4f2
+
+LoadBattleFontsHPBar: ; fb4f2
+ ld de, FontBattleExtra
+ ld hl, vTiles2 tile $60
+ lb bc, BANK(FontBattleExtra), 12
+ call Get2bpp_2
+ ld hl, vTiles2 tile $70
+ ld de, FontBattleExtra + 16 tiles ; "<DO>"
+ lb bc, BANK(FontBattleExtra), 3 ; "<DO>" to "『"
+ call Get2bpp_2
+ call LoadFrame
+
+LoadHPBar: ; fb50d
+ ld de, EnemyHPBarBorderGFX
+ ld hl, vTiles2 tile $6c
+ lb bc, BANK(EnemyHPBarBorderGFX), 4
+ call Get1bpp_2
+ ld de, HPExpBarBorderGFX
+ ld hl, vTiles2 tile $73
+ lb bc, BANK(HPExpBarBorderGFX), 6
+ call Get1bpp_2
+ ld de, ExpBarGFX
+ ld hl, vTiles2 tile $55
+ lb bc, BANK(ExpBarGFX), 9
+ call Get2bpp_2
+ ld de, MobilePhoneTilesGFX + 7 tiles ; mobile phone icon
+ ld hl, vTiles2 tile $5e
+ lb bc, BANK(MobilePhoneTilesGFX), 2
+ call Get2bpp_2
+ ret
+; fb53e
+
+StatsScreen_LoadFont: ; fb53e
+ call _LoadFontsBattleExtra
+ ld de, EnemyHPBarBorderGFX
+ ld hl, vTiles2 tile $6c
+ lb bc, BANK(EnemyHPBarBorderGFX), 4
+ call Get1bpp_2
+ ld de, HPExpBarBorderGFX
+ ld hl, vTiles2 tile $78
+ lb bc, BANK(HPExpBarBorderGFX), 1
+ call Get1bpp_2
+ ld de, HPExpBarBorderGFX + 3 * LEN_1BPP_TILE
+ ld hl, vTiles2 tile $76
+ lb bc, BANK(HPExpBarBorderGFX), 2
+ call Get1bpp_2
+ ld de, ExpBarGFX
+ ld hl, vTiles2 tile $55
+ lb bc, BANK(ExpBarGFX), 8
+ call Get2bpp_2
+LoadStatsScreenPageTilesGFX: ; fb571
+ ld de, StatsScreenPageTilesGFX
+ ld hl, vTiles2 tile $31
+ lb bc, BANK(StatsScreenPageTilesGFX), 17
+ call Get2bpp_2
+ ret
+; fb57e
diff --git a/engine/gfx/load_overworld_font.asm b/engine/gfx/load_overworld_font.asm
new file mode 100644
index 000000000..f23f01c4e
--- /dev/null
+++ b/engine/gfx/load_overworld_font.asm
@@ -0,0 +1,17 @@
+LoadOverworldFont:: ; 106594
+ ld de, .OverworldFontGFX
+ ld hl, vTiles1
+ lb bc, BANK(.OverworldFontGFX), $80
+ call Get2bpp
+ ld de, .OverworldFontSpaceGFX
+ ld hl, vTiles2 tile " "
+ lb bc, BANK(.OverworldFontSpaceGFX), 1
+ call Get2bpp
+ ret
+; 1065ad
+
+.OverworldFontGFX:
+INCBIN "gfx/font/overworld.2bpp"
+
+.OverworldFontSpaceGFX:
+INCBIN "gfx/font/overworld_space.2bpp"
diff --git a/engine/gfx/load_pics.asm b/engine/gfx/load_pics.asm
new file mode 100644
index 000000000..b533ee56b
--- /dev/null
+++ b/engine/gfx/load_pics.asm
@@ -0,0 +1,491 @@
+GetUnownLetter: ; 51040
+; Return Unown letter in wUnownLetter based on DVs at hl
+
+; Take the middle 2 bits of each DV and place them in order:
+; atk def spd spc
+; .ww..xx. .yy..zz.
+
+ ; atk
+ ld a, [hl]
+ and %01100000
+ sla a
+ ld b, a
+ ; def
+ ld a, [hli]
+ and %00000110
+ swap a
+ srl a
+ or b
+ ld b, a
+
+ ; spd
+ ld a, [hl]
+ and %01100000
+ swap a
+ sla a
+ or b
+ ld b, a
+ ; spc
+ ld a, [hl]
+ and %00000110
+ srl a
+ or b
+
+; Divide by 10 to get 0-25
+ ld [hDividend + 3], a
+ xor a
+ ld [hDividend], a
+ ld [hDividend + 1], a
+ ld [hDividend + 2], a
+ ld a, $ff / NUM_UNOWN + 1
+ ld [hDivisor], a
+ ld b, 4
+ call Divide
+
+; Increment to get 1-26
+ ld a, [hQuotient + 2]
+ inc a
+ ld [wUnownLetter], a
+ ret
+
+GetMonFrontpic: ; 51077
+ ld a, [wCurPartySpecies]
+ ld [wCurSpecies], a
+ call IsAPokemon
+ ret c
+ ld a, [rSVBK]
+ push af
+ call _GetFrontpic
+ pop af
+ ld [rSVBK], a
+ ret
+
+GetAnimatedFrontpic: ; 5108b
+ ld a, [wCurPartySpecies]
+ ld [wCurSpecies], a
+ call IsAPokemon
+ ret c
+ ld a, [rSVBK]
+ push af
+ xor a
+ ld [hBGMapMode], a
+ call _GetFrontpic
+ call GetAnimatedEnemyFrontpic
+ pop af
+ ld [rSVBK], a
+ ret
+
+_GetFrontpic: ; 510a5
+ push de
+ call GetBaseData
+ ld a, [wBasePicSize]
+ and $f
+ ld b, a
+ push bc
+ call GetFrontpicPointer
+ ld a, BANK(wDecompressEnemyFrontpic)
+ ld [rSVBK], a
+ ld a, b
+ ld de, wDecompressEnemyFrontpic
+ call FarDecompress
+ pop bc
+ ld hl, wDecompressScratch
+ ld de, wDecompressEnemyFrontpic
+ call PadFrontpic
+ pop hl
+ push hl
+ ld de, wDecompressScratch
+ ld c, 7 * 7
+ ld a, [hROMBank]
+ ld b, a
+ call Get2bpp
+ pop hl
+ ret
+
+GetFrontpicPointer: ; 510d7
+ ld a, [wCurPartySpecies]
+ cp UNOWN
+ jr z, .unown
+ ld a, [wCurPartySpecies]
+ ld d, BANK(PokemonPicPointers)
+ jr .ok
+
+.unown
+ ld a, [wUnownLetter]
+ ld d, BANK(UnownPicPointers)
+
+.ok
+ ld hl, PokemonPicPointers ; UnownPicPointers
+ dec a
+ ld bc, 6
+ call AddNTimes
+ ld a, d
+ call GetFarByte
+ call FixPicBank
+ push af
+ inc hl
+ ld a, d
+ call GetFarHalfword
+ pop bc
+ ret
+
+GetAnimatedEnemyFrontpic: ; 51103
+ ld a, BANK(vTiles3)
+ ld [rVBK], a
+ push hl
+ ld de, wDecompressScratch
+ ld c, 7 * 7
+ ld a, [hROMBank]
+ ld b, a
+ call Get2bpp
+ pop hl
+ ld de, 7 * 7 tiles
+ add hl, de
+ push hl
+ ld a, BANK(wBasePicSize)
+ ld hl, wBasePicSize
+ call GetFarWRAMByte
+ pop hl
+ and $f
+ ld de, wDecompressEnemyFrontpic + 5 * 5 tiles
+ ld c, 5 * 5
+ cp 5
+ jr z, .got_dims
+ ld de, wDecompressEnemyFrontpic + 6 * 6 tiles
+ ld c, 6 * 6
+ cp 6
+ jr z, .got_dims
+ ld de, wDecompressEnemyFrontpic + 7 * 7 tiles
+ ld c, 7 * 7
+.got_dims
+
+ push hl
+ push bc
+ call LoadFrontpicTiles
+ pop bc
+ pop hl
+ ld de, wDecompressScratch
+ ld a, [hROMBank]
+ ld b, a
+ call Get2bpp
+ xor a
+ ld [rVBK], a
+ ret
+
+LoadFrontpicTiles: ; 5114f
+ ld hl, wDecompressScratch
+ swap c
+ ld a, c
+ and $f
+ ld b, a
+ ld a, c
+ and $f0
+ ld c, a
+ push bc
+ call LoadOrientedFrontpic
+ pop bc
+.loop
+ push bc
+ ld c, 0
+ call LoadOrientedFrontpic
+ pop bc
+ dec b
+ jr nz, .loop
+ ret
+
+GetMonBackpic: ; 5116c
+ ld a, [wCurPartySpecies]
+ call IsAPokemon
+ ret c
+
+ ld a, [wCurPartySpecies]
+ ld b, a
+ ld a, [wUnownLetter]
+ ld c, a
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wDecompressScratch)
+ ld [rSVBK], a
+ push de
+
+ ; These are assumed to be at the same address in their respective banks.
+ ld hl, PokemonPicPointers ; UnownPicPointers
+ ld a, b
+ ld d, BANK(PokemonPicPointers)
+ cp UNOWN
+ jr nz, .ok
+ ld a, c
+ ld d, BANK(UnownPicPointers)
+.ok
+ dec a
+ ld bc, 6
+ call AddNTimes
+ ld bc, 3
+ add hl, bc
+ ld a, d
+ call GetFarByte
+ call FixPicBank
+ push af
+ inc hl
+ ld a, d
+ call GetFarHalfword
+ ld de, wDecompressScratch
+ pop af
+ call FarDecompress
+ ld hl, wDecompressScratch
+ ld c, 6 * 6
+ call FixBackpicAlignment
+ pop hl
+ ld de, wDecompressScratch
+ ld a, [hROMBank]
+ ld b, a
+ call Get2bpp
+ pop af
+ ld [rSVBK], a
+ ret
+
+FixPicBank: ; 511c5
+; This is a thing for some reason.
+
+PICS_FIX EQU $36
+GLOBAL PICS_FIX
+
+ push hl
+ push bc
+ sub BANK(Pics_1) - PICS_FIX
+ ld c, a
+ ld b, 0
+ ld hl, .PicsBanks
+ add hl, bc
+ ld a, [hl]
+ pop bc
+ pop hl
+ ret
+
+.PicsBanks: ; 511d4
+ db BANK(Pics_1) + 0
+ db BANK(Pics_1) + 1
+ db BANK(Pics_1) + 2
+ db BANK(Pics_1) + 3
+ db BANK(Pics_1) + 4
+ db BANK(Pics_1) + 5
+ db BANK(Pics_1) + 6
+ db BANK(Pics_1) + 7
+ db BANK(Pics_1) + 8
+ db BANK(Pics_1) + 9
+ db BANK(Pics_1) + 10
+ db BANK(Pics_1) + 11
+ db BANK(Pics_1) + 12
+ db BANK(Pics_1) + 13
+ db BANK(Pics_1) + 14
+ db BANK(Pics_1) + 15
+ db BANK(Pics_1) + 16
+ db BANK(Pics_1) + 17
+ db BANK(Pics_1) + 18
+ db BANK(Pics_1) + 19
+ db BANK(Pics_1) + 20
+ db BANK(Pics_1) + 21
+ db BANK(Pics_1) + 22
+ db BANK(Pics_1) + 23
+
+Function511ec: ; 511ec
+ ld a, c
+ push de
+ ld hl, PokemonPicPointers
+ dec a
+ ld bc, 6
+ call AddNTimes
+ ld a, BANK(PokemonPicPointers)
+ call GetFarByte
+ call FixPicBank
+ push af
+ inc hl
+ ld a, BANK(PokemonPicPointers)
+ call GetFarHalfword
+ pop af
+ pop de
+ call FarDecompress
+ ret
+
+GetTrainerPic: ; 5120d
+ ld a, [wTrainerClass]
+ and a
+ ret z
+ cp NUM_TRAINER_CLASSES
+ ret nc
+ call WaitBGMap
+ xor a
+ ld [hBGMapMode], a
+ ld hl, TrainerPicPointers
+ ld a, [wTrainerClass]
+ dec a
+ ld bc, 3
+ call AddNTimes
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wDecompressScratch)
+ ld [rSVBK], a
+ push de
+ ld a, BANK(TrainerPicPointers)
+ call GetFarByte
+ call FixPicBank
+ push af
+ inc hl
+ ld a, BANK(TrainerPicPointers)
+ call GetFarHalfword
+ pop af
+ ld de, wDecompressScratch
+ call FarDecompress
+ pop hl
+ ld de, wDecompressScratch
+ ld c, 7 * 7
+ ld a, [hROMBank]
+ ld b, a
+ call Get2bpp
+ pop af
+ ld [rSVBK], a
+ call WaitBGMap
+ ld a, $1
+ ld [hBGMapMode], a
+ ret
+
+DecompressGet2bpp: ; 5125d
+; Decompress lz data from b:hl to scratch space at 6:d000, then copy it to address de.
+
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wDecompressScratch)
+ ld [rSVBK], a
+
+ push de
+ push bc
+ ld a, b
+ ld de, wDecompressScratch
+ call FarDecompress
+ pop bc
+ ld de, wDecompressScratch
+ pop hl
+ ld a, [hROMBank]
+ ld b, a
+ call Get2bpp
+
+ pop af
+ ld [rSVBK], a
+ ret
+
+FixBackpicAlignment: ; 5127c
+ push de
+ push bc
+ ld a, [wBoxAlignment]
+ and a
+ jr z, .keep_dims
+ ld a, c
+ cp 7 * 7
+ ld de, 7 * 7 tiles
+ jr z, .got_dims
+ cp 6 * 6
+ ld de, 6 * 6 tiles
+ jr z, .got_dims
+ ld de, 5 * 5 tiles
+
+.got_dims
+ ld a, [hl]
+ ld b, 0
+ ld c, 8
+.loop
+ rra
+ rl b
+ dec c
+ jr nz, .loop
+ ld a, b
+ ld [hli], a
+ dec de
+ ld a, e
+ or d
+ jr nz, .got_dims
+
+.keep_dims
+ pop bc
+ pop de
+ ret
+
+PadFrontpic: ; 512ab
+; pads frontpic to fill 7x7 box
+ ld a, b
+ cp 6
+ jr z, .six
+ cp 5
+ jr z, .five
+
+.seven_loop
+ ld c, $70
+ call LoadOrientedFrontpic
+ dec b
+ jr nz, .seven_loop
+ ret
+
+.six
+ ld c, $70
+ xor a
+ call .Fill
+.six_loop
+ ld c, $10
+ xor a
+ call .Fill
+ ld c, $60
+ call LoadOrientedFrontpic
+ dec b
+ jr nz, .six_loop
+ ret
+
+.five
+ ld c, $70
+ xor a
+ call .Fill
+.five_loop
+ ld c, $20
+ xor a
+ call .Fill
+ ld c, $50
+ call LoadOrientedFrontpic
+ dec b
+ jr nz, .five_loop
+ ld c, $70
+ xor a
+ call .Fill
+ ret
+
+.Fill:
+ ld [hli], a
+ dec c
+ jr nz, .Fill
+ ret
+
+LoadOrientedFrontpic: ; 512f2
+ ld a, [wBoxAlignment]
+ and a
+ jr nz, .x_flip
+.left_loop
+ ld a, [de]
+ inc de
+ ld [hli], a
+ dec c
+ jr nz, .left_loop
+ ret
+
+.x_flip
+ push bc
+.right_loop
+ ld a, [de]
+ inc de
+ ld b, a
+ xor a
+rept 8
+ rr b
+ rla
+endr
+ ld [hli], a
+ dec c
+ jr nz, .right_loop
+ pop bc
+ ret
diff --git a/engine/gfx/load_push_oam.asm b/engine/gfx/load_push_oam.asm
new file mode 100644
index 000000000..95f67ff73
--- /dev/null
+++ b/engine/gfx/load_push_oam.asm
@@ -0,0 +1,21 @@
+WriteOAMDMACodeToHRAM:: ; 4031
+ ld c, hTransferVirtualOAM - $ff00
+ ld b, .PushOAMEnd - .PushOAM
+ ld hl, .PushOAM
+.loop
+ ld a, [hli]
+ ld [$ff00+c], a
+ inc c
+ dec b
+ jr nz, .loop
+ ret
+
+.PushOAM: ; 403f
+ ld a, HIGH(wVirtualOAM)
+ ld [rDMA], a
+ ld a, NUM_SPRITE_OAM_STRUCTS
+.pushoam_loop
+ dec a
+ jr nz, .pushoam_loop
+ ret
+.PushOAMEnd
diff --git a/engine/gfx/mon_icons.asm b/engine/gfx/mon_icons.asm
new file mode 100644
index 000000000..5a26d2d7c
--- /dev/null
+++ b/engine/gfx/mon_icons.asm
@@ -0,0 +1,471 @@
+LoadOverworldMonIcon: ; 8e82b
+ ld a, e
+ call ReadMonMenuIcon
+ ld l, a
+ ld h, 0
+ add hl, hl
+ ld de, IconPointers
+ add hl, de
+ ld a, [hli]
+ ld e, a
+ ld d, [hl]
+ ld b, BANK(Icons)
+ ld c, 8
+ ret
+; 8e83f
+
+LoadMenuMonIcon: ; 8e83f
+ push hl
+ push de
+ push bc
+ call .LoadIcon
+ pop bc
+ pop de
+ pop hl
+ ret
+; 8e849
+
+.LoadIcon: ; 8e849
+ ld d, 0
+ ld hl, .Jumptable
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+; 8e854
+
+
+.Jumptable: ; 8e854 (23:6854)
+ dw PartyMenu_InitAnimatedMonIcon ; party menu
+ dw NamingScreen_InitAnimatedMonIcon ; naming screen
+ dw MoveList_InitAnimatedMonIcon ; moves (?)
+ dw Trade_LoadMonIconGFX ; trade
+ dw Mobile_InitAnimatedMonIcon ; mobile
+ dw Mobile_InitPartyMenuBGPal71 ; mobile
+ dw .GetPartyMenuMonIcon ; unused
+
+.GetPartyMenuMonIcon: ; 8e862 (23:6862)
+ call InitPartyMenuIcon
+ call .GetPartyMonItemGFX
+ call SetPartyMonIconAnimSpeed
+ ret
+
+.GetPartyMonItemGFX: ; 8e86c (23:686c)
+ push bc
+ ld a, [hObjectStructIndexBuffer]
+ ld hl, wPartyMon1Item
+ ld bc, PARTYMON_STRUCT_LENGTH
+ call AddNTimes
+ pop bc
+ ld a, [hl]
+ and a
+ jr z, .no_item
+ push hl
+ push bc
+ ld d, a
+ callfar ItemIsMail
+ pop bc
+ pop hl
+ jr c, .not_mail
+ ld a, $6
+ jr .got_tile
+.not_mail
+ ld a, $5
+ ; jr .got_tile
+
+.no_item
+ ld a, $4
+.got_tile
+ ld hl, SPRITEANIMSTRUCT_FRAMESET_ID
+ add hl, bc
+ ld [hl], a
+ ret
+
+Mobile_InitAnimatedMonIcon: ; 8e898 (23:6898)
+ call PartyMenu_InitAnimatedMonIcon
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld a, SPRITE_ANIM_SEQ_NULL
+ ld [hl], a
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld a, 9 * 8
+ ld [hl], a
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ ld a, 9 * 8
+ ld [hl], a
+ ret
+
+Mobile_InitPartyMenuBGPal71: ; 8e8b1 (23:68b1)
+ call InitPartyMenuIcon
+ call SetPartyMonIconAnimSpeed
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld a, SPRITE_ANIM_SEQ_NULL
+ ld [hl], a
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld a, 3 * 8
+ ld [hl], a
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ ld a, 12 * 8
+ ld [hl], a
+ ld a, c
+ ld [wc608], a
+ ld a, b
+ ld [wc608 + 1], a
+ ret
+
+PartyMenu_InitAnimatedMonIcon: ; 8e8d5 (23:68d5)
+ call InitPartyMenuIcon
+ call .SpawnItemIcon
+ call SetPartyMonIconAnimSpeed
+ ret
+
+.SpawnItemIcon: ; 8e8df (23:68df)
+ push bc
+ ld a, [hObjectStructIndexBuffer]
+ ld hl, wPartyMon1Item
+ ld bc, PARTYMON_STRUCT_LENGTH
+ call AddNTimes
+ pop bc
+ ld a, [hl]
+ and a
+ ret z
+ push hl
+ push bc
+ ld d, a
+ callfar ItemIsMail
+ pop bc
+ pop hl
+ jr c, .mail
+ ld a, SPRITE_ANIM_FRAMESET_PARTY_MON_WITH_ITEM
+ jr .okay
+
+.mail
+ ld a, SPRITE_ANIM_FRAMESET_PARTY_MON_WITH_MAIL
+.okay
+ ld hl, SPRITEANIMSTRUCT_FRAMESET_ID
+ add hl, bc
+ ld [hl], a
+ ret
+
+InitPartyMenuIcon: ; 8e908 (23:6908)
+ ld a, [wCurIconTile]
+ push af
+ ld a, [hObjectStructIndexBuffer]
+ ld hl, wPartySpecies
+ ld e, a
+ ld d, $0
+ add hl, de
+ ld a, [hl]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ call GetMemIconGFX
+ ld a, [hObjectStructIndexBuffer]
+; y coord
+ add a
+ add a
+ add a
+ add a
+ add $1c
+ ld d, a
+; x coord
+ ld e, $10
+; type is partymon icon
+ ld a, SPRITE_ANIM_INDEX_PARTY_MON
+ call InitSpriteAnimStruct
+ pop af
+ ld hl, SPRITEANIMSTRUCT_TILE_ID
+ add hl, bc
+ ld [hl], a
+ ret
+
+SetPartyMonIconAnimSpeed: ; 8e936 (23:6936)
+ push bc
+ ld a, [hObjectStructIndexBuffer]
+ ld b, a
+ call .getspeed
+ ld a, b
+ pop bc
+ ld hl, SPRITEANIMSTRUCT_DURATIONOFFSET
+ add hl, bc
+ ld [hl], a
+ rlca
+ rlca
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld [hl], a
+ ret
+
+.getspeed ; 8e94c (23:694c)
+ farcall PlacePartymonHPBar
+ call GetHPPal
+ ld e, d
+ ld d, 0
+ ld hl, .speeds
+ add hl, de
+ ld b, [hl]
+ ret
+; 8e95e (23:695e)
+
+.speeds ; 8e95e
+ db $00 ; HP_GREEN
+ db $40 ; HP_YELLOW
+ db $80 ; HP_RED
+; 8e961
+
+NamingScreen_InitAnimatedMonIcon: ; 8e961 (23:6961)
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ xor a
+ call GetIconGFX
+ depixel 4, 4, 4, 0
+ ld a, SPRITE_ANIM_INDEX_PARTY_MON
+ call InitSpriteAnimStruct
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld [hl], SPRITE_ANIM_SEQ_NULL
+ ret
+
+MoveList_InitAnimatedMonIcon: ; 8e97d (23:697d)
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ xor a
+ call GetIconGFX
+ ld d, 3 * 8 + 2 ; depixel 3, 4, 2, 4
+ ld e, 4 * 8 + 4
+ ld a, SPRITE_ANIM_INDEX_PARTY_MON
+ call InitSpriteAnimStruct
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld [hl], SPRITE_ANIM_SEQ_NULL
+ ret
+
+Trade_LoadMonIconGFX: ; 8e99a (23:699a)
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ ld a, $62
+ ld [wCurIconTile], a
+ call GetMemIconGFX
+ ret
+
+GetSpeciesIcon: ; 8e9ac
+; Load species icon into VRAM at tile a
+ push de
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ pop de
+ ld a, e
+ call GetIconGFX
+ ret
+; 8e9bc
+
+
+FlyFunction_GetMonIcon: ; 8e9bc (23:69bc)
+ push de
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ pop de
+ ld a, e
+ call GetIcon_a
+ ret
+; 8e9cc (23:69cc)
+
+Unreferenced_GetMonIcon2: ; 8e9cc
+ push de
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ pop de
+ call GetIcon_de
+ ret
+; 8e9db
+
+GetMemIconGFX: ; 8e9db (23:69db)
+ ld a, [wCurIconTile]
+GetIconGFX: ; 8e9de
+ call GetIcon_a
+ ld de, 8 tiles
+ add hl, de
+ ld de, HeldItemIcons
+ lb bc, BANK(HeldItemIcons), 2
+ call GetGFXUnlessMobile
+ ld a, [wCurIconTile]
+ add 10
+ ld [wCurIconTile], a
+ ret
+
+HeldItemIcons:
+INCBIN "gfx/icons/mail.2bpp"
+INCBIN "gfx/icons/item.2bpp"
+; 8ea17
+
+GetIcon_de: ; 8ea17
+; Load icon graphics into VRAM starting from tile de.
+ ld l, e
+ ld h, d
+ jr GetIcon
+
+GetIcon_a: ; 8ea1b
+; Load icon graphics into VRAM starting from tile a.
+ ld l, a
+ ld h, 0
+
+GetIcon: ; 8ea1e
+; Load icon graphics into VRAM starting from tile hl.
+
+; One tile is 16 bytes long.
+rept 4
+ add hl, hl
+endr
+
+ ld de, vTiles0
+ add hl, de
+ push hl
+
+; The icons are contiguous, in order and of the same
+; size, so the pointer table is somewhat redundant.
+ ld a, [wCurIcon]
+ push hl
+ ld l, a
+ ld h, 0
+ add hl, hl
+ ld de, IconPointers
+ add hl, de
+ ld a, [hli]
+ ld e, a
+ ld d, [hl]
+ pop hl
+
+ lb bc, BANK(Icons), 8
+ call GetGFXUnlessMobile
+
+ pop hl
+ ret
+; 8ea3f
+
+GetGFXUnlessMobile: ; 8ea3f
+ ld a, [wLinkMode]
+ cp LINK_MOBILE
+ jp nz, Request2bpp
+ jp Get2bpp_2
+; 8ea4a
+
+FreezeMonIcons: ; 8ea4a
+ ld hl, wSpriteAnimationStructs
+ ld e, PARTY_LENGTH
+ ld a, [wMenuCursorY]
+ ld d, a
+.loop
+ ld a, [hl]
+ and a
+ jr z, .next
+ cp d
+ jr z, .loadwithtwo
+ ld a, SPRITE_ANIM_SEQ_NULL
+ jr .ok
+
+.loadwithtwo
+ ld a, SPRITE_ANIM_SEQ_PARTY_MON_SWITCH
+
+.ok
+ push hl
+ ld c, l
+ ld b, h
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld [hl], a
+ pop hl
+
+.next
+ ld bc, $10
+ add hl, bc
+ dec e
+ jr nz, .loop
+ ret
+; 8ea71
+
+UnfreezeMonIcons: ; 8ea71
+ ld hl, wSpriteAnimationStructs
+ ld e, PARTY_LENGTH
+.loop
+ ld a, [hl]
+ and a
+ jr z, .next
+ push hl
+ ld c, l
+ ld b, h
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld [hl], SPRITE_ANIM_SEQ_PARTY_MON
+ pop hl
+.next
+ ld bc, $10
+ add hl, bc
+ dec e
+ jr nz, .loop
+ ret
+; 8ea8c (23:6a8c)
+
+HoldSwitchmonIcon: ; 8ea8c
+ ld hl, wSpriteAnimationStructs
+ ld e, PARTY_LENGTH
+ ld a, [wSwitchMon]
+ ld d, a
+.loop
+ ld a, [hl]
+ and a
+ jr z, .next
+ cp d
+ jr z, .is_switchmon
+ ld a, SPRITE_ANIM_SEQ_PARTY_MON_SELECTED
+ jr .join_back
+
+.is_switchmon
+ ld a, SPRITE_ANIM_SEQ_PARTY_MON_SWITCH
+.join_back
+ push hl
+ ld c, l
+ ld b, h
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld [hl], a
+ pop hl
+.next
+ ld bc, $10
+ add hl, bc
+ dec e
+ jr nz, .loop
+ ret
+
+ReadMonMenuIcon: ; 8eab3
+ cp EGG
+ jr z, .egg
+ dec a
+ ld hl, MonMenuIcons
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld a, [hl]
+ ret
+.egg
+ ld a, ICON_EGG
+ ret
+; 8eac4
+
+
+INCLUDE "data/pokemon/menu_icons.asm"
+
+INCLUDE "data/icon_pointers.asm"
+
+INCLUDE "gfx/icons.asm"
diff --git a/engine/gfx/pic_animation.asm b/engine/gfx/pic_animation.asm
new file mode 100644
index 000000000..8781c2fd0
--- /dev/null
+++ b/engine/gfx/pic_animation.asm
@@ -0,0 +1,1141 @@
+; Pic animation arrangement.
+
+Unused_AnimateMon_Slow_Normal: ; d0000
+ hlcoord 12, 0
+ ld a, [wBattleMode]
+ cp WILD_BATTLE
+ jr z, .wild
+ ld e, ANIM_MON_SLOW
+ ld d, $0
+ call AnimateFrontpic
+ ret
+
+.wild
+ ld e, ANIM_MON_NORMAL
+ ld d, $0
+ call AnimateFrontpic
+ ret
+; d001a
+
+AnimateMon_Menu: ; d001a
+ ld e, ANIM_MON_MENU
+ ld d, $0
+ call AnimateFrontpic
+ ret
+; d0022
+
+AnimateMon_Trade: ; d0022
+ ld e, ANIM_MON_TRADE
+ ld d, $0
+ call AnimateFrontpic
+ ret
+; d002a
+
+AnimateMon_Evolve: ; d002a
+ ld e, ANIM_MON_EVOLVE
+ ld d, $0
+ call AnimateFrontpic
+ ret
+; d0032
+
+AnimateMon_Hatch: ; d0032
+ ld e, ANIM_MON_HATCH
+ ld d, $0
+ call AnimateFrontpic
+ ret
+; d003a
+
+AnimateMon_Unused: ; d003a
+ ld e, ANIM_MON_UNUSED
+ ld d, $0
+ call AnimateFrontpic
+ ret
+; d0042
+
+pokeanim: MACRO
+ rept _NARG
+; Workaround for a bug where macro args can't come after the start of a symbol
+if !DEF(\1_POKEANIM)
+\1_POKEANIM EQUS "PokeAnim_\1_"
+endc
+ db (\1_POKEANIM - PokeAnim_SetupCommands) / 2
+ shift
+ endr
+ db (PokeAnim_Finish_ - PokeAnim_SetupCommands) / 2
+ENDM
+
+PokeAnims: ; d0042
+ dw .Slow
+ dw .Normal
+ dw .Menu
+ dw .Trade
+ dw .Evolve
+ dw .Hatch
+ dw .Unused ; same as .Menu
+ dw .Egg1
+ dw .Egg2
+
+.Slow: pokeanim StereoCry, Setup2, Play
+.Normal: pokeanim StereoCry, Setup, Play
+.Menu: pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
+.Trade: pokeanim Idle, Play2, Idle, Play, SetWait, Wait, Cry, Setup, Play
+.Evolve: pokeanim Idle, Play, SetWait, Wait, CryNoWait, Setup, Play
+.Hatch: pokeanim Idle, Play, CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
+.Unused: pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
+.Egg1: pokeanim Setup, Play
+.Egg2: pokeanim Idle, Play
+
+
+AnimateFrontpic: ; d008e
+ call AnimateMon_CheckIfPokemon
+ ret c
+ call LoadMonAnimation
+.loop
+ call SetUpPokeAnim
+ push af
+ farcall HDMATransferTileMapToWRAMBank3
+ pop af
+ jr nc, .loop
+ ret
+; d00a3
+
+LoadMonAnimation: ; d00a3
+ push hl
+ ld c, e
+ ld b, 0
+ ld hl, PokeAnims
+ add hl, bc
+ add hl, bc
+ ld a, [hli]
+ ld b, [hl]
+ ld c, a
+ pop hl
+ call PokeAnim_InitPicAttributes
+ ret
+; d00b4
+
+SetUpPokeAnim: ; d00b4
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wPokeAnimSceneIndex)
+ ld [rSVBK], a
+ ld a, [wPokeAnimSceneIndex]
+ ld c, a
+ ld b, 0
+ ld hl, wPokeAnimPointer
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ add hl, bc
+ ld a, [hl]
+ ld hl, PokeAnim_SetupCommands
+ rst JumpTable
+ ld a, [wPokeAnimSceneIndex]
+ ld c, a
+ pop af
+ ld [rSVBK], a
+ ld a, c
+ and $80
+ ret z
+ scf
+ ret
+; d00da
+
+PokeAnim_SetupCommands: ; d00da
+setup_command: MACRO
+\1_: dw \1
+ENDM
+ setup_command PokeAnim_Finish
+ setup_command PokeAnim_BasePic
+ setup_command PokeAnim_SetWait
+ setup_command PokeAnim_Wait
+ setup_command PokeAnim_Setup
+ setup_command PokeAnim_Setup2
+ setup_command PokeAnim_Idle
+ setup_command PokeAnim_Play
+ setup_command PokeAnim_Play2
+ setup_command PokeAnim_Cry
+ setup_command PokeAnim_CryNoWait
+ setup_command PokeAnim_StereoCry
+; d00f2
+
+PokeAnim_SetWait: ; d00f2
+ ld a, 18
+ ld [wPokeAnimWaitCounter], a
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+
+PokeAnim_Wait: ; d00fe
+ ld hl, wPokeAnimWaitCounter
+ dec [hl]
+ ret nz
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d010b
+
+PokeAnim_Setup: ; d010b
+ ld c, FALSE
+ ld b, 0
+ call PokeAnim_InitAnim
+ call PokeAnim_SetVBank1
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d011d
+
+PokeAnim_Setup2: ; d011d
+ ld c, FALSE
+ ld b, 4
+ call PokeAnim_InitAnim
+ call PokeAnim_SetVBank1
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d012f
+
+PokeAnim_Idle: ; d012f
+ ld c, TRUE
+ ld b, 0
+ call PokeAnim_InitAnim
+ call PokeAnim_SetVBank1
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d0141
+
+PokeAnim_Play: ; d0141
+ call PokeAnim_DoAnimScript
+ ld a, [wPokeAnimJumptableIndex]
+ bit 7, a
+ ret z
+ call PokeAnim_PlaceGraphic
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d0155
+
+PokeAnim_Play2: ; d0155
+ call PokeAnim_DoAnimScript
+ ld a, [wPokeAnimJumptableIndex]
+ bit 7, a
+ ret z
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d0166
+
+PokeAnim_BasePic: ; d0166
+ call PokeAnim_DeinitFrames
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d0171
+
+PokeAnim_Finish: ; d0171
+ call PokeAnim_DeinitFrames
+ ld hl, wPokeAnimSceneIndex
+ set 7, [hl]
+ ret
+; d017a
+
+PokeAnim_Cry: ; d017a
+ ld a, [wPokeAnimSpecies]
+ call _PlayMonCry
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d0188
+
+PokeAnim_CryNoWait: ; d0188
+ ld a, [wPokeAnimSpecies]
+ call PlayMonCry2
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d0196
+
+PokeAnim_StereoCry: ; d0196
+ ld a, $f
+ ld [wCryTracks], a
+ ld a, [wPokeAnimSpecies]
+ call PlayStereoCry2
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+; d01a9
+
+PokeAnim_DeinitFrames: ; d01a9
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wPokeAnimCoord)
+ ld [rSVBK], a
+ call PokeAnim_PlaceGraphic
+ farcall HDMATransferTileMapToWRAMBank3
+ call PokeAnim_SetVBank0
+ farcall HDMATransferAttrMapToWRAMBank3
+ pop af
+ ld [rSVBK], a
+ ret
+; d01c6
+
+AnimateMon_CheckIfPokemon: ; d01c6
+ ld a, [wCurPartySpecies]
+ cp EGG
+ jr z, .fail
+ call IsAPokemon
+ jr c, .fail
+ and a
+ ret
+
+.fail
+ scf
+ ret
+; d01d6
+
+PokeAnim_InitPicAttributes: ; d01d6
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wPokeAnimSceneIndex)
+ ld [rSVBK], a
+
+ push bc
+ push de
+ push hl
+ ld hl, wPokeAnimSceneIndex
+ ld bc, wPokeAnimStructEnd - wPokeAnimSceneIndex
+ xor a
+ call ByteFill
+ pop hl
+ pop de
+ pop bc
+
+; bc contains anim pointer
+ ld a, c
+ ld [wPokeAnimPointer], a
+ ld a, b
+ ld [wPokeAnimPointer + 1], a
+; hl contains tilemap coords
+ ld a, l
+ ld [wPokeAnimCoord], a
+ ld a, h
+ ld [wPokeAnimCoord + 1], a
+; d = start tile
+ ld a, d
+ ld [wPokeAnimGraphicStartTile], a
+
+ ld a, BANK(wCurPartySpecies)
+ ld hl, wCurPartySpecies
+ call GetFarWRAMByte
+ ld [wPokeAnimSpecies], a
+
+ ld a, BANK(wUnownLetter)
+ ld hl, wUnownLetter
+ call GetFarWRAMByte
+ ld [wPokeAnimUnownLetter], a
+
+ call PokeAnim_GetSpeciesOrUnown
+ ld [wPokeAnimSpeciesOrUnown], a
+
+ call PokeAnim_GetFrontpicDims
+ ld a, c
+ ld [wPokeAnimFrontpicHeight], a
+
+ pop af
+ ld [rSVBK], a
+ ret
+; d0228
+
+PokeAnim_InitAnim: ; d0228
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wPokeAnimIdleFlag)
+ ld [rSVBK], a
+ push bc
+ ld hl, wPokeAnimIdleFlag
+ ld bc, wPokeAnimStructEnd - wPokeAnimIdleFlag
+ xor a
+ call ByteFill
+ pop bc
+ ld a, b
+ ld [wPokeAnimSpeed], a
+ ld a, c
+ ld [wPokeAnimIdleFlag], a
+ call GetMonAnimPointer
+ call GetMonFramesPointer
+ call GetMonBitmaskPointer
+ pop af
+ ld [rSVBK], a
+ ret
+; d0250
+
+PokeAnim_DoAnimScript: ; d0250
+ xor a
+ ld [hBGMapMode], a
+.loop
+ ld a, [wPokeAnimJumptableIndex]
+ and $7f
+ ld hl, .Jumptable
+ rst JumpTable
+ ret
+; d025d
+
+.Jumptable: ; d025d
+ dw .RunAnim
+ dw .WaitAnim
+; d0261
+
+.RunAnim: ; d0261
+ call PokeAnim_GetPointer
+ ld a, [wPokeAnimCommand]
+ cp -1
+ jr z, PokeAnim_End
+ cp -2
+ jr z, .SetRepeat
+ cp -3
+ jr z, .DoRepeat
+ call PokeAnim_GetFrame
+ ld a, [wPokeAnimParameter]
+ call PokeAnim_GetDuration
+ ld [wPokeAnimWaitCounter], a
+ call PokeAnim_StartWaitAnim
+.WaitAnim: ; d0282
+ ld a, [wPokeAnimWaitCounter]
+ dec a
+ ld [wPokeAnimWaitCounter], a
+ ret nz
+ call PokeAnim_StopWaitAnim
+ ret
+; d028e
+
+.SetRepeat: ; d028e
+ ld a, [wPokeAnimParameter]
+ ld [wPokeAnimRepeatTimer], a
+ jr .loop
+; d0296
+
+.DoRepeat: ; d0296
+ ld a, [wPokeAnimRepeatTimer]
+ and a
+ ret z
+ dec a
+ ld [wPokeAnimRepeatTimer], a
+ ret z
+ ld a, [wPokeAnimParameter]
+ ld [wPokeAnimFrame], a
+ jr .loop
+; d02a8
+
+PokeAnim_End: ; d02a8
+ ld hl, wPokeAnimJumptableIndex
+ set 7, [hl]
+ ret
+; d02ae
+
+PokeAnim_GetDuration: ; d02ae
+; a * (1 + [wPokeAnimSpeed] / 16)
+ ld c, a
+ ld b, $0
+ ld hl, 0
+ ld a, [wPokeAnimSpeed]
+ call AddNTimes
+ ld a, h
+ swap a
+ and $f0
+ ld h, a
+ ld a, l
+ swap a
+ and $f
+ or h
+ add c
+ ret
+; d02c8
+
+PokeAnim_GetFrame: ; d02c8
+ call PokeAnim_PlaceGraphic
+ ld a, [wPokeAnimCommand]
+ and a
+ ret z
+ call PokeAnim_GetBitmaskIndex
+ push hl
+ call PokeAnim_CopyBitmaskToBuffer
+ pop hl
+ call PokeAnim_ConvertAndApplyBitmask
+ ret
+; d02dc
+
+PokeAnim_StartWaitAnim: ; d02dc
+ ld a, [wPokeAnimJumptableIndex]
+ inc a
+ ld [wPokeAnimJumptableIndex], a
+ ret
+; d02e4
+
+PokeAnim_StopWaitAnim: ; d02e4
+ ld a, [wPokeAnimJumptableIndex]
+ dec a
+ ld [wPokeAnimJumptableIndex], a
+ ret
+; d02ec
+
+PokeAnim_IsUnown: ; d02ec
+ ld a, [wPokeAnimSpecies]
+ cp UNOWN
+ ret
+; d02f2
+
+PokeAnim_IsEgg: ; d02f2
+ ld a, [wPokeAnimSpecies]
+ cp EGG
+ ret
+; d02f8
+
+PokeAnim_GetPointer: ; d02f8
+ push hl
+ ld a, [wPokeAnimFrame]
+ ld e, a
+ ld d, $0
+ ld hl, wPokeAnimPointerAddr
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ add hl, de
+ add hl, de
+ ld a, [wPokeAnimPointerBank]
+ call GetFarHalfword
+ ld a, l
+ ld [wPokeAnimCommand], a
+ ld a, h
+ ld [wPokeAnimParameter], a
+ ld hl, wPokeAnimFrame
+ inc [hl]
+ pop hl
+ ret
+; d031b
+
+PokeAnim_GetBitmaskIndex: ; d031b
+ ld a, [wPokeAnimCommand]
+ dec a
+ ld c, a
+ ld b, $0
+ ld hl, wPokeAnimFramesAddr
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ add hl, bc
+ add hl, bc
+ ld a, [wPokeAnimFramesBank]
+ call GetFarHalfword
+ ld a, [wPokeAnimFramesBank]
+ call GetFarByte
+ ld [wPokeAnimCurBitmask], a
+ inc hl
+ ret
+; d033b
+
+PokeAnim_CopyBitmaskToBuffer: ; d033b
+ call .GetSize
+ push bc
+ ld hl, wPokeAnimBitmaskAddr
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, [wPokeAnimCurBitmask]
+ call AddNTimes
+ pop bc
+ ld de, wPokeAnimBitmaskBuffer
+ ld a, [wPokeAnimBitmaskBank]
+ call FarCopyBytes
+ ret
+; d0356
+
+.GetSize: ; d0356
+ push hl
+ ld a, [wPokeAnimFrontpicHeight]
+ sub 5 ; to get a number 0, 1, or 2
+ ld c, a
+ ld b, 0
+ ld hl, .Sizes
+ add hl, bc
+ ld c, [hl]
+ ld b, 0
+ pop hl
+ ret
+; d0368
+
+.Sizes: db 4, 5, 7
+
+poke_anim_box: MACRO
+y = 7
+rept \1
+x = 7 + -\1
+rept \1
+ db x + y
+x = x + 1
+endr
+y = y + 7
+endr
+ENDM
+
+PokeAnim_ConvertAndApplyBitmask: ; d036b
+ xor a
+ ld [wPokeAnimBitmaskCurBit], a
+ ld [wPokeAnimBitmaskCurRow], a
+ ld [wPokeAnimBitmaskCurCol], a
+.loop
+ push hl
+ call .IsCurBitSet
+ pop hl
+ ld a, b
+ and a
+ jr z, .next
+
+ ld a, [wPokeAnimFramesBank]
+ call GetFarByte
+ inc hl
+ push hl
+ call .ApplyFrame
+ pop hl
+
+.next
+ push hl
+ call .NextBit
+ pop hl
+ jr nc, .loop
+ ret
+; d0392
+
+.IsCurBitSet: ; d0392
+; which byte
+ ld a, [wPokeAnimBitmaskCurBit]
+ and $f8
+ rrca
+ rrca
+ rrca
+ ld e, a
+ ld d, 0
+ ld hl, wPokeAnimBitmaskBuffer
+ add hl, de
+ ld b, [hl]
+; which bit
+ ld a, [wPokeAnimBitmaskCurBit]
+ and $7
+ jr z, .skip
+
+ ld c, a
+ ld a, b
+.loop2
+ rrca
+ dec c
+ jr nz, .loop2
+ ld b, a
+
+.skip
+ xor a
+ bit 0, b
+ jr z, .finish
+ ld a, 1
+
+.finish
+ ld b, a
+ ld hl, wPokeAnimBitmaskCurBit
+ inc [hl]
+ ret
+; d03bd
+
+.ApplyFrame: ; d03bd
+ push af
+ call .GetCoord
+ pop af
+ push hl
+ call .GetTilemap
+ ld hl, wPokeAnimGraphicStartTile
+ add [hl]
+ pop hl
+ ld [hl], a
+ ret
+; d03cd
+
+.GetCoord: ; d03cd
+ call .GetStartCoord
+ ld a, [wPokeAnimBitmaskCurRow]
+ ld bc, SCREEN_WIDTH
+ call AddNTimes
+ ld a, [wBoxAlignment]
+ and a
+ jr nz, .go
+ ld a, [wPokeAnimBitmaskCurCol]
+ ld e, a
+ ld d, 0
+ add hl, de
+ jr .skip2
+
+.go
+ ld a, [wPokeAnimBitmaskCurCol]
+ ld e, a
+ ld a, l
+ sub e
+ ld l, a
+ ld a, h
+ sbc 0
+ ld h, a
+
+.skip2
+ ret
+; d03f4
+
+; unused
+ db 6, 5, 4
+
+.GetTilemap: ; d03f7
+ push af
+ ld a, [wPokeAnimFrontpicHeight]
+ cp 5
+ jr z, .check_add_24
+ cp 6
+ jr z, .check_add_13
+ pop af
+ ret
+
+.check_add_24
+ pop af
+ cp 5 * 5
+ jr nc, .add_24
+ push hl
+ push de
+ ld hl, ._5by5
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld a, [hl]
+ pop de
+ pop hl
+ ret
+
+.add_24
+ add 24
+ ret
+
+.check_add_13
+ pop af
+ cp 6 * 6
+ jr nc, .add_13
+ push hl
+ push de
+ ld hl, ._6by6
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld a, [hl]
+ pop de
+ pop hl
+ ret
+
+.add_13
+ add 13
+ ret
+; d042f
+
+._5by5:
+ poke_anim_box 5
+ ; db 9, 10, 11, 12, 13
+ ; db 16, 17, 18, 19, 20
+ ; db 23, 24, 25, 26, 27
+ ; db 30, 31, 32, 33, 34
+ ; db 37, 38, 39, 40, 41
+
+._6by6:
+ poke_anim_box 6
+ ; db 8, 9, 10, 11, 12, 13
+ ; db 15, 16, 17, 18, 19, 20
+ ; db 22, 23, 24, 25, 26, 27
+ ; db 29, 30, 31, 32, 33, 34
+ ; db 36, 37, 38, 39, 40, 41
+ ; db 43, 44, 45, 46, 47, 48
+
+
+.GetStartCoord: ; d046c
+ ld hl, wPokeAnimCoord
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+
+ ld a, [wPokeAnimFrontpicHeight]
+ ld de, 0
+ ld bc, 6
+ cp 7
+ jr z, .okay
+ ld de, SCREEN_WIDTH + 1
+ ld bc, SCREEN_WIDTH + 5
+ cp 6
+ jr z, .okay
+ ld de, 2 * SCREEN_WIDTH + 1
+ ld bc, 2 * SCREEN_WIDTH + 5
+.okay
+
+ ld a, [wBoxAlignment]
+ and a
+ jr nz, .add_bc
+ add hl, de
+ ret
+
+.add_bc
+ add hl, bc
+ ret
+; d0499
+
+.NextBit: ; d0499
+ ld a, [wPokeAnimBitmaskCurRow]
+ inc a
+ ld [wPokeAnimBitmaskCurRow], a
+ ld c, a
+ ld a, [wPokeAnimFrontpicHeight]
+ cp c
+ jr nz, .no_carry
+ xor a
+ ld [wPokeAnimBitmaskCurRow], a
+ ld a, [wPokeAnimBitmaskCurCol]
+ inc a
+ ld [wPokeAnimBitmaskCurCol], a
+ ld c, a
+ ld a, [wPokeAnimFrontpicHeight]
+ cp c
+ jr nz, .no_carry
+ scf
+ ret
+
+.no_carry
+ xor a
+ ret
+; d04bd
+
+PokeAnim_PlaceGraphic: ; d04bd
+ call .ClearBox
+ ld a, [wBoxAlignment]
+ and a
+ jr nz, .flipped
+ ld de, 1
+ ld bc, 0
+ jr .okay
+
+.flipped
+ ld de, -1
+ ld bc, 6
+
+.okay
+ ld hl, wPokeAnimCoord
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ add hl, bc
+ ld c, 7
+ ld b, 7
+ ld a, [wPokeAnimGraphicStartTile]
+.loop
+ push bc
+ push hl
+ push de
+ ld de, SCREEN_WIDTH
+.loop2
+ ld [hl], a
+ inc a
+ add hl, de
+ dec b
+ jr nz, .loop2
+ pop de
+ pop hl
+ add hl, de
+ pop bc
+ dec c
+ jr nz, .loop
+ ret
+; d04f6
+
+.ClearBox: ; d04f6
+ ld hl, wPokeAnimCoord
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld b, 7
+ ld c, 7
+ call ClearBox
+ ret
+; d0504
+
+PokeAnim_SetVBank1: ; d0504
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wPokeAnimCoord)
+ ld [rSVBK], a
+ xor a
+ ld [hBGMapMode], a
+ call .SetFlag
+ farcall HDMATransferAttrMapToWRAMBank3
+ pop af
+ ld [rSVBK], a
+ ret
+; d051b
+
+.SetFlag: ; d051b
+ call PokeAnim_GetAttrMapCoord
+ ld b, 7
+ ld c, 7
+ ld de, SCREEN_WIDTH
+.row
+ push bc
+ push hl
+.col
+ ld a, [hl]
+ or 8
+ ld [hl], a
+ add hl, de
+ dec c
+ jr nz, .col
+ pop hl
+ inc hl
+ pop bc
+ dec b
+ jr nz, .row
+ ret
+; d0536
+
+PokeAnim_SetVBank0: ; d0536
+ call PokeAnim_GetAttrMapCoord
+ ld b, 7
+ ld c, 7
+ ld de, SCREEN_WIDTH
+.row
+ push bc
+ push hl
+.col
+ ld a, [hl]
+ and $f7
+ ld [hl], a
+ add hl, de
+ dec c
+ jr nz, .col
+ pop hl
+ inc hl
+ pop bc
+ dec b
+ jr nz, .row
+ ret
+; d0551
+
+PokeAnim_GetAttrMapCoord: ; d0551
+ ld hl, wPokeAnimCoord
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, wAttrMap - wTileMap
+ add hl, de
+ ret
+; d055c
+
+GetMonAnimPointer: ; d055c
+ call PokeAnim_IsEgg
+ jr z, .egg
+
+ ld c, BANK(UnownAnimations)
+ ld hl, UnownAnimationPointers
+ ld de, UnownAnimationIdlePointers
+ call PokeAnim_IsUnown
+ jr z, .unown
+ ld c, BANK(PicAnimations)
+ ld hl, AnimationPointers
+ ld de, AnimationIdlePointers
+.unown
+
+ ld a, [wPokeAnimIdleFlag]
+ and a
+ jr z, .idles
+ ld h, d
+ ld l, e
+.idles
+
+ ld a, [wPokeAnimSpeciesOrUnown]
+ dec a
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ ld a, c
+ ld [wPokeAnimPointerBank], a
+ call GetFarHalfword
+ ld a, l
+ ld [wPokeAnimPointerAddr], a
+ ld a, h
+ ld [wPokeAnimPointerAddr + 1], a
+ ret
+
+.egg
+ ld hl, EggAnimation
+ ld c, BANK(EggAnimation)
+ ld a, [wPokeAnimIdleFlag]
+ and a
+ jr z, .idles_egg
+ ld hl, EggAnimationIdle
+ ld c, BANK(EggAnimationIdle)
+.idles_egg
+
+ ld a, c
+ ld [wPokeAnimPointerBank], a
+ ld a, l
+ ld [wPokeAnimPointerAddr], a
+ ld a, h
+ ld [wPokeAnimPointerAddr + 1], a
+ ret
+; d05b4
+
+PokeAnim_GetFrontpicDims: ; d05b4
+ ld a, [rSVBK]
+ push af
+ ld a, BANK(wCurPartySpecies)
+ ld [rSVBK], a
+ ld a, [wCurPartySpecies]
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld a, [wBasePicSize]
+ and $f
+ ld c, a
+ pop af
+ ld [rSVBK], a
+ ret
+; d05ce
+
+GetMonFramesPointer: ; d05ce
+ call PokeAnim_IsEgg
+ jr z, .egg
+
+ call PokeAnim_IsUnown
+ ld b, BANK(UnownFramesPointers)
+ ld c, BANK(UnownsFrames)
+ ld hl, UnownFramesPointers
+ jr z, .got_frames
+ ld a, [wPokeAnimSpecies]
+ cp JOHTO_POKEMON
+ ld b, BANK(FramesPointers)
+ ld c, BANK(KantoFrames)
+ ld hl, FramesPointers
+ jr c, .got_frames
+ ld c, BANK(JohtoFrames)
+.got_frames
+ ld a, c
+ ld [wPokeAnimFramesBank], a
+
+ ld a, [wPokeAnimSpeciesOrUnown]
+ dec a
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ ld a, b
+ call GetFarHalfword
+ ld a, l
+ ld [wPokeAnimFramesAddr], a
+ ld a, h
+ ld [wPokeAnimFramesAddr + 1], a
+ ret
+
+.egg
+ ld hl, EggFrames
+ ld c, BANK(EggFrames)
+ ld a, c
+ ld [wPokeAnimFramesBank], a
+ ld a, l
+ ld [wPokeAnimFramesAddr], a
+ ld a, h
+ ld [wPokeAnimFramesAddr + 1], a
+ ret
+; d061b
+
+GetMonBitmaskPointer: ; d061b
+ call PokeAnim_IsEgg
+ jr z, .egg
+
+ call PokeAnim_IsUnown
+ ld a, BANK(UnownBitmasksPointers)
+ ld hl, UnownBitmasksPointers
+ jr z, .unown
+ ld a, BANK(BitmasksPointers)
+ ld hl, BitmasksPointers
+.unown
+ ld [wPokeAnimBitmaskBank], a
+
+ ld a, [wPokeAnimSpeciesOrUnown]
+ dec a
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ ld a, [wPokeAnimBitmaskBank]
+ call GetFarHalfword
+ ld a, l
+ ld [wPokeAnimBitmaskAddr], a
+ ld a, h
+ ld [wPokeAnimBitmaskAddr + 1], a
+ ret
+
+.egg
+ ld c, BANK(EggBitmasks)
+ ld hl, EggBitmasks
+ ld a, c
+ ld [wPokeAnimBitmaskBank], a
+ ld a, l
+ ld [wPokeAnimBitmaskAddr], a
+ ld a, h
+ ld [wPokeAnimBitmaskAddr + 1], a
+ ret
+; d065c
+
+PokeAnim_GetSpeciesOrUnown: ; d065c
+ call PokeAnim_IsUnown
+ jr z, .unown
+ ld a, [wPokeAnimSpecies]
+ ret
+
+.unown
+ ld a, [wPokeAnimUnownLetter]
+ ret
+; d0669
+
+Unused_HOF_AnimateAlignedFrontpic: ; d0669
+ ld a, $1
+ ld [wBoxAlignment], a
+
+HOF_AnimateFrontpic: ; d066e
+ call AnimateMon_CheckIfPokemon
+ jr c, .fail
+ ld h, d
+ ld l, e
+ push bc
+ push hl
+ ld de, vTiles2
+ predef GetAnimatedFrontpic
+ pop hl
+ pop bc
+ ld d, 0
+ ld e, c
+ call AnimateFrontpic
+ xor a
+ ld [wBoxAlignment], a
+ ret
+
+.fail
+ xor a
+ ld [wBoxAlignment], a
+ inc a
+ ld [wCurPartySpecies], a
+ ret
+; d0695
diff --git a/engine/gfx/place_graphic.asm b/engine/gfx/place_graphic.asm
new file mode 100644
index 000000000..21b914950
--- /dev/null
+++ b/engine/gfx/place_graphic.asm
@@ -0,0 +1,55 @@
+PlaceGraphic: ; 2ef6e
+; Fill wBoxAlignment-aligned box width b height c
+; with iterating tile starting from hGraphicStartTile at hl.
+
+ ld de, SCREEN_WIDTH
+
+ ld a, [wBoxAlignment]
+ and a
+ jr nz, .right
+
+ ld a, [hGraphicStartTile]
+.x1
+ push bc
+ push hl
+
+.y1
+ ld [hl], a
+ add hl, de
+ inc a
+ dec c
+ jr nz, .y1
+
+ pop hl
+ inc hl
+ pop bc
+ dec b
+ jr nz, .x1
+ ret
+
+.right
+; Right-aligned.
+ push bc
+ ld b, 0
+ dec c
+ add hl, bc
+ pop bc
+
+ ld a, [hGraphicStartTile]
+.x2
+ push bc
+ push hl
+
+.y2
+ ld [hl], a
+ add hl, de
+ inc a
+ dec c
+ jr nz, .y2
+
+ pop hl
+ dec hl
+ pop bc
+ dec b
+ jr nz, .x2
+ ret
diff --git a/engine/gfx/player_gfx.asm b/engine/gfx/player_gfx.asm
new file mode 100644
index 000000000..deb16ad3a
--- /dev/null
+++ b/engine/gfx/player_gfx.asm
@@ -0,0 +1,224 @@
+Unreferenced_Function88248: ; 88248
+ ld c, CAL
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .okay
+ ld c, KAREN
+
+.okay
+ ld a, c
+ ld [wTrainerClass], a
+ ret
+
+MovePlayerPicRight: ; 88258
+ hlcoord 6, 4
+ ld de, 1
+ jr MovePlayerPic
+
+MovePlayerPicLeft: ; 88260
+ hlcoord 13, 4
+ ld de, -1
+ ; fallthrough
+
+MovePlayerPic: ; 88266
+; Move player pic at hl by de * 7 tiles.
+ ld c, $8
+.loop
+ push bc
+ push hl
+ push de
+ xor a
+ ld [hBGMapMode], a
+ lb bc, 7, 7
+ predef PlaceGraphic
+ xor a
+ ld [hBGMapThird], a
+ call WaitBGMap
+ call DelayFrame
+ pop de
+ pop hl
+ add hl, de
+ pop bc
+ dec c
+ ret z
+ push hl
+ push bc
+ ld a, l
+ sub e
+ ld l, a
+ ld a, h
+ sbc d
+ ld h, a
+ lb bc, 7, 7
+ call ClearBox
+ pop bc
+ pop hl
+ jr .loop
+
+ShowPlayerNamingChoices: ; 88297
+ ld hl, ChrisNameMenuHeader
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .GotGender
+ ld hl, KrisNameMenuHeader
+.GotGender:
+ call LoadMenuHeader
+ call VerticalMenu
+ ld a, [wMenuCursorY]
+ dec a
+ call CopyNameFromMenu
+ call CloseWindow
+ ret
+
+INCLUDE "data/player_names.asm"
+
+GetPlayerNameArray: ; 88318 This Function is never called
+ ld hl, wPlayerName
+ ld de, MalePlayerNameArray
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .done
+ ld de, FemalePlayerNameArray
+
+.done
+ call InitName
+ ret
+
+GetPlayerIcon: ; 8832c
+; Get the player icon corresponding to gender
+
+; Male
+ ld de, ChrisSpriteGFX
+ ld b, BANK(ChrisSpriteGFX)
+
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .done
+
+; Female
+ ld de, KrisSpriteGFX
+ ld b, BANK(KrisSpriteGFX)
+
+.done
+ ret
+
+GetCardPic: ; 8833e
+ ld hl, ChrisCardPic
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .GotClass
+ ld hl, KrisCardPic
+.GotClass:
+ ld de, vTiles2 tile $00
+ ld bc, $23 tiles
+ ld a, BANK(ChrisCardPic) ; BANK(KrisCardPic)
+ call FarCopyBytes
+ ld hl, CardGFX
+ ld de, vTiles2 tile $23
+ ld bc, 6 tiles
+ ld a, BANK(CardGFX)
+ call FarCopyBytes
+ ret
+
+ChrisCardPic: ; 88365
+INCBIN "gfx/trainer_card/chris_card.2bpp"
+
+KrisCardPic: ; 88595
+INCBIN "gfx/trainer_card/kris_card.2bpp"
+
+CardGFX: ; 887c5
+INCBIN "gfx/trainer_card/trainer_card.2bpp"
+
+GetPlayerBackpic: ; 88825
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, GetChrisBackpic
+ call GetKrisBackpic
+ ret
+
+GetChrisBackpic: ; 88830
+ ld hl, ChrisBackpic
+ ld b, BANK(ChrisBackpic)
+ ld de, vTiles2 tile $31
+ ld c, 7 * 7
+ predef DecompressGet2bpp
+ ret
+
+HOF_LoadTrainerFrontpic: ; 88840
+ call WaitBGMap
+ xor a
+ ld [hBGMapMode], a
+ ld e, 0
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .GotClass
+ ld e, 1
+
+.GotClass:
+ ld a, e
+ ld [wTrainerClass], a
+ ld de, ChrisPic
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .GotPic
+ ld de, KrisPic
+
+.GotPic:
+ ld hl, vTiles2
+ ld b, BANK(ChrisPic) ; BANK(KrisPic)
+ ld c, 7 * 7
+ call Get2bpp
+ call WaitBGMap
+ ld a, $1
+ ld [hBGMapMode], a
+ ret
+
+DrawIntroPlayerPic: ; 88874
+; Draw the player pic at (6,4).
+
+; Get class
+ ld e, CHRIS
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .GotClass
+ ld e, KRIS
+.GotClass:
+ ld a, e
+ ld [wTrainerClass], a
+
+; Load pic
+ ld de, ChrisPic
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, .GotPic
+ ld de, KrisPic
+.GotPic:
+ ld hl, vTiles2
+ ld b, BANK(ChrisPic) ; BANK(KrisPic)
+ ld c, 7 * 7 ; dimensions
+ call Get2bpp
+
+; Draw
+ xor a
+ ld [hGraphicStartTile], a
+ hlcoord 6, 4
+ lb bc, 7, 7
+ predef PlaceGraphic
+ ret
+
+ChrisPic: ; 888a9
+INCBIN "gfx/player/chris.2bpp"
+
+KrisPic: ; 88bb9
+INCBIN "gfx/player/kris.2bpp"
+
+GetKrisBackpic: ; 88ec9
+; Kris's backpic is uncompressed.
+ ld de, KrisBackpic
+ ld hl, vTiles2 tile $31
+ lb bc, BANK(KrisBackpic), 7 * 7 ; dimensions
+ call Get2bpp
+ ret
+
+KrisBackpic: ; 88ed6
+INCBIN "gfx/player/kris_back.2bpp"
diff --git a/engine/gfx/sgb_layouts.asm b/engine/gfx/sgb_layouts.asm
new file mode 100644
index 000000000..24bdc952e
--- /dev/null
+++ b/engine/gfx/sgb_layouts.asm
@@ -0,0 +1,605 @@
+LoadSGBLayout: ; 864c
+ call CheckCGB
+ jp nz, LoadSGBLayoutCGB
+
+ ld a, b
+ cp SCGB_RAM
+ jr nz, .not_ram
+ ld a, [wSGBPredef]
+.not_ram
+ cp SCGB_PARTY_MENU_HP_PALS
+ jp z, SGB_ApplyPartyMenuHPPals
+ ld l, a
+ ld h, 0
+ add hl, hl
+ ld de, .Jumptable
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, _LoadSGBLayout_ReturnFromJumpTable
+ push de
+ jp hl
+; 866f
+
+.Jumptable: ; 866f
+ dw .SGB_BattleGrayscale
+ dw .SGB_BattleColors
+ dw .SGB_PokegearPals
+ dw .SGB_StatsScreenHPPals
+ dw .SGB_Pokedex
+ dw .SGB_SlotMachine
+ dw .SGB06
+ dw .SGB_GSIntro
+ dw .SGB_Diploma
+ dw .SGB_MapPals
+ dw .SGB_PartyMenu
+ dw .SGB_Evolution
+ dw .SGB_GSTitleScreen
+ dw .SGB0d
+ dw .SGB_MoveList
+ dw .SGB0f
+ dw .SGB_PokedexSearchOption
+ dw .SGB11
+ dw .SGB12
+ dw .SGB13
+ dw .SGB_PackPals
+ dw .SGB_TrainerCard
+ dw .SGB_PokedexUnownMode
+ dw .SGB_BillsPC
+ dw .SGB_UnownPuzzle
+ dw .SGB_GamefreakLogo
+ dw .SGB_PlayerOrMonFrontpicPals
+ dw .SGB_TradeTube
+ dw .SGB_TrainerOrMonFrontpicPals
+ dw .SGB_MysteryGift
+ dw .SGB1e
+; 86ad
+
+.SGB_BattleGrayscale: ; 86ad
+ ld hl, PalPacket_BattleGrayscale
+ ld de, BlkPacket_Battle
+ ret
+; 86b4
+
+.SGB_BattleColors: ; 86b4
+ ld hl, BlkPacket_Battle
+ call PushSGBPals_
+
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+
+ ld a, [wPlayerHPPal]
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ ld de, HPBarPals
+ add hl, de
+
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+
+ ld a, [wEnemyHPPal]
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+
+ ld de, HPBarPals
+ add hl, de
+ ld a, [hli]
+ ld [wSGBPals + 9], a
+ ld a, [hli]
+ ld [wSGBPals + 10], a
+ ld a, [hli]
+ ld [wSGBPals + 11], a
+ ld a, [hl]
+ ld [wSGBPals + 12], a
+
+ ld hl, PalPacket_9cf6
+ ld de, wSGBPals + PALPACKET_LENGTH
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+
+ call GetBattlemonBackpicPalettePointer
+
+ ld a, [hli]
+ ld [wSGBPals + 19], a
+ ld a, [hli]
+ ld [wSGBPals + 20], a
+ ld a, [hli]
+ ld [wSGBPals + 21], a
+ ld a, [hl]
+ ld [wSGBPals + 22], a
+ call GetEnemyFrontpicPalettePointer
+ ld a, [hli]
+ ld [wSGBPals + 25], a
+ ld a, [hli]
+ ld [wSGBPals + 26], a
+ ld a, [hli]
+ ld [wSGBPals + 27], a
+ ld a, [hl]
+ ld [wSGBPals + 28], a
+
+ ld hl, wSGBPals
+ ld de, wSGBPals + PALPACKET_LENGTH
+ ld a, SCGB_BATTLE_COLORS
+ ld [wSGBPredef], a
+ ret
+; 873c
+
+.SGB_MoveList: ; 873c
+ ld hl, PalPacket_9bd6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+
+ ld hl, wSGBPals + 1
+ ld [hl], $10
+ inc hl
+ inc hl
+
+ ld a, [wPlayerHPPal]
+ add PREDEFPAL_HP_GREEN
+ ld [hl], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_MoveList
+ ret
+; 875c
+
+.SGB_PokegearPals: ; 875c
+ ld hl, PalPacket_Pokegear
+ ld de, BlkPacket_9a86
+ ret
+; 8763
+
+.SGB_StatsScreenHPPals: ; 8763
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld a, [wCurHPPal]
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ ld de, HPBarPals
+ add hl, de
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ ld a, [hli]
+ ld [wSGBPals + 9], a
+ ld a, [hli]
+ ld [wSGBPals + 10], a
+ ld a, [hli]
+ ld [wSGBPals + 11], a
+ ld a, [hl]
+ ld [wSGBPals + 12], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_StatsScreen
+ ret
+; 87ab
+
+.SGB_PartyMenu: ; 87ab
+ ld hl, PalPacket_PartyMenu
+ ld de, wSGBPals + 1
+ ret
+; 87b2
+
+.SGB_Pokedex: ; 87b2
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld hl, wSGBPals + 3
+ ld [hl], LOW(palred 31 + palgreen 20 + palblue 10)
+ inc hl
+ ld [hl], HIGH(palred 31 + palgreen 20 + palblue 10)
+ inc hl
+ ld [hl], LOW(palred 26 + palgreen 10 + palblue 6)
+ inc hl
+ ld [hl], HIGH(palred 26 + palgreen 10 + palblue 6)
+ ld a, [wCurPartySpecies]
+ call GetMonPalettePointer_
+ ld a, [hli]
+ ld [wSGBPals + 9], a
+ ld a, [hli]
+ ld [wSGBPals + 10], a
+ ld a, [hli]
+ ld [wSGBPals + 11], a
+ ld a, [hl]
+ ld [wSGBPals + 12], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_Pokedex_PC
+ ret
+; 87e9
+
+.SGB_BillsPC: ; 87e9
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld hl, wSGBPals + 3
+ ld [hl], LOW(palred 31 + palgreen 20 + palblue 10)
+ inc hl
+ ld [hl], HIGH(palred 31 + palgreen 20 + palblue 10)
+ inc hl
+ ld [hl], LOW(palred 26 + palgreen 10 + palblue 6)
+ inc hl
+ ld [hl], HIGH(palred 26 + palgreen 10 + palblue 6)
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ ld a, [hli]
+ ld [wSGBPals + 9], a
+ ld a, [hli]
+ ld [wSGBPals + 10], a
+ ld a, [hli]
+ ld [wSGBPals + 11], a
+ ld a, [hl]
+ ld [wSGBPals + 12], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_Pokedex_PC
+ ret
+; 8823
+
+.SGB_PokedexUnownMode: ; 8823
+ call .SGB_Pokedex
+ ld de, BlkPacket_PokedexUnownMode
+ ret
+; 882a
+
+.SGB_PokedexSearchOption: ; 882a
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld hl, wSGBPals + 3
+ ld [hl], LOW(palred 31 + palgreen 20 + palblue 10)
+ inc hl
+ ld [hl], HIGH(palred 31 + palgreen 20 + palblue 10)
+ inc hl
+ ld [hl], LOW(palred 26 + palgreen 10 + palblue 6)
+ inc hl
+ ld [hl], HIGH(palred 26 + palgreen 10 + palblue 6)
+ ld hl, wSGBPals
+ ld de, BlkPacket_9a86
+ ret
+; 884b
+
+.SGB_PackPals: ; 884b
+ ld hl, PalPacket_Pack
+ ld de, BlkPacket_9a86
+ ret
+; 8852
+
+.SGB_SlotMachine: ; 8852
+ ld hl, PalPacket_SlotMachine
+ ld de, BlkPacket_SlotMachine
+ ret
+; 8859
+
+.SGB06: ; 8859
+ ld hl, PalPacket_SCGB_06
+ ld de, BlkPacket_SCGB_06
+ ret
+; 8860
+
+.SGB_Diploma:
+.SGB_MysteryGift: ; 8860
+ ld hl, PalPacket_Diploma
+ ld de, BlkPacket_9a86
+ ret
+; 8867
+
+.SGB_GSIntro: ; 8867
+ ld b, 0
+ ld hl, .BlkPacketTable_GSIntro
+rept 4
+ add hl, bc
+endr
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ inc hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ret
+; 8878
+
+.BlkPacketTable_GSIntro: ; 8878
+ dw BlkPacket_9a86, PalPacket_GSIntroShellderLapras
+ dw BlkPacket_GSIntroJigglypuffPikachu, PalPacket_GSIntroJigglypuffPikachu
+ dw BlkPacket_9a86, PalPacket_GSIntroStartersTransition
+; 8884
+
+.SGB_GSTitleScreen: ; 8884
+ ld hl, PalPacket_GSTitleScreen
+ ld de, BlkPacket_GSTitleScreen
+ ld a, SCGB_DIPLOMA
+ ld [wSGBPredef], a
+ ret
+; 8890
+
+.SGB13: ; 8890
+ ld hl, PalPacket_SCGB_13
+ ld de, BlkPacket_SCGB_13
+ ret
+; 8897
+
+.SGB0f: ; 8897
+ ld hl, PalPacket_SCGB_0F
+ ld de, BlkPacket_9a86
+ ret
+; 889e
+
+.SGB11: ; 889e
+ ld hl, BlkPacket_9a86
+ ld de, wPlayerLightScreenCount ; ???
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld hl, PalPacket_SCGB_11
+ ld de, BlkPacket_9a86
+ ret
+; 88b1
+
+.SGB_MapPals: ; 88b1
+ ld hl, PalPacket_9bd6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ call .GetMapPalsIndex
+ ld hl, wSGBPals + 1
+ ld [hld], a
+ ld de, BlkPacket_9a86
+ ld a, SCGB_MAPPALS
+ ld [wSGBPredef], a
+ ret
+; 88cd
+
+.SGB_Evolution: ; 88cd
+ push bc
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ pop bc
+ ld a, c
+ and a
+ jr z, .partymon
+ ; Egg
+ ld hl, wSGBPals + 3
+ ld [hl], LOW(palred 7 + palgreen 7 + palblue 7)
+ inc hl
+ ld [hl], HIGH(palred 7 + palgreen 7 + palblue 7)
+ inc hl
+ ld [hl], LOW(palred 2 + palgreen 3 + palblue 3)
+ inc hl
+ ld [hl], HIGH(palred 2 + palgreen 3 + palblue 3)
+ jr .done
+
+.partymon
+ ld hl, wPartyMon1DVs
+ ld bc, PARTYMON_STRUCT_LENGTH
+ ld a, [wCurPartyMon]
+ call AddNTimes
+ ld c, l
+ ld b, h
+ ld a, [wPlayerHPPal]
+ call GetPlayerOrMonPalettePointer
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+
+.done
+ ld hl, wSGBPals
+ ld de, BlkPacket_9a86
+ ret
+; 891a
+
+.SGB0d:
+.SGB_TrainerCard: ; 891a
+ ld hl, PalPacket_Diploma
+ ld de, BlkPacket_9a86
+ ret
+; 8921
+
+.SGB_UnownPuzzle: ; 8921
+ ld hl, PalPacket_UnownPuzzle
+ ld de, BlkPacket_9a86
+ ret
+; 8928
+
+.SGB12: ; 8928
+ ld hl, PalPacket_9bd6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld hl, BlkPacket_9a86
+ ld de, wSGBPals + PALPACKET_LENGTH
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ call .GetMapPalsIndex
+ ld hl, wSGBPals + 1
+ ld [hl], a
+ ld hl, wSGBPals + 3
+ ld [hl], $2e
+ ld hl, wSGBPals + $13
+ ld a, 5
+ ld [hli], a
+ ld a, [wMenuBorderLeftCoord]
+ ld [hli], a
+ ld a, [wMenuBorderTopCoord]
+ ld [hli], a
+ ld a, [wMenuBorderRightCoord]
+ ld [hli], a
+ ld a, [wMenuBorderBottomCoord]
+ ld [hl], a
+ ld hl, wSGBPals
+ ld de, wSGBPals + PALPACKET_LENGTH
+ ret
+; 8969
+
+.SGB1e: ; 8969
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld a, [wCurPartySpecies]
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld de, PokemonPalettes
+ add hl, de
+ ld a, [wcf65]
+ and 3
+ sla a
+ sla a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_9a86
+ ret
+; 89a6
+
+.SGB_GamefreakLogo: ; 89a6
+ ld hl, PalPacket_GamefreakLogo
+ ld de, BlkPacket_9a86
+ ret
+; 89ad
+
+.SGB_PlayerOrMonFrontpicPals: ; 89ad
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_9a86
+ ret
+; 89d9
+
+.SGB_TradeTube: ; 89d9
+ ld hl, PalPacket_TradeTube
+ ld de, BlkPacket_9a86
+ ret
+; 89e0
+
+.SGB_TrainerOrMonFrontpicPals: ; 89e0
+ ld hl, PalPacket_9ce6
+ ld de, wSGBPals
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetFrontpicPalettePointer
+ ld a, [hli]
+ ld [wSGBPals + 3], a
+ ld a, [hli]
+ ld [wSGBPals + 4], a
+ ld a, [hli]
+ ld [wSGBPals + 5], a
+ ld a, [hl]
+ ld [wSGBPals + 6], a
+ ld hl, wSGBPals
+ ld de, BlkPacket_9a86
+ ret
+; 8a0c
+
+.GetMapPalsIndex: ; 8a0c
+ ld a, [wTimeOfDayPal]
+ cp NITE_F
+ jr c, .morn_day
+ ld a, PREDEFPAL_NITE
+ ret
+
+.morn_day
+ ld a, [wEnvironment]
+ cp ROUTE
+ jr z, .route
+ cp CAVE
+ jr z, .cave
+ cp DUNGEON
+ jr z, .cave
+ cp ENVIRONMENT_5
+ jr z, .perm5
+ cp GATE
+ jr z, .gate
+ ld a, [wMapGroup]
+ ld e, a
+ ld d, 0
+ ld hl, MapGroupRoofSGBPalInds
+ add hl, de
+ ld a, [hl]
+ ret
+
+.route
+ ld a, PREDEFPAL_00
+ ret
+
+.cave
+ ld a, PREDEFPAL_DUNGEONS
+ ret
+
+.perm5
+ ld a, PREDEFPAL_VERMILION
+ ret
+
+.gate
+ ld a, PREDEFPAL_PEWTER
+ ret
+; 8a45
+
+INCLUDE "data/maps/sgb_roof_pal_inds.asm"
+
+_LoadSGBLayout_ReturnFromJumpTable: ; 8a60
+ push de
+ call PushSGBPals_
+ pop hl
+ jp PushSGBPals_
+; 8a68
diff --git a/engine/gfx/sprite_anims.asm b/engine/gfx/sprite_anims.asm
new file mode 100644
index 000000000..9353b71c9
--- /dev/null
+++ b/engine/gfx/sprite_anims.asm
@@ -0,0 +1,889 @@
+DoAnimFrame: ; 8d24b
+ ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
+ add hl, bc
+ ld e, [hl]
+ ld d, 0
+ ld hl, .Jumptable
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+; 8d25b
+
+.Jumptable: ; 8d25b (23:525b)
+; entries correspond to SPRITE_ANIM_SEQ_* constants
+ dw .Null
+ dw .PartyMon
+ dw .PartyMonSwitch
+ dw .PartyMonSelected
+ dw .GSTitleTrail
+ dw .NamingScreenCursor
+ dw .GameFreakLogo
+ dw .GSIntroStar
+ dw .GSIntroSparkle
+ dw .SlotsGolem
+ dw .SlotsChansey
+ dw .SlotsChanseyEgg
+ dw .MailCursor
+ dw .UnusedCursor
+ dw .DummyGameCursor
+ dw .PokegearArrow
+ dw .TradePokeBall
+ dw .TradeTubeBulge
+ dw .TrademonInTube
+ dw .RevealNewMon
+ dw .RadioTuningKnob
+ dw .CutLeaves
+ dw .FlyFrom
+ dw .FlyLeaf
+ dw .FlyTo
+ dw .GSIntroHoOh
+ dw .EZChatCursor
+ dw .MobileTradeSentPulse
+ dw .MobileTradeOTPulse
+ dw .IntroSuicune
+ dw .IntroPichuWooper
+ dw .Celebi
+ dw .IntroUnown
+ dw .IntroUnownF
+ dw .IntroSuicuneAway
+
+.Null: ; 8d2a1 (23:52a1)
+ ret
+
+.PartyMon ; 8d2a2 (23:52a2)
+ ld a, [wMenuCursorY]
+
+ ld hl, SPRITEANIMSTRUCT_INDEX
+ add hl, bc
+ cp [hl]
+ jr z, .PartyMonSwitch
+
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld [hl], 8 * 2
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], $0
+ ret
+
+.PartyMonSwitch ; 8d2b9 (23:52b9)
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld [hl], 8 * 3
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ ld d, a
+ inc [hl]
+ and $f
+ ret nz
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld e, [hl]
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld a, d
+ and $10 ; bit 4
+ jr z, .load_zero
+ ld a, e
+ and a
+ jr z, .load_minus_two
+ cp $1
+ jr z, .load_minus_one
+.load_zero
+ xor a
+ ld [hl], a
+ ret
+
+.load_minus_one
+ ld a, -1
+ ld [hl], a
+ ret
+
+.load_minus_two
+ ld a, -2
+ ld [hl], a
+ ret
+
+.PartyMonSelected ; 8d2ea (23:52ea)
+ ld a, [wMenuCursorY]
+
+ ld hl, SPRITEANIMSTRUCT_INDEX
+ add hl, bc
+ cp [hl]
+ jr z, .three_offset_right
+
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld [hl], 8 * 2
+ ret
+
+.three_offset_right
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld [hl], 8 * 3
+ ret
+
+.GSTitleTrail ; 8d302 (23:5302)
+ call .AnonymousJumptable
+ jp hl
+; 8d306 (23:5306)
+
+; Anonymous dw (see .AnonymousJumptable)
+ dw .four_zero
+ dw .four_one
+; 8d30a
+
+.four_zero ; 8d30a
+ call .IncrementJumptableIndex
+
+ ld hl, SPRITEANIMSTRUCT_INDEX
+ add hl, bc
+ ld a, [hl]
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ and $3
+ ld [hl], a
+ inc [hl]
+ swap a
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld [hl], a
+
+.four_one ; 8d321
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld a, [hl]
+ cp $a4
+ jr nc, .asm_8d356
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ add $4
+
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld [hl], a
+
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ inc [hl]
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld a, [hl]
+ sla a
+ sla a
+ ld d, $2
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ add $3
+ ld [hl], a
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.asm_8d356
+ call DeinitializeSprite
+ ret
+; 8d35a
+
+.GSIntroHoOh ; 8d35a (23:535a)
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ inc a
+ ld [hl], a
+ ld d, $2
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.NamingScreenCursor ; 8d36c (23:536c)
+ callfar NamingScreen_AnimateCursor
+ ret
+
+.MailCursor ; 8d373 (23:5373)
+ callfar ComposeMail_AnimateCursor
+ ret
+
+.GameFreakLogo: ; 8d37a (23:537a)
+ callfar GameFreakLogoJumper
+ ret
+
+.GSIntroStar ; 8d381 (23:5381)
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .asm_8d3ba
+ dec [hl]
+ dec [hl]
+ ld d, a
+ and $1f
+ jr nz, .asm_8d395
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ dec [hl]
+.asm_8d395
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld a, [hl]
+ push af
+ push de
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ pop de
+ pop af
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld a, [hl]
+
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ add [hl]
+ ld [hl], a
+ ret
+
+.asm_8d3ba
+ ld a, $1
+ ld [wcf64], a
+ call DeinitializeSprite
+ ret
+
+.GSIntroSparkle ; 8d3c3 (23:53c3)
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hli]
+ or [hl]
+ jr z, .asm_8d41e
+
+ ld hl, SPRITEANIMSTRUCT_0F
+ add hl, bc
+ ld d, [hl]
+
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld a, [hl]
+ push af
+ push de
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ pop de
+ pop af
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+
+ ld hl, SPRITEANIMSTRUCT_0E
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ add hl, de
+ ld e, l
+ ld d, h
+
+ ld hl, SPRITEANIMSTRUCT_0E
+ add hl, bc
+ ld [hl], e
+ inc hl
+ ld [hl], d
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, -$10
+ add hl, de
+ ld e, l
+ ld d, h
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld [hl], e
+ inc hl
+ ld [hl], d
+
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld a, [hl]
+ xor $20
+ ld [hl], a
+ ret
+
+.asm_8d41e
+ call DeinitializeSprite
+ ret
+
+.SlotsGolem: ; 8d422 (23:5422)
+ callfar Slots_AnimateGolem
+ ret
+
+.SlotsChansey: ; 8d429 (23:5429)
+ callfar Slots_AnimateChansey
+ ld hl, wcf64
+ ld a, [hl]
+ cp $2
+ ret nz
+ ld [hl], $3
+ ld a, SPRITE_ANIM_FRAMESET_SLOTS_CHANSEY_2
+ call _ReinitSpriteAnimFrame
+ ret
+
+.SlotsChanseyEgg: ; 8d43e (23:543e)
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld a, [hl]
+ dec [hl]
+ ld e, a
+ and $1
+ jr z, .move_vertical
+
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld a, [hl]
+ cp 15 * 8
+ jr c, .move_right
+ call DeinitializeSprite
+ ld a, $4
+ ld [wcf64], a
+ ld de, SFX_PLACE_PUZZLE_PIECE_DOWN
+ call PlaySFX
+ ret
+
+.move_right
+ inc [hl]
+.move_vertical
+ ld a, e
+ ld d, $20
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.UnusedCursor ; 8d46e (23:546e)
+ callfar ret_e00ed
+ ret
+
+.PokegearArrow ; 8d475 (23:5475)
+ callfar AnimatePokegearModeIndicatorArrow
+ ret
+
+.DummyGameCursor ; 8d47c (23:547c)
+ callfar DummyGame_InterpretJoypad_AnimateCursor
+ ret
+
+.TradePokeBall ; 8d483 (23:5483)
+ call .AnonymousJumptable
+ jp hl
+; 8d487 (23:5487)
+
+; Anonymous dw (see .AnonymousJumptable)
+ dw .TradePokeBall_zero
+ dw .TradePokeBall_one
+ dw .TradePokeBall_two
+ dw .TradePokeBall_three
+ dw .TradePokeBall_four
+ dw .TradePokeBall_five
+; 8d493
+
+.TradePokeBall_zero ; 8d493
+ ld a, SPRITE_ANIM_FRAMESET_TRADE_POKE_BALL_WOBBLE
+ call _ReinitSpriteAnimFrame
+
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld [hl], $2
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld [hl], $20
+ ret
+; 8d4a5
+
+.TradePokeBall_two ; 8d4a5
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .asm_8d4af
+ dec [hl]
+ ret
+
+.asm_8d4af
+ call .IncrementJumptableIndex
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld [hl], $40
+
+.TradePokeBall_three ; 8d4b8
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ cp $30
+ jr c, .asm_8d4cd
+ dec [hl]
+ ld d, $28
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.asm_8d4cd
+ ld de, SFX_GOT_SAFARI_BALLS
+ call PlaySFX
+ jr .TradePokeBall_five
+; 8d4d5
+
+.TradePokeBall_one ; 8d4d5
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld [hl], $4
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld [hl], $30
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld [hl], $24
+ ret
+; 8d4e8
+
+.TradePokeBall_four ; 8d4e8
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .asm_8d51c
+ ld d, a
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ call Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ inc [hl]
+ ld a, [hl]
+ and $3f
+ ret nz
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld [hl], $20
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld a, [hl]
+ sub $c
+ ld [hl], a
+ ld de, SFX_SWITCH_POKEMON
+ call PlaySFX
+ ret
+
+.asm_8d51c
+ xor a
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ call .IncrementJumptableIndex
+ ret
+
+.TradePokeBall_five ; 8d526
+ call DeinitializeSprite
+ ret
+; 8d52a
+
+.TradeTubeBulge ; 8d52a (23:552a)
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld a, [hl]
+ inc [hl]
+ inc [hl]
+ cp $b0
+ jr nc, .delete
+ and $3
+ ret nz
+ ld de, SFX_POKEBALLS_PLACED_ON_TABLE
+ call PlaySFX
+ ret
+
+.delete
+ call DeinitializeSprite
+ ret
+
+.TrademonInTube ; 8d543 (23:5543)
+ callfar TradeAnim_AnimateTrademonInTube
+ ret
+
+.RevealNewMon: ; 8d54a (23:554a)
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ cp $80
+ jr nc, .finish_EggShell
+ ld d, a
+ add $8
+ ld [hl], a
+
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld a, [hl]
+ xor $20
+ ld [hl], a
+
+ push af
+ push de
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+
+ pop de
+ pop af
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.finish_EggShell
+ call DeinitializeSprite
+ ret
+
+.RadioTuningKnob: ; 8d578 (23:5578)
+ callfar AnimateTuningKnob
+ ret
+
+.CutLeaves ; 8d57f (23:557f)
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, $80
+ add hl, de
+ ld e, l
+ ld d, h
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld [hl], e
+ inc hl
+ ld [hl], d
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ inc [hl]
+ inc [hl]
+ inc [hl]
+ push af
+ push de
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ pop de
+ pop af
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.FlyFrom: ; 8d5b0 (23:55b0)
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ ld a, [hl]
+ and a
+ ret z
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld a, [hl]
+ inc [hl]
+ cp $40
+ ret c
+
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ dec [hl]
+ dec [hl]
+
+ ld hl, SPRITEANIMSTRUCT_0F
+ add hl, bc
+ ld a, [hl]
+ ld d, a
+ cp $40
+ jr nc, .skip
+ add $8
+ ld [hl], a
+.skip
+ ld hl, SPRITEANIMSTRUCT_0E
+ add hl, bc
+ ld a, [hl]
+ inc [hl]
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.FlyLeaf: ; 8d5e2 (23:55e2)
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+ ld a, [hl]
+ cp -9 * 8
+ jr nc, .delete_leaf
+ inc [hl]
+ inc [hl]
+
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ dec [hl]
+
+ ld d, $40
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ inc [hl]
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.delete_leaf
+ call DeinitializeSprite
+ ret
+
+.FlyTo: ; 8d607 (23:5607)
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ ld a, [hl]
+ cp 10 * 8 + 4
+ ret z
+
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ inc [hl]
+ inc [hl]
+
+ ld hl, SPRITEANIMSTRUCT_0F
+ add hl, bc
+ ld a, [hl]
+ ld d, a
+ and a
+ jr z, .asm_8d621
+ sub $2
+ ld [hl], a
+.asm_8d621
+ ld hl, SPRITEANIMSTRUCT_0E
+ add hl, bc
+ ld a, [hl]
+ inc [hl]
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.MobileTradeSentPulse ; 8d630 (23:5630)
+ farcall Function108bc7
+ ret
+
+.MobileTradeOTPulse ; 8d637 (23:5637)
+ farcall Function108be0
+ ret
+
+.IntroSuicune ; 8d63e (23:563e)
+ ld a, [wcf65]
+ and a
+ jr nz, .asm_8d645
+ ret
+.asm_8d645
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], $0
+
+ ld hl, SPRITEANIMSTRUCT_0D
+ add hl, bc
+ ld a, [hl]
+ add $2
+ ld [hl], a
+ xor $ff
+ inc a
+ ld d, $20
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ ld a, SPRITE_ANIM_FRAMESET_INTRO_SUICUNE_2
+ call _ReinitSpriteAnimFrame
+ ret
+
+.IntroPichuWooper ; 8d666 (23:5666)
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ cp $14
+ jr nc, .asm_8d67f
+ add $2
+ ld [hl], a
+ xor $ff
+ inc a
+ ld d, $20
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+.asm_8d67f
+ ret
+
+.IntroUnown ; 8d680 (23:5680)
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld d, [hl]
+ inc [hl]
+ inc [hl]
+ inc [hl]
+
+ ld hl, SPRITEANIMSTRUCT_0C
+ add hl, bc
+ ld a, [hl]
+ push af
+ push de
+ call .Sprites_Sine
+
+ ld hl, SPRITEANIMSTRUCT_YOFFSET
+ add hl, bc
+ ld [hl], a
+ pop de
+ pop af
+ call .Sprites_Cosine
+
+ ld hl, SPRITEANIMSTRUCT_XOFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+.IntroUnownF ; 8d6a2 (23:56a2)
+ ld a, [wcf64]
+ cp $40
+ ret nz
+ ld a, SPRITE_ANIM_FRAMESET_INTRO_UNOWN_F_2
+ call _ReinitSpriteAnimFrame
+ ret
+
+.IntroSuicuneAway ; 8d6ae (23:56ae)
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ ld a, [hl]
+ add $10
+ ld [hl], a
+ ret
+
+.EZChatCursor ; 8d6b7 (23:56b7)
+ farcall AnimateEZChatCursor
+ ret
+
+.Celebi ; 8d6be (23:56be)
+ farcall UpdateCelebiPosition
+ ret
+
+.AnonymousJumptable: ; 8d6c5 (23:56c5)
+ ld hl, sp+$0
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ inc de
+
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ ld l, [hl]
+ ld h, $0
+ add hl, hl
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ret
+; 8d6d8 (23:56d8)
+
+.IncrementJumptableIndex: ; 8d6d8
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ inc [hl]
+ ret
+; 8d6de
+
+.Sprites_Sine: ; 8d6de (23:56de)
+ call Sprites_Sine
+ ret
+
+.Sprites_Cosine: ; 8d6e2 (23:56e2)
+ call Sprites_Cosine
+ ret
+; 8d6e6 (23:56e6)
diff --git a/engine/gfx/sprites.asm b/engine/gfx/sprites.asm
new file mode 100644
index 000000000..63666c624
--- /dev/null
+++ b/engine/gfx/sprites.asm
@@ -0,0 +1,677 @@
+ClearSpriteAnims: ; 8cf53
+ ld hl, wSpriteAnimDict
+ ld bc, wSpriteAnimsEnd - wSpriteAnimDict
+.loop
+ ld [hl], $0
+ inc hl
+ dec bc
+ ld a, c
+ or b
+ jr nz, .loop
+ ret
+; 8cf62
+
+PlaySpriteAnimationsAndDelayFrame: ; 8cf62
+ call PlaySpriteAnimations
+ call DelayFrame
+ ret
+; 8cf69
+
+PlaySpriteAnimations: ; 8cf69
+ push hl
+ push de
+ push bc
+ push af
+
+ ld a, LOW(wVirtualOAM)
+ ld [wCurrSpriteOAMAddr], a
+ call DoNextFrameForAllSprites
+
+ pop af
+ pop bc
+ pop de
+ pop hl
+ ret
+; 8cf7a
+
+DoNextFrameForAllSprites: ; 8cf7a
+ ld hl, wSpriteAnimationStructs
+ ld e, NUM_SPRITE_ANIM_STRUCTS
+
+.loop
+ ld a, [hl]
+ and a
+ jr z, .next ; This struct is deinitialized.
+ ld c, l
+ ld b, h
+ push hl
+ push de
+ call DoAnimFrame ; Uses a massive dw
+ call UpdateAnimFrame
+ pop de
+ pop hl
+ jr c, .done
+
+.next
+ ld bc, SPRITEANIMSTRUCT_LENGTH
+ add hl, bc
+ dec e
+ jr nz, .loop
+
+ ld a, [wCurrSpriteOAMAddr]
+ ld l, a
+ ld h, HIGH(wVirtualOAM)
+
+.loop2 ; Clear (wVirtualOAM + [wCurrSpriteOAMAddr] --> wVirtualOAMEnd)
+ ld a, l
+ cp LOW(wVirtualOAMEnd)
+ jr nc, .done
+ xor a
+ ld [hli], a
+ jr .loop2
+
+.done
+ ret
+; 8cfa8
+
+DoNextFrameForFirst16Sprites: ; 8cfa8 (23:4fa8)
+ ld hl, wSpriteAnimationStructs
+ ld e, NUM_SPRITE_ANIM_STRUCTS
+
+.loop
+ ld a, [hl]
+ and a
+ jr z, .next
+ ld c, l
+ ld b, h
+ push hl
+ push de
+ call DoAnimFrame ; Uses a massive dw
+ call UpdateAnimFrame
+ pop de
+ pop hl
+ jr c, .done
+
+.next
+ ld bc, SPRITEANIMSTRUCT_LENGTH
+ add hl, bc
+ dec e
+ jr nz, .loop
+
+ ld a, [wCurrSpriteOAMAddr]
+ ld l, a
+ ld h, HIGH(wVirtualOAMSprite16)
+
+.loop2 ; Clear (wVirtualOAM + [wCurrSpriteOAMAddr] --> Sprites + $40)
+ ld a, l
+ cp LOW(wVirtualOAMSprite16)
+ jr nc, .done
+ xor a
+ ld [hli], a
+ jr .loop2
+
+.done
+ ret
+
+InitSpriteAnimStruct:: ; 8cfd6
+; Initialize animation a at pixel x=e, y=d
+; Find if there's any room in the wSpriteAnimationStructs array, which is 10x16
+ push de
+ push af
+ ld hl, wSpriteAnimationStructs
+ ld e, NUM_SPRITE_ANIM_STRUCTS
+.loop
+ ld a, [hl]
+ and a
+ jr z, .found
+ ld bc, SPRITEANIMSTRUCT_LENGTH
+ add hl, bc
+ dec e
+ jr nz, .loop
+; We've reached the end. There is no more room here.
+; Return carry.
+ pop af
+ pop de
+ scf
+ ret
+
+.found
+; Back up the structure address to bc.
+ ld c, l
+ ld b, h
+; Value [wSpriteAnimCount] is initially set to -1. Set it to
+; the number of objects loaded into this array.
+ ld hl, wSpriteAnimCount
+ inc [hl]
+ ld a, [hl]
+ and a
+ jr nz, .initialized
+ inc [hl]
+
+.initialized
+; Get row a of SpriteAnimSeqData, copy the pointer into de
+ pop af
+ ld e, a
+ ld d, 0
+ ld hl, SpriteAnimSeqData
+ add hl, de
+ add hl, de
+ add hl, de
+ ld e, l
+ ld d, h
+; Set hl to the first field (field 0) in the current structure.
+ ld hl, SPRITEANIMSTRUCT_INDEX
+ add hl, bc
+; Load the index.
+ ld a, [wSpriteAnimCount]
+ ld [hli], a
+; Copy the table entry to the next two fields.
+ ld a, [de]
+ ld [hli], a
+ inc de
+ ld a, [de]
+ ld [hli], a
+ inc de
+; Look up the third field from the table in the wSpriteAnimDict array (10x2).
+; Take the value and load it in
+ ld a, [de]
+ call GetSpriteAnimVTile
+ ld [hli], a
+ pop de
+; Set hl to field 4 (X coordinate). Kinda pointless, because we're presumably already here.
+ ld hl, SPRITEANIMSTRUCT_XCOORD
+ add hl, bc
+; Load the original value of de into here.
+ ld a, e
+ ld [hli], a
+ ld a, d
+ ld [hli], a
+; load 0 into the next four fields
+ xor a
+ ld [hli], a
+ ld [hli], a
+ xor a
+ ld [hli], a
+ ld [hli], a
+; load -1 into the next field
+ dec a
+ ld [hli], a
+; load 0 into the last five fields
+ xor a
+rept 4
+ ld [hli], a
+endr
+ ld [hl], a
+; back up the address of the first field to wSpriteAnimAddrBackup
+ ld a, c
+ ld [wSpriteAnimAddrBackup], a
+ ld a, b
+ ld [wSpriteAnimAddrBackup + 1], a
+ ret
+; 8d036
+
+DeinitializeSprite: ; 8d036
+; Clear the index field of the struct in bc.
+ ld hl, SPRITEANIMSTRUCT_INDEX
+ add hl, bc
+ ld [hl], $0
+ ret
+; 8d03d
+
+
+DeinitializeAllSprites: ; 8d03d (23:503d)
+; Clear the index field of every struct in the wSpriteAnimationStructs array.
+ ld hl, wSpriteAnimationStructs
+ ld bc, SPRITEANIMSTRUCT_LENGTH
+ ld e, NUM_SPRITE_ANIM_STRUCTS
+ xor a
+.loop
+ ld [hl], a
+ add hl, bc
+ dec e
+ jr nz, .loop
+ ret
+
+
+UpdateAnimFrame: ; 8d04c
+ call InitSpriteAnimBuffer ; init WRAM
+ call GetSpriteAnimFrame ; read from a memory array
+ cp -3
+ jr z, .done
+ cp -4
+ jr z, .delete
+ call GetFrameOAMPointer
+ ; add byte to [wCurrAnimVTile]
+ ld a, [wCurrAnimVTile]
+ add [hl]
+ ld [wCurrAnimVTile], a
+ inc hl
+ ; load pointer into hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ push bc
+ ld a, [wCurrSpriteOAMAddr]
+ ld e, a
+ ld d, HIGH(wVirtualOAM)
+ ld a, [hli]
+ ld c, a ; number of objects
+.loop
+ ; first byte: y (px)
+ ; [de] = [wCurrAnimYCoord] + [wCurrAnimYOffset] + [wGlobalAnimYOffset] + AddOrSubtractY([hl])
+ ld a, [wCurrAnimYCoord]
+ ld b, a
+ ld a, [wCurrAnimYOffset]
+ add b
+ ld b, a
+ ld a, [wGlobalAnimYOffset]
+ add b
+ ld b, a
+ call AddOrSubtractY
+ add b
+ ld [de], a
+ inc hl
+ inc de
+ ; second byte: x (px)
+ ; [de] = [wCurrAnimXCoord] + [wCurrAnimXOffset] + [wGlobalAnimXOffset] + AddOrSubtractX([hl])
+ ld a, [wCurrAnimXCoord]
+ ld b, a
+ ld a, [wCurrAnimXOffset]
+ add b
+ ld b, a
+ ld a, [wGlobalAnimXOffset]
+ add b
+ ld b, a
+ call AddOrSubtractX
+ add b
+ ld [de], a
+ inc hl
+ inc de
+ ; third byte: vtile
+ ; [de] = [wCurrAnimVTile] + [hl]
+ ld a, [wCurrAnimVTile]
+ add [hl]
+ ld [de], a
+ inc hl
+ inc de
+ ; fourth byte: attributes
+ ; [de] = GetSpriteOAMAttr([hl])
+ call GetSpriteOAMAttr
+ ld [de], a
+ inc hl
+ inc de
+ ld a, e
+ ld [wCurrSpriteOAMAddr], a
+ cp LOW(wVirtualOAMEnd)
+ jr nc, .reached_the_end
+ dec c
+ jr nz, .loop
+ pop bc
+ jr .done
+
+.delete
+ call DeinitializeSprite
+.done
+ and a
+ ret
+
+.reached_the_end
+ pop bc
+ scf
+ ret
+; 8d0be
+
+AddOrSubtractY: ; 8d0be
+ push hl
+ ld a, [hl]
+ ld hl, wCurrSpriteAddSubFlags
+ bit 6, [hl]
+ jr z, .ok
+ ; 8 - a
+ add $8
+ xor $ff
+ inc a
+
+.ok
+ pop hl
+ ret
+; 8d0ce
+
+AddOrSubtractX: ; 8d0ce
+ push hl
+ ld a, [hl]
+ ld hl, wCurrSpriteAddSubFlags
+ bit 5, [hl] ; x flip
+ jr z, .ok
+ ; 8 - a
+ add $8
+ xor $ff
+ inc a
+
+.ok
+ pop hl
+ ret
+; 8d0de
+
+GetSpriteOAMAttr: ; 8d0de
+ ld a, [wCurrSpriteAddSubFlags]
+ ld b, a
+ ld a, [hl]
+ xor b
+ and $e0
+ ld b, a
+ ld a, [hl]
+ and $1f
+ or b
+ ret
+; 8d0ec
+
+InitSpriteAnimBuffer: ; 8d0ec
+ xor a
+ ld [wCurrSpriteAddSubFlags], a
+ ld hl, SPRITEANIMSTRUCT_TILE_ID
+ add hl, bc
+ ld a, [hli]
+ ld [wCurrAnimVTile], a
+ ld a, [hli]
+ ld [wCurrAnimXCoord], a
+ ld a, [hli]
+ ld [wCurrAnimYCoord], a
+ ld a, [hli]
+ ld [wCurrAnimXOffset], a
+ ld a, [hli]
+ ld [wCurrAnimYOffset], a
+ ret
+; 8d109
+
+GetSpriteAnimVTile: ; 8d109
+; a = wSpriteAnimDict[a] if a in wSpriteAnimDict else 0
+; vTiles offset
+ push hl
+ push bc
+ ld hl, wSpriteAnimDict
+ ld b, a
+ ld c, NUM_SPRITE_ANIM_STRUCTS
+.loop
+ ld a, [hli]
+ cp b
+ jr z, .ok
+ inc hl
+ dec c
+ jr nz, .loop
+ xor a
+ jr .done
+
+.ok
+ ld a, [hl]
+
+.done
+ pop bc
+ pop hl
+ ret
+; 8d120
+
+_ReinitSpriteAnimFrame:: ; 8d120
+ ld hl, SPRITEANIMSTRUCT_FRAMESET_ID
+ add hl, bc
+ ld [hl], a
+ ld hl, SPRITEANIMSTRUCT_DURATION
+ add hl, bc
+ ld [hl], 0
+ ld hl, SPRITEANIMSTRUCT_FRAME
+ add hl, bc
+ ld [hl], -1
+ ret
+; 8d132
+
+
+GetSpriteAnimFrame: ; 8d132
+.loop
+ ld hl, SPRITEANIMSTRUCT_DURATION
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .next_frame ; finished the current sequence
+ dec [hl]
+ call .GetPointer ; load pointer from SpriteAnimFrameData
+ ld a, [hli]
+ push af
+ jr .okay
+
+.next_frame
+ ld hl, SPRITEANIMSTRUCT_FRAME
+ add hl, bc
+ inc [hl]
+ call .GetPointer ; load pointer from SpriteAnimFrameData
+ ld a, [hli]
+ cp dorestart_command
+ jr z, .restart
+ cp endanim_command
+ jr z, .repeat_last
+
+ push af
+ ld a, [hl]
+ push hl
+ and $3f
+ ld hl, SPRITEANIMSTRUCT_DURATIONOFFSET
+ add hl, bc
+ add [hl]
+ ld hl, SPRITEANIMSTRUCT_DURATION
+ add hl, bc
+ ld [hl], a
+ pop hl
+.okay
+ ld a, [hl]
+ and $c0
+ srl a
+ ld [wCurrSpriteAddSubFlags], a
+ pop af
+ ret
+
+.repeat_last
+ xor a
+ ld hl, SPRITEANIMSTRUCT_DURATION
+ add hl, bc
+ ld [hl], a
+
+ ld hl, SPRITEANIMSTRUCT_FRAME
+ add hl, bc
+ dec [hl]
+ dec [hl]
+ jr .loop
+
+.restart
+ xor a
+ ld hl, SPRITEANIMSTRUCT_DURATION
+ add hl, bc
+ ld [hl], a
+
+ dec a
+ ld hl, SPRITEANIMSTRUCT_FRAME
+ add hl, bc
+ ld [hl], a
+ jr .loop
+; 8d189
+
+.GetPointer: ; 8d189
+ ; Get the data for the current frame for the current animation sequence
+
+ ; SpriteAnimFrameData[SpriteAnim[SPRITEANIMSTRUCT_FRAMESET_ID]][SpriteAnim[SPRITEANIMSTRUCT_FRAME]]
+ ld hl, SPRITEANIMSTRUCT_FRAMESET_ID
+ add hl, bc
+ ld e, [hl]
+ ld d, 0
+ ld hl, SpriteAnimFrameData
+ add hl, de
+ add hl, de
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, SPRITEANIMSTRUCT_FRAME
+ add hl, bc
+ ld l, [hl]
+ ld h, 0
+ add hl, hl
+ add hl, de
+ ret
+; 8d1a2
+
+GetFrameOAMPointer: ; 8d1a2
+; Load OAM data pointer
+ ld e, a
+ ld d, 0
+ ld hl, SpriteAnimOAMData
+ add hl, de
+ add hl, de
+ add hl, de
+ ret
+; 8d1ac
+
+Unreferenced_BrokenGetStdGraphics: ; 8d1ac
+ push hl
+ ld l, a
+ ld h, 0
+ add hl, hl
+ add hl, hl
+ ld de, BrokenStdGFXPointers ; broken 2bpp pointers
+ add hl, de
+ ld c, [hl]
+ inc hl
+ ld b, [hl]
+ inc hl
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ pop hl
+ push bc
+ call Request2bpp
+ pop bc
+ ret
+; 8d1c4
+
+
+INCLUDE "data/sprite_anims/sequences.asm"
+
+INCLUDE "engine/gfx/sprite_anims.asm"
+
+INCLUDE "data/sprite_anims/framesets.asm"
+
+INCLUDE "data/sprite_anims/oam.asm"
+
+
+BrokenStdGFXPointers:
+ ; tile count, bank, pointer
+ ; (all pointers were dummied out to .deleted)
+ dbbw 128, $01, .deleted
+ dbbw 128, $01, .deleted
+ dbbw 128, $01, .deleted
+ dbbw 128, $01, .deleted
+ dbbw 16, $37, .deleted
+ dbbw 16, $11, .deleted
+ dbbw 16, $39, .deleted
+ dbbw 16, $24, .deleted
+ dbbw 16, $21, .deleted
+
+.deleted
+; 8e72a (23:672a)
+
+
+Sprites_Cosine: ; 8e72a
+; a = d * cos(a * pi/32)
+ add %010000 ; cos(x) = sin(x + pi/2)
+ ; fallthrough
+Sprites_Sine: ; 8e72c
+; a = d * sin(a * pi/32)
+ calc_sine_wave
+
+
+AnimateEndOfExpBar: ; 8e79d
+ ld a, [hSGB]
+ ld de, EndOfExpBarGFX
+ and a
+ jr z, .load
+ ld de, SGBEndOfExpBarGFX
+
+.load
+ ld hl, vTiles0 tile $00
+ lb bc, BANK(EndOfExpBarGFX), 1
+ call Request2bpp
+ ld c, 8
+ ld d, 0
+.loop
+ push bc
+ call .AnimateFrame
+ call DelayFrame
+ pop bc
+ inc d
+ inc d
+ dec c
+ jr nz, .loop
+ call ClearSprites
+ ret
+; 8e7c6
+
+.AnimateFrame: ; 8e7c6
+ ld hl, wVirtualOAMSprite00
+ ld c, 8 ; number of animated circles
+.anim_loop
+ ld a, c
+ and a
+ ret z
+ dec c
+ ld a, c
+; multiply by 8
+ sla a
+ sla a
+ sla a
+ push af
+
+ push de
+ push hl
+ call Sprites_Sine
+ pop hl
+ pop de
+ add 13 * TILE_WIDTH
+ ld [hli], a ; y
+
+ pop af
+ push de
+ push hl
+ call Sprites_Cosine
+ pop hl
+ pop de
+ add 10 * TILE_WIDTH + 4
+ ld [hli], a ; x
+
+ ld a, $0
+ ld [hli], a ; tile id
+ ld a, PAL_BATTLE_OB_BLUE
+ ld [hli], a ; attributes
+ jr .anim_loop
+; 8e7f4
+
+EndOfExpBarGFX: ; 8e7f4
+INCBIN "gfx/battle/expbarend.2bpp"
+SGBEndOfExpBarGFX: ; 8e804
+INCBIN "gfx/battle/expbarend_sgb.2bpp"
+
+ClearSpriteAnims2: ; 8e814
+ push hl
+ push de
+ push bc
+ push af
+ ld hl, wSpriteAnimDict
+ ld bc, wSpriteAnimsEnd - wSpriteAnimDict
+.loop
+ ld [hl], 0
+ inc hl
+ dec bc
+ ld a, c
+ or b
+ jr nz, .loop
+ pop af
+ pop bc
+ pop de
+ pop hl
+ ret
+; 8e82b
diff --git a/engine/gfx/trademon_frontpic.asm b/engine/gfx/trademon_frontpic.asm
new file mode 100644
index 000000000..d5f7b55de
--- /dev/null
+++ b/engine/gfx/trademon_frontpic.asm
@@ -0,0 +1,38 @@
+GetTrademonFrontpic: ; 4d7fd
+ ld a, [wOTTrademonSpecies]
+ ld hl, wOTTrademonDVs
+ ld de, vTiles2
+ push de
+ push af
+ predef GetUnownLetter
+ pop af
+ ld [wCurPartySpecies], a
+ ld [wCurSpecies], a
+ call GetBaseData
+ pop de
+ predef GetAnimatedFrontpic
+ ret
+
+AnimateTrademonFrontpic: ; 4d81e
+ ld a, [wOTTrademonSpecies]
+ call IsAPokemon
+ ret c
+ farcall ShowOTTrademonStats
+ ld a, [wOTTrademonSpecies]
+ ld [wCurPartySpecies], a
+ ld a, [wOTTrademonDVs]
+ ld [wTempMonDVs], a
+ ld a, [wOTTrademonDVs + 1]
+ ld [wTempMonDVs + 1], a
+ ld b, SCGB_PLAYER_OR_MON_FRONTPIC_PALS
+ call GetSGBLayout
+ ld a, %11100100 ; 3,2,1,0
+ call DmgToCgbBGPals
+ farcall TradeAnim_ShowGetmonFrontpic
+ ld a, [wOTTrademonSpecies]
+ ld [wCurPartySpecies], a
+ hlcoord 7, 2
+ ld d, $0
+ ld e, ANIM_MON_TRADE
+ predef AnimateFrontpic
+ ret