Overworld HUD implementation (#15)

This commit is contained in:
xCrystal 2023-08-29 18:33:20 +02:00
parent b275d642cd
commit f386a63cf8
21 changed files with 214 additions and 98 deletions

View File

@ -55,3 +55,8 @@ DEF SPRITE_GFX_LIST_CAPACITY EQU 32 ; see wUsedSprites
const ANIM_MON_HOF
const ANIM_MON_EGG1
const ANIM_MON_EGG2
; LoadHUD indexes (see engine/gfx/hud.asm)
const_def 1
const HUD_OVERWORLD ; 1
DEF NUM_HUD_TYPES EQU const_value - 1

View File

@ -132,15 +132,15 @@ DEF rLCDC_ENABLE EQU 7 ; 0=Off, 1=On
DEF LCDC_DEFAULT EQU (1 << rLCDC_ENABLE) | (1 << rLCDC_WINDOW_TILEMAP) | (1 << rLCDC_WINDOW_ENABLE) | (1 << rLCDC_SPRITES_ENABLE) | (1 << rLCDC_BG_PRIORITY)
DEF rSTAT EQU $ff41 ; LCDC Status (R/W)
DEF rSTAT_STATUS_FLAGS EQU %00000011 ; LCD controller status (Read Only)
DEF rSTAT_HBLANK_MASK EQU %00000000 ; In H-Blank (Read Only)
DEF rSTAT_VBLANK_MASK EQU %00000001 ; In V-Blank (Read Only)
DEF rSTAT_OAM_MASK EQU %00000010 ; OAM is used by system (Read Only)
DEF rSTAT_LCD_MASK EQU %00000011 ; Both OAM and VRAM used by system (Read Only)
DEF rSTAT_HBLANK_MASK EQU %00000000 ; In Mode 0: H-Blank (Read Only)
DEF rSTAT_VBLANK_MASK EQU %00000001 ; In Mode 1: V-Blank (Read Only)
DEF rSTAT_OAM_MASK EQU %00000010 ; In Mode 2: OAM is used by system (Read Only)
DEF rSTAT_LCD_MASK EQU %00000011 ; In Mode 3: Both OAM and VRAM used by system (Read Only)
DEF rSTAT_BUSY EQU 1 ; When set, VRAM access is unsafe (Read Only)
DEF rSTAT_LYC EQU 2 ; LYC=LY (0=Different, 1=Equal) (Read Only)
DEF rSTAT_INT_HBLANK EQU 3 ; Mode 00: H-Blank (Selectable)
DEF rSTAT_INT_VBLANK EQU 4 ; Mode 01: V-Blank (Selectable)
DEF rSTAT_INT_OAM EQU 5 ; Mode 02: OAM (Selectable)
DEF rSTAT_INT_HBLANK EQU 3 ; Mode 0: H-Blank (Selectable)
DEF rSTAT_INT_VBLANK EQU 4 ; Mode 1: V-Blank (Selectable)
DEF rSTAT_INT_OAM EQU 5 ; Mode 2: OAM (Selectable)
DEF rSTAT_INT_LYC EQU 6 ; LYC=LY Coincidence (Selectable)
DEF rSCY EQU $ff42 ; Scroll Y (R/W)
DEF rSCX EQU $ff43 ; Scroll X (R/W)

View File

@ -41,12 +41,12 @@ HDMATransferTilemapAndAttrmap_OverworldEffect::
call PadTilemapForHDMATransfer
call DelayFrame
ldh a, [hWindowHUD]
ldh a, [hWindowHUDLY]
and a
jr z, .go
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUD] >= 0)
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUDLY] >= 0)
.wait_lcd
; ldh a, [hWindowHUD]
; ldh a, [hWindowHUDLY]
ld b, a
ldh a, [rLY]
sub b
@ -86,12 +86,12 @@ _HDMATransferTilemapAndAttrmap_OpenAndCloseMenu::
call PadTilemapForHDMATransfer
call DelayFrame
ldh a, [hWindowHUD]
ldh a, [hWindowHUDLY]
and a
jr z, .go
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUD] >= 0)
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUDLY] >= 0)
.wait_lcd
; ldh a, [hWindowHUD]
; ldh a, [hWindowHUDLY]
ld b, a
ldh a, [rLY]
sub b
@ -276,12 +276,12 @@ _continue_HDMATransfer:
cp d
jr nc, .ly_loop
ldh a, [hWindowHUD]
ldh a, [hWindowHUDLY]
and a
jr z, .go
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUD] >= 0)
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUDLY] >= 0)
.wait_lcd
; ldh a, [hWindowHUD]
; ldh a, [hWindowHUDLY]
ld b, a
ldh a, [rLY]
sub b

