; DMGTRIS ; Copyright (C) 2023 - Randy Thiemann ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . IF !DEF(STATE_GAMEPLAY_ASM) DEF STATE_GAMEPLAY_ASM EQU 1 INCLUDE "globals.asm" INCLUDE "res/gameplay_data.inc" INCLUDE "res/gameplay_big_data.inc" DEF MODE_LEADY EQU 0 DEF MODE_GO EQU 3 DEF MODE_POSTGO EQU 6 DEF MODE_PREFETCHED_PIECE EQU 9 DEF MODE_SPAWN_PIECE EQU 12 DEF MODE_PIECE_IN_MOTION EQU 15 DEF MODE_DELAY EQU 18 DEF MODE_GAME_OVER EQU 21 DEF MODE_PRE_GAME_OVER EQU 24 DEF MODE_PAUSED EQU 27 SECTION "High Gameplay Variables", HRAM hCurrentPiece:: ds 1 hCurrentPieceX:: ds 1 hCurrentPieceY:: ds 1 hCurrentPieceRotationState:: ds 1 hHeldPiece:: ds 1 hHoldSpent:: ds 1 hMode: ds 1 hModeCounter: ds 1 hPrePause: ds 1 hRequestedJingle: ds 1 SECTION "Gameplay Function Trampolines", ROM0 ; Trampolines to the banked function. SwitchToGameplay:: ld b, BANK_GAMEPLAY rst RSTSwitchBank call SwitchToGameplayB rst RSTRestoreBank jp EventLoopPostHandler ; Trampolines to the banked function. SwitchToGameplayBig:: ld b, BANK_GAMEPLAY_BIG rst RSTSwitchBank call SwitchToGameplayBigB rst RSTRestoreBank jp EventLoopPostHandler ; Banks and jumps to the actual handler. GamePlayEventLoopHandler:: ld b, BANK_GAMEPLAY rst RSTSwitchBank call GamePlayEventLoopHandlerB rst RSTRestoreBank jp EventLoopPostHandler ; Banks and jumps to the actual handler. GamePlayBigEventLoopHandler:: ld b, BANK_GAMEPLAY_BIG rst RSTSwitchBank call GamePlayBigEventLoopHandlerB rst RSTRestoreBank jp EventLoopPostHandler SECTION "Gameplay Function Banked", ROMX, BANK[BANK_GAMEPLAY] ; Change to game play mode. The event loop will call the event loop and vblank handlers for this mode after this returns. SwitchToGameplayB: ; Turn the screen off if it's on. ldh a, [rLCDC] and LCDCF_ON jr z, :+ ; Screen is already off. wait_vram xor a, a ldh [rLCDC], a ; Load the gameplay tilemap. : ld de, sGameplayTileMap ld hl, $9800 ld bc, sGameplayTileMapEnd - sGameplayTileMap call UnsafeMemCopy ; Clear OAM. call ClearOAM call SetNumberSpritePositions call ApplyTells ; Set up the palettes. ld a, PALETTE_REGULAR set_bg_palette set_obj0_palette ld a, PALETTE_LIGHTER_1 set_obj1_palette ; Initialize the RNG. call RNGInit ; Initialize the score, level and field. call ScoreInit call LevelInit call FieldInit ; We don't start with hold spent. xor a, a ldh [hHoldSpent], a ; Leady mode. ld a, MODE_LEADY ldh [hMode], a ld a, LEADY_TIME ldh [hModeCounter], a ; GBC init call GBCGameplayInit ; Install the event loop handlers. ld a, STATE_GAMEPLAY ldh [hGameState], a ; And turn the LCD back on before we start. ld a, LCDCF_ON | LCDCF_BGON | LCDCF_OBJON | LCDCF_BLK01 ldh [rLCDC], a ; Music end call SFXKill ; Make sure the first game loop starts just like all the future ones. wait_vblank wait_vblank_end ret ; Main gameplay event loop. GamePlayEventLoopHandlerB:: ; What mode are we in? ld hl, .modejumps ldh a, [hMode] ld b, 0 ld c, a add hl, bc jp hl .modejumps jp leadyMode jp goMode jp postGoMode jp prefetchedPieceMode jp spawnPieceMode jp pieceInMotionMode jp delayMode jp gameOverMode jp preGameOverMode jp pauseMode ; Draw "READY" and wait a bit. leadyMode: ldh a, [hModeCounter] cp a, LEADY_TIME jr nz, :+ call SFXKill ld a, SFX_READYGO call SFXEnqueue ldh a, [hModeCounter] : dec a jr nz, :+ ld a, MODE_GO ldh [hMode], a ld a, GO_TIME : ldh [hModeCounter], a ld de, sLeady ld hl, wField+(14*10) ld bc, 10 call UnsafeMemCopy jp drawStaticInfo ; Draw "GO" and wait a bit. goMode: ldh a, [hModeCounter] dec a jr nz, :+ ld a, MODE_POSTGO ldh [hMode], a xor a, a : ldh [hModeCounter], a ld de, sGo ld hl, wField+(14*10) ld bc, 10 call UnsafeMemCopy jp drawStaticInfo ; Clear the field, fetch the piece, ready for gameplay. postGoMode: ld a, MODE_PREFETCHED_PIECE ldh [hMode], a call FieldClear call ToShadowField ldh a, [hNextPiece] ldh [hCurrentPiece], a call GetNextPiece jp drawStaticInfo ; Fetch the next piece. prefetchedPieceMode: ; A piece will spawn in the middle, at the top of the screen, not rotated by default. ld a, $FF ldh [hRequestedJingle], a ld a, PIECE_SPAWN_X ldh [hCurrentPieceX], a ld a, PIECE_SPAWN_Y ldh [hCurrentPieceY], a xor a, a ; ROTATION_STATE_DEF ldh [hCurrentPieceRotationState], a ldh [hHoldSpent], a ; Check if IHS is requested. ; Apply the hold if so. .checkIHS ldh a, [hSelectState] cp a, 0 jr z, .loaddefaultjingle call DoHold jr .postjingle ; Enqueue the jingle. .loaddefaultjingle ldh a, [hNextPiece] ldh [hRequestedJingle], a ; Check if IRS is requested. ; Apply the rotation if so. .checkIRSA ld a, [wSwapABState] cp a, 0 jr z, .lda1 .ldb1 ldh a, [hBState] cp a, 0 jr z, .checkIRSB ld a, $FF ldh [hBState], a jr .cp1 .lda1 ldh a, [hAState] cp a, 0 jr z, .checkIRSB ld a, $FF ldh [hAState], a .cp1 ld a, ROTATION_STATE_CCW ldh [hCurrentPieceRotationState], a ldh a, [hNextPiece] ld b, a ld a, SFX_IRS or a, b ldh [hRequestedJingle], a jr .postjingle .checkIRSB ld a, [wSwapABState] cp a, 0 jr z, .ldb2 .lda2 ldh a, [hAState] cp a, 0 jr z, .postjingle ld a, $FF ldh [hAState], a jr .cp2 .ldb2 ldh a, [hBState] cp a, 0 jr z, .postjingle ld a, $FF ldh [hBState], a .cp2 ld a, ROTATION_STATE_CW ldh [hCurrentPieceRotationState], a ldh a, [hNextPiece] ld b, a ld a, SFX_IRS or a, b ldh [hRequestedJingle], a jr .postjingle .postjingle ld a, MODE_SPAWN_PIECE ldh [hMode], a ; State falls through to the next. ; Spawn the piece. spawnPieceMode: call TrySpawnPiece cp a, $FF jr z, :+ ld a, MODE_PRE_GAME_OVER ldh [hMode], a jp drawStaticInfo : ld a, MODE_PIECE_IN_MOTION ldh [hMode], a ; Play the next jingle... maybe! ldh a, [hHoldSpent] cp a, $FF jr z, pieceInMotionMode ldh a, [hRequestedJingle] cp a, $FF jr z, pieceInMotionMode call SFXEnqueue ; This mode lasts for as long as the piece is in motion. ; Field will let us know when it has locked in place. pieceInMotionMode: ldh a, [hStartState] cp a, 1 jr nz, :+ call ToBackupField ldh a, [hMode] ldh [hPrePause], a ld a, MODE_PAUSED ldh [hMode], a jp drawStaticInfo : call FieldProcess ; Do we hold? ldh a, [hSelectState] cp a, 1 jr nz, :+ ldh a, [hHoldSpent] cp a, $FF jr z, :+ ; Reset position and rotation. ld a, PIECE_SPAWN_X ldh [hCurrentPieceX], a ld a, PIECE_SPAWN_Y ldh [hCurrentPieceY], a xor a, a ; ROTATION_STATE_DEF ldh [hCurrentPieceRotationState], a call DoHold ld a, MODE_SPAWN_PIECE ldh [hMode], a ; Do we go into delay state? : ldh a, [hCurrentLockDelayRemaining] cp a, 0 jr nz, :+ ld a, MODE_DELAY ldh [hMode], a ; No fall through this time. : jp drawStaticInfo delayMode: ldh a, [hStartState] cp a, 1 jr nz, :+ call ToBackupField ldh a, [hMode] ldh [hPrePause], a ld a, MODE_PAUSED ldh [hMode], a jp drawStaticInfo : call FieldDelay ldh a, [hRemainingDelay] cp a, 0 jr nz, :+ ld a, MODE_PREFETCHED_PIECE ldh [hMode], a : jp drawStaticInfo preGameOverMode: ; Spawn the failed piece. call ForceSpawnPiece ; Draw the field in grey. ; Yes. This really unrolls the loop that many times. ld hl, wField+(4*10) REPT 60 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty1\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip1\@ .notempty1\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip1\@ ENDR DEF off = 0 REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty2\@ ld a, GAME_OVER_R10+10+off ld [hl+], a jr .skip2\@ .notempty2\@ ld a, GAME_OVER_R10+off ld [hl+], a .skip2\@ DEF off += 1 ENDR REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty3\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip3\@ .notempty3\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip3\@ ENDR DEF off = 0 REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty4\@ ld a, GAME_OVER_R12+10+off ld [hl+], a jr .skip4\@ .notempty4\@ ld a, GAME_OVER_R12+off ld [hl+], a .skip4\@ DEF off += 1 ENDR REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty5\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip5\@ .notempty5\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip5\@ ENDR DEF off = 0 REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty6\@ ld a, GAME_OVER_R14+10+off ld [hl+], a jr .skip6\@ .notempty6\@ ld a, GAME_OVER_R14+off ld [hl+], a .skip6\@ DEF off += 1 ENDR REPT 90 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty7\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip7\@ .notempty7\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip7\@ ENDR ld a, MODE_GAME_OVER ldh [hMode], a gameOverMode: ; Retry? ldh a, [hAState] cp a, 1 jr nz, :+ call RNGInit call ScoreInit call LevelInit call FieldInit xor a, a ldh [hHoldSpent], a ld a, MODE_LEADY ldh [hMode], a ld a, LEADY_TIME ldh [hModeCounter], a jp drawStaticInfo ; Quit : ldh a, [hBState] cp a, 1 jp nz, drawStaticInfo call SwitchToTitle jp EventLoopPostHandler pauseMode: ; Quick reset. ldh a, [hAState] cp a, 0 jr z, :+ ldh a, [hBState] cp a, 0 jr z, :+ ldh a, [hSelectState] cp a, 0 jr z, :+ call SwitchToTitle jp EventLoopPostHandler ; Unpause : ldh a, [hStartState] cp a, 1 jr nz, :+ call FromBackupField ldh a, [hPrePause] ldh [hMode], a jr drawStaticInfo ; Draw PAUSE all over the field. : ld de, sPause ld hl, wField+(4*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(6*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(8*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(10*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(12*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(14*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(16*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(18*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(20*10) ld bc, 20 call UnsafeMemCopy ld de, sPause ld hl, wField+(22*10) ld bc, 20 call UnsafeMemCopy jr drawStaticInfo ; Always draw the score, level, next piece, and held piece. drawStaticInfo: : ldh a, [hNextPiece] call ApplyNext ldh a, [hHeldPiece] call ApplyHold ld hl, wSPRScore1 ld de, hScore call ApplyNumbers ld hl, wSPRCLevel1 ld de, hCLevel call ApplyNumbers ld hl, wSPRNLevel1 ld de, hNLevel call ApplyNumbers jp GBCGameplayProcess ; Do the hold action. DoHold: ; Mark hold as spent. ld a, $FF ldh [hHoldSpent], a ; Check if IRS is requested. ; Apply the rotation if so. .checkIRSHA ld a, [wSwapABState] cp a, 0 jr z, .lda3 .ldb3 ldh a, [hBState] cp a, 0 jr z, .checkIRSHB ld a, $FF ldh [hBState], a jr .cp3 .lda3 ldh a, [hAState] cp a, 0 jr z, .checkIRSHB ld a, $FF ldh [hAState], a .cp3 ld a, ROTATION_STATE_CCW ldh [hCurrentPieceRotationState], a call SFXKill ld a, SFX_IRS | SFX_IHS call SFXEnqueue jr .doHoldOperation .checkIRSHB ld a, [wSwapABState] cp a, 0 jr z, .ldb4 .lda4 ldh a, [hAState] cp a, 0 jr z, .noRotation ld a, $FF ldh [hAState], a jr .cp4 .ldb4 ldh a, [hBState] cp a, 0 jr z, .noRotation ld a, $FF ldh [hBState], a .cp4 ld a, ROTATION_STATE_CW ldh [hCurrentPieceRotationState], a call SFXKill ld a, SFX_IRS | SFX_IHS call SFXEnqueue jr .doHoldOperation .noRotation call SFXKill ld a, SFX_IHS call SFXEnqueue xor a, a ; ROTATION_STATE_DEF ldh [hCurrentPieceRotationState], a .doHoldOperation ldh a, [hHeldPiece] ld b, a ldh a, [hCurrentPiece] ldh [hHeldPiece], a ld a, b ldh [hCurrentPiece], a ret SECTION "Gameplay Function Big Banked", ROMX, BANK[BANK_GAMEPLAY_BIG] ; Change to game play mode. The event loop will call the event loop and vblank handlers for this mode after this returns. SwitchToGameplayBigB: ; Turn the screen off if it's on. ldh a, [rLCDC] and LCDCF_ON jr z, :+ ; Screen is already off. wait_vram xor a, a ldh [rLCDC], a ; Load the gameplay tilemap. : ld de, sBigGameplayTileMap ld hl, $9800 ld bc, sBigGameplayTileMapEnd - sBigGameplayTileMap call UnsafeMemCopy ; Clear OAM. call ClearOAM call SetNumberSpritePositions call ApplyTells ; Set up the palettes. ld a, PALETTE_REGULAR set_bg_palette set_obj0_palette ld a, PALETTE_LIGHTER_1 set_obj1_palette ; Initialize the RNG. call RNGInit ; Initialize the score, level and field. call ScoreInit call LevelInit call BigFieldInit ; We don't start with hold spent. xor a, a ldh [hHoldSpent], a ; Leady mode. ld a, MODE_LEADY ldh [hMode], a ld a, LEADY_TIME ldh [hModeCounter], a ; GBC init call GBCGameplayInit ; Install the event loop handlers. ld a, STATE_GAMEPLAY_BIG ldh [hGameState], a ; And turn the LCD back on before we start. ld a, LCDCF_ON | LCDCF_BGON | LCDCF_OBJON | LCDCF_BLK01 ldh [rLCDC], a ; Music end call SFXKill ; Make sure the first game loop starts just like all the future ones. wait_vblank wait_vblank_end ret ; Main gameplay event loop. GamePlayBigEventLoopHandlerB: ; What mode are we in? ld hl, .modejumps ldh a, [hMode] ld b, 0 ld c, a add hl, bc jp hl .modejumps jp .leadyMode jp .goMode jp .postGoMode jp .prefetchedPieceMode jp .spawnPieceMode jp .pieceInMotionMode jp .delayMode jp .gameOverMode jp .preGameOverMode jp .pauseMode ; Draw "READY" and wait a bit. .leadyMode ldh a, [hModeCounter] cp a, LEADY_TIME jr nz, :+ call SFXKill ld a, SFX_READYGO call SFXEnqueue ldh a, [hModeCounter] : dec a jr nz, :+ ld a, MODE_GO ldh [hMode], a ld a, GO_TIME : ldh [hModeCounter], a ld de, sBigLeady ld hl, wField+(14*10) ld bc, 10 call UnsafeMemCopy jp .drawStaticInfo ; Draw "GO" and wait a bit. .goMode ldh a, [hModeCounter] dec a jr nz, :+ ld a, MODE_POSTGO ldh [hMode], a xor a, a : ldh [hModeCounter], a ld de, sBigGo ld hl, wField+(14*10) ld bc, 10 call UnsafeMemCopy jp .drawStaticInfo ; Clear the field, fetch the piece, ready for gameplay. .postGoMode ld a, MODE_PREFETCHED_PIECE ldh [hMode], a call BigFieldClear call BigToShadowField ldh a, [hNextPiece] ldh [hCurrentPiece], a call GetNextPiece jp .drawStaticInfo ; Fetch the next piece. .prefetchedPieceMode ; A piece will spawn in the middle, at the top of the screen, not rotated by default. ld a, $FF ldh [hRequestedJingle], a ld a, PIECE_SPAWN_X_BIG ldh [hCurrentPieceX], a ld a, PIECE_SPAWN_Y_BIG ldh [hCurrentPieceY], a xor a, a ; ROTATION_STATE_DEF ldh [hCurrentPieceRotationState], a ldh [hHoldSpent], a ; Check if IHS is requested. ; Apply the hold if so. .checkIHS ldh a, [hSelectState] cp a, 0 jr z, .loaddefaultjingle call BigDoHold jr .postjingle ; Enqueue the jingle. .loaddefaultjingle ldh a, [hNextPiece] ldh [hRequestedJingle], a ; Check if IRS is requested. ; Apply the rotation if so. .checkIRSA ld a, [wSwapABState] cp a, 0 jr z, .lda1 .ldb1 ldh a, [hBState] cp a, 0 jr z, .checkIRSB ld a, $FF ldh [hBState], a jr .cp1 .lda1 ldh a, [hAState] cp a, 0 jr z, .checkIRSB ld a, $FF ldh [hAState], a .cp1 ld a, ROTATION_STATE_CCW ldh [hCurrentPieceRotationState], a ldh a, [hNextPiece] ld b, a ld a, SFX_IRS or a, b ldh [hRequestedJingle], a jr .postjingle .checkIRSB ld a, [wSwapABState] cp a, 0 jr z, .ldb2 .lda2 ldh a, [hAState] cp a, 0 jr z, .postjingle ld a, $FF ldh [hAState], a jr .cp2 .ldb2 ldh a, [hBState] cp a, 0 jr z, .postjingle ld a, $FF ldh [hBState], a .cp2 ld a, ROTATION_STATE_CW ldh [hCurrentPieceRotationState], a ldh a, [hNextPiece] ld b, a ld a, SFX_IRS or a, b ldh [hRequestedJingle], a jr .postjingle .postjingle ld a, MODE_SPAWN_PIECE ldh [hMode], a ; State falls through to the next. ; Spawn the piece. .spawnPieceMode call BigTrySpawnPiece cp a, $FF jr z, :+ ld a, MODE_PRE_GAME_OVER ldh [hMode], a jp .drawStaticInfo : ld a, MODE_PIECE_IN_MOTION ldh [hMode], a ; Play the next jingle... maybe! ldh a, [hHoldSpent] cp a, $FF jr z, .pieceInMotionMode ldh a, [hRequestedJingle] cp a, $FF jr z, .pieceInMotionMode call SFXEnqueue ; This mode lasts for as long as the piece is in motion. ; Field will let us know when it has locked in place. .pieceInMotionMode ldh a, [hStartState] cp a, 1 jr nz, :+ call BigToBackupField ldh a, [hMode] ldh [hPrePause], a ld a, MODE_PAUSED ldh [hMode], a jp .drawStaticInfo : call BigFieldProcess ; Do we hold? ldh a, [hSelectState] cp a, 1 jr nz, :+ ldh a, [hHoldSpent] cp a, $FF jr z, :+ ; Reset position and rotation. ld a, PIECE_SPAWN_X_BIG ldh [hCurrentPieceX], a ld a, PIECE_SPAWN_Y_BIG ldh [hCurrentPieceY], a xor a, a ; ROTATION_STATE_DEF ldh [hCurrentPieceRotationState], a call BigDoHold ld a, MODE_SPAWN_PIECE ldh [hMode], a ; Do we go into delay state? : ldh a, [hCurrentLockDelayRemaining] cp a, 0 jr nz, :+ ld a, MODE_DELAY ldh [hMode], a ; No fall through this time. : jp .drawStaticInfo .delayMode ldh a, [hStartState] cp a, 1 jr nz, :+ call BigToBackupField ldh a, [hMode] ldh [hPrePause], a ld a, MODE_PAUSED ldh [hMode], a jp .drawStaticInfo : call BigFieldDelay ldh a, [hRemainingDelay] cp a, 0 jr nz, :+ ld a, MODE_PREFETCHED_PIECE ldh [hMode], a : jp .drawStaticInfo .preGameOverMode ; Spawn the failed piece. call BigForceSpawnPiece ; Draw the field in grey. ; Yes. This really unrolls the loop that many times. ld hl, wField+(4*10) REPT 60 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty1\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip1\@ .notempty1\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip1\@ ENDR DEF off = 0 REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty2\@ ld a, GAME_OVER_R10+10+off ld [hl+], a jr .skip2\@ .notempty2\@ ld a, GAME_OVER_R10+off ld [hl+], a .skip2\@ DEF off += 1 ENDR REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty3\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip3\@ .notempty3\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip3\@ ENDR DEF off = 0 REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty4\@ ld a, GAME_OVER_R12+10+off ld [hl+], a jr .skip4\@ .notempty4\@ ld a, GAME_OVER_R12+off ld [hl+], a .skip4\@ DEF off += 1 ENDR REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty5\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip5\@ .notempty5\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip5\@ ENDR DEF off = 0 REPT 10 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty6\@ ld a, GAME_OVER_R14+10+off ld [hl+], a jr .skip6\@ .notempty6\@ ld a, GAME_OVER_R14+off ld [hl+], a .skip6\@ DEF off += 1 ENDR REPT 90 ld a, [hl] cp a, TILE_FIELD_EMPTY jr nz, .notempty7\@ ld a, GAME_OVER_OTHER+1 ld [hl+], a jr .skip7\@ .notempty7\@ ld a, GAME_OVER_OTHER ld [hl+], a .skip7\@ ENDR ld a, MODE_GAME_OVER ldh [hMode], a .gameOverMode ; Retry? ldh a, [hAState] cp a, 1 jr nz, :+ call RNGInit call ScoreInit call LevelInit call BigFieldInit xor a, a ldh [hHoldSpent], a ld a, MODE_LEADY ldh [hMode], a ld a, LEADY_TIME ldh [hModeCounter], a jp .drawStaticInfo ; Quit : ldh a, [hBState] cp a, 1 jp nz, .drawStaticInfo call SwitchToTitle jp EventLoopPostHandler .pauseMode ; Quick reset. ldh a, [hAState] cp a, 0 jr z, :+ ldh a, [hBState] cp a, 0 jr z, :+ ldh a, [hSelectState] cp a, 0 jr z, :+ call SwitchToTitle jp EventLoopPostHandler ; Unpause : ldh a, [hStartState] cp a, 1 jr nz, :+ call BigFromBackupField ldh a, [hPrePause] ldh [hMode], a jr .drawStaticInfo ; Draw PAUSE all over the field. : ld de, sBigPause ld hl, wField+(4*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(6*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(8*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(10*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(12*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(14*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(16*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(18*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(20*10) ld bc, 20 call UnsafeMemCopy ld de, sBigPause ld hl, wField+(22*10) ld bc, 20 call UnsafeMemCopy jr .drawStaticInfo ; Always draw the score, level, next piece, and held piece. .drawStaticInfo : ldh a, [hNextPiece] call ApplyNext ldh a, [hHeldPiece] call ApplyHold ld hl, wSPRScore1 ld de, hScore call ApplyNumbers ld hl, wSPRCLevel1 ld de, hCLevel call ApplyNumbers ld hl, wSPRNLevel1 ld de, hNLevel call ApplyNumbers jp GBCGameplayProcess ; Do the hold action. BigDoHold: ; Mark hold as spent. ld a, $FF ldh [hHoldSpent], a ; Check if IRS is requested. ; Apply the rotation if so. .checkIRSHA ld a, [wSwapABState] cp a, 0 jr z, .lda3 .ldb3 ldh a, [hBState] cp a, 0 jr z, .checkIRSHB ld a, $FF ldh [hBState], a jr .cp3 .lda3 ldh a, [hAState] cp a, 0 jr z, .checkIRSHB ld a, $FF ldh [hAState], a .cp3 ld a, ROTATION_STATE_CCW ldh [hCurrentPieceRotationState], a call SFXKill ld a, SFX_IRS | SFX_IHS call SFXEnqueue jr .doHoldOperation .checkIRSHB ld a, [wSwapABState] cp a, 0 jr z, .ldb4 .lda4 ldh a, [hAState] cp a, 0 jr z, .noRotation ld a, $FF ldh [hAState], a jr .cp4 .ldb4 ldh a, [hBState] cp a, 0 jr z, .noRotation ld a, $FF ldh [hBState], a .cp4 ld a, ROTATION_STATE_CW ldh [hCurrentPieceRotationState], a call SFXKill ld a, SFX_IRS | SFX_IHS call SFXEnqueue jr .doHoldOperation .noRotation call SFXKill ld a, SFX_IHS call SFXEnqueue xor a, a ; ROTATION_STATE_DEF ldh [hCurrentPieceRotationState], a .doHoldOperation ldh a, [hHeldPiece] ld b, a ldh a, [hCurrentPiece] ldh [hHeldPiece], a ld a, b ldh [hCurrentPiece], a ret ENDC