Fix crash, improve ARS2, improve initial hold.

This commit is contained in:
Randy Thiemann 2023-10-23 20:31:18 +02:00
parent 3576cb9bc6
commit 9c9a210d9e
7 changed files with 182 additions and 102 deletions

1
.vscode/launch.json vendored
View File

@ -8,6 +8,7 @@
"type": "emulicious-debugger", "type": "emulicious-debugger",
"request": "launch", "request": "launch",
"name": "Launch in Emulicious", "name": "Launch in Emulicious",
"emuliciousPath": "${workspaceFolder}\\tools",
"program": "${workspaceFolder}\\bin\\out.gb", "program": "${workspaceFolder}\\bin\\out.gb",
"port": 58870, "port": 58870,
"stopOnEntry": false, "stopOnEntry": false,

Binary file not shown.

View File

@ -24,7 +24,9 @@ INCLUDE "globals.asm"
DEF DELAY_STATE_DETERMINE_DELAY EQU 0 DEF DELAY_STATE_DETERMINE_DELAY EQU 0
DEF DELAY_STATE_LINE_CLEAR EQU 1 DEF DELAY_STATE_LINE_CLEAR EQU 1
DEF DELAY_STATE_ARE EQU 2 DEF DELAY_STATE_LINE_PRE_CLEAR EQU 2
DEF DELAY_STATE_ARE EQU 3
DEF DELAY_STATE_PRE_ARE EQU 4
SECTION "Field Variables", WRAM0 SECTION "Field Variables", WRAM0
@ -531,9 +533,6 @@ TrySpawnPiece::
ld a, DELAY_STATE_DETERMINE_DELAY ld a, DELAY_STATE_DETERMINE_DELAY
ld [wDelayState], a ld [wDelayState], a
; Copy the field to the shadow field.
call ToShadowField
; Point the piece data to the correct piece. ; Point the piece data to the correct piece.
call SetPieceData call SetPieceData
call SetPieceDataOffset call SetPieceDataOffset
@ -750,19 +749,10 @@ FieldProcess::
.firstframe .firstframe
ldh a, [hStalePiece] ldh a, [hStalePiece]
cp a, 0 cp a, 0
jr nz, .secondframe
ld a, 1
ldh [hStalePiece], a
jp .skipmovement
.secondframe
; Is this the first input frame?
cp a, 1
jr nz, .handleselect jr nz, .handleselect
ld a, $FF ld a, $FF
ldh [hStalePiece], a ldh [hStalePiece], a
xor a, a jp .skipmovement
ldh [hLockDelayForce], a
; ************************************************************** ; **************************************************************
@ -843,6 +833,11 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1.
cp a, 1
jp nz, .norot
inc a
ldh [hLockDelayForce], a
jp .norot jp .norot
; Try kicks if the piece isn't I or O. And in the case of J L and T, only if the blocked side is the left or right. ; Try kicks if the piece isn't I or O. And in the case of J L and T, only if the blocked side is the left or right.
@ -865,12 +860,15 @@ FieldProcess::
ld a, [wRotModeState] ld a, [wRotModeState]
cp a, ROT_MODE_ARSTI cp a, ROT_MODE_ARSTI
jp nz, .norot jp nz, .norot
ldh a, [hWantRotation]
bit 0, a
jp nz, .checki
jr .trykickright jr .trykickright
; T/L/J only kick if not through the middle axis. ; T/L/J only kick if not through the middle axis.
: ld a, c : ld a, c
cp a, 1 cp a, 1
jr z, .maybetgm3rot jp z, .maybetgm3rot
cp a, 5 cp a, 5
jr z, .maybetgm3rot jr z, .maybetgm3rot
cp a, 9 cp a, 9
@ -907,6 +905,11 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1.
cp a, 1
jp nz, .norot
inc a
ldh [hLockDelayForce], a
jp .norot jp .norot
; And a step to the left. ; And a step to the left.
@ -940,6 +943,11 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1.
cp a, 1
jp nz, .norot
inc a
ldh [hLockDelayForce], a
jp .norot jp .norot
; In TGM3, TGW3, EASY, and EAWY modes, there are a few other kicks possible. ; In TGM3, TGW3, EASY, and EAWY modes, there are a few other kicks possible.
@ -983,7 +991,15 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ld a, $FF ldh a, [hLockDelayForce] ; Set lock delay forcing to 1 if it's 0.
cp a, 0
jr nz, :+
inc a
ldh [hLockDelayForce], a
jp .norot
: cp a, 1 ; Or to 2 if it's 1.
jp nz, .norot
inc a
ldh [hLockDelayForce], a ldh [hLockDelayForce], a
jp .norot jp .norot
@ -993,11 +1009,16 @@ FieldProcess::
cp a, PIECE_I cp a, PIECE_I
jp nz, .norot jp nz, .norot
; Are we grounded? ; What direction do we want to end up?
; If not, we can only kick right twice. ldh a, [hWantRotation]
bit 0, a
jp z, .tryiright2 ; Flat? Sideways kicks are fine.
; Upright? Only up kicks.
; Are we grounded? Don't kick if we aren't.
ldh a, [hActualG] ldh a, [hActualG]
cp a, 0 cp a, 0
jr nz, .tryiright2 jp nz, .norot
; Try up once. ; Try up once.
.tryiup1 .tryiup1
@ -1030,9 +1051,17 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ld a, $FF ldh a, [hLockDelayForce] ; Set lock delay forcing to 1 if it's 0.
cp a, 0
jr nz, :+
inc a
ldh [hLockDelayForce], a ldh [hLockDelayForce], a
jr .norot jp .norot
: cp a, 1 ; Or to 2 if it's 1.
jp nz, .norot
inc a
ldh [hLockDelayForce], a
jp .norot
; Try up twice. ; Try up twice.
.tryiup2 .tryiup2
@ -1059,7 +1088,7 @@ FieldProcess::
pop bc pop bc
call CanPieceFitFast call CanPieceFitFast
cp a, $FF cp a, $FF
jr nz, .tryiright2 jr nz, .norot
ldh a, [hCurrentPieceY] ldh a, [hCurrentPieceY]
dec a dec a
dec a dec a
@ -1067,9 +1096,17 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ld a, $FF ldh a, [hLockDelayForce] ; Set lock delay forcing to 1 if it's 0.
cp a, 0
jr nz, :+
inc a
ldh [hLockDelayForce], a ldh [hLockDelayForce], a
jr .norot jp .norot
: cp a, 1 ; Or to 2 if it's 1.
jp nz, .norot
inc a
ldh [hLockDelayForce], a
jp .norot
; Try right twice. ; Try right twice.
.tryiright2 .tryiright2
@ -1104,6 +1141,11 @@ FieldProcess::
ldh a, [hWantRotation] ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset call SetPieceDataOffset
ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1.
cp a, 1
jp nz, .norot
inc a
ldh [hLockDelayForce], a
; ************************************************************** ; **************************************************************
@ -1324,7 +1366,7 @@ FieldProcess::
; TGM3 sometimes forces a piece to immediately lock. ; TGM3 sometimes forces a piece to immediately lock.
.checkfortgm3lockexception .checkfortgm3lockexception
ldh a, [hLockDelayForce] ldh a, [hLockDelayForce]
cp a, $FF cp a, 2
jr nz, .draw ; It's not forced, so go to drawing. jr nz, .draw ; It's not forced, so go to drawing.
xor a, a ; It is forced, so force it! xor a, a ; It is forced, so force it!
ldh [hCurrentLockDelayRemaining], a ldh [hCurrentLockDelayRemaining], a
@ -1346,6 +1388,11 @@ FieldProcess::
; HANDLE DRAWING ; HANDLE DRAWING
; Draw the piece. ; Draw the piece.
.draw .draw
; If the piece is locked, skip the ghost piece.
ldh a, [hCurrentLockDelayRemaining]
cp a, 0
jr z, :+
; If the gravity is <= 1G, draw a ghost piece. ; If the gravity is <= 1G, draw a ghost piece.
ldh a, [hWantedG] ldh a, [hWantedG]
cp a, 1 cp a, 1
@ -1678,15 +1725,24 @@ FieldDelay::
ld a, [wDelayState] ld a, [wDelayState]
cp DELAY_STATE_DETERMINE_DELAY cp DELAY_STATE_DETERMINE_DELAY
jr z, .determine jr z, .determine
cp DELAY_STATE_LINE_PRE_CLEAR
jr z, .prelineclear
cp DELAY_STATE_LINE_CLEAR cp DELAY_STATE_LINE_CLEAR
jr z, .lineclear jp z, .lineclear
cp DELAY_STATE_ARE cp DELAY_STATE_PRE_ARE
jr z, .are jp z, .preare
jp .are
; Check if there were line clears. ; Check if there were line clears.
; If so, we need to do a line clear delay. ; If so, we need to do a line clear delay.
; Otherwise, we skip to ARE delay. ; Otherwise, we skip to ARE delay.
.determine .determine
; Increment bravo by 4.
ldh a, [hBravo]
add a, 4
ldh [hBravo], a
call ToShadowField
call FindClearedLines call FindClearedLines
ldh a, [hClearedLines] ldh a, [hClearedLines]
ld b, a ld b, a
@ -1700,72 +1756,34 @@ FieldDelay::
and a, d and a, d
cp a, $FF cp a, $FF
jr z, .skip ; If there were no line clears, skip to ARE delay. jr z, .skip ; If there were no line clears, skip to ARE delay.
ld a, DELAY_STATE_LINE_CLEAR ; Otherwise, do a line clear delay. ld a, DELAY_STATE_LINE_PRE_CLEAR ; Otherwise, do a line clear delay.
ld [wDelayState], a ld [wDelayState], a
ldh a, [hCurrentLineClearDelay] ldh a, [hCurrentLineClearDelay]
ldh [hRemainingDelay], a ldh [hRemainingDelay], a
call MarkClear call MarkClear
jr .lineclear jp .prelineclear
.skip .skip
ld a, DELAY_STATE_ARE ; If there were no line clears, do an ARE delay. ld a, DELAY_STATE_PRE_ARE ; If there were no line clears, do an ARE delay.
ld [wDelayState], a ld [wDelayState], a
ldh a, [hCurrentARE] ldh a, [hCurrentARE]
ldh [hRemainingDelay], a ldh [hRemainingDelay], a
jr .are jp .preare
; Line clear delay. ; Pre-line clear delay.
; Count doen the delay. If we're out of delay, clear the lines and go to ARE. ; If we had line clears, immediately hand out the score and the levels.
.lineclear .prelineclear:
ldh a, [hRemainingDelay] ld a, DELAY_STATE_LINE_CLEAR
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
ld [wDelayState], a ld [wDelayState], a
ldh a, [hCurrentARE]
ldh [hRemainingDelay], a
; ARE delay.
; Count down the delay. If it hits 0, award levels and score if necessary, then end the delay phase.
.are
ldh a, [hRemainingDelay]
dec a
ldh [hRemainingDelay], a
cp a, 0
ret nz
; Increment bravo by 4.
ldh a, [hBravo]
add a, 4
ldh [hBravo], a
; Check if there were any line clears.
call SFXKill
ldh a, [hLineClearCt] ldh a, [hLineClearCt]
cp a, 0 cp a, 0
jr nz, :+ ; If there were, award levels and score. jr z, .lineclear ; If not, just skip the phase.
ld a, 1 ; Otherwise, increment the level counter by one if it's not at a breakpoint, and end the delay phase.
ldh [hComboCt], a
ldh a, [hRequiresLineClear]
cp a, $FF
ret z
ld e, 1
call LevelUp
ret
; There were line clears! Clear the level counter breakpoint. ; There were line clears! Clear the level counter breakpoint.
: xor a, a xor a, a
ldh [hRequiresLineClear], a ldh [hRequiresLineClear], a
; Decrement bravo by 10 for each line clear. ; Decrement bravo by 10 for each line clear.
ldh a, [hLineClearCt] ldh a, [hLineClearCt]
ld b, a ld b, a
@ -1775,7 +1793,6 @@ FieldDelay::
jr nz, :- jr nz, :-
ldh [hBravo], a ldh [hBravo], a
; Check if we are in a TGM3 mode and thus need to handle line counts of 3 and 4 differently. ; Check if we are in a TGM3 mode and thus need to handle line counts of 3 and 4 differently.
ldh a, [hLineClearCt] ldh a, [hLineClearCt]
ld e, a ld e, a
@ -1867,6 +1884,65 @@ FieldDelay::
ldh [hScoreIncrement+1], a ldh [hScoreIncrement+1], a
call IncreaseScore call IncreaseScore
; Line clear delay.
; Count doen the delay. If we're out of delay, clear the lines and go to 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
ldh a, [hCurrentARE]
ldh [hRemainingDelay], a
; Pre-ARE delay.
.preare:
ld a, DELAY_STATE_ARE
ld [wDelayState], a
; Copy over the newly cleaned field.
call ToShadowField
; Don't do anything if there were line clears
ldh a, [hLineClearCt]
cp a, 0
jr nz, .are
; Otherwise, increment the level counter by one if it's not at a breakpoint.
ld a, 1
ldh [hComboCt], a
ldh a, [hRequiresLineClear]
cp a, $FF
jr z, .are
ld e, 1
call LevelUp
; ARE delay.
; Count down the delay. If it hits 0, award levels and score if necessary, then end the delay phase.
.are
ldh a, [hRemainingDelay]
dec a
ldh [hRemainingDelay], a
cp a, 0
ret nz
; Cycle the RNG.
ldh a, [hNextPiece]
ldh [hCurrentPiece], a
call GetNextPiece
; Kill the sound for the next piece.
call SFXKill
ret ret