29
engine/gfx/hud.asm Executable file
View File

@ -0,0 +1,29 @@
_LoadHUD::
jumptable .Jumptable, wWhichHUD
.Jumptable:
; entries correspond to HUD_* constants (see constants/gfx_constants.asm)
table_width 2, _LoadHUD.Jumptable
dw .None
dw _LoadOverworldHUDTilemapAndAttrmap
assert_table_length NUM_HUD_TYPES + 1
.None:
ret
_LoadOverworldHUDTilemapAndAttrmap:
call _LoadOverworldHUDAttrmap
; fallthrough
_LoadOverworldHUDTilemap:
; overworld HUD reads SCREEN_WIDTH tiles from wOverworldHUDTiles
ld hl, wOverworldHUDTiles
decoord 0, 0, wTilemap
ld bc, wOverworldHUDTilesEnd - wOverworldHUDTiles ; SCREEN_WIDTH
jp CopyBytes
_LoadOverworldHUDAttrmap:
hlcoord 0, 0, wAttrmap
ld bc, SCREEN_WIDTH
ld a, PAL_BG_TEXT | PRIORITY
jp ByteFill

View File

@ -126,7 +126,9 @@ EnterMap:
ld [wPoisonStepCount], a
.dontresetpoison
call EnableOverworldWindowHUD
call ConstructOverworldHUDTilemap
call TransferOverworldHUDToBGMap
call EnableOverworldHUD
xor a ; end map entry
ldh [hMapEntryMethod], a

View File

@ -25,17 +25,20 @@ ReanchorBGMap_NoOAMUpdate::
xor a
ldh [hLCDCPointer], a
ldh [hBGMapMode], a
; prepare vBGMap1/vBGMap3 to be displayed while vBGMap0/vBGMap2 is reanchored.
; draw screen at wTilemap and wAttrmap and then transfer it.
ld a, $90
ldh [hWY], a
call LoadScreenTilemapAndAttrmapPals
call LoadWindowHUD
ld a, HIGH(vBGMap1)
call .LoadBGMapAddrIntoHRAM
call HDMATransferTilemapAndAttrmap_OpenAndCloseMenu
farcall ApplyPals
ld a, TRUE
ldh [hCGBPalUpdate], a
; display window while BG map is reanchored.
; disable LCD interrupt to prevent cropping the window due to hWindowHUD
; display window using vBGMap1/vBGMap3 while vBGMap0/vBGMap2 is reanchored.
; disable LCD interrupt to prevent cropping the window if window HUD is active
; (caller must re-enable when window is hidden again).
xor a
ldh [rSTAT], a

View File

@ -33,4 +33,5 @@ _LoadScreenTilemap::
.carry
dec b
jr nz, .loop
ret

View File

@ -1165,7 +1165,7 @@ Script_loadtrainer:
ret
Script_startbattle:
call DisableWindowHUD
call DisableOverworldHUD
call BufferScreen
predef StartBattle
ld a, [wBattleResult]

View File

@ -1,5 +1,4 @@
HandleNewMap:
call ClearUnusedMapBuffer
call ResetMapBufferEventFlags
call ResetFlashIfOutOfCave
call GetCurrentMapSceneID

View File

