From 79222085598f4109dda9522ebac1b68dd09c896e Mon Sep 17 00:00:00 2001 From: Randy Thiemann Date: Fri, 20 Oct 2023 16:28:11 +0200 Subject: [PATCH] Almost all the gameplay is in. --- src/constants.asm | 32 ++++ src/field.asm | 399 ++++++++++++++++++++++++++++++++++++---- src/include/globals.asm | 1 + src/sfx.asm | 12 ++ src/state_gameplay.asm | 15 +- tools/Emulicious.ini | 38 ++-- tools/projects/out.exp | 2 + 7 files changed, 451 insertions(+), 48 deletions(-) diff --git a/src/constants.asm b/src/constants.asm index 6f3902a..2020670 100644 --- a/src/constants.asm +++ b/src/constants.asm @@ -68,6 +68,38 @@ sPieceYOffsets:: ; How to draw each piece. Y-offsets of the sprites. sSpeedCurve:: ; Speed curve of the game. dw $0000 ; Level 0000 + db 1, 16 ; 1G every 16 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0015 ; Level 0015 + db 1, 15 ; 1G every 15 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0030 ; Level 0030 + db 1, 14 ; 1G every 14 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0040 ; Level 0040 + db 1, 13 ; 1G every 13 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0050 ; Level 0050 + db 1, 12 ; 1G every 12 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0060 ; Level 0060 + db 1, 11 ; 1G every 11 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0070 ; Level 0070 + db 1, 10 ; 1G every 10 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0080 ; Level 0080 + db 1, 9 ; 1G every 9 frames + db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR + + dw $0090 ; Level 0090 db 1, 8 ; 1G every 8 frames db 25, 14, 30, 40 ; ARE, DAS, LOCK, LINECLEAR diff --git a/src/field.asm b/src/field.asm index 6ed22dd..19a2cf2 100644 --- a/src/field.asm +++ b/src/field.asm @@ -5,6 +5,11 @@ DEF FIELD_ASM EQU 1 INCLUDE "globals.asm" +DEF DELAY_STATE_DETERMINE_DELAY EQU 0 +DEF DELAY_STATE_LINE_CLEAR EQU 1 +DEF DELAY_STATE_ARE EQU 2 + + SECTION "Field Variables", WRAM0 wField:: ds (10*24) wShadowField:: ds (14*26) @@ -14,11 +19,17 @@ SECTION "Field High Variables", HRAM hPieceDataBase: ds 2 hPieceDataOffset: ds 1 hCurrentLockDelayRemaining:: ds 1 +hDeepestY: ds 1 hWantedTile: ds 1 +hWantedG: ds 1 +hActualG: ds 1 hTicksUntilG: ds 1 hWantX: ds 1 hYPosAtStartOfFrame: ds 1 hWantRotation: ds 1 +hRemainingDelay:: ds 1 +hDelayState: ds 1 +hClearedLines: ds 4 SECTION "Field Functions", ROM0 @@ -42,7 +53,7 @@ FieldClear:: ret -ToShadowField: +ToShadowField:: ld hl, wField ld de, wShadowField+2 ld c, 24 @@ -367,6 +378,10 @@ TrySpawnPiece:: ldh [hCurrentLockDelayRemaining], a ldh a, [hCurrentFramesPerGravityTick] ldh [hTicksUntilG], a + ld a, $FF + ldh [hRemainingDelay], a + ld a, DELAY_STATE_DETERMINE_DELAY + ldh [hDelayState], a ; Copy the field to the shadow field. call ToShadowField @@ -507,6 +522,58 @@ DrawPiece: ret +FindMaxG: + ; Find the deepest the piece can go. + ; We cache this pointer, cause it otherwise takes too much time. + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call XYToSFieldPtr + push hl + ld a, 2 + ldh [hActualG], a +.try + ld de, 28 + pop hl + add hl, de + push hl + ld d, h + ld e, l + call GetPieceData + call CanPieceFit + cp a, $FF + jr nz, .foundmaybe + ldh a, [hActualG] + inc a + inc a + ldh [hActualG], a + jr .try + +.foundmaybe + ldh a, [hActualG] + dec a + ldh [hActualG], a + ld de, -14 + pop hl + add hl, de + push hl + ld d, h + ld e, l + call GetPieceData + call CanPieceFit + cp a, $FF + jr nz, .found + pop hl + ret + +.found + pop hl + ldh a, [hActualG] + dec a + ldh [hActualG], a + ret + + FieldProcess:: ; Wipe out the piece. ldh a, [hCurrentPieceY] @@ -523,11 +590,21 @@ FieldProcess:: ret nz + ; How deep can we go? +: call FindMaxG + ; If we press up, we want to do a sonic drop. -: ldh a, [hUpState] + ldh a, [hUpState] cp a, 1 jr nz, :+ - ld b, 20 + ld a, 20 + ldh [hWantedG], a + ldh a, [hTicksUntilG] + dec a + ldh [hTicksUntilG], a + jr nz, .grav + ldh a, [hCurrentFramesPerGravityTick] + ldh [hTicksUntilG], a jr .grav @@ -546,39 +623,30 @@ FieldProcess:: jr nz, .nograv ldh a, [hCurrentFramesPerGravityTick] ldh [hTicksUntilG], a - - - ; Move the piece down, but first check if there's still sufficient "down" to go. ldh a, [hCurrentGravityPerTick] - ld b, a + ldh [hWantedG], a + + .grav -: ldh a, [hCurrentPieceY] - add a, b - cp a, 23 - jr c, :+ - dec b - jr z, .nograv - jr :- -: push bc + ldh a, [hWantedG] + ld b, a + ldh a, [hActualG] + cp a, b + jr c, .smallg + + +.bigg + ldh a, [hWantedG] + ld b, a ldh a, [hCurrentPieceY] add a, b + ldh [hCurrentPieceY], a + jr .nograv + + +.smallg + ldh a, [hActualG] ld b, a - ldh a, [hCurrentPieceX] - call XYToSFieldPtr - ld d, h - ld e, l - call GetPieceData - call CanPieceFit - cp a, $FF - jr z, .dolower - pop bc - dec b - jr z, .nograv - jr :- - - -.dolower - pop bc ldh a, [hCurrentPieceY] add a, b ldh [hCurrentPieceY], a @@ -811,6 +879,9 @@ FieldProcess:: ldh a, [hYPosAtStartOfFrame] cp a, b jr z, :+ + ldh a, [hDownState] + cp a, 0 + jr nz, :+ ld a, SFX_DROP call SFXEnqueue ; If the down button is held, lock. @@ -837,8 +908,34 @@ FieldProcess:: ; Draw the piece. .draw + ; If the gravity is <= 1G, draw a ghost piece. + ldh a, [hWantedG] + cp a, 1 + jr nz, :+ + ldh a, [hEvenFrame] + cp a, 1 + jr nz, :+ + + ldh a, [hYPosAtStartOfFrame] + ld b, a + ldh a, [hActualG] + add a, b + ld b, a + ldh a, [hCurrentPieceX] + call XYToFieldPtr + ld d, h + ld e, l + call GetPieceData + ld a, 0 + ld b, a + push hl + push de + pop hl + pop de + call DrawPiece + ; If the lock delay is at the highest value, draw the piece normally. - ldh a, [hCurrentPiece] +: ldh a, [hCurrentPiece] ld b, TILE_PIECE_0 add a, b ldh [hWantedTile], a @@ -1131,4 +1228,242 @@ GetTileShade: ret +FieldDelay:: + ldh a, [hDelayState] + cp DELAY_STATE_DETERMINE_DELAY + jr z, .determine + cp DELAY_STATE_LINE_CLEAR + jr z, .lineclear + cp DELAY_STATE_ARE + jr z, .are + +.determine + call FindClearedLines + ldh a, [hClearedLines] + ld b, a + ldh a, [hClearedLines+1] + ld c, a + ldh a, [hClearedLines+2] + ld d, a + ldh a, [hClearedLines+3] + and a, b + and a, c + and a, d + cp a, $FF + jr z, .skip + ld a, DELAY_STATE_LINE_CLEAR + ldh [hDelayState], a + ldh a, [hCurrentLineClearDelay] + ldh [hRemainingDelay], a + call MarkClear + jr .lineclear +.skip + ld a, DELAY_STATE_ARE + ldh [hDelayState], a + ldh a, [hCurrentARE] + ldh [hRemainingDelay], a + jr .are + + +.lineclear + ldh a, [hRemainingDelay] + dec a + ldh [hRemainingDelay], a + cp a, 0 + ret nz + + call ClearLines + call SFXKill + ld a, SFX_DROP + call SFXEnqueue + + ld a, DELAY_STATE_ARE + ldh [hDelayState], a + ldh a, [hCurrentARE] + ldh [hRemainingDelay], a + +.are + ldh a, [hRemainingDelay] + dec a + ldh [hRemainingDelay], a + cp a, 0 + ret nz + + ; If we're out of delay, spawn a new piece. + call SFXKill + + ret + + +AppendClearedLine: + ldh a, [hClearedLines+2] + ldh [hClearedLines+3], a + ldh a, [hClearedLines+1] + ldh [hClearedLines+2], a + ldh a, [hClearedLines] + ldh [hClearedLines+1], a + ld a, b + ldh [hClearedLines], a + ret + + +FindClearedLines: + ld a, $FF + ld c, 0 + ldh [hClearedLines], a + ldh [hClearedLines+1], a + ldh [hClearedLines+2], a + ldh [hClearedLines+3], a + + DEF row = 23 + REPT 24 + ld hl, wShadowField+2+(row*14) + ld b, 11 +: ld a, [hl+] + dec b + cp a, $FF + jr z, :+ + cp a, TILE_FIELD_EMPTY + jr nz, :- +: xor a, a + cp a, b + jr nz, .next\@ + ld b, 23-row + call AppendClearedLine + inc c + ld a, 4 + cp a, c + ret z + DEF row -= 1 +.next\@ + ENDR + + ret + + +MarkClear: + ld a, [hClearedLines] + cp a, $FF + ret z + ld hl, wField+(24*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 10 + ld d, TILE_CLEARING + call UnsafeMemSet + + ld a, [hClearedLines+1] + cp a, $FF + ret z + ld hl, wField+(24*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 10 + ld d, TILE_CLEARING + call UnsafeMemSet + + ld a, [hClearedLines+2] + cp a, $FF + ret z + ld hl, wField+(24*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 10 + ld d, TILE_CLEARING + call UnsafeMemSet + + ld a, [hClearedLines+3] + cp a, $FF + ret z + ld hl, wField+(24*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 10 + ld d, TILE_CLEARING + call UnsafeMemSet + ret + + +ClearLines: + ld de, 0 + + DEF row = 23 + REPT 24 + ; Check if the row begins with a clearing tile. + ld hl, wField+(row*10) + ld a, [hl] + cp a, TILE_CLEARING + + ; If it does, increment the clearing counter, but skip this line. + jr nz, .clear\@ + inc de + inc de + inc de + inc de + inc de + inc de + inc de + inc de + inc de + inc de + jr .r\@ + +.clear\@ + ; If there's 0 lines that need to be moved down, skip this line. + xor a, a + cp a, e + jr z, .r\@ + + ; Otherwise... + ld bc, wField+(row*10) + add hl, de +: ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc +.r\@ + DEF row -= 1 + ENDR + + ret + + ENDC diff --git a/src/include/globals.asm b/src/include/globals.asm index b58fbba..4344f07 100644 --- a/src/include/globals.asm +++ b/src/include/globals.asm @@ -95,6 +95,7 @@ DEF FIELD_TOP_LEFT EQU $9800+1 DEF TILE_FIELD_EMPTY EQU 4 DEF TILE_PIECE_0 EQU 10 DEF TILE_0 EQU 66 +DEF TILE_CLEARING EQU 0 DEF NEXT_BASE_X EQU 120 DEF NEXT_BASE_Y EQU 40 DEF HOLD_BASE_X EQU 120 diff --git a/src/sfx.asm b/src/sfx.asm index 074e4a0..9629778 100644 --- a/src/sfx.asm +++ b/src/sfx.asm @@ -1241,6 +1241,18 @@ SFXEnqueue:: ret +SFXKill:: + ld a, $FF + ldh [hPlayQueue], a + ldh [hPlayQueue+1], a + ldh [hPlayQueue+2], a + ldh [hPlayQueue+3], a + xor a, a + ldh [hPlayhead], a + ldh [hPlayhead+1], a + ret + + ; This play routine must be called every frame. SFXPlay:: ; Load the playhead position into HL. diff --git a/src/state_gameplay.asm b/src/state_gameplay.asm index 15478b2..df05d4f 100644 --- a/src/state_gameplay.asm +++ b/src/state_gameplay.asm @@ -253,13 +253,22 @@ pieceInMotionMode: jr nz, :+ ld a, MODE_DELAY ld [wMode], a + call ToShadowField ; No fall through this time. : jr drawStaticInfo delayMode: - ; TODO. + call FieldDelay + + ldh a, [hRemainingDelay] + cp a, 0 + jr nz, :+ + ld a, MODE_FETCH_PIECE + ld [wMode], a + +: jr drawStaticInfo gameOverMode: @@ -268,11 +277,11 @@ gameOverMode: ld bc, 10 call UnsafeMemCopy ld de, sGameOver2 - ld hl, wField+(11*10) + ld hl, wField+(12*10) ld bc, 10 call UnsafeMemCopy ld de, sGameOver3 - ld hl, wField+(12*10) + ld hl, wField+(14*10) ld bc, 10 call UnsafeMemCopy diff --git a/tools/Emulicious.ini b/tools/Emulicious.ini index 419ce03..80a1cb4 100644 --- a/tools/Emulicious.ini +++ b/tools/Emulicious.ini @@ -1,5 +1,6 @@ #Emulicious settings file -#Fri Oct 20 12:46:32 CEST 2023 +#Fri Oct 20 16:26:17 CEST 2023 +WindowProfilerWindowOpen=false WindowEventViewerWindowHeight=1416 WindowEventViewerWindowDivider=876 WindowMemoryTracerWindowY=631 @@ -7,14 +8,19 @@ WindowMemoryTracerWindowX=383 Update=2 AudioSync=false DebuggerMemoryTabVisibleRect=0,0,0,0 +WindowProfilerWindowWidth=1073 UninitializedMemoryBreakpointSuspend=true GameBoyErrorBreakpointEnabled32=false SMSGamepadBThreshold=50 RegistersGameBoy=AF,BC,DE,HL,SP,PC,LCDC,STAT,LY,DIV,IE,IF SMSbuttonsThreshold=50 +ProfileInterruptsInSeparateRoots=true +WindowRAMWatchWindowY=737 +WindowRAMWatchWindowX=1766 GBGamepadThreshold=50 GameBoyErrorBreakpointEnabled20=false StretchToWindow=false +WindowProfilerWindowHeight=563 WindowTileViewerOpen=false DebuggerWestPanelSelectedTab=0 WindowMemoryTracerWindowWidth=1136 @@ -72,7 +78,7 @@ InterruptBreakpointEnabled=false OutlineWidth=425 DebuggerEventFiltersGameBoy= GameBoyErrorBreakpointSuspend9=true -WindowMemoryEditorOpen=true +WindowMemoryEditorOpen=false GameBoyErrorBreakpointSuspend8=true GameBoyErrorBreakpointSuspend7=true WindowPaletteViewerY=619 @@ -99,7 +105,7 @@ Gamepad1Key30=-1 BankSwapAtPCBreakpointEnabled=false DebuggerMemorySelectedTab=HRAM WindowVideoViewerOpen=false -WindowMemoryEditorTabVisibleRect=0,496,583,384 +WindowMemoryEditorTabVisibleRect=0,0,583,128 Gamepad1Key29=-1 Gamepad1Key28=-1 Gamepad1Key27=-1 @@ -126,9 +132,9 @@ Key1=75 Key0=74 Gamepad1Key19=-1 Gamepad1Key18=-1 -WindowEventViewerWindowY=99 +WindowEventViewerWindowY=24 Gamepad1Key17=-1 -WindowEventViewerWindowX=570 +WindowEventViewerWindowX=0 Gamepad1Key16=-1 Gamepad1Key15=-1 Gamepad1Key14=-1 @@ -136,7 +142,7 @@ Gamepad1Key13=-1 Gamepad1Key12=-1 Gamepad1Key11=-1 Gamepad1Key10=-1 -WindowMemoryEditorSelectedAddress=503 +WindowMemoryEditorSelectedAddress=37 WindowMemoryEditorWidth=665 GameBoyErrorBreakpointCondition9= GameBoyErrorBreakpointCondition8= @@ -155,7 +161,7 @@ Gamepad0Key35=-1 Gamepad0Key34=-1 Gamepad0Key33=-1 Gamepad0Key32=-1 -WindowMemoryEditorSelectedTab=RAM +WindowMemoryEditorSelectedTab=HRAM Gamepad0Key31=-1 Gamepad0Key30=-1 SMSGamepadAThreshold=50 @@ -178,8 +184,8 @@ Gamepad0Key20=-1 DebuggerSouthPanelSelectedTab=1 WindowEmuliciousWidth=816 WindowVideoViewerWidth=980 -WindowMemoryEditorY=732 -WindowMemoryEditorX=2558 +WindowMemoryEditorY=695 +WindowMemoryEditorX=243 Gamepad0Key19=-1 Gamepad0Key18=-1 Gamepad0Key17=-1 @@ -199,8 +205,8 @@ GameBoyErrorBreakpointMessage32= InterruptBreakpointCondition= Recent0=C\:\\workspace\\dmgtris\\bin\\out.gb GameBoyErrorBreakpointMessage20= -WindowEmuliciousY=302 -WindowEmuliciousX=153 +WindowEmuliciousY=367 +WindowEmuliciousX=982 GameBoyErrorBreakpointEnabled9=false GameBoyErrorBreakpointEnabled8=false GameBoyErrorBreakpointEnabled7=false @@ -264,14 +270,16 @@ Gamepad0Key3=-1 Gamepad0Key2=-1 Gamepad0Key1=-1 Gamepad0Key0=-1 -WindowDebuggerY=201 -WindowDebuggerX=727 +WindowDebuggerY=730 +WindowDebuggerX=1234 InterruptBreakpointSuspend=true SMSGamepadAKeyboard=false GameBoyErrorBreakpointSuspend32=true +WindowRAMWatchWindowHeight=470 SMSGamepadB=-1 SMSGamepadA=-1 WindowEventViewerWindowWidth=2576 +WindowRAMWatchWindowOpen=false BankSwapAtPCBreakpointCondition= GameBoyErrorBreakpointSuspend20=true SouthPanelHeight=635 @@ -285,7 +293,11 @@ GameBoyErrorBreakpointSuspend19=true GameBoyErrorBreakpointSuspend18=true GameBoyErrorBreakpointSuspend17=true GameBoyErrorBreakpointSuspend16=true +WindowRAMWatchWindowWidth=376 GameBoyErrorBreakpointSuspend10=true WindowSpriteViewerOpen=false +WindowProfilerWindowY=639 +WindowProfilerWindowX=461 Scale=5.0 KeyboardRequireWindowFocus=true +WindowProfilerWindowProcedureProfiler=true diff --git a/tools/projects/out.exp b/tools/projects/out.exp index f8d0ae4..ac66464 100644 --- a/tools/projects/out.exp +++ b/tools/projects/out.exp @@ -9,3 +9,5 @@ hPieceDataOffset 1 Hexadecimal hCurrentPiece 1 Hexadecimal hCurrentPieceY 1 Hexadecimal hUpState 1 Hexadecimal +hActualG 1 Hexadecimal +hClearedLines 4 Hexadecimal