From f386a63cf869acd657b684a3f6a6b8210b1a5d3a Mon Sep 17 00:00:00 2001 From: xCrystal Date: Tue, 29 Aug 2023 18:33:20 +0200 Subject: [PATCH] Overworld HUD implementation (#15) --- constants/gfx_constants.asm | 5 + constants/hardware_constants.asm | 14 +-- engine/gfx/dma_transfer.asm | 18 ++-- engine/gfx/hud.asm | 29 ++++++ engine/overworld/events.asm | 4 +- engine/overworld/init_map.asm | 7 +- engine/overworld/load_screen_tilemap.asm | 1 + engine/overworld/scripting.asm | 2 +- engine/overworld/warp_connection.asm | 1 - engine/phone/phone.asm | 4 +- home.asm | 1 + home/hud.asm | 122 +++++++++++++++++++++++ home/lcd.asm | 6 +- home/map.asm | 8 -- home/tilemap.asm | 16 +-- home/vblank.asm | 18 ++-- home/window.asm | 34 +------ layout.link | 2 +- main.asm | 1 + ram/hram.asm | 2 +- ram/wram.asm | 17 ++-- 21 files changed, 214 insertions(+), 98 deletions(-) create mode 100755 engine/gfx/hud.asm create mode 100755 home/hud.asm diff --git a/constants/gfx_constants.asm b/constants/gfx_constants.asm index 4a56ce2b6..61e4c0c7c 100644 --- a/constants/gfx_constants.asm +++ b/constants/gfx_constants.asm @@ -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 diff --git a/constants/hardware_constants.asm b/constants/hardware_constants.asm index 577889bd8..579571864 100644 --- a/constants/hardware_constants.asm +++ b/constants/hardware_constants.asm @@ -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) diff --git a/engine/gfx/dma_transfer.asm b/engine/gfx/dma_transfer.asm index be0d8d892..fb6814508 100644 --- a/engine/gfx/dma_transfer.asm +++ b/engine/gfx/dma_transfer.asm @@ -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 diff --git a/engine/gfx/hud.asm b/engine/gfx/hud.asm new file mode 100755 index 000000000..61fcbce52 --- /dev/null +++ b/engine/gfx/hud.asm @@ -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 diff --git a/engine/overworld/events.asm b/engine/overworld/events.asm index ecad53294..8614bd18c 100644 --- a/engine/overworld/events.asm +++ b/engine/overworld/events.asm @@ -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 diff --git a/engine/overworld/init_map.asm b/engine/overworld/init_map.asm index e4d70651f..7f9b3643a 100644 --- a/engine/overworld/init_map.asm +++ b/engine/overworld/init_map.asm @@ -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 diff --git a/engine/overworld/load_screen_tilemap.asm b/engine/overworld/load_screen_tilemap.asm index 6f130f864..0f964b23b 100644 --- a/engine/overworld/load_screen_tilemap.asm +++ b/engine/overworld/load_screen_tilemap.asm @@ -33,4 +33,5 @@ _LoadScreenTilemap:: .carry dec b jr nz, .loop + ret diff --git a/engine/overworld/scripting.asm b/engine/overworld/scripting.asm index 956bade69..be2a2e649 100644 --- a/engine/overworld/scripting.asm +++ b/engine/overworld/scripting.asm @@ -1165,7 +1165,7 @@ Script_loadtrainer: ret Script_startbattle: - call DisableWindowHUD + call DisableOverworldHUD call BufferScreen predef StartBattle ld a, [wBattleResult] diff --git a/engine/overworld/warp_connection.asm b/engine/overworld/warp_connection.asm index 625cbaada..7c20df81c 100644 --- a/engine/overworld/warp_connection.asm +++ b/engine/overworld/warp_connection.asm @@ -1,5 +1,4 @@ HandleNewMap: - call ClearUnusedMapBuffer call ResetMapBufferEventFlags call ResetFlashIfOutOfCave call GetCurrentMapSceneID diff --git a/engine/phone/phone.asm b/engine/phone/phone.asm index d06d4c6b8..ba16139b9 100644 --- a/engine/phone/phone.asm +++ b/engine/phone/phone.asm @@ -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: diff --git a/home.asm b/home.asm index f56c43967..cb661bb2b 100644 --- a/home.asm +++ b/home.asm @@ -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" diff --git a/home/hud.asm b/home/hud.asm new file mode 100755 index 000000000..90b1532cc --- /dev/null +++ b/home/hud.asm @@ -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 diff --git a/home/lcd.asm b/home/lcd.asm index 443746523..7a9171231 100644 --- a/home/lcd.asm +++ b/home/lcd.asm @@ -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 diff --git a/home/map.asm b/home/map.asm index 3319a2f5a..e68f8ccea 100644 --- a/home/map.asm +++ b/home/map.asm @@ -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 diff --git a/home/tilemap.asm b/home/tilemap.asm index 6466da78a..031983e34 100644 --- a/home/tilemap.asm +++ b/home/tilemap.asm @@ -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 diff --git a/home/vblank.asm b/home/vblank.asm index eccbec702..3737c4317 100644 --- a/home/vblank.asm +++ b/home/vblank.asm @@ -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 diff --git a/home/window.asm b/home/window.asm index 1a05077c9..92c2ad188 100644 --- a/home/window.asm +++ b/home/window.asm @@ -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 diff --git a/layout.link b/layout.link index a98ac0ebe..b42fd67e0 100644 --- a/layout.link +++ b/layout.link @@ -249,7 +249,7 @@ WRAM0 "Sprites" "Tilemap" "Miscellaneous" - "Unused Map Buffer" + "Overworld HUD" align 8 "Overworld Map" align 4 diff --git a/main.asm b/main.asm index b42c43cd2..1980b9f0b 100644 --- a/main.asm +++ b/main.asm @@ -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 diff --git a/ram/hram.asm b/ram/hram.asm index 2f64d66fe..890344ba4 100644 --- a/ram/hram.asm +++ b/ram/hram.asm @@ -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 diff --git a/ram/wram.asm b/ram/wram.asm index fb0283fa0..948827b69 100644 --- a/ram/wram.asm +++ b/ram/wram.asm @@ -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