@ -547,7 +547,7 @@ Phone_StartRinging:
call PlaySFX
call Phone_CallerTextbox
call UpdateSprites
farcall PhoneRing_CopyTilemapAtOnce
call PhoneRing_CopyTilemapAtOnce
ret
HangUp_Wait20Frames:
@ -556,7 +556,7 @@ HangUp_Wait20Frames:
Phone_Wait20Frames:
ld c, 20
call DelayFrames
farcall PhoneRing_CopyTilemapAtOnce
call PhoneRing_CopyTilemapAtOnce
ret
PhoneRing_CopyTilemapAtOnce:

View File

@ -29,6 +29,7 @@ INCLUDE "home/map.asm"
INCLUDE "home/farcall.asm"
INCLUDE "home/predef.asm"
INCLUDE "home/window.asm"
INCLUDE "home/hud.asm"
INCLUDE "home/flag.asm"
INCLUDE "home/sprite_updates.asm"
INCLUDE "home/string.asm"

122
home/hud.asm Executable file
View File

@ -0,0 +1,122 @@
OVERWORLD_HUD_HEIGHT EQU 8
EnableOverworldHUD::
ld a, HUD_OVERWORLD
ld [wWhichHUD], a
ld a, OVERWORLD_HUD_HEIGHT - 1
; fallthrough
EnableWindowHUD:
ldh [hWindowHUDLY], a
; configure LCD interrupt
ldh [rLYC], a
; make window hidden this frame to prevent graphical glitches
ld a, $90
ldh [hWY], a
; configure LCD interrupt
ld a, 1 << rSTAT_INT_LYC ; LYC=LC
ldh [rSTAT], a
ret
DisableOverworldHUD::
xor a
ld [wWhichHUD], a
; fallthrough
DisableWindowHUD::
xor a
ldh [hWindowHUDLY], a
; configure LCD interrupt
xor a
ldh [rLYC], a
ld a, 1 << rSTAT_INT_HBLANK ; hblank (default)
ldh [rSTAT], a
; leave window in default state (hidden with WY=$90)
; rLCDC[rLCDC_WINDOW_ENABLE] will be set during next vblank
ld a, $90
ldh [hWY], a
ret
LoadWindowHUD::
; like LoadHUD, but for HUDs that require a Window overlay
ldh a, [hWindowHUDLY]
and a
ret z
; fallthrough
LoadHUD::
; load the HUD at wWhichHUD to the top of wTilemap and wAttrmap
ld a, [wWhichHUD]
and a
ret z
farcall _LoadHUD
ret
ConstructOverworldHUDTilemap::
; draw the overworld HUD's tilemap into wOverworldHUDTiles
ld hl, .Tilemap
ld de, wOverworldHUDTiles
ld bc, .TilemapEnd - .Tilemap ; SCREEN_WIDTH
call CopyBytes
ret
.Tilemap:
db "▶- ▶- ▶ ▶ "
.TilemapEnd:
assert .TilemapEnd - .Tilemap == wOverworldHUDTilesEnd - wOverworldHUDTiles
TransferOverworldHUDToBGMap::
; transfer overworld HUD to vBGMap1/vBGMap3 during v/hblank(s)
; tilemap is read from wOverworldHUDTiles, attrmap is all PAL_BG_TEXT | PRIORITY
ldh a, [rVBK]
push af
; Tilemap
ld a, BANK(vBGMap1)
ldh [rVBK], a
ld de, vBGMap1
ld hl, wOverworldHUDTiles
ld b, 1 << rSTAT_BUSY ; not in v/hblank
ld c, LOW(rSTAT)
rept SCREEN_WIDTH / 2
; if not in v/hblank, wait until in v/hblank
.loop\@
ldh a, [c]
and b
jr nz, .loop\@
; copy
; we have at least a margin of 16 cycles of Mode2 left
ld a, [hli]
ld [de], a
inc de
ld a, [hli]
ld [de], a
inc de
endr
; Attrmap
ld a, BANK(vBGMap3)
ldh [rVBK], a
ld hl, vBGMap3
rept SCREEN_WIDTH / 5
; if not in v/hblank, wait until in v/hblank
.loop\@
ldh a, [c]
and b
jr nz, .loop\@
; fill
; we have at least a margin of 16 cycles of Mode2 left
ld a, PAL_BG_TEXT | PRIORITY
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
ld [hli], a
endr
pop af
ld [rVBK], a
ret

