summaryrefslogtreecommitdiff
path: root/engine/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'engine/gfx')
-rw-r--r--engine/gfx/cgb_layouts.asm981
-rw-r--r--engine/gfx/color.asm1352
-rw-r--r--engine/gfx/crystal_layouts.asm304
-rw-r--r--engine/gfx/dma_transfer.asm604
-rw-r--r--engine/gfx/load_font.asm147
-rw-r--r--engine/gfx/load_overworld_font.asm16
-rw-r--r--engine/gfx/load_pics.asm490
-rw-r--r--engine/gfx/load_push_oam.asm21
-rw-r--r--engine/gfx/mon_icons.asm454
-rw-r--r--engine/gfx/pic_animation.asm1079
-rw-r--r--engine/gfx/place_graphic.asm55
-rw-r--r--engine/gfx/player_gfx.asm224
-rw-r--r--engine/gfx/sgb_layouts.asm571
-rw-r--r--engine/gfx/sprite_anims.asm876
-rw-r--r--engine/gfx/sprites.asm649
-rw-r--r--engine/gfx/trademon_frontpic.asm38
16 files changed, 7861 insertions, 0 deletions
diff --git a/engine/gfx/cgb_layouts.asm b/engine/gfx/cgb_layouts.asm
new file mode 100644
index 000000000..d8f53d7fc
--- /dev/null
+++ b/engine/gfx/cgb_layouts.asm
@@ -0,0 +1,981 @@
+; Replaces the functionality of sgb.asm to work with CGB hardware.
+
+CheckCGB:
+ ld a, [hCGB]
+ and a
+ ret
+
+LoadSGBLayoutCGB:
+ 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
+
+.ReturnFromJumpTable:
+ ret
+
+.dw
+ 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 _CGB_BetaPikachuMinigame
+ 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
+
+_CGB_BattleGrayscale:
+ 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:
+ 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:
+ 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
+
+InitPartyMenuBGPal7:
+ farcall Function100dc0
+Mobile_InitPartyMenuBGPal7:
+ 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
+
+InitPartyMenuBGPal0:
+ 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
+
+_CGB_PokegearPals:
+ 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
+
+_CGB_StatsScreenHPPals:
+ 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
+
+StatsScreenPagePals:
+INCLUDE "gfx/stats/pages.pal"
+
+StatsScreenPals:
+INCLUDE "gfx/stats/stats.pal"
+
+_CGB_Pokedex:
+ 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
+
+.PokedexQuestionMarkPalette:
+INCLUDE "gfx/pokedex/question_mark.pal"
+
+.PokedexCursorPalette:
+INCLUDE "gfx/pokedex/cursor.pal"
+
+_CGB_BillsPC:
+ 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
+
+.Function9009:
+ 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
+
+.BillsPCOrangePalette:
+INCLUDE "gfx/pc/orange.pal"
+
+_CGB_PokedexUnownMode:
+ 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
+
+_CGB_SlotMachine:
+ 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
+
+_CGB06:
+ 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
+
+_CGB_GSIntro:
+ ld b, 0
+ ld hl, .Jumptable
+ add hl, bc
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+.Jumptable:
+ dw .ShellderLaprasScene
+ dw .JigglypuffPikachuScene
+ dw .StartersCharizardScene
+
+.ShellderLaprasScene:
+ 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
+
+.ShellderLaprasBGPalette:
+ RGB 19, 31, 19
+ RGB 18, 23, 31
+ RGB 11, 21, 28
+ RGB 04, 16, 24
+
+.ShellderLaprasOBPals:
+ 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
+
+.JigglypuffPikachuScene:
+ 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
+
+.StartersCharizardScene:
+ ld hl, PalPacket_Pack + 1
+ call CopyFourPalettes
+ ld de, wOBPals1
+ ld a, PREDEFPAL_GS_INTRO_STARTERS_TRANSITION
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ ret
+
+_CGB11:
+ ld hl, BetaPokerPals
+ ld de, wBGPals1
+ ld bc, 5 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ call ApplyPals
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+
+_CGB_Diploma:
+ 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
+
+_CGB_MapPals:
+ call LoadMapPals
+ ld a, SCGB_MAPPALS
+ ld [wSGBPredef], a
+ ret
+
+_CGB_PartyMenu:
+ ld hl, PalPacket_PartyMenu + 1
+ call CopyFourPalettes
+ call InitPartyMenuBGPal0
+ call InitPartyMenuBGPal7
+ call InitPartyMenuOBPals
+ call ApplyAttrMap
+ ret
+
+_CGB_Evolution:
+ 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
+
+_CGB_GSTitleScreen:
+ 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
+
+_CGB0d:
+ ld hl, PalPacket_Diploma + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+
+_CGB_UnownPuzzle:
+ 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
+
+_CGB_TrainerCard:
+ 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
+
+_CGB_MoveList:
+ 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
+
+_CGB_BetaPikachuMinigame:
+ ld hl, PalPacket_BetaPikachuMinigame + 1
+ call CopyFourPalettes
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+
+_CGB_PokedexSearchOption:
+ ld de, wBGPals1
+ ld a, PREDEFPAL_POKEDEX
+ call GetPredefPal
+ call LoadHLPaletteIntoDE
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ld a, $1
+ ld [hCGBPalUpdate], a
+ ret
+
+_CGB_PackPals:
+; 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
+
+.ChrisPackPals:
+INCLUDE "gfx/pack/pack.pal"
+
+.KrisPackPals:
+INCLUDE "gfx/pack/pack_f.pal"
+
+_CGB_Pokepic:
+ 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
+
+_CGB13:
+ 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
+
+_CGB_GamefreakLogo:
+ 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
+
+.Palette:
+INCLUDE "gfx/splash/logo.pal"
+
+_CGB_PlayerOrMonFrontpicPals:
+ ld de, wBGPals1
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetPlayerOrMonPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ret
+
+_CGB1e:
+ ld de, wBGPals1
+ ld a, [wCurPartySpecies]
+ call GetMonPalettePointer_
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ ret
+
+_CGB_TradeTube:
+ 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
+
+_CGB_TrainerOrMonFrontpicPals:
+ ld de, wBGPals1
+ ld a, [wCurPartySpecies]
+ ld bc, wTempMonDVs
+ call GetFrontpicPalettePointer
+ call LoadPalette_White_Col1_Col2_Black
+ call WipeAttrMap
+ call ApplyAttrMap
+ call ApplyPals
+ ret
+
+_CGB_MysteryGift:
+ 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
+
+.Palettes:
+INCLUDE "gfx/mystery_gift/mystery_gift.pal"
diff --git a/engine/gfx/color.asm b/engine/gfx/color.asm
new file mode 100644
index 000000000..9a3b4f8ef
--- /dev/null
+++ b/engine/gfx/color.asm
@@ -0,0 +1,1352 @@
+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_CheckShininess:
+; Return carry if the DVs at hl are all 10 or higher.
+
+; Attack
+ ld a, [hl]
+ cp 10 << 4
+ jr c, .NotShiny
+
+; Defense
+ ld a, [hli]
+ and $f
+ cp 10
+ jr c, .NotShiny
+
+; Speed
+ ld a, [hl]
+ cp 10 << 4
+ jr c, .NotShiny
+
+; Special
+ ld a, [hl]
+ and $f
+ cp 10
+ jr c, .NotShiny
+
+.Shiny:
+ scf
+ ret
+
+.NotShiny:
+ 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:
+ 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_BetaIntroVenusaur
+ jp PushSGBPals_
+
+.cgb
+ ld de, wOBPals1
+ ld a, PREDEFPAL_BETA_INTRO_VENUSAUR
+ 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:
+ 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:
+; 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
+
+ClearBytes:
+; 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
+
+DrawDefaultTiles:
+; 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
+
+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"
+
+BetaPokerPals:
+INCLUDE "gfx/beta_poker/beta_poker.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..b9686701c
--- /dev/null
+++ b/engine/gfx/crystal_layouts.asm
@@ -0,0 +1,304 @@
+GetMysteryGift_MobileAdapterLayout:
+ 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
+
+.dw
+ dw MG_Mobile_Layout00
+ dw MG_Mobile_Layout01
+ dw MG_Mobile_Layout02
+
+MG_Mobile_Layout_FillBox:
+.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
+
+MG_Mobile_Layout_WipeAttrMap:
+ hlcoord 0, 0, wAttrMap
+ ld bc, SCREEN_HEIGHT * SCREEN_WIDTH
+ xor a
+ call ByteFill
+ ret
+
+MG_Mobile_Layout_LoadPals:
+ 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:
+ call MG_Mobile_Layout_LoadPals
+ call MG_Mobile_Layout_WipeAttrMap
+ call MG_Mobile_Layout_CreatePalBoxes
+ farcall ApplyAttrMap
+ farcall ApplyPals
+ ret
+
+MG_Mobile_Layout_CreatePalBoxes:
+ 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
+
+Palette_MysteryGiftMobile:
+INCLUDE "gfx/mystery_gift/mg_mobile.pal"
+
+LoadOW_BGPal7::
+ ld hl, Palette_TextBG7
+ ld de, wBGPals1 palette PAL_BG_TEXT
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+
+Palette_TextBG7:
+INCLUDE "gfx/font/bg_text.pal"
+
+Function49420::
+ ld hl, MansionPalette1 + 8 palettes
+ ld de, wBGPals1 palette PAL_BG_ROOF
+ ld bc, 1 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ ret
+
+MG_Mobile_Layout01:
+ 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
+
+.Palette_49478:
+ RGB 31, 31, 31
+ RGB 26, 31, 00
+ RGB 20, 16, 03
+ RGB 00, 00, 00
+
+Function49480:
+ 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
+
+Function49496:
+ 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
+
+INCLUDE "engine/tilesets/tileset_palettes.asm"
+
+MG_Mobile_Layout02:
+ 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
+
+.Palette_49732:
+ RGB 31, 31, 31
+ RGB 23, 16, 07
+ RGB 23, 07, 07
+ RGB 03, 07, 20
+
+.Palette_4973a:
+ RGB 00, 00, 00
+ RGB 07, 05, 31
+ RGB 14, 18, 31
+ RGB 31, 31, 31
+
+Function49742:
+ ld hl, .Palette_49757
+ ld de, wBGPals1
+ ld bc, 8 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ farcall ApplyPals
+ ret
+
+.Palette_49757:
+INCLUDE "gfx/unknown/49757.pal"
+
+_InitMG_Mobile_LinkTradePalMap:
+ 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
+
+LoadTradeRoomBGPals:
+ ld hl, TradeRoomPalette
+ ld de, wBGPals1 palette PAL_BG_GREEN
+ ld bc, 6 palettes
+ ld a, BANK(wBGPals1)
+ call FarCopyWRAM
+ farcall ApplyPals
+ ret
+
+TradeRoomPalette:
+INCLUDE "gfx/trade/border.pal"
+
+InitMG_Mobile_LinkTradePalMap:
+ call _InitMG_Mobile_LinkTradePalMap
+ ret
+
+; 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..241649dc5
--- /dev/null
+++ b/engine/gfx/dma_transfer.asm
@@ -0,0 +1,604 @@
+HDMATransferAttrMapAndTileMapToWRAMBank3::
+ 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
+
+HDMATransferTileMapToWRAMBank3::
+ 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
+
+HDMATransferAttrMapToWRAMBank3:
+ 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
+
+ReloadMapPart::
+ 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:
+ 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
+
+; 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
+
+; 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
+
+OpenAndCloseMenu_HDMATransferTileMapAndAttrMap::
+; 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
+
+Mobile_OpenAndCloseMenu_HDMATransferTileMapAndAttrMap:
+ 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
+
+CallInSafeGFXMode:
+ 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
+
+._hl_
+ jp hl
+
+HDMATransferToWRAMBank3:
+ call _LoadHDMAParameters
+ ld a, $23
+ ld [hDMATransfer], a
+
+WaitDMATransfer:
+.loop
+ call DelayFrame
+ ld a, [hDMATransfer]
+ and a
+ jr nz, .loop
+ ret
+
+HDMATransfer_Wait127Scanlines_toBGMap:
+; 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:
+; 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
+
+HDMATransfer_NoDI:
+; 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
+
+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
+
+_LoadHDMAParameters:
+ 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:
+ ld c, " "
+ jr PadMapForHDMATransfer
+
+PadAttrMapForHDMATransfer:
+ ld c, $0
+
+PadMapForHDMATransfer:
+; 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::
+ ; 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
+
+_Get1bpp::
+ ; 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
+
+.bankswitch
+ 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
+
+HDMATransfer_OnlyTopFourRows:
+ ld hl, .Function
+ jp CallInSafeGFXMode
+
+.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:
+ 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
diff --git a/engine/gfx/load_font.asm b/engine/gfx/load_font.asm
new file mode 100644
index 000000000..41c0721d6
--- /dev/null
+++ b/engine/gfx/load_font.asm
@@ -0,0 +1,147 @@
+INCLUDE "gfx/font.asm"
+
+; This and the following two functions are unreferenced.
+; Debug, perhaps?
+Unreferenced_fb434:
+ db 0
+
+Unreferenced_Functionfb435:
+ ld a, [Unreferenced_fb434]
+ and a
+ jp nz, Get1bpp_2
+ jp Get1bpp
+
+Unreferenced_Functionfb43f:
+ ld a, [Unreferenced_fb434]
+ and a
+ jp nz, Get2bpp_2
+ jp Get2bpp
+; End unreferenced block
+
+_LoadStandardFont::
+ 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
+
+_LoadFontsExtra1::
+ 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
+
+_LoadFontsExtra2::
+ ld de, FontsExtra2_UpArrowGFX
+ ld hl, vTiles2 tile "▲" ; $61
+ ld b, BANK(FontsExtra2_UpArrowGFX)
+ ld c, 1
+ call Get2bpp_2
+ ret
+
+_LoadFontsBattleExtra::
+ ld de, FontBattleExtra
+ ld hl, vTiles2 tile $60
+ lb bc, BANK(FontBattleExtra), 25
+ call Get2bpp_2
+ jr LoadFrame
+
+LoadFrame:
+ 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
+
+LoadBattleFontsHPBar:
+ 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:
+ 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
+
+StatsScreen_LoadFont:
+ 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:
+ ld de, StatsScreenPageTilesGFX
+ ld hl, vTiles2 tile $31
+ lb bc, BANK(StatsScreenPageTilesGFX), 17
+ call Get2bpp_2
+ ret
diff --git a/engine/gfx/load_overworld_font.asm b/engine/gfx/load_overworld_font.asm
new file mode 100644
index 000000000..f4ef84619
--- /dev/null
+++ b/engine/gfx/load_overworld_font.asm
@@ -0,0 +1,16 @@
+LoadOverworldFont::
+ 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
+
+.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..64190083d
--- /dev/null
+++ b/engine/gfx/load_pics.asm
@@ -0,0 +1,490 @@
+GetUnownLetter:
+; 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:
+ ld a, [wCurPartySpecies]
+ ld [wCurSpecies], a
+ call IsAPokemon
+ ret c
+ ld a, [rSVBK]
+ push af
+ call _GetFrontpic
+ pop af
+ ld [rSVBK], a
+ ret
+
+GetAnimatedFrontpic:
+ 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:
+ 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:
+ 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:
+ 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:
+ 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:
+ 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:
+; 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:
+ db BANK("Pics 1") ; BANK("Pics 1") + 0
+ db BANK("Pics 2") ; BANK("Pics 1") + 1
+ db BANK("Pics 3") ; BANK("Pics 1") + 2
+ db BANK("Pics 4") ; BANK("Pics 1") + 3
+ db BANK("Pics 5") ; BANK("Pics 1") + 4
+ db BANK("Pics 6") ; BANK("Pics 1") + 5
+ db BANK("Pics 7") ; BANK("Pics 1") + 6
+ db BANK("Pics 8") ; BANK("Pics 1") + 7
+ db BANK("Pics 9") ; BANK("Pics 1") + 8
+ db BANK("Pics 10") ; BANK("Pics 1") + 9
+ db BANK("Pics 11") ; BANK("Pics 1") + 10
+ db BANK("Pics 12") ; BANK("Pics 1") + 11
+ db BANK("Pics 13") ; BANK("Pics 1") + 12
+ db BANK("Pics 14") ; BANK("Pics 1") + 13
+ db BANK("Pics 15") ; BANK("Pics 1") + 14
+ db BANK("Pics 16") ; BANK("Pics 1") + 15
+ db BANK("Pics 17") ; BANK("Pics 1") + 16
+ db BANK("Pics 18") ; BANK("Pics 1") + 17
+ db BANK("Pics 19") ; BANK("Pics 1") + 18
+ db BANK("Pics 20") ; BANK("Pics 1") + 19
+ db BANK("Pics 21") ; BANK("Pics 1") + 20
+ db BANK("Pics 22") ; BANK("Pics 1") + 21
+ db BANK("Pics 23") ; BANK("Pics 1") + 22
+ db BANK("Pics 24") ; BANK("Pics 1") + 23
+
+Function511ec:
+ 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:
+ 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:
+; 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:
+ 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:
+; pads frontpic to fill 7x7 box
+ ld a, b
+ cp 6
+ jr z, .six
+ cp 5
+ jr z, .five
+
+.seven_loop
+ ld c, 7 << 4
+ call LoadOrientedFrontpic
+ dec b
+ jr nz, .seven_loop
+ ret
+
+.six
+ ld c, 7 << 4
+ xor a
+ call .Fill
+.six_loop
+ ld c, (7 - 6) << 4
+ xor a
+ call .Fill
+ ld c, 6 << 4
+ call LoadOrientedFrontpic
+ dec b
+ jr nz, .six_loop
+ ret
+
+.five
+ ld c, 7 << 4
+ xor a
+ call .Fill
+.five_loop
+ ld c, (7 - 5) << 4
+ xor a
+ call .Fill
+ ld c, 5 << 4
+ call LoadOrientedFrontpic
+ dec b
+ jr nz, .five_loop
+ ld c, 7 << 4
+ xor a
+ call .Fill
+ ret
+
+.Fill:
+ ld [hli], a
+ dec c
+ jr nz, .Fill
+ ret
+
+LoadOrientedFrontpic:
+ 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..11045bb9d
--- /dev/null
+++ b/engine/gfx/load_push_oam.asm
@@ -0,0 +1,21 @@
+WriteOAMDMACodeToHRAM::
+ 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:
+ 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..5ae2fbf8b
--- /dev/null
+++ b/engine/gfx/mon_icons.asm
@@ -0,0 +1,454 @@
+LoadOverworldMonIcon:
+ 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
+
+LoadMenuMonIcon:
+ push hl
+ push de
+ push bc
+ call .LoadIcon
+ pop bc
+ pop de
+ pop hl
+ ret
+
+.LoadIcon:
+ ld d, 0
+ ld hl, .Jumptable
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+.Jumptable:
+ 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:
+ call InitPartyMenuIcon
+ call .GetPartyMonItemGFX
+ call SetPartyMonIconAnimSpeed
+ ret
+
+.GetPartyMonItemGFX:
+ 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:
+ 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:
+ 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:
+ call InitPartyMenuIcon
+ call .SpawnItemIcon
+ call SetPartyMonIconAnimSpeed
+ ret
+
+.SpawnItemIcon:
+ 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:
+ 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:
+ 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
+ farcall PlacePartymonHPBar
+ call GetHPPal
+ ld e, d
+ ld d, 0
+ ld hl, .speeds
+ add hl, de
+ ld b, [hl]
+ ret
+
+.speeds
+ db $00 ; HP_GREEN
+ db $40 ; HP_YELLOW
+ db $80 ; HP_RED
+
+NamingScreen_InitAnimatedMonIcon:
+ 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:
+ 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:
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ ld a, $62
+ ld [wCurIconTile], a
+ call GetMemIconGFX
+ ret
+
+GetSpeciesIcon:
+; 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
+
+FlyFunction_GetMonIcon:
+ push de
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ pop de
+ ld a, e
+ call GetIcon_a
+ ret
+
+Unreferenced_GetMonIcon2:
+ push de
+ ld a, [wd265]
+ call ReadMonMenuIcon
+ ld [wCurIcon], a
+ pop de
+ call GetIcon_de
+ ret
+
+GetMemIconGFX:
+ ld a, [wCurIconTile]
+GetIconGFX:
+ 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"
+
+GetIcon_de:
+; Load icon graphics into VRAM starting from tile de.
+ ld l, e
+ ld h, d
+ jr GetIcon
+
+GetIcon_a:
+; Load icon graphics into VRAM starting from tile a.
+ ld l, a
+ ld h, 0
+
+GetIcon:
+; 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
+
+GetGFXUnlessMobile:
+ ld a, [wLinkMode]
+ cp LINK_MOBILE
+ jp nz, Request2bpp
+ jp Get2bpp_2
+
+FreezeMonIcons:
+ 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
+
+UnfreezeMonIcons:
+ 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
+
+HoldSwitchmonIcon:
+ 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:
+ 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
+
+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..843e5cba4
--- /dev/null
+++ b/engine/gfx/pic_animation.asm
@@ -0,0 +1,1079 @@
+; Pic animation arrangement.
+
+Unused_AnimateMon_Slow_Normal:
+ 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
+
+AnimateMon_Menu:
+ ld e, ANIM_MON_MENU
+ ld d, $0
+ call AnimateFrontpic
+ ret
+
+AnimateMon_Trade:
+ ld e, ANIM_MON_TRADE
+ ld d, $0
+ call AnimateFrontpic
+ ret
+
+AnimateMon_Evolve:
+ ld e, ANIM_MON_EVOLVE
+ ld d, $0
+ call AnimateFrontpic
+ ret
+
+AnimateMon_Hatch:
+ ld e, ANIM_MON_HATCH
+ ld d, $0
+ call AnimateFrontpic
+ ret
+
+AnimateMon_Unused:
+ ld e, ANIM_MON_UNUSED
+ ld d, $0
+ call AnimateFrontpic
+ ret
+
+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:
+ 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:
+ call AnimateMon_CheckIfPokemon
+ ret c
+ call LoadMonAnimation
+.loop
+ call SetUpPokeAnim
+ push af
+ farcall HDMATransferTileMapToWRAMBank3
+ pop af
+ jr nc, .loop
+ ret
+
+LoadMonAnimation:
+ 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
+
+SetUpPokeAnim:
+ 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
+
+PokeAnim_SetupCommands:
+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
+
+PokeAnim_SetWait:
+ ld a, 18
+ ld [wPokeAnimWaitCounter], a
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+
+PokeAnim_Wait:
+ ld hl, wPokeAnimWaitCounter
+ dec [hl]
+ ret nz
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_Setup:
+ ld c, FALSE
+ ld b, 0
+ call PokeAnim_InitAnim
+ call PokeAnim_SetVBank1
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_Setup2:
+ ld c, FALSE
+ ld b, 4
+ call PokeAnim_InitAnim
+ call PokeAnim_SetVBank1
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_Idle:
+ ld c, TRUE
+ ld b, 0
+ call PokeAnim_InitAnim
+ call PokeAnim_SetVBank1
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_Play:
+ call PokeAnim_DoAnimScript
+ ld a, [wPokeAnimJumptableIndex]
+ bit 7, a
+ ret z
+ call PokeAnim_PlaceGraphic
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_Play2:
+ call PokeAnim_DoAnimScript
+ ld a, [wPokeAnimJumptableIndex]
+ bit 7, a
+ ret z
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_BasePic:
+ call PokeAnim_DeinitFrames
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_Finish:
+ call PokeAnim_DeinitFrames
+ ld hl, wPokeAnimSceneIndex
+ set 7, [hl]
+ ret
+
+PokeAnim_Cry:
+ ld a, [wPokeAnimSpecies]
+ call _PlayMonCry
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_CryNoWait:
+ ld a, [wPokeAnimSpecies]
+ call PlayMonCry2
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_StereoCry:
+ ld a, $f
+ ld [wCryTracks], a
+ ld a, [wPokeAnimSpecies]
+ call PlayStereoCry2
+ ld a, [wPokeAnimSceneIndex]
+ inc a
+ ld [wPokeAnimSceneIndex], a
+ ret
+
+PokeAnim_DeinitFrames:
+ 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
+
+AnimateMon_CheckIfPokemon:
+ ld a, [wCurPartySpecies]
+ cp EGG
+ jr z, .fail
+ call IsAPokemon
+ jr c, .fail
+ and a
+ ret
+
+.fail
+ scf
+ ret
+
+PokeAnim_InitPicAttributes:
+ 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
+
+PokeAnim_InitAnim:
+ 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
+
+PokeAnim_DoAnimScript:
+ xor a
+ ld [hBGMapMode], a
+.loop
+ ld a, [wPokeAnimJumptableIndex]
+ and $7f
+ ld hl, .Jumptable
+ rst JumpTable
+ ret
+
+.Jumptable:
+ dw .RunAnim
+ dw .WaitAnim
+
+.RunAnim:
+ call PokeAnim_GetPointer
+ ld a, [wPokeAnimCommand]
+ cp endanim_command
+ jr z, PokeAnim_End
+ cp setrepeat_command
+ jr z, .SetRepeat
+ cp dorepeat_command
+ jr z, .DoRepeat
+ call PokeAnim_GetFrame
+ ld a, [wPokeAnimParameter]
+ call PokeAnim_GetDuration
+ ld [wPokeAnimWaitCounter], a
+ call PokeAnim_StartWaitAnim
+.WaitAnim:
+ ld a, [wPokeAnimWaitCounter]
+ dec a
+ ld [wPokeAnimWaitCounter], a
+ ret nz
+ call PokeAnim_StopWaitAnim
+ ret
+
+.SetRepeat:
+ ld a, [wPokeAnimParameter]
+ ld [wPokeAnimRepeatTimer], a
+ jr .loop
+
+.DoRepeat:
+ ld a, [wPokeAnimRepeatTimer]
+ and a
+ ret z
+ dec a
+ ld [wPokeAnimRepeatTimer], a
+ ret z
+ ld a, [wPokeAnimParameter]
+ ld [wPokeAnimFrame], a
+ jr .loop
+
+PokeAnim_End:
+ ld hl, wPokeAnimJumptableIndex
+ set 7, [hl]
+ ret
+
+PokeAnim_GetDuration:
+; 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
+
+PokeAnim_GetFrame:
+ call PokeAnim_PlaceGraphic
+ ld a, [wPokeAnimCommand]
+ and a
+ ret z
+ call PokeAnim_GetBitmaskIndex
+ push hl
+ call PokeAnim_CopyBitmaskToBuffer
+ pop hl
+ call PokeAnim_ConvertAndApplyBitmask
+ ret
+
+PokeAnim_StartWaitAnim:
+ ld a, [wPokeAnimJumptableIndex]
+ inc a
+ ld [wPokeAnimJumptableIndex], a
+ ret
+
+PokeAnim_StopWaitAnim:
+ ld a, [wPokeAnimJumptableIndex]
+ dec a
+ ld [wPokeAnimJumptableIndex], a
+ ret
+
+PokeAnim_IsUnown:
+ ld a, [wPokeAnimSpecies]
+ cp UNOWN
+ ret
+
+PokeAnim_IsEgg:
+ ld a, [wPokeAnimSpecies]
+ cp EGG
+ ret
+
+PokeAnim_GetPointer:
+ 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
+
+PokeAnim_GetBitmaskIndex:
+ 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
+
+PokeAnim_CopyBitmaskToBuffer:
+ 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
+
+.GetSize:
+ 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
+
+.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:
+ 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
+
+.IsCurBitSet:
+; 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
+
+.ApplyFrame:
+ push af
+ call .GetCoord
+ pop af
+ push hl
+ call .GetTilemap
+ ld hl, wPokeAnimGraphicStartTile
+ add [hl]
+ pop hl
+ ld [hl], a
+ ret
+
+.GetCoord:
+ 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
+
+; unused
+ db 6, 5, 4
+
+.GetTilemap:
+ 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
+
+._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:
+ 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
+
+.NextBit:
+ 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
+
+PokeAnim_PlaceGraphic:
+ 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
+
+.ClearBox:
+ ld hl, wPokeAnimCoord
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld b, 7
+ ld c, 7
+ call ClearBox
+ ret
+
+PokeAnim_SetVBank1:
+ 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
+
+.SetFlag:
+ 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
+
+PokeAnim_SetVBank0:
+ 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
+
+PokeAnim_GetAttrMapCoord:
+ ld hl, wPokeAnimCoord
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, wAttrMap - wTileMap
+ add hl, de
+ ret
+
+GetMonAnimPointer:
+ 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
+
+PokeAnim_GetFrontpicDims:
+ 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
+
+GetMonFramesPointer:
+ 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
+
+GetMonBitmaskPointer:
+ 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
+
+PokeAnim_GetSpeciesOrUnown:
+ call PokeAnim_IsUnown
+ jr z, .unown
+ ld a, [wPokeAnimSpecies]
+ ret
+
+.unown
+ ld a, [wPokeAnimUnownLetter]
+ ret
+
+Unused_HOF_AnimateAlignedFrontpic:
+ ld a, $1
+ ld [wBoxAlignment], a
+
+HOF_AnimateFrontpic:
+ 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
diff --git a/engine/gfx/place_graphic.asm b/engine/gfx/place_graphic.asm
new file mode 100644
index 000000000..628c72413
--- /dev/null
+++ b/engine/gfx/place_graphic.asm
@@ -0,0 +1,55 @@
+PlaceGraphic:
+; 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..9954143a8
--- /dev/null
+++ b/engine/gfx/player_gfx.asm
@@ -0,0 +1,224 @@
+Unreferenced_Function88248:
+ 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:
+ hlcoord 6, 4
+ ld de, 1
+ jr MovePlayerPic
+
+MovePlayerPicLeft:
+ hlcoord 13, 4
+ ld de, -1
+ ; fallthrough
+
+MovePlayerPic:
+; 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:
+ 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"
+
+Unreferenced_GetPlayerNameArray:
+ 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:
+; 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:
+ 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:
+INCBIN "gfx/trainer_card/chris_card.2bpp"
+
+KrisCardPic:
+INCBIN "gfx/trainer_card/kris_card.2bpp"
+
+CardGFX:
+INCBIN "gfx/trainer_card/trainer_card.2bpp"
+
+GetPlayerBackpic:
+ ld a, [wPlayerGender]
+ bit PLAYERGENDER_FEMALE_F, a
+ jr z, GetChrisBackpic
+ call GetKrisBackpic
+ ret
+
+GetChrisBackpic:
+ ld hl, ChrisBackpic
+ ld b, BANK(ChrisBackpic)
+ ld de, vTiles2 tile $31
+ ld c, 7 * 7
+ predef DecompressGet2bpp
+ ret
+
+HOF_LoadTrainerFrontpic:
+ 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:
+; 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:
+INCBIN "gfx/player/chris.2bpp"
+
+KrisPic:
+INCBIN "gfx/player/kris.2bpp"
+
+GetKrisBackpic:
+; Kris's backpic is uncompressed.
+ ld de, KrisBackpic
+ ld hl, vTiles2 tile $31
+ lb bc, BANK(KrisBackpic), 7 * 7 ; dimensions
+ call Get2bpp
+ ret
+
+KrisBackpic:
+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..538132ce2
--- /dev/null
+++ b/engine/gfx/sgb_layouts.asm
@@ -0,0 +1,571 @@
+LoadSGBLayout:
+ 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
+
+.Jumptable:
+ 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 .SGB_BetaPikachuMinigame
+ dw .SGB_PokedexSearchOption
+ dw .SGB_BetaPoker
+ 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
+
+.SGB_BattleGrayscale:
+ ld hl, PalPacket_BattleGrayscale
+ ld de, BlkPacket_Battle
+ ret
+
+.SGB_BattleColors:
+ 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
+
+.SGB_MoveList:
+ 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
+
+.SGB_PokegearPals:
+ ld hl, PalPacket_Pokegear
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_StatsScreenHPPals:
+ 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
+
+.SGB_PartyMenu:
+ ld hl, PalPacket_PartyMenu
+ ld de, wSGBPals + 1
+ ret
+
+.SGB_Pokedex:
+ 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
+
+.SGB_BillsPC:
+ 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
+
+.SGB_PokedexUnownMode:
+ call .SGB_Pokedex
+ ld de, BlkPacket_PokedexUnownMode
+ ret
+
+.SGB_PokedexSearchOption:
+ 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
+
+.SGB_PackPals:
+ ld hl, PalPacket_Pack
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_SlotMachine:
+ ld hl, PalPacket_SlotMachine
+ ld de, BlkPacket_SlotMachine
+ ret
+
+.SGB06:
+ ld hl, PalPacket_SCGB_06
+ ld de, BlkPacket_SCGB_06
+ ret
+
+.SGB_Diploma:
+.SGB_MysteryGift:
+ ld hl, PalPacket_Diploma
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_GSIntro:
+ 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
+
+.BlkPacketTable_GSIntro:
+ dw BlkPacket_9a86, PalPacket_GSIntroShellderLapras
+ dw BlkPacket_GSIntroJigglypuffPikachu, PalPacket_GSIntroJigglypuffPikachu
+ dw BlkPacket_9a86, PalPacket_GSIntroStartersTransition
+
+.SGB_GSTitleScreen:
+ ld hl, PalPacket_GSTitleScreen
+ ld de, BlkPacket_GSTitleScreen
+ ld a, SCGB_DIPLOMA
+ ld [wSGBPredef], a
+ ret
+
+.SGB13:
+ ld hl, PalPacket_SCGB_13
+ ld de, BlkPacket_SCGB_13
+ ret
+
+.SGB_BetaPikachuMinigame:
+ ld hl, PalPacket_BetaPikachuMinigame
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_BetaPoker:
+ ld hl, BlkPacket_9a86
+ ld de, wPlayerLightScreenCount ; ???
+ ld bc, PALPACKET_LENGTH
+ call CopyBytes
+ ld hl, PalPacket_BetaPoker
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_MapPals:
+ 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
+
+.SGB_Evolution:
+ 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
+
+.SGB0d:
+.SGB_TrainerCard:
+ ld hl, PalPacket_Diploma
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_UnownPuzzle:
+ ld hl, PalPacket_UnownPuzzle
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB12:
+ 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
+
+.SGB1e:
+ 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
+
+.SGB_GamefreakLogo:
+ ld hl, PalPacket_GamefreakLogo
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_PlayerOrMonFrontpicPals:
+ 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
+
+.SGB_TradeTube:
+ ld hl, PalPacket_TradeTube
+ ld de, BlkPacket_9a86
+ ret
+
+.SGB_TrainerOrMonFrontpicPals:
+ 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
+
+.GetMapPalsIndex:
+ 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
+
+INCLUDE "data/maps/sgb_roof_pal_inds.asm"
+
+_LoadSGBLayout_ReturnFromJumpTable:
+ push de
+ call PushSGBPals_
+ pop hl
+ jp PushSGBPals_
diff --git a/engine/gfx/sprite_anims.asm b/engine/gfx/sprite_anims.asm
new file mode 100644
index 000000000..1d3aa6a8b
--- /dev/null
+++ b/engine/gfx/sprite_anims.asm
@@ -0,0 +1,876 @@
+DoAnimFrame:
+ 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
+
+.Jumptable:
+; 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:
+ ret
+
+.PartyMon
+ 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
+ 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
+ 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
+ call .AnonymousJumptable
+ jp hl
+
+; Anonymous dw (see .AnonymousJumptable)
+ dw .four_zero
+ dw .four_one
+
+.four_zero
+ 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
+ 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
+
+.GSIntroHoOh
+ 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
+ callfar NamingScreen_AnimateCursor
+ ret
+
+.MailCursor
+ callfar ComposeMail_AnimateCursor
+ ret
+
+.GameFreakLogo:
+ callfar GameFreakLogoJumper
+ ret
+
+.GSIntroStar
+ 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
+ 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:
+ callfar Slots_AnimateGolem
+ ret
+
+.SlotsChansey:
+ 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:
+ 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
+ callfar ret_e00ed
+ ret
+
+.PokegearArrow
+ callfar AnimatePokegearModeIndicatorArrow
+ ret
+
+.DummyGameCursor
+ callfar DummyGame_InterpretJoypad_AnimateCursor
+ ret
+
+.TradePokeBall
+ call .AnonymousJumptable
+ jp hl
+
+; Anonymous dw (see .AnonymousJumptable)
+ dw .TradePokeBall_zero
+ dw .TradePokeBall_one
+ dw .TradePokeBall_two
+ dw .TradePokeBall_three
+ dw .TradePokeBall_four
+ dw .TradePokeBall_five
+
+.TradePokeBall_zero
+ 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
+
+.TradePokeBall_two
+ 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
+ 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
+
+.TradePokeBall_one
+ 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
+
+.TradePokeBall_four
+ 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
+ call DeinitializeSprite
+ ret
+
+.TradeTubeBulge
+ 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
+ callfar TradeAnim_AnimateTrademonInTube
+ ret
+
+.RevealNewMon:
+ 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:
+ callfar AnimateTuningKnob
+ ret
+
+.CutLeaves
+ 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:
+ 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:
+ 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:
+ 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
+ farcall Function108bc7
+ ret
+
+.MobileTradeOTPulse
+ farcall Function108be0
+ ret
+
+.IntroSuicune
+ 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
+ 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
+ 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
+ ld a, [wcf64]
+ cp $40
+ ret nz
+ ld a, SPRITE_ANIM_FRAMESET_INTRO_UNOWN_F_2
+ call _ReinitSpriteAnimFrame
+ ret
+
+.IntroSuicuneAway
+ ld hl, SPRITEANIMSTRUCT_YCOORD
+ add hl, bc
+ ld a, [hl]
+ add $10
+ ld [hl], a
+ ret
+
+.EZChatCursor
+ farcall AnimateEZChatCursor
+ ret
+
+.Celebi
+ farcall UpdateCelebiPosition
+ ret
+
+.AnonymousJumptable:
+ 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
+
+.IncrementJumptableIndex:
+ ld hl, SPRITEANIMSTRUCT_JUMPTABLE_INDEX
+ add hl, bc
+ inc [hl]
+ ret
+
+.Sprites_Sine:
+ call Sprites_Sine
+ ret
+
+.Sprites_Cosine:
+ call Sprites_Cosine
+ ret
diff --git a/engine/gfx/sprites.asm b/engine/gfx/sprites.asm
new file mode 100644
index 000000000..1b22e5832
--- /dev/null
+++ b/engine/gfx/sprites.asm
@@ -0,0 +1,649 @@
+ClearSpriteAnims:
+ ld hl, wSpriteAnimDict
+ ld bc, wSpriteAnimsEnd - wSpriteAnimDict
+.loop
+ ld [hl], $0
+ inc hl
+ dec bc
+ ld a, c
+ or b
+ jr nz, .loop
+ ret
+
+PlaySpriteAnimationsAndDelayFrame:
+ call PlaySpriteAnimations
+ call DelayFrame
+ ret
+
+PlaySpriteAnimations:
+ 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
+
+DoNextFrameForAllSprites:
+ 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
+
+DoNextFrameForFirst16Sprites:
+ 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::
+; 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
+
+DeinitializeSprite:
+; Clear the index field of the struct in bc.
+ ld hl, SPRITEANIMSTRUCT_INDEX
+ add hl, bc
+ ld [hl], $0
+ ret
+
+DeinitializeAllSprites:
+; 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:
+ 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
+
+AddOrSubtractY:
+ 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
+
+AddOrSubtractX:
+ 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
+
+GetSpriteOAMAttr:
+ ld a, [wCurrSpriteAddSubFlags]
+ ld b, a
+ ld a, [hl]
+ xor b
+ and $e0
+ ld b, a
+ ld a, [hl]
+ and $1f
+ or b
+ ret
+
+InitSpriteAnimBuffer:
+ 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
+
+GetSpriteAnimVTile:
+; 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
+
+_ReinitSpriteAnimFrame::
+ 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
+
+GetSpriteAnimFrame:
+.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
+
+.GetPointer:
+ ; 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
+
+GetFrameOAMPointer:
+; Load OAM data pointer
+ ld e, a
+ ld d, 0
+ ld hl, SpriteAnimOAMData
+ add hl, de
+ add hl, de
+ add hl, de
+ ret
+
+Unreferenced_BrokenGetStdGraphics:
+ 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
+
+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
+
+Sprites_Cosine:
+; a = d * cos(a * pi/32)
+ add %010000 ; cos(x) = sin(x + pi/2)
+ ; fallthrough
+Sprites_Sine:
+; a = d * sin(a * pi/32)
+ calc_sine_wave
+
+AnimateEndOfExpBar:
+ 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
+
+.AnimateFrame:
+ 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
+
+EndOfExpBarGFX:
+INCBIN "gfx/battle/expbarend.2bpp"
+SGBEndOfExpBarGFX:
+INCBIN "gfx/battle/expbarend_sgb.2bpp"
+
+ClearSpriteAnims2:
+ 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
diff --git a/engine/gfx/trademon_frontpic.asm b/engine/gfx/trademon_frontpic.asm
new file mode 100644
index 000000000..d557123e9
--- /dev/null
+++ b/engine/gfx/trademon_frontpic.asm
@@ -0,0 +1,38 @@
+GetTrademonFrontpic:
+ 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:
+ 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