Binary file not shown.

View File

@ -530,8 +530,8 @@ Tiles::
DB $4A,$4A,$4A,$4A,$E4,$E4,$00,$00 DB $4A,$4A,$4A,$4A,$E4,$E4,$00,$00
DB $CC,$CC,$22,$22,$22,$22,$44,$44 DB $CC,$CC,$22,$22,$22,$22,$44,$44
DB $82,$82,$82,$82,$EC,$EC,$00,$00 DB $82,$82,$82,$82,$EC,$EC,$00,$00
DB $0C,$0C,$0A,$0A,$0A,$0A,$0A,$0A DB $0E,$0E,$08,$08,$08,$08,$0C,$0C
DB $0A,$0A,$0A,$0A,$0C,$0C,$00,$00 DB $08,$08,$08,$08,$0E,$0E,$00,$00
TilesEnd:: TilesEnd::

View File

@ -64,8 +64,12 @@ RNGInit::
ld bc, 7 ld bc, 7
call UnsafeMemCopy call UnsafeMemCopy
; Start with a random piece held. ; Start with a random non-S/Z piece held.
call Next7Piece : call Next7Piece
cp a, PIECE_Z
jr z, :-
cp a, PIECE_S
jr z, :-
ldh [hHeldPiece], a ldh [hHeldPiece], a
; If we're in HELL mode, we don't care about anything but a random piece to start with. ; If we're in HELL mode, we don't care about anything but a random piece to start with.