View File

@ -4,7 +4,7 @@ LCD::
push af
; hLCDCPointer is used in battle transition, battle anims, and movies (crystal intro, credits, etc.)
; uses rSTAT_INT_HBLANK and doesn't overlap with hWindowHUD.
; uses rSTAT_INT_HBLANK and doesn't overlap with hWindowHUDLY.
ldh a, [hLCDCPointer]
and a
jr z, .next
@ -23,8 +23,8 @@ LCD::
pop bc
.next
; hWindowHUD uses rSTAT_INT_LYC
ldh a, [hWindowHUD]
; hWindowHUDLY uses rSTAT_INT_LYC
ldh a, [hWindowHUDLY]
and a
jr z, .done

View File

@ -1,12 +1,5 @@
; Functions dealing with rendering and interacting with maps.
ClearUnusedMapBuffer::
ld hl, wUnusedMapBuffer
ld bc, wUnusedMapBufferEnd - wUnusedMapBuffer
ld a, 0
call ByteFill
ret
CheckScenes::
; Checks wCurMapSceneScriptPointer. If it's empty, returns -1 in a. Otherwise, returns the active scene ID in a.
push hl
@ -146,7 +139,6 @@ LoadMetatiles::
ld e, l
ld d, h
; Set hl to the address of the current metatile data ([wTilesetBlocksAddress] + (a) tiles).
; BUG: LoadMetatiles wraps around past 128 blocks (see docs/bugs_and_glitches.md)
ld l, a
ld h, 0
add hl, hl

View File

@ -49,20 +49,15 @@ CopyTilemapAtOnce::
xor a
ldh [hMapAnims], a
; .wait
; ldh a, [rLY]
; cp $80 - 1
; jr c, .wait
call DelayFrame
ldh a, [hWindowHUD]
ldh a, [hWindowHUDLY]
and a
jr z, .go
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUD] >= 0)
; wait until LCD interrupt has ocurred this frame ([rLY] - [hWindowHUDLY] >= 0)
.wait_lcd
; ldh a, [hWindowHUD]
; ldh a, [hWindowHUDLY]
ld b, a
ldh a, [rLY]
sub b
@ -78,11 +73,6 @@ CopyTilemapAtOnce::
ldh [rVBK], a
hlcoord 0, 0
call .CopyBGMapViaStack
; .wait2
; ldh a, [rLY]
; cp $80 - 1
; jr c, .wait2
ei
pop af

View File

@ -65,12 +65,12 @@ VBlank0::
ldh a, [hROMBank]
ldh [hROMBankBackup], a
; enable window back in case LCD interrupt disabled it mid-frame due to hWindowHUD
; enable window back in case LCD interrupt disabled it mid-frame due to hWindowHUDLY
ldh a, [rLCDC]
set rLCDC_WINDOW_ENABLE, a
ldh [rLCDC], a
ld a, [hWindowHUD]
ld a, [hWindowHUDLY]
and a
jr z, .next
@ -117,8 +117,8 @@ VBlank0::
xor a
ld [wVBlankOccurred], a
; if hWindowHUD is active, enable interrupts so the LCD interrupt can trigger
ldh a, [hWindowHUD]
; if hWindowHUDLY is active, enable interrupts so the LCD interrupt can trigger
ldh a, [hWindowHUDLY]
and a
jr z, .next2
@ -163,17 +163,17 @@ VBlank0::
ldh a, [hROMBankBackup]
rst Bankswitch
; if hWindowHUD is not active, we're done
ldh a, [hWindowHUD]
; if hWindowHUDLY is not active, we're done
ldh a, [hWindowHUDLY]
and a
ret z
; interrupts must be enabled in the cycle that rLY becomes [hWindowHUD] to prevent flickering
; wait until [hWindowHUD] - [rLY] is NOT between 0 and 2 before disabling interrupts
; interrupts must be enabled in the cycle that rLY becomes [hWindowHUDLY] to prevent flickering
; wait until [hWindowHUDLY] - [rLY] is NOT between 0 and 2 before disabling interrupts
.wait_loop
ldh a, [rLY]
ld b, a
ldh a, [hWindowHUD]
ldh a, [hWindowHUDLY]
sub b
cp 2 + 1
jr c, .wait_loop

