summaryrefslogtreecommitdiff
path: root/engine/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'engine/gfx')
-rw-r--r--engine/gfx/animated_objects.asm394
-rw-r--r--engine/gfx/bg_map_attributes.asm217
-rw-r--r--engine/gfx/hp_bar.asm6
-rw-r--r--engine/gfx/mon_icons.asm20
-rw-r--r--engine/gfx/palettes.asm561
-rw-r--r--engine/gfx/screen_effects.asm10
-rw-r--r--engine/gfx/sprite_oam.asm163
7 files changed, 1256 insertions, 115 deletions
diff --git a/engine/gfx/animated_objects.asm b/engine/gfx/animated_objects.asm
new file mode 100644
index 00000000..971af6b5
--- /dev/null
+++ b/engine/gfx/animated_objects.asm
@@ -0,0 +1,394 @@
+ClearObjectAnimationBuffers:
+ ld hl, wAnimatedObjectsData
+ ld bc, wAnimatedObjectsDataEnd - wAnimatedObjectsData
+ xor a
+ call FillMemory
+ ret
+
+RunObjectAnimations:
+ ld hl, wAnimatedObjectDataStructs
+ ld e, 10
+.loop
+ ld a, [hl]
+ and a
+ jr z, .next
+ ld c, l
+ ld b, h
+ push hl
+ push de
+ call ExecuteCurrentAnimatedObjectCallback
+ call UpdateCurrentAnimatedObjectFrame
+ pop de
+ pop hl
+ jr c, .quit
+.next
+ ld bc, $10
+ add hl, bc
+ dec e
+ jr nz, .loop
+ ld a, [wCurrentAnimatedObjectOAMBufferOffset]
+ ld l, a
+ ld h, HIGH(wOAMBuffer)
+.deinit_unused_oam_loop
+ ld a, l
+ cp LOW(wOAMBufferEnd)
+ jr nc, .quit
+ xor a
+ ld [hli], a
+ jr .deinit_unused_oam_loop
+
+.quit
+ ret
+
+SpawnAnimatedObject:
+ push de
+ push af
+ ld hl, wAnimatedObjectDataStructs
+ ld e, 10
+.loop
+ ld a, [hl]
+ and a
+ jr z, .init
+ ld bc, $10
+ add hl, bc
+ dec e
+ jr nz, .loop
+ pop af
+ pop de
+ scf
+ ret
+
+.init
+ pop af
+ ld c, l
+ ld b, h
+ ld hl, wNumLoadedAnimatedObjects
+ inc [hl]
+ ld e, a
+ ld d, $0
+ ld a, [wAnimatedObjectSpawnStateDataPointer]
+ ld l, a
+ ld a, [wAnimatedObjectSpawnStateDataPointer + 1]
+ ld h, a
+ add hl, de
+ add hl, de
+ add hl, de
+ ld e, l
+ ld d, h
+ ld hl, $0
+ add hl, bc
+ ld a, [wNumLoadedAnimatedObjects]
+ ld [hli], a
+ ld a, [de]
+ ld [hli], a
+ inc de
+ ld a, [de]
+ ld [hli], a
+ inc de
+ xor a
+ ld [hli], a
+ pop de
+ ld hl, $4
+ add hl, bc
+ ld a, e
+ ld [hli], a
+ ld a, d
+ ld [hli], a
+ xor a
+ ld [hli], a
+ ld [hli], a
+ xor a
+ ld [hli], a
+ ld [hli], a
+ dec a
+ ld [hli], a
+ xor a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ret
+
+MaskCurrentAnimatedObjectStruct:
+ ld hl, $0
+ add hl, bc
+ ld [hl], $0
+ ret
+
+MaskAllAnimatedObjectStructs:
+ ld hl, wAnimatedObjectDataStructs
+ ld e, 10
+.loop
+ ld [hl], $0
+ ld bc, $10
+ add hl, bc
+ dec e
+ jr nz, .loop
+ ret
+
+UpdateCurrentAnimatedObjectFrame:
+ xor a
+ ld [wCurAnimatedObjectOAMAttributes], a
+ ld hl, $3
+ add hl, bc
+ ld a, [hli]
+ ld [wCurrentAnimatedObjectVTileOffset], a
+ ld a, [hli]
+ ld [wCurrentAnimatedObjectXCoord], a
+ ld a, [hli]
+ ld [wCurrentAnimatedObjectYCoord], a
+ ld a, [hli]
+ ld [wCurrentAnimatedObjectXOffset], a
+ ld a, [hl]
+ ld [wCurrentAnimatedObjectYOffset], a
+ call UpdateDurationTimerAndFrameStateForCurrentAnimatedObject
+ cp $fd
+ jr z, .finish
+ cp $fc
+ jr z, .delete_animation
+ call GetCurrentAnimatedObjectOAMDataPointer
+ ld a, [wCurrentAnimatedObjectVTileOffset]
+ add [hl]
+ ld [wCurrentAnimatedObjectVTileOffset], a
+ inc hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ push bc
+ ld a, [wCurrentAnimatedObjectOAMBufferOffset]
+ ld e, a
+ ld d, HIGH(wOAMBuffer)
+ ld a, [hli]
+ ld c, a
+.loop
+ ld a, [wCurrentAnimatedObjectYCoord]
+ ld b, a
+ ld a, [wCurrentAnimatedObjectYOffset]
+ add b
+ ld b, a
+ ld a, [wAnimatedObjectGlobalYOffset]
+ add b
+ ld b, a
+ call GetCurrentAnimatedObjectTileYCoordinate
+ add b
+ ld [de], a
+ inc hl
+ inc de
+ ld a, [wCurrentAnimatedObjectXCoord]
+ ld b, a
+ ld a, [wCurrentAnimatedObjectXOffset]
+ add b
+ ld b, a
+ ld a, [wAnimatedObjectGlobalXOffset]
+ add b
+ ld b, a
+ call GetCurrentAnimatedObjectTileXCoordinate
+ add b
+ ld [de], a
+ inc hl
+ inc de
+ ld a, [wCurrentAnimatedObjectVTileOffset]
+ add [hl]
+ ld [de], a
+ inc hl
+ inc de
+ call SetCurrentAnimatedObjectOAMAttributes
+ ld b, a
+ ld a, [wc634]
+ cp $7
+ ld a, b
+ jr z, .skip_load
+ ld [de], a
+.skip_load
+ inc hl
+ inc de
+ ld a, e
+ ld [wCurrentAnimatedObjectOAMBufferOffset], a
+ cp LOW(wOAMBufferEnd)
+ jr nc, .oam_is_full
+ dec c
+ jr nz, .loop
+ pop bc
+ jr .finish
+
+.delete_animation
+ call MaskCurrentAnimatedObjectStruct
+.finish
+ and a
+ ret
+
+.oam_is_full
+ pop bc
+ scf
+ ret
+
+GetCurrentAnimatedObjectTileYCoordinate:
+ push hl
+ ld a, [hl]
+ ld hl, wCurAnimatedObjectOAMAttributes
+ bit 6, [hl]
+ jr z, .no_flip
+ add $8
+ xor $ff
+ inc a
+.no_flip
+ pop hl
+ ret
+
+GetCurrentAnimatedObjectTileXCoordinate:
+ push hl
+ ld a, [hl]
+ ld hl, wCurAnimatedObjectOAMAttributes
+ bit 5, [hl]
+ jr z, .no_flip
+ add $8
+ xor $ff
+ inc a
+.no_flip
+ pop hl
+ ret
+
+SetCurrentAnimatedObjectOAMAttributes:
+ ld a, [wCurAnimatedObjectOAMAttributes]
+ ld b, a
+ ld a, [hl]
+ xor b
+ and $e0
+ ld b, a
+ ld a, [hl]
+ and $10
+ or b
+ bit 4, a
+ ret z
+ or $4
+ ret
+
+GetCurrentAnimatedObjectOAMDataPointer:
+ ld e, a
+ ld d, $0
+ ld a, [wAnimatedObjectOAMDataPointer]
+ ld l, a
+ ld a, [wAnimatedObjectOAMDataPointer + 1]
+ ld h, a
+ add hl, de
+ add hl, de
+ add hl, de
+ ret
+
+SetCurrentAnimatedObjectCallbackAndResetFrameStateRegisters:
+ ld hl, $1
+ add hl, bc
+ ld [hl], a
+ ld hl, $8
+ add hl, bc
+ ld [hl], $0
+ ld hl, $9
+ add hl, bc
+ ld [hl], $0
+ ld hl, $a
+ add hl, bc
+ ld [hl], $ff
+ ret
+
+UpdateDurationTimerAndFrameStateForCurrentAnimatedObject:
+.loop
+ ld hl, $8
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .next_frame
+ dec [hl]
+ call GetPointerToCurrentAnimatedObjectFrameScript
+ ld a, [hli]
+ push af
+ jr .finish
+
+.next_frame
+ ld hl, $a
+ add hl, bc
+ inc [hl]
+ call GetPointerToCurrentAnimatedObjectFrameScript
+ ld a, [hli]
+ cp $fe
+ jr z, .restart_anim
+ cp $ff
+ jr z, .hold_last_frame_state
+ push af
+ ld a, [hl]
+ push hl
+ and $3f
+ ld hl, $9
+ add hl, bc
+ add [hl]
+ ld hl, $8
+ add hl, bc
+ ld [hl], a
+ pop hl
+.finish
+ ld a, [hl]
+ and $c0
+ srl a
+ ld [wCurAnimatedObjectOAMAttributes], a
+ pop af
+ ret
+
+.hold_last_frame_state
+ xor a
+ ld hl, $8
+ add hl, bc
+ ld [hl], a
+ ld hl, $a
+ add hl, bc
+ dec [hl]
+ dec [hl]
+ jr .loop
+
+.restart_anim
+ xor a
+ ld hl, $8
+ add hl, bc
+ ld [hl], a
+ dec a
+ ld hl, $a
+ add hl, bc
+ ld [hl], a
+ jr .loop
+
+GetPointerToCurrentAnimatedObjectFrameScript:
+ ld hl, $1
+ add hl, bc
+ ld e, [hl]
+ ld d, $0
+ ld a, [wAnimatedObjectFramesDataPointer]
+ ld l, a
+ ld a, [wAnimatedObjectFramesDataPointer + 1]
+ ld h, a
+ add hl, de
+ add hl, de
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, $a
+ add hl, bc
+ ld l, [hl]
+ ld h, $0
+ add hl, hl
+ add hl, de
+ ret
+
+ExecuteCurrentAnimatedObjectCallback:
+ ld hl, $2
+ add hl, bc
+ ld e, [hl]
+ ld d, $0
+ ld a, [wAnimatedObjectJumptablePointer]
+ ld l, a
+ ld a, [wAnimatedObjectJumptablePointer + 1]
+ ld h, a
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
diff --git a/engine/gfx/bg_map_attributes.asm b/engine/gfx/bg_map_attributes.asm
new file mode 100644
index 00000000..55313846
--- /dev/null
+++ b/engine/gfx/bg_map_attributes.asm
@@ -0,0 +1,217 @@
+LoadBGMapAttributes::
+ ld hl, BGMapAttributesPointers
+ ld a, c ; c = which packet
+ push af ; save for later (to determine if we're handling the trainer card or party menu)
+ dec a ; read this code as:
+ add a ; dec a
+ ld e, a ; add a
+ xor a ; ld e, a
+ ld d, a ; ld d, 0
+ add hl, de ; add hl, de
+ ld a, [hli] ; ld a, [hli]
+ ld e, a ; ld h, [hl]
+ ld a, [hl] ; ld l, a
+ ld h, a
+ ld a, e
+ ld l, a
+
+ di
+ ld a, $1
+ ldh [rVBK], a
+ push hl
+ ld a, [hl]
+ ld c, a ; save attribute count for later
+ ld de, $10
+ add hl, de
+ ld a, h
+ ldh [rHDMA1], a
+ ld a, l
+ ldh [rHDMA2], a
+ ld de, vBGMap0
+ ld a, d
+ ldh [rHDMA3], a
+ ld a, e
+ ldh [rHDMA4], a
+
+ ldh a, [rLCDC]
+ and rLCDC_ENABLE_MASK ; is LCD off?
+ jr z, .lcdOff ; if off, transfer immediately
+; wait for VBlank if LCD is on
+.waitForVBlankLoop1
+ ldh a, [rLY]
+ cp $90
+ jr nz, .waitForVBlankLoop1
+.waitForAccessibleVRAMLoop1
+ ldh a, [rSTAT]
+ and %10 ; are we in HBlank or VBlank?
+ jr nz, .waitForAccessibleVRAMLoop1 ; loop until we're in a safe period to transfer to VRAM
+.lcdOff
+ ld a, c ; number of BG attributes to transfer, plus 1 times 16
+ ldh [rHDMA5], a ; initiate transfer
+ call Func_3082 ; update audio so it doesn't "lag"
+ pop hl
+ ld a, [hli]
+ ld c, a ; number of BG attributes to transfer, plus 1 times 16
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a ; offset of the attributes
+ add hl, de ; hl = new pointer
+ ld a, h
+ ldh [rHDMA1], a
+ ld a, l
+ ldh [rHDMA2], a
+ ld de, vBGMap1 ; copy to vBGMap1
+ ld a, d
+ ldh [rHDMA3], a
+ ld a, e
+ ldh [rHDMA4], a
+; LCD check again
+ ldh a, [rLCDC]
+ and rLCDC_ENABLE_MASK ; is LCD off?
+ jr z, .lcdOff2 ; if off, transfer immediately
+; wait for VBlank if LCD is on
+.waitForVBlankLoop2
+ ldh a, [rLY]
+ cp $90
+ jr nz, .waitForVBlankLoop2
+.waitForAccessibleVRAMLoop2
+ ldh a, [rSTAT]
+ and %10 ; are we in HBlank or VBlank?
+ jr nz, .waitForAccessibleVRAMLoop2 ; loop until we're in a safe period to transfer to VRAM
+.lcdOff2
+ ld a, c
+ ldh [rHDMA5], a
+ pop af
+ dec a
+ dec a
+ dec a
+ dec a
+ jr nz, .checkIfHandlingPartyMenu
+ call HandleBadgeFaceAttributes
+ jr .done
+.checkIfHandlingPartyMenu
+ dec a
+ call z, HandlePartyHPBarAttributes
+.done
+ call Func_3082
+ ldh a, [rIF]
+ res VBLANK, a
+ ldh [rIF], a
+ xor a
+ ldh [rVBK], a
+ ei
+ ret
+
+BGMapAttributesPointers:
+ dw BGMapAttributes_Unknown1
+ dw BGMapAttributes_Unknown2
+ dw BGMapAttributes_GameFreakIntro
+ dw BGMapAttributes_TrainerCard
+ dw BGMapAttributes_PartyMenu
+ dw BGMapAttributes_NidorinoIntro
+ dw BGMapAttributes_TitleScreen
+ dw BGMapAttributes_Slots
+ dw BGMapAttributes_Pokedex
+ dw BGMapAttributes_StatusScreen
+ dw BGMapAttributes_Battle
+ dw BGMapAttributes_WholeScreen
+ dw BGMapAttributes_Unknown13
+
+HandleBadgeFaceAttributes:
+; zero out the attributes if the player doesn't have the respective badge
+; BOULDERBADGE
+ ld hl, vBGMap1 + $183
+ ld de, wTrainerCardBadgeAttributes + 6 * 0
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; CASCADEBADGE
+ ld hl, vBGMap1 + $187
+ ld de, wTrainerCardBadgeAttributes + 6 * 1
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; THUNDERBADGE
+ ld hl, vBGMap1 + $18b
+ ld de, wTrainerCardBadgeAttributes + 6 * 2
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; RAINBOWBADGE
+ ld hl, vBGMap1 + $18f
+ ld de, wTrainerCardBadgeAttributes + 6 * 3
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; SOULBADGE
+ ld hl, vBGMap1 + $1e3
+ ld de, wTrainerCardBadgeAttributes + 6 * 6
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; MARSHBADGE
+ ld hl, vBGMap1 + $1e7
+ ld de, wTrainerCardBadgeAttributes + 6 * 7
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; VOLCANOBADGE
+ ld hl, vBGMap1 + $1eb
+ ld de, wTrainerCardBadgeAttributes + 6 * 8
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+; EARTHBADGE
+ ld hl, vBGMap1 + $1ef
+ ld de, wTrainerCardBadgeAttributes + 6 * 9
+ ld a, [de]
+ and a
+ call z, ZeroOutCurrentBadgeAttributes
+ ret
+
+ZeroOutCurrentBadgeAttributes:
+ push hl
+ xor a
+ ld [hli], a
+ ld [hl], a
+ ld bc, $1f
+ add hl, bc
+ ld [hli], a
+ ld [hl], a
+ pop hl
+ ret
+
+HandlePartyHPBarAttributes:
+; hp bars require 3 (green, orange, red) colours, when there are only 2 "free" colours per palette
+; therefore, we must transfer individual bg attributes where the locations of the hp bars are in vram
+ ld hl, vBGMap1 + $25 ; location of start of the HP bar in vram
+ ld de, wPartyHPBarAttributes
+ ld c, PARTY_LENGTH
+.loop
+ push bc
+ push hl
+ ld a, [de]
+ and $3 ; 4 possible palettes
+REPT 7 ; hp bar length in tiles
+ ld [hli], a
+ENDR
+ pop hl
+ ld bc, $40 ; get 2nd party location
+ add hl, bc
+ push hl
+
+ push de ; (inefficiently) copy de to hl
+ pop hl
+
+ ld bc, $6
+ add hl, bc ; get the next palette
+
+ push hl
+ pop de ; copy back to de
+
+ pop hl
+ pop bc
+ dec c
+ jr nz, .loop
+ ret
diff --git a/engine/gfx/hp_bar.asm b/engine/gfx/hp_bar.asm
index e4de0b95..fcbf1a29 100644
--- a/engine/gfx/hp_bar.asm
+++ b/engine/gfx/hp_bar.asm
@@ -213,13 +213,11 @@ UpdateHPBar_PrintHPNumber:
ld a, [wHPBarOldHP + 1]
ld [wHPBarTempHP], a
push hl
+ ld de, $15
ldh a, [hUILayoutFlags]
bit 0, a
- jr z, .asm_fb15
+ jr z, .next
ld de, $9
- jr .next
-.asm_fb15
- ld de, $15
.next
add hl, de
push hl
diff --git a/engine/gfx/mon_icons.asm b/engine/gfx/mon_icons.asm
index 16409640..6e2cf0ba 100644
--- a/engine/gfx/mon_icons.asm
+++ b/engine/gfx/mon_icons.asm
@@ -91,7 +91,7 @@ PartyMonSpeeds:
LoadMonPartySpriteGfx:
; Load mon party sprite tile patterns into VRAM during V-blank.
ld hl, MonPartySpritePointers
- ld a, $1c
+ ld a, $1e
LoadAnimSpriteGfx:
; Load animated sprite tile patterns into VRAM during V-blank. hl is the address
@@ -130,7 +130,7 @@ LoadMonPartySpriteGfxWithLCDDisabled:
; LCD.
call DisableLCD
ld hl, MonPartySpritePointers
- ld a, $1c
+ ld a, $1e
ld bc, $0
.loop
push af
@@ -151,7 +151,7 @@ LoadMonPartySpriteGfxWithLCDDisabled:
inc hl
ld d, [hl]
pop hl
- call FarCopyData2
+ call FarCopyData
pop hl
pop bc
ld a, $6
@@ -170,6 +170,8 @@ WriteMonPartySpriteOAMByPartyIndex:
push de
push bc
ldh a, [hPartyMonIndex]
+ cp $ff
+ jr z, .asm_7191f
ld hl, wPartySpecies
ld e, a
ld d, 0
@@ -183,6 +185,16 @@ WriteMonPartySpriteOAMByPartyIndex:
pop hl
ret
+.asm_7191f
+ ld hl, wOAMBuffer
+ ld de, wMonPartySpritesSavedOAM
+ ld bc, $60
+ call CopyData
+ pop bc
+ pop de
+ pop hl
+ ret
+
WriteMonPartySpriteOAMBySpecies:
; Write OAM blocks for the party sprite of the species in
; [wMonPartySpriteSpecies].
@@ -204,7 +216,7 @@ UnusedPartyMonSpriteFunction:
ld hl, vSprites tile $00
call .LoadTilePatterns
pop af
- add $54
+ add $5A
ld hl, vSprites tile $04
call .LoadTilePatterns
xor a
diff --git a/engine/gfx/palettes.asm b/engine/gfx/palettes.asm
index a43c66ba..b480c6d0 100644
--- a/engine/gfx/palettes.asm
+++ b/engine/gfx/palettes.asm
@@ -30,11 +30,17 @@ SetPal_Battle:
ld de, wPalPacket
ld bc, $10
call CopyData
- ld a, [wPlayerBattleStatus3]
ld hl, wBattleMonSpecies
+ ld a, [hl]
+ and a
+ jr z, .asm_71ef9
+ ld hl, wPartyMon1
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+.asm_71ef9
call DeterminePaletteID
ld b, a
- ld a, [wEnemyBattleStatus3]
ld hl, wEnemyMonSpecies2
call DeterminePaletteID
ld c, a
@@ -155,6 +161,10 @@ SetPal_Overworld:
jr z, .Lorelei
cp BRUNOS_ROOM
jr z, .caveOrBruno
+ cp TRADE_CENTER
+ jr z, .trade_center_colosseum
+ cp COLOSSEUM
+ jr z, .trade_center_colosseum
.normalDungeonOrBuilding
ld a, [wLastMap] ; town or route that current dungeon or building is located
.townOrRoute
@@ -178,6 +188,9 @@ SetPal_Overworld:
.Lorelei
xor a
jr .town
+.trade_center_colosseum
+ ld a, PAL_GREYMON - 1
+ jr .town
; used when a Pokemon is the only thing on the screen
; such as evolution, trading and the Hall of Fame
@@ -240,6 +253,16 @@ SetPal_TrainerCard:
ld de, wTrainerCardBlkPacket
ret
+SendUnknownPalPacket_7205d::
+ ld hl, UnknownPalPacket_72811
+ ld de, BlkPacket_WholeScreen
+ ret
+
+SendUnknownPalPacket_72064::
+ ld hl, UnknownPalPacket_72821
+ ld de, UnknownPacket_72751
+ ret
+
SetPalFunctions:
; entries correspond to SET_PAL_* constants
dw SetPal_BattleBlack
@@ -256,6 +279,8 @@ SetPalFunctions:
dw SetPal_PokemonWholeScreen
dw SetPal_GameFreakIntro
dw SetPal_TrainerCard
+ dw SendUnknownPalPacket_7205d
+ dw SendUnknownPalPacket_72064
; The length of the blk data of each badge on the Trainer Card.
; The Rainbow Badge has 3 entries because of its many colors.
@@ -270,9 +295,6 @@ BadgeBlkDataLengths:
db 6 ; Earth Badge
DeterminePaletteID:
- bit TRANSFORMED, a ; a is battle status 3
- ld a, PAL_GREYMON ; if the mon has used Transform, use Ditto's palette
- ret nz
ld a, [hl]
DeterminePaletteIDOutOfBattle:
ld [wd11e], a
@@ -290,6 +312,132 @@ DeterminePaletteIDOutOfBattle:
ld a, [hl]
ret
+YellowIntroPaletteAction::
+ ld a, e
+ and a
+ jr nz, .asm_720bd
+ ld hl, PalPacket_Generic
+ ldh a, [hGBC]
+ and a
+ jp z, SendSGBPacket
+ jp InitGBCPalettes
+
+.asm_720bd
+ ld hl, UnknownPalPacket_72811
+ ldh a, [hGBC]
+ and a
+ jp z, SendSGBPacket
+ call InitGBCPalettes
+ ld hl, PalPacket_Generic
+ inc hl
+ ld a, [hli]
+ call GetGBCBasePalAddress
+ ld a, e
+ ld [wGBCBasePalPointers + 2], a
+ ld a, d
+ ld [wGBCBasePalPointers + 2 + 1], a
+ xor a ; CONVERT_BGP
+ call DMGPalToGBCPal
+ ld a, 1
+ call TransferCurBGPData
+ ret
+
+LoadOverworldPikachuFrontpicPalettes::
+ ld hl, PalPacket_Empty
+ ld de, wPalPacket
+ ld bc, $10
+ call CopyData
+ call GetPal_Pikachu
+ ld hl, wPartyMenuBlkPacket
+ ld [hl], a
+ ld hl, wPartyMenuBlkPacket + 2
+ ld a, $26
+ ld [hl], a
+ ld hl, wPalPacket
+ ldh a, [hGBC]
+ and a
+ jr nz, .cgb_1
+ call SendSGBPacket
+ jr .okay_1
+
+.cgb_1
+ call InitGBCPalettes
+.okay_1
+ ld hl, BlkPacket_WholeScreen
+ ld de, wPalPacket
+ ld bc, $10
+ call CopyData
+ ld hl, wPartyMenuBlkPacket + 2
+ ld a, $5
+ ld [hli], a
+ ld a, $7
+ ld [hli], a
+ ld a, $6
+ ld [hli], a
+ ld a, $b
+ ld [hli], a
+ ld a, $a
+ ld [hl], a
+ ld hl, wPalPacket
+ ldh a, [hGBC]
+ and a
+ jr nz, .cgb_2
+ call SendSGBPacket
+ jr .okay_2
+
+.cgb_2
+ call InitGBCPalettes
+.okay_2
+ ret
+
+GetPal_Pikachu::
+; similar to SetPal_Overworld
+ ld a, [wCurMapTileset]
+ cp CEMETERY
+ jr z, .PokemonTowerOrAgatha
+ cp CAVERN
+ jr z, .caveOrBruno
+ ld a, [wCurMap]
+ cp REDS_HOUSE_1F
+ jr c, .townOrRoute
+ cp CERULEAN_CAVE_2F
+ jr c, .normalDungeonOrBuilding
+ cp NAME_RATERS_HOUSE
+ jr c, .caveOrBruno
+ cp LORELEIS_ROOM
+ jr z, .Lorelei
+ cp BRUNOS_ROOM
+ jr z, .caveOrBruno
+ cp TRADE_CENTER
+ jr z, .battleOrTradeCenter
+ cp COLOSSEUM
+ jr z, .battleOrTradeCenter
+.normalDungeonOrBuilding
+ ld a, [wLastMap] ; town or route that current dungeon or building is located
+.townOrRoute
+ cp SAFFRON_CITY + 1
+ jr c, .town
+ ld a, PAL_ROUTE - 1
+.town
+ inc a ; a town's pallete ID is its map ID + 1
+ ret
+
+.PokemonTowerOrAgatha
+ ld a, PAL_GREYMON - 1
+ jr .town
+
+.caveOrBruno
+ ld a, PAL_CAVE - 1
+ jr .town
+
+.Lorelei
+ xor a ; PAL_PALLET - 1
+ jr .town
+
+.battleOrTradeCenter
+ ld a, PAL_GREYMON - 1
+ jr .town
+
InitPartyMenuBlkPacket:
ld hl, BlkPacket_PartyMenu
ld de, wPartyMenuBlkPacket
@@ -325,6 +473,14 @@ UpdatePartyMenuBlkPacket:
ret
SendSGBPacket:
+ ld a, 1
+ ldh [hDisableJoypadPolling], a ; don't poll joypad while sending packet
+ call _SendSGBPacket
+ xor a
+ ldh [hDisableJoypadPolling], a
+ ret
+
+_SendSGBPacket:
;check number of packets
ld a, [hl]
and $07
@@ -334,9 +490,6 @@ SendSGBPacket:
.loop2
; save B for later use
push bc
-; disable ReadJoypad to prevent it from interfering with sending the packet
- ld a, 1
- ldh [hDisableJoypadPolling], a
; send RESET signal (P14=LOW, P15=LOW)
xor a
ldh [rJOYP], a
@@ -377,8 +530,6 @@ SendSGBPacket:
; set P14=HIGH,P15=HIGH
ld a, $30
ldh [rJOYP], a
- xor a
- ldh [hDisableJoypadPolling], a
; wait for about 70000 cycles
call Wait7000
; restore (previously pushed) number of packets
@@ -393,14 +544,17 @@ LoadSGB:
xor a
ld [wOnSGB], a
call CheckSGB
- ret nc
- ld a, 1
- ld [wOnSGB], a
- ld a, [wGBC]
+ jr c, .onSGB
+ ldh a, [hGBC]
and a
- jr z, .notGBC
+ jr z, .onDMG
+ ld a, $1
+ ld [wOnSGB], a
+.onDMG
ret
-.notGBC
+.onSGB
+ ld a, $1
+ ld [wOnSGB], a
di
call PrepareSuperNintendoVRAMTransfer
ei
@@ -443,23 +597,19 @@ PrepareSuperNintendoVRAMTransfer:
.packetPointers
; Only the first packet is needed.
dw MaskEnFreezePacket
- dw DataSnd_72548
- dw DataSnd_72558
- dw DataSnd_72568
- dw DataSnd_72578
- dw DataSnd_72588
- dw DataSnd_72598
- dw DataSnd_725a8
- dw DataSnd_725b8
+ dw DataSnd_728a1
+ dw DataSnd_728b1
+ dw DataSnd_728c1
+ dw DataSnd_728d1
+ dw DataSnd_728e1
+ dw DataSnd_728f1
+ dw DataSnd_72901
+ dw DataSnd_72911
CheckSGB:
; Returns whether the game is running on an SGB in carry.
ld hl, MltReq2Packet
- di
call SendSGBPacket
- ld a, 1
- ldh [hDisableJoypadPolling], a
- ei
call Wait7000
ldh a, [rJOYP]
and $3
@@ -515,6 +665,7 @@ CopyGfxToSuperNintendoVRAM:
call DisableLCD
ld a, $e4
ldh [rBGP], a
+ call _UpdateGBCPal_BGP_CheckDMG
ld de, vChars1
ld a, [wCopyingSGBTileData]
and a
@@ -545,6 +696,7 @@ CopyGfxToSuperNintendoVRAM:
call SendSGBPacket
xor a
ldh [rBGP], a
+ call _UpdateGBCPal_BGP_CheckDMG
ei
ret
@@ -562,13 +714,17 @@ Wait7000:
ret
SendSGBPackets:
- ld a, [wGBC]
+ ldh a, [hGBC]
and a
jr z, .notGBC
push de
call InitGBCPalettes
pop hl
- call EmptyFunc3
+ call InitGBCPalettes
+ ldh a, [rLCDC]
+ and rLCDC_ENABLE_MASK
+ ret z
+ call Delay3
ret
.notGBC
push de
@@ -577,30 +733,349 @@ SendSGBPackets:
jp SendSGBPacket
InitGBCPalettes:
- ld a, $80 ; index 0 with auto-increment
- ldh [rBGPI], a
- inc hl
- ld c, $20
-.loop
- ld a, [hli]
+ ld a, [hl]
+ and $f8
+ cp $20
+ jp z, TranslatePalPacketToBGMapAttributes
+
inc hl
+
+index = 0
+
+ REPT NUM_ACTIVE_PALS
+ IF index > 0
+ pop hl
+ ENDC
+
+ ld a, [hli]
+ inc hl
+
+ IF index < NUM_ACTIVE_PALS - 1
+ push hl
+ ENDC
+
+ call GetGBCBasePalAddress
+ ld a, e
+ ld [wGBCBasePalPointers + index * 2], a
+ ld a, d
+ ld [wGBCBasePalPointers + index * 2 + 1], a
+
+ xor a ; CONVERT_BGP
+ call DMGPalToGBCPal
+ ld a, index
+ call TransferCurBGPData
+
+ ld a, CONVERT_OBP0
+ call DMGPalToGBCPal
+ ld a, index
+ call TransferCurOBPData
+
+ ld a, CONVERT_OBP1
+ call DMGPalToGBCPal
+ ld a, index + 4
+ call TransferCurOBPData
+
+index = index + 1
+ ENDR
+
+ ret
+
+GetGBCBasePalAddress::
+; Input: a = palette ID
+; Output: de = palette address
+ push hl
+ ld l, a
+ xor a
+ ld h, a
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld de, GBCBasePalettes
+ add hl, de
+ ld a, l
+ ld e, a
+ ld a, h
+ ld d, a
+ pop hl
+ ret
+
+DMGPalToGBCPal::
+; Populate wGBCPal with colors from a base palette, selected using one of the
+; DMG palette registers.
+; Input:
+; a = which DMG palette register
+; de = address of GBC base palette
+ and a
+ jr nz, .notBGP
+ ldh a, [rBGP]
+ ld [wLastBGP], a
+ jr .convert
+.notBGP
+ dec a
+ jr nz, .notOBP0
+ ldh a, [rOBP0]
+ ld [wLastOBP0], a
+ jr .convert
+.notOBP0
+ ldh a, [rOBP1]
+ ld [wLastOBP1], a
+.convert
+color_index = 0
+ REPT NUM_PAL_COLORS
+ ld b, a
+ and %11
+ call .GetColorAddress
+ ld a, [hli]
+ ld [wGBCPal + color_index * 2], a
+ ld a, [hl]
+ ld [wGBCPal + color_index * 2 + 1], a
+
+ IF color_index < NUM_PAL_COLORS - 1
+ ld a, b
+ rrca
+ rrca
+ ENDC
+
+color_index = color_index + 1
+ ENDR
+ ret
+
+.GetColorAddress:
add a
+ ld l, a
+ xor a
+ ld h, a
+ add hl, de
+ ret
+
+TransferCurBGPData::
+ push de
+ add a
+ add a
+ add a
+ or $80 ; auto-increment
+ ldh [rBGPI], a
+ ld de, rBGPD
+ ld hl, wGBCPal
+ ld b, %10 ; mask for non-V-blank/non-H-blank STAT mode
+ ldh a, [rLCDC]
+ and rLCDC_ENABLE_MASK
+ jr nz, .lcdEnabled
+ REPT NUM_PAL_COLORS
+ call TransferPalColorLCDDisabled
+ ENDR
+ jr .done
+.lcdEnabled
+ REPT NUM_PAL_COLORS
+ call TransferPalColorLCDEnabled
+ ENDR
+.done
+ pop de
+ ret
+
+BufferBGPPal::
+; Copy wGBCPal to palette a in wBGPPalsBuffer.
+ push de
add a
add a
- ld de, SuperPalettes
- add e
- jr nc, .noCarry
- inc d
-.noCarry
+ add a
+ ld l, a
+ xor a
+ ld h, a
+ ld de, wBGPPalsBuffer
+ add hl, de
+ ld de, wGBCPal
+ ld c, PALETTE_SIZE
+.loop
ld a, [de]
- ldh [rBGPD], a
+ ld [hli], a
+ inc de
dec c
jr nz, .loop
+ pop de
ret
-EmptyFunc3:
+TransferBGPPals::
+; Transfer the buffered BG palettes.
+ ldh a, [rLCDC]
+ and rLCDC_ENABLE_MASK
+ jr z, .lcdDisabled
+ di
+.waitLoop
+ ldh a, [rLY]
+ cp 144
+ jr c, .waitLoop
+.lcdDisabled
+ call .DoTransfer
+ ei
ret
+.DoTransfer:
+ xor a
+ or $80 ; auto-increment
+ ldh [rBGPI], a
+ ld de, rBGPD
+ ld hl, wBGPPalsBuffer
+ ld c, 4 * PALETTE_SIZE
+.loop
+ ld a, [hli]
+ ld [de], a
+ dec c
+ jr nz, .loop
+ ret
+
+TransferCurOBPData:
+ push de
+ add a
+ add a
+ add a
+ or $80 ; auto-increment
+ ldh [rOBPI], a
+ ld de, rOBPD
+ ld hl, wGBCPal
+ ld b, %10 ; mask for non-V-blank/non-H-blank STAT mode
+ ldh a, [rLCDC]
+ and rLCDC_ENABLE_MASK
+ jr nz, .lcdEnabled
+ REPT NUM_PAL_COLORS
+ call TransferPalColorLCDDisabled
+ ENDR
+ jr .done
+.lcdEnabled
+ REPT NUM_PAL_COLORS
+ call TransferPalColorLCDEnabled
+ ENDR
+.done
+ pop de
+ ret
+
+TransferPalColorLCDEnabled:
+; Transfer a palette color while the LCD is enabled.
+
+; In case we're already in H-blank or V-blank, wait for it to end. This is a
+; precaution so that the transfer doesn't extend past the blanking period.
+ ldh a, [rSTAT]
+ and b
+ jr z, TransferPalColorLCDEnabled
+
+; Wait for H-blank or V-blank to begin.
+.notInBlankingPeriod
+ ldh a, [rSTAT]
+ and b
+ jr nz, .notInBlankingPeriod
+; fall through
+
+TransferPalColorLCDDisabled:
+; Transfer a palette color while the LCD is disabled.
+ ld a, [hli]
+ ld [de], a
+ ld a, [hli]
+ ld [de], a
+ ret
+
+_UpdateGBCPal_BGP_CheckDMG::
+ ldh a, [hGBC]
+ and a
+ ret z
+; fall through
+
+_UpdateGBCPal_BGP::
+index = 0
+
+ REPT NUM_ACTIVE_PALS
+ ld a, [wGBCBasePalPointers + index * 2]
+ ld e, a
+ ld a, [wGBCBasePalPointers + index * 2 + 1]
+ ld d, a
+ xor a ; CONVERT_BGP
+ call DMGPalToGBCPal
+ ld a, index
+ call BufferBGPPal
+
+index = index + 1
+ ENDR
+
+ call TransferBGPPals
+ ret
+
+_UpdateGBCPal_OBP::
+index = 0
+
+ REPT NUM_ACTIVE_PALS
+ ld a, [wGBCBasePalPointers + index * 2]
+ ld e, a
+ ld a, [wGBCBasePalPointers + index * 2 + 1]
+ ld d, a
+ ld a, c
+ call DMGPalToGBCPal
+ ld a, c
+ dec a
+ rlca
+ rlca
+
+ IF index > 0
+ IF index == 1
+ inc a
+ ELSE
+ add index
+ ENDC
+ ENDC
+
+ call TransferCurOBPData
+
+index = index + 1
+ ENDR
+
+ ret
+
+TranslatePalPacketToBGMapAttributes::
+; translate the SGB pal packets into something usable for the GBC
+ push hl
+ pop de
+ ld hl, PalPacketPointers
+ ld a, [hli]
+ ld c, a
+.loop
+ ld a, e
+.innerLoop
+ cp [hl]
+ jr z, .checkHighByte
+ inc hl
+ inc hl
+ dec c
+ jr nz, .innerLoop
+ ret
+.checkHighByte
+; the low byte of pointer matched, so check the high byte
+ inc hl
+ ld a, d
+ cp [hl]
+ jr z, .foundMatchingPointer
+ inc hl
+ dec c
+ jr nz, .loop
+ ret
+.foundMatchingPointer
+ farcall LoadBGMapAttributes
+ ret
+
+PalPacketPointers::
+ db (palPacketPointersEnd - palPacketPointers) / 2
+palPacketPointers:
+ dw BlkPacket_WholeScreen
+ dw BlkPacket_Battle
+ dw BlkPacket_StatusScreen
+ dw BlkPacket_Pokedex
+ dw BlkPacket_Slots
+ dw BlkPacket_Titlescreen
+ dw BlkPacket_NidorinoIntro
+ dw wPartyMenuBlkPacket
+ dw wTrainerCardBlkPacket
+ dw BlkPacket_GameFreakIntro
+ dw wPalPacket
+ dw UnknownPacket_72751
+palPacketPointersEnd:
+
CopySGBBorderTiles:
; SGB tile data is stored in a 4BPP planar format.
; Each tile is 32 bytes. The first 16 bytes contain bit planes 1 and 2, while
diff --git a/engine/gfx/screen_effects.asm b/engine/gfx/screen_effects.asm
index c7a68734..973a951a 100644
--- a/engine/gfx/screen_effects.asm
+++ b/engine/gfx/screen_effects.asm
@@ -1,14 +1,16 @@
-; b = new color for BG color 0 (usually white) for 4 frames
+; inverts the BGP for 4 (6 on CGB due to lag) frames
ChangeBGPalColor0_4Frames:
- call GetPredefRegisters
+ call GetPredefRegisters ; leftover of red/blue, has no use here
ldh a, [rBGP]
- or b
+ xor $ff
ldh [rBGP], a
+ call UpdateGBCPal_BGP
ld c, 4
call DelayFrames
ldh a, [rBGP]
- and %11111100
+ xor $ff
ldh [rBGP], a
+ call UpdateGBCPal_BGP
ret
PredefShakeScreenVertically:
diff --git a/engine/gfx/sprite_oam.asm b/engine/gfx/sprite_oam.asm
index b3c07ecd..3ffd995f 100644
--- a/engine/gfx/sprite_oam.asm
+++ b/engine/gfx/sprite_oam.asm
@@ -1,6 +1,8 @@
PrepareOAMData::
; Determine OAM data for currently visible
; sprites and write it to wOAMBuffer.
+; Yellow code has been changed to use registers more efficiently
+; as well as tweaking the code to show gbc palettes
ld a, [wUpdateSpritesEnabled]
dec a
@@ -18,9 +20,9 @@ PrepareOAMData::
.spriteLoop
ldh [hSpriteOffset2], a
- ld d, HIGH(wSpriteStateData1)
- ldh a, [hSpriteOffset2]
ld e, a
+ ld d, HIGH(wSpriteStateData1)
+
ld a, [de] ; [x#SPRITESTATEDATA1_PICTUREID]
and a
jp z, .nextSprite
@@ -40,16 +42,22 @@ PrepareOAMData::
jr c, .usefacing
; unchanging
- and $f
- add $10 ; skip to the second half of the table which doesn't account for facing direction
+ ld a, $0
jr .next
.usefacing
and $f
.next
+; read the entry from the table
+ ld c, a
+ ld b, 0
+ ld hl, SpriteFacingAndAnimationTable
+ add hl, bc
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
ld l, a
-
; get sprite priority
push de
inc d
@@ -61,65 +69,46 @@ PrepareOAMData::
ldh [hSpritePriority], a ; temp store sprite priority
pop de
-; read the entry from the table
- ld h, 0
- ld bc, SpriteFacingAndAnimationTable
- add hl, hl
- add hl, hl
- add hl, bc
- ld a, [hli]
- ld c, a
- ld a, [hli]
- ld b, a
- ld a, [hli]
- ld h, [hl]
- ld l, a
call GetSpriteScreenXY
ldh a, [hOAMBufferOffset]
+ add [hl]
+ cp $a0
+ jr z, .hidden
+ jr nc, .asm_4a41
+.hidden
+ call Func_4a7b
+ ld [wd5cd], a
+ ldh a, [hOAMBufferOffset]
+
ld e, a
ld d, HIGH(wOAMBuffer)
.tileLoop
+ ld a, [hli]
+ ld c, a
+.loop
ldh a, [hSpriteScreenY] ; temp for sprite Y position
add $10 ; Y=16 is top of screen (Y=0 is invisible)
add [hl] ; add Y offset from table
ld [de], a ; write new sprite OAM Y position
inc hl
+ inc e
ldh a, [hSpriteScreenX] ; temp for sprite X position
add $8 ; X=8 is left of screen (X=0 is invisible)
add [hl] ; add X offset from table
+ ld [de], a
+ inc hl
inc e
- ld [de], a ; write new sprite OAM X position
- inc e
- ld a, [bc] ; read pattern number offset (accommodates orientation (offset 0,4 or 8) and animation (offset 0 or $80))
- inc bc
- push bc
+ ld a, [wd5cd]
+ add [hl]
+ cp $80
+ jr c, .asm_4a1c
ld b, a
-
- ld a, [wd5cd] ; temp copy of [x#SPRITESTATEDATA1_IMAGEINDEX]
- swap a ; high nybble determines sprite used (0 is always player sprite, next are some npcs)
- and $f
-
- ; Sprites $a and $b have one face (and therefore 4 tiles instead of 12).
- ; As a result, sprite $b's tile offset is less than normal.
- cp $b
- jr nz, .notFourTileSprite
- ld a, $a * 12 + 4
- jr .next2
-
-.notFourTileSprite
- ; a *= 12
- sla a
- sla a
- ld c, a
- sla a
- add c
-
-.next2
- add b ; add the tile offset from the table (based on frame and facing direction)
- pop bc
+ ldh a, [hPikachuSpriteVRAMOffset]
+ add b
+.asm_4a1c
ld [de], a ; tile id
inc hl
inc e
@@ -129,15 +118,19 @@ PrepareOAMData::
ldh a, [hSpritePriority]
or [hl]
.skipPriority
- inc hl
+ and $f0
+ bit OAM_OBP_NUM, a
+ jr z, .spriteusesOBP0
+ or OAM_HIGH_PALS
+.spriteusesOBP0
ld [de], a
+ inc hl
inc e
- bit 0, a ; OAMFLAG_ENDOFDATA
- jr z, .tileLoop
+ dec c
+ jr nz, .loop
ld a, e
ldh [hOAMBufferOffset], a
-
.nextSprite
ldh a, [hSpriteOffset2]
add $10
@@ -145,26 +138,31 @@ PrepareOAMData::
jp nz, .spriteLoop
; Clear unused OAM.
- ldh a, [hOAMBufferOffset]
- ld l, a
- ld h, HIGH(wOAMBuffer)
- ld de, $4
- ld b, $a0
+.asm_4a41
ld a, [wd736]
bit 6, a ; jumping down ledge or fishing animation?
- ld a, $a0
+ ld c, $a0
jr z, .clear
; Don't clear the last 4 entries because they are used for the shadow in the
; jumping down ledge animation and the rod in the fishing animation.
- ld a, $90
+ ld c, $90
.clear
- cp l
- ret z
+ ldh a, [hOAMBufferOffset]
+ cp c
+ ret nc
+ ld l, a
+ ld h, HIGH(wOAMBuffer)
+ ld a, c
+ ld de, $4 ; entry size
+ ld b, $a0
+.clearLoop
ld [hl], b
add hl, de
- jr .clear
+ cp l
+ jr nz, .clearLoop
+ ret
GetSpriteScreenXY:
inc e
@@ -187,3 +185,48 @@ GetSpriteScreenXY:
and $f0
ld [de], a ; [x#SPRITESTATEDATA1_XADJUSTED]
ret
+
+Func_4a7b:
+ push bc
+ ld a, [wd5cd] ; temp copy of [x#SPRITESTATEDATA1_IMAGEINDEX]
+ swap a ; high nybble determines sprite used (0 is always player sprite, next are some npcs)
+ and $f
+
+ ; Sprites $a and $b have one face (and therefore 4 tiles instead of 12).
+ ; As a result, sprite $b's tile offset is less than normal.
+ cp $b
+ jr nz, .notFourTileSprite
+ ld a, $a * 12 + 4 ; $7c
+ jr .done
+
+.notFourTileSprite
+ ; a *= 12
+ add a
+ add a
+ ld c, a
+ add a
+ add c
+.done
+ pop bc
+ ret
+
+INCLUDE "engine/gfx/oam_dma.asm"
+
+_IsTilePassable::
+ ld hl, wTilesetCollisionPtr ; pointer to list of passable tiles
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a ; hl now points to passable tiles
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .tileNotPassable
+ cp c
+ jr nz, .loop
+ xor a
+ ret
+.tileNotPassable
+ scf
+ ret
+
+INCLUDE "data/tilesets/collision_tile_ids.asm"