View File

@ -22,16 +22,16 @@ DEF STATE_GAMEPLAY_ASM EQU 1
INCLUDE "globals.asm" INCLUDE "globals.asm"
DEF MODE_LEADY EQU 0 DEF MODE_LEADY EQU 0
DEF MODE_GO EQU 1 DEF MODE_GO EQU 1
DEF MODE_POSTGO EQU 2 DEF MODE_POSTGO EQU 2
DEF MODE_FETCH_PIECE EQU 3 DEF MODE_PREFETCHED_PIECE EQU 4
DEF MODE_SPAWN_PIECE EQU 4 DEF MODE_SPAWN_PIECE EQU 5
DEF MODE_PIECE_IN_MOTION EQU 5 DEF MODE_PIECE_IN_MOTION EQU 6
DEF MODE_DELAY EQU 6 DEF MODE_DELAY EQU 7
DEF MODE_GAME_OVER EQU 7 DEF MODE_GAME_OVER EQU 8
DEF MODE_PRE_GAME_OVER EQU 8 DEF MODE_PRE_GAME_OVER EQU 9
DEF MODE_PAUSED EQU 9 DEF MODE_PAUSED EQU 10
SECTION "High Gameplay Variables", HRAM SECTION "High Gameplay Variables", HRAM
@ -133,8 +133,8 @@ GamePlayEventLoopHandler::
jr z, goMode jr z, goMode
cp MODE_POSTGO cp MODE_POSTGO
jr z, postGoMode jr z, postGoMode
cp MODE_FETCH_PIECE cp MODE_PREFETCHED_PIECE
jr z, fetchPieceMode jr z, prefetchedPieceMode
cp MODE_SPAWN_PIECE cp MODE_SPAWN_PIECE
jp z, spawnPieceMode jp z, spawnPieceMode
cp MODE_PIECE_IN_MOTION cp MODE_PIECE_IN_MOTION
@ -186,20 +186,20 @@ goMode:
jp drawStaticInfo jp drawStaticInfo
; Clear the field, ready for gameplay. ; Clear the field, fetch the piece, ready for gameplay.
postGoMode: postGoMode:
ld a, MODE_FETCH_PIECE ld a, MODE_PREFETCHED_PIECE
ldh [hMode], a ldh [hMode], a
call FieldClear call FieldClear
call ToShadowField
ldh a, [hNextPiece]
ldh [hCurrentPiece], a
call GetNextPiece
jp drawStaticInfo jp drawStaticInfo
; Fetch the next piece. ; Fetch the next piece.
fetchPieceMode: prefetchedPieceMode:
ldh a, [hNextPiece]
ldh [hCurrentPiece], a
call GetNextPiece
; A piece will spawn in the middle, at the top of the screen, not rotated by default. ; A piece will spawn in the middle, at the top of the screen, not rotated by default.
ld a, 5 ld a, 5
ldh [hCurrentPieceX], a ldh [hCurrentPieceX], a
@ -336,7 +336,6 @@ pieceInMotionMode:
jr nz, :+ jr nz, :+
ld a, MODE_DELAY ld a, MODE_DELAY
ldh [hMode], a ldh [hMode], a
call ToShadowField
; No fall through this time. ; No fall through this time.
: jp drawStaticInfo : jp drawStaticInfo
@ -358,7 +357,7 @@ delayMode:
ldh a, [hRemainingDelay] ldh a, [hRemainingDelay]
cp a, 0 cp a, 0
jr nz, :+ jr nz, :+
ld a, MODE_FETCH_PIECE ld a, MODE_PREFETCHED_PIECE
ldh [hMode], a ldh [hMode], a
: jp drawStaticInfo : jp drawStaticInfo