View File

@ -110,7 +110,7 @@ SafeUpdateSprites::
HideWindow_EnableLCDInt::
ld a, $90
ldh [hWY], a
ldh a, [hWindowHUD]
ldh a, [hWindowHUDLY]
and a
ld a, 1 << rSTAT_INT_HBLANK
jr z, .ok
@ -118,35 +118,3 @@ HideWindow_EnableLCDInt::
.ok
ldh [rSTAT], a
ret
OVERWORLD_HUD_HEIGHT EQU 8
EnableOverworldWindowHUD::
ld a, OVERWORLD_HUD_HEIGHT - 1
; fallthrough
EnableWindowHUD:
ldh [hWindowHUD], a
; configure LCD interrupt
ldh [rLYC], a
; make window hidden this frame to prevent graphical glitches
ld a, $90
ldh [hWY], a
; configure LCD interrupt
ld a, 1 << rSTAT_INT_LYC ; LYC=LC
ldh [rSTAT], a
ret
DisableWindowHUD::
xor a
ldh [hWindowHUD], a
; configure LCD interrupt
xor a
ldh [rLYC], a
ld a, 1 << rSTAT_INT_HBLANK ; hblank (default)
ldh [rSTAT], a
; leave window in default state (hidden with WY=$90)
; rLCDC[rLCDC_WINDOW_ENABLE] will be set during next vblank
ld a, $90
ldh [hWY], a
ret

View File

@ -249,7 +249,7 @@ WRAM0
"Sprites"
"Tilemap"
"Miscellaneous"
"Unused Map Buffer"
"Overworld HUD"
align 8
"Overworld Map"
align 4

View File

@ -189,6 +189,7 @@ INCLUDE "engine/menus/empty_sram.asm"
INCLUDE "engine/events/checksave.asm"
INCLUDE "data/maps/scenes.asm"
INCLUDE "engine/overworld/load_screen_tilemap.asm"
INCLUDE "engine/gfx/hud.asm"
SECTION "bank13_2", ROMX

View File

@ -148,7 +148,7 @@ if DEF(_DEBUG)
hDebugRoomMenuPage:: db
endc
hWindowHUD::
hWindowHUDLY::
; Window HUD is enabled when non-0.
; Its value indicates the last scanline that the window spans from the top.
db

View File

@ -697,12 +697,11 @@ wPuzzlePieces:: ds 6 * 6
ENDU
SECTION "Unused Map Buffer", WRAM0
SECTION "Overworld HUD", WRAM0
; This was a buffer for map-related pointers in the 1997 G/S prototype.
; See wMapBuffer in pokegold-spaceworld's wram.asm.
wUnusedMapBuffer:: ds 24
wUnusedMapBufferEnd::
wOverworldHUDTiles:: ds SCREEN_WIDTH
wOverworldHUDTilesEnd::
ds 4
SECTION UNION "Overworld Map", WRAM0
@ -933,8 +932,8 @@ SECTION "Video", WRAM0
UNION
; bg map
wBGMapBuffer:: ds 40
wBGMapPalBuffer:: ds 40
wBGMapBuffer:: ds 2 * SCREEN_WIDTH
wBGMapPalBuffer:: ds 2 * SCREEN_WIDTH
wBGMapBufferPointers:: ds 20 * 2
wBGMapBufferEnd::
@ -1276,6 +1275,10 @@ wMinutesSince:: db
wHoursSince:: db
wDaysSince:: db
wWhichHUD::
; index to LoadHUD
db
SECTION "WRAM 1", WRAMX