dmgtris/src/field.asm

1594 lines
26 KiB
NASM
Raw Normal View History

2023-10-13 09:20:28 +00:00
IF !DEF(FIELD_ASM)
DEF FIELD_ASM EQU 1
2023-10-16 05:47:11 +00:00
INCLUDE "globals.asm"
2023-10-20 14:28:11 +00:00
DEF DELAY_STATE_DETERMINE_DELAY EQU 0
DEF DELAY_STATE_LINE_CLEAR EQU 1
DEF DELAY_STATE_ARE EQU 2
2023-10-13 09:20:28 +00:00
SECTION "Field Variables", WRAM0
2023-10-18 11:14:48 +00:00
wField:: ds (10*24)
wShadowField:: ds (14*26)
2023-10-13 09:20:28 +00:00
SECTION "Field High Variables", HRAM
2023-10-20 10:46:55 +00:00
hPieceDataBase: ds 2
2023-10-20 15:25:42 +00:00
hPieceDataBaseFast: ds 2
2023-10-20 10:46:55 +00:00
hPieceDataOffset: ds 1
hCurrentLockDelayRemaining:: ds 1
2023-10-20 14:28:11 +00:00
hDeepestY: ds 1
2023-10-20 10:46:55 +00:00
hWantedTile: ds 1
2023-10-20 14:28:11 +00:00
hWantedG: ds 1
hActualG: ds 1
2023-10-20 10:46:55 +00:00
hTicksUntilG: ds 1
hWantX: ds 1
hYPosAtStartOfFrame: ds 1
hWantRotation: ds 1
2023-10-20 14:28:11 +00:00
hRemainingDelay:: ds 1
hDelayState: ds 1
hClearedLines: ds 4
2023-10-20 15:25:42 +00:00
hLineClearCt: ds 1
hComboCt: ds 1
2023-10-13 09:20:28 +00:00
SECTION "Field Functions", ROM0
FieldInit::
2023-10-20 15:25:42 +00:00
ld a, 1
ldh [hComboCt], a
2023-10-16 05:47:11 +00:00
ld hl, wField
2023-10-18 11:14:48 +00:00
ld bc, 10*24
2023-10-16 05:47:11 +00:00
ld d, 1
call UnsafeMemSet
2023-10-18 11:14:48 +00:00
ld hl, wShadowField
ld bc, 14*26
ld d, $FF
call UnsafeMemSet
2023-10-16 05:47:11 +00:00
ret
FieldClear::
2023-10-13 09:20:28 +00:00
ld hl, wField
2023-10-18 11:14:48 +00:00
ld bc, 10*24
2023-10-13 09:20:28 +00:00
ld d, TILE_FIELD_EMPTY
call UnsafeMemSet
ret
2023-10-20 14:28:11 +00:00
ToShadowField::
2023-10-18 11:14:48 +00:00
ld hl, wField
ld de, wShadowField+2
ld c, 24
.outer
ld b, 10
.inner
ld a, [hl+]
ld [de], a
inc de
dec b
jr nz, .inner
inc de
inc de
inc de
inc de
dec c
jr nz, .outer
ret
FromShadowField:
2023-10-18 11:14:48 +00:00
ld hl, wField
ld de, wShadowField+2
ld c, 24
.outer
ld b, 10
.inner
ld a, [de]
ld [hl+], a
inc de
dec b
jr nz, .inner
inc de
inc de
inc de
inc de
dec c
jr nz, .outer
ret
2023-10-18 01:32:25 +00:00
; This routine will copy wField onto the screen.
2023-10-13 09:20:28 +00:00
BlitField::
2023-10-18 01:32:25 +00:00
; What to copy
2023-10-18 11:14:48 +00:00
ld de, wField + 40
2023-10-18 01:32:25 +00:00
; Where to put it
ld hl, FIELD_TOP_LEFT
; How much to increment hl after each row
2023-10-18 03:26:06 +00:00
ld bc, 32-10
2023-10-18 01:32:25 +00:00
; The first 14 rows can be blitted without checking for vram access.
2023-10-13 09:20:28 +00:00
REPT 14
REPT 10
ld a, [de]
ld [hl+], a
inc de
ENDR
2023-10-18 01:32:25 +00:00
add hl, bc
2023-10-13 09:20:28 +00:00
ENDR
2023-10-18 03:26:06 +00:00
: ldh a, [rLY]
cp a, 0
jr nz, :-
2023-10-13 09:20:28 +00:00
; The last 6 rows need some care.
REPT 6
2023-10-18 03:26:06 +00:00
; Wait until start of drawing, then insert 35 nops.
: ldh a, [rSTAT]
and a, 3
cp a, 3
jr nz, :-
REPT 35
nop
ENDR
; Blit a line.
REPT 10
ld a, [de]
ld [hl+], a
inc de
2023-10-13 09:20:28 +00:00
ENDR
2023-10-18 03:26:06 +00:00
; Increment HL so that the next line can be blitted.
2023-10-18 01:32:25 +00:00
add hl, bc
2023-10-13 09:20:28 +00:00
ENDR
2023-10-16 05:47:11 +00:00
; This has to finish just before the first LCDC interrupt of the frame or stuff will break in weird ways.
2023-10-18 01:32:25 +00:00
jp EventLoop
2023-10-13 09:20:28 +00:00
SetPieceData:
ldh a, [hCurrentPiece]
ld hl, sPieceRotationStates
ld de, 16
: cp a, 0
jr z, :+
add hl, de
dec a
jr :-
: ld a, l
ldh [hPieceDataBase], a
ld a, h
ldh [hPieceDataBase+1], a
2023-10-20 15:25:42 +00:00
ldh a, [hCurrentPiece]
ld hl, sPieceFastRotationStates
ld de, 16
: cp a, 0
jr z, :+
add hl, de
dec a
jr :-
: ld a, l
ldh [hPieceDataBaseFast], a
ld a, h
ldh [hPieceDataBaseFast+1], a
ret
SetPieceDataOffset:
ldh a, [hCurrentPieceRotationState]
rlc a
rlc a
ldh [hPieceDataOffset], a
ret
; Converts piece Y in B and a piece X in A to a pointer to the shadow field in HL.
XYToSFieldPtr:
ld hl, wShadowField
ld de, 14
inc a
inc b
: dec b
jr z, :+
add hl, de
jr :-
: dec a
ret z
inc hl
jr :-
ret
; Converts piece Y in B and a piece X in A to a pointer to the field in HL.
XYToFieldPtr:
ld hl, wField-2
ld de, 10
inc a
inc b
: dec b
jr z, :+
add hl, de
jr :-
: dec a
ret z
inc hl
jr :-
ret
GetPieceData:
ldh a, [hPieceDataBase]
ld l, a
ldh a, [hPieceDataBase+1]
ld h, a
ldh a, [hPieceDataOffset]
ld c, a
xor a, a
ld b, a
add hl, bc
ret
2023-10-20 15:25:42 +00:00
GetPieceDataFast:
ldh a, [hPieceDataBaseFast]
ld l, a
ldh a, [hPieceDataBaseFast+1]
ld h, a
ldh a, [hPieceDataOffset]
ld c, a
xor a, a
ld b, a
add hl, bc
ret
; Checks if the piece can fit at the current position, but fast.
; HL should point to the piece's rotation state data.
; DE should be pointing to the right place in the SHADOW field.
CanPieceFitFast:
ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, [hl+]
add a, e
ld e, a
adc a, d
sub e
ld d, a
ld a, [de]
cp a, TILE_FIELD_EMPTY
jr z, :+
xor a, a
ret
: ld a, $FF
ret
; Checks if the piece can fit at the current position.
; HL should point to the piece's rotation state data.
; DE should be pointing to the right place in the SHADOW field.
CanPieceFit:
xor a, a
ld b, a
; Row 1
bit 3, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 2, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 1, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 0, [hl]
jr z, .r1end
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
.r1end
REPT 11
inc de
ENDR
; Row 2
inc hl
bit 3, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 2, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 1, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 0, [hl]
jr z, .r2end
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
.r2end
REPT 11
inc de
ENDR
; Row 3
inc hl
bit 3, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 2, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 1, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 0, [hl]
jr z, .r3end
ld a, [de]
cp a, TILE_FIELD_EMPTY
ret nz
.r3end
REPT 11
inc de
ENDR
; Row 4
inc hl
bit 3, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 2, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 1, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
: inc de
inc b
bit 0, [hl]
jr z, :+
ld a, [de]
cp a, TILE_FIELD_EMPTY
ld a, b
ret nz
; If we got here, the piece can fit.
: ld a, $FF
ret
TrySpawnPiece::
2023-10-20 06:46:11 +00:00
; Always reset these for a new piece.
ldh a, [hCurrentLockDelay]
ldh [hCurrentLockDelayRemaining], a
ldh a, [hCurrentFramesPerGravityTick]
ldh [hTicksUntilG], a
2023-10-20 14:28:11 +00:00
ld a, $FF
ldh [hRemainingDelay], a
ld a, DELAY_STATE_DETERMINE_DELAY
ldh [hDelayState], a
; Copy the field to the shadow field.
call ToShadowField
; Point the piece data to the correct piece.
call SetPieceData
call SetPieceDataOffset
; Get the piece's spawn position.
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hCurrentPieceX]
call XYToSFieldPtr
; Check if the piece can spawn.
ld d, h
ld e, l
call GetPieceData
call CanPieceFit
; A will be $FF if the piece can fit.
cp a, $FF
ret z
; Otherwise check the rotation, and if it's not zero, try to reset it.
ldh a, [hCurrentPieceRotationState]
cp a, 0
ret nz
; Reset the rotation.
xor a, a
ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hCurrentPieceX]
call XYToSFieldPtr
ld d, h
ld e, l
call GetPieceData
jp CanPieceFit
; Draws the piece onto the field.
; B is the tile.
; DE should point to the piece's rotation state data.
; HL should be pointing to the right place in the NORMAL field.
DrawPiece:
ld a, [de]
inc de
bit 3, a
jr z, :+
ld [hl], b
: inc hl
bit 2, a
jr z, :+
ld [hl], b
: inc hl
bit 1, a
jr z, :+
ld [hl], b
: inc hl
bit 0, a
jr z, .r1end2
ld [hl], b
.r1end2
REPT 7
inc hl
ENDR
ld a, [de]
inc de
bit 3, a
jr z, :+
ld [hl], b
: inc hl
bit 2, a
jr z, :+
ld [hl], b
: inc hl
bit 1, a
jr z, :+
ld [hl], b
: inc hl
bit 0, a
jr z, .r2end2
ld [hl], b
.r2end2
REPT 7
inc hl
ENDR
ld a, [de]
inc de
bit 3, a
jr z, :+
ld [hl], b
: inc hl
bit 2, a
jr z, :+
ld [hl], b
: inc hl
bit 1, a
jr z, :+
ld [hl], b
: inc hl
bit 0, a
jr z, .r3end2
ld [hl], b
.r3end2
REPT 7
inc hl
ENDR
ld a, [de]
inc de
bit 3, a
jr z, :+
ld [hl], b
: inc hl
bit 2, a
jr z, :+
ld [hl], b
: inc hl
bit 1, a
jr z, :+
ld [hl], b
: inc hl
bit 0, a
ret z
ld [hl], b
ret
2023-10-20 14:28:11 +00:00
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
2023-10-20 15:25:42 +00:00
ld a, 1
2023-10-20 14:28:11 +00:00
ldh [hActualG], a
.try
2023-10-20 15:25:42 +00:00
ld de, 14
2023-10-20 14:28:11 +00:00
pop hl
add hl, de
push hl
ld d, h
ld e, l
2023-10-20 15:25:42 +00:00
call GetPieceDataFast
call CanPieceFitFast
2023-10-20 14:28:11 +00:00
cp a, $FF
2023-10-20 15:25:42 +00:00
jr nz, .found
2023-10-20 14:28:11 +00:00
ldh a, [hActualG]
inc a
ldh [hActualG], a
jr .try
.found
pop hl
ldh a, [hActualG]
dec a
ldh [hActualG], a
ret
FieldProcess::
; Wipe out the piece.
ldh a, [hCurrentPieceY]
2023-10-20 10:46:55 +00:00
ldh [hYPosAtStartOfFrame], a
call FromShadowField
2023-10-20 06:40:34 +00:00
; Check if we're about to hold.
ld a, [hSelectState]
cp a, 1
jr nz, :+
ld a, [hHoldSpent]
cp a, $FF
ret nz
2023-10-20 14:28:11 +00:00
; How deep can we go?
: call FindMaxG
2023-10-20 06:40:34 +00:00
; If we press up, we want to do a sonic drop.
2023-10-20 14:28:11 +00:00
ldh a, [hUpState]
2023-10-20 06:40:34 +00:00
cp a, 1
jr nz, :+
2023-10-20 14:28:11 +00:00
ld a, 20
ldh [hWantedG], a
ldh a, [hTicksUntilG]
dec a
ldh [hTicksUntilG], a
jr nz, .grav
ldh a, [hCurrentFramesPerGravityTick]
ldh [hTicksUntilG], a
2023-10-20 06:40:34 +00:00
jr .grav
2023-10-20 10:46:55 +00:00
; If we press down, we want to do a soft drop.
: ldh a, [hDownState]
cp a, 0
jr z, :+
ld a, 1
ldh [hTicksUntilG], a
; Gravity?
2023-10-20 06:40:34 +00:00
: ldh a, [hTicksUntilG]
dec a
ldh [hTicksUntilG], a
jr nz, .nograv
ldh a, [hCurrentFramesPerGravityTick]
ldh [hTicksUntilG], a
2023-10-20 14:28:11 +00:00
ldh a, [hCurrentGravityPerTick]
ldh [hWantedG], a
2023-10-20 06:40:34 +00:00
.grav
2023-10-20 14:28:11 +00:00
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
2023-10-20 14:28:11 +00:00
ldh [hCurrentPieceY], a
jr .nograv
2023-10-20 06:40:34 +00:00
2023-10-20 14:28:11 +00:00
.smallg
ldh a, [hActualG]
ld b, a
ldh a, [hCurrentPieceY]
add a, b
ldh [hCurrentPieceY], a
2023-10-20 06:40:34 +00:00
.nograv
ldh a, [hCurrentPieceX]
ldh [hWantX], a
ldh a, [hCurrentPieceRotationState]
ldh [hWantRotation], a
2023-10-20 06:40:34 +00:00
; Want left?
.wantleft
ldh a, [hLeftState]
cp a, 1
jr z, :+
ld b, a
ldh a, [hCurrentDAS]
ld c, a
ld a, b
cp a, c
jr c, .wantright
: ldh a, [hWantX]
dec a
ldh [hWantX], a
2023-10-20 06:40:34 +00:00
; Want right?
.wantright
ldh a, [hRightState]
cp a, 1
jr z, :+
ld b, a
ldh a, [hCurrentDAS]
ld c, a
ld a, b
cp a, c
jr c, .wantrotccw
: ldh a, [hWantX]
inc a
ldh [hWantX], a
; Want rotate CCW?
.wantrotccw
ldh a, [hAState]
cp a, 1
jr nz, .wantrotcw
ldh a, [hWantRotation]
inc a
and a, $03
ldh [hWantRotation], a
; Want rotate CW?
.wantrotcw
ldh a, [hBState]
cp a, 1
jr nz, .moverotrequested
ldh a, [hWantRotation]
dec a
and a, $03
ldh [hWantRotation], a
; Do we need to try to move/rotate the piece?
.moverotrequested
ldh a, [hWantRotation]
ld b, a
ldh a, [hCurrentPieceRotationState]
cp a, b
jr nz, .trymoverot ; Move and rotate.
ldh a, [hWantX]
ld b, a
ldh a, [hCurrentPieceX]
cp a, b
2023-10-20 06:40:34 +00:00
jp z, .postmove ; Neither move nor rotate.
; Move only.
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hWantX]
call XYToSFieldPtr
ld d, h
ld e, l
call GetPieceData
call CanPieceFit
cp a, $FF
2023-10-20 06:40:34 +00:00
jp nz, .postmove
ldh a, [hWantX]
ldh [hCurrentPieceX], a
2023-10-20 06:40:34 +00:00
jp .postmove
.trymoverot
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hWantX]
call XYToSFieldPtr
ld d, h
ld e, l
ldh a, [hPieceDataBase]
ld l, a
ldh a, [hPieceDataBase+1]
ld h, a
ldh a, [hWantRotation]
rlc a
rlc a
push bc
ld c, a
xor a, a
ld b, a
add hl, bc
pop bc
call CanPieceFit
cp a, $FF
2023-10-20 06:40:34 +00:00
jr nz, .maybekick
ldh a, [hWantX]
ldh [hCurrentPieceX], a
ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset
jp .postmove
; 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.
.maybekick
ld c, a
ldh a, [hCurrentPiece]
cp a, PIECE_I
jr z, .postmove
cp a, PIECE_O
jr z, .postmove
cp a, PIECE_S
jr z, .trykickright
cp a, PIECE_Z
jr z, .trykickright
ld a, c
cp a, 1
jr z, .postmove
cp a, 5
jr z, .postmove
cp a, 9
jr z, .postmove
.trykickright
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hWantX]
inc a
call XYToSFieldPtr
ld d, h
ld e, l
ldh a, [hPieceDataBase]
ld l, a
ldh a, [hPieceDataBase+1]
ld h, a
ldh a, [hWantRotation]
rlc a
rlc a
push bc
ld c, a
xor a, a
ld b, a
add hl, bc
pop bc
call CanPieceFit
cp a, $FF
jr nz, .trykickleft
ldh a, [hWantX]
inc a
ldh [hCurrentPieceX], a
ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset
jr .postmove
.trykickleft
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hWantX]
dec a
call XYToSFieldPtr
ld d, h
ld e, l
ldh a, [hPieceDataBase]
ld l, a
ldh a, [hPieceDataBase+1]
ld h, a
ldh a, [hWantRotation]
rlc a
rlc a
push bc
ld c, a
xor a, a
ld b, a
add hl, bc
pop bc
call CanPieceFit
cp a, $FF
jr nz, .postmove
ldh a, [hWantX]
2023-10-20 06:40:34 +00:00
dec a
ldh [hCurrentPieceX], a
ldh a, [hWantRotation]
ldh [hCurrentPieceRotationState], a
call SetPieceDataOffset
.postmove
2023-10-20 10:46:55 +00:00
; Are we grounded?
ldh a, [hCurrentPieceY]
inc a
ld b, a
ldh a, [hCurrentPieceX]
call XYToSFieldPtr
ld d, h
ld e, l
call GetPieceData
call CanPieceFit
cp a, $FF
jr z, .notgrounded
; We're grounded.
; If the y position changed, play a sound.
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hYPosAtStartOfFrame]
cp a, b
jr z, :+
2023-10-20 14:28:11 +00:00
ldh a, [hDownState]
cp a, 0
jr nz, :+
2023-10-20 10:46:55 +00:00
ld a, SFX_DROP
call SFXEnqueue
; If the down button is held, lock.
: ldh a, [hDownState]
cp a, 0
jr z, :+
ld a, 1
ldh [hCurrentLockDelayRemaining], a
: ldh a, [hCurrentLockDelayRemaining]
dec a
ldh [hCurrentLockDelayRemaining], a
; If we're out of lock delay, play a sound.
cp a, 0
jr nz, .draw
ld a, SFX_LOCK
call SFXEnqueue
jr .draw
.notgrounded
; Otherwise reset the lock delay.
ldh a, [hCurrentLockDelay]
ldh [hCurrentLockDelayRemaining], a
; Draw the piece.
.draw
2023-10-20 14:28:11 +00:00
; 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
2023-10-20 10:46:55 +00:00
; If the lock delay is at the highest value, draw the piece normally.
2023-10-20 14:28:11 +00:00
: ldh a, [hCurrentPiece]
2023-10-20 10:46:55 +00:00
ld b, TILE_PIECE_0
add a, b
ldh [hWantedTile], a
ldh a, [hCurrentLockDelay]
ld b, a
ldh a, [hCurrentLockDelayRemaining]
cp a, b
jr z, .drawpiece
; If the lock delay is 0, draw the piece in the final color.
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+7
add a, b
ldh [hWantedTile], a
ldh a, [hCurrentLockDelayRemaining]
cp a, 0
jr z, .drawpiece
; Otherwise, look it up.
call GetTileShade
; TODO: What tile do we use to draw the piece?
.drawpiece
ldh a, [hCurrentPieceY]
ld b, a
ldh a, [hCurrentPieceX]
call XYToFieldPtr
ld d, h
ld e, l
call GetPieceData
2023-10-20 10:46:55 +00:00
ldh a, [hWantedTile]
ld b, a
push hl
push de
pop hl
pop de
call DrawPiece
ret
2023-10-20 10:46:55 +00:00
GetTileShade:
; Possible values for tile delay:
; 30, 25, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1
; We don't need to handle the 1 case.
ld a, 0
ld b, a
: ldh a, [hCurrentLockDelay]
cp a, 30
jr z, .max30
: cp a, 25
jr z, .max25
: cp a, 20
jr z, .max20
: cp a, 18
jp z, .max18
: cp a, 16
jp z, .max16
: cp a, 14
jp z, .max14
: cp a, 12
jp z, .max12
: cp a, 10
jp z, .max10
: cp a, 8
jp z, .max8
: cp a, 6
jp z, .max6
: cp a, 4
jp z, .max4
: cp a, 2
jp z, .max2
ret
.max30
ld a, [hCurrentLockDelayRemaining]
cp a, 4
ret c
cp a, 8
jp c, .s6
cp a, 12
jp c, .s5
cp a, 16
jp c, .s4
cp a, 20
jp c, .s3
cp a, 24
jp c, .s2
cp a, 28
jp c, .s1
jp .s0
.max25
ld a, [hCurrentLockDelayRemaining]
cp a, 3
ret c
cp a, 6
jp c, .s6
cp a, 9
jp c, .s5
cp a, 12
jp c, .s4
cp a, 15
jp c, .s3
cp a, 18
jp c, .s2
cp a, 21
jp c, .s1
jp .s0
.max20
ld a, [hCurrentLockDelayRemaining]
cp a, 2
ret c
cp a, 5
jp c, .s6
cp a, 7
jp c, .s5
cp a, 10
jp c, .s4
cp a, 12
jp c, .s3
cp a, 15
jp c, .s2
cp a, 17
jp c, .s1
jp .s0
.max18
ld a, [hCurrentLockDelayRemaining]
cp a, 2
ret c
cp a, 4
jp c, .s6
cp a, 6
jp c, .s5
cp a, 9
jp c, .s4
cp a, 11
jp c, .s3
cp a, 13
jp c, .s2
cp a, 15
jp c, .s1
jp .s0
.max16
ld a, [hCurrentLockDelayRemaining]
cp a, 2
ret c
cp a, 4
jp c, .s6
cp a, 6
jp c, .s5
cp a, 8
jp c, .s4
cp a, 10
jp c, .s3
cp a, 12
jp c, .s2
cp a, 14
jp c, .s1
jp .s0
.max14
ld a, [hCurrentLockDelayRemaining]
cp a, 2
ret c
cp a, 4
jp c, .s6
cp a, 6
jp c, .s5
cp a, 7
jp c, .s4
cp a, 9
jp c, .s3
cp a, 11
jp c, .s2
cp a, 13
jp c, .s1
jp .s0
.max12
ld a, [hCurrentLockDelayRemaining]
cp a, 1
ret c
cp a, 3
jp c, .s6
cp a, 4
jp c, .s5
cp a, 6
jp c, .s4
cp a, 7
jp c, .s3
cp a, 9
jp c, .s2
cp a, 10
jp c, .s1
jp .s0
.max10
ld a, [hCurrentLockDelayRemaining]
cp a, 1
ret c
cp a, 2
jp c, .s6
cp a, 3
jp c, .s5
cp a, 5
jr c, .s4
cp a, 6
jr c, .s3
cp a, 7
jr c, .s2
cp a, 8
jr c, .s1
jr .s0
.max8
ld a, [hCurrentLockDelayRemaining]
cp a, 1
ret c
cp a, 2
jr c, .s6
cp a, 3
jr c, .s5
cp a, 4
jr c, .s4
cp a, 5
jr c, .s3
cp a, 6
jr c, .s2
cp a, 7
jr c, .s1
jr .s0
.max6
ld a, [hCurrentLockDelayRemaining]
cp a, 1
ret c
cp a, 2
jr c, .s5
cp a, 3
jr c, .s3
cp a, 4
jr c, .s2
cp a, 5
jr c, .s1
jr .s0
.max4
ld a, [hCurrentLockDelayRemaining]
cp a, 1
ret c
cp a, 2
jr c, .s4
jr .s0
.max2
ld a, [hCurrentLockDelayRemaining]
jr .s4
.s0
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0
add a, b
ldh [hWantedTile], a
ret
.s1
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+(2*7)
add a, b
ldh [hWantedTile], a
ret
.s2
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+(3*7)
add a, b
ldh [hWantedTile], a
ret
.s3
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+(4*7)
add a, b
ldh [hWantedTile], a
ret
.s4
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+(5*7)
add a, b
ldh [hWantedTile], a
ret
.s5
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+(6*7)
add a, b
ldh [hWantedTile], a
ret
.s6
ldh a, [hCurrentPiece]
ld b, TILE_PIECE_0+(7*7)
add a, b
ldh [hWantedTile], a
ret
2023-10-20 14:28:11 +00:00
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
2023-10-20 15:25:42 +00:00
ldh a, [hLineClearCt]
cp a, 0
jr nz, :+
ld a, 1
ldh [hComboCt], a
ldh a, [hRequiresLineClear]
cp a, $FF
ret z
ld e, 1
call LevelUp
ret
: ldh a, [hLineClearCt]
ld e, a
call LevelUp
ld c, a
ld b, a
ldh a, [hComboCt]
add b
add b
sub 2
ldh [hComboCt], a
; Score the line clears.
xor a, a
ld b, a
ldh a, [hLevel]
ld l, a
ldh a, [hLevel+1]
ld h, a
add hl, bc
rrc h
rr l
rrc h
rr l
inc hl
ld b, h
ld c, l
ldh a, [hComboCt]
: add hl, bc
dec a
cp a, 0
jr nz, :-
ldh a, [hLineClearCt]
: add hl, bc
dec a
cp a, 0
jr nz, :-
ld a, l
ld [wScoreIncrement], a
ld a, h
ld [wScoreIncrement+1], a
call IncreaseScore
2023-10-20 14:28:11 +00:00
ret
AppendClearedLine:
2023-10-20 15:25:42 +00:00
ldh a, [hLineClearCt]
inc a
ldh [hLineClearCt], a
2023-10-20 14:28:11 +00:00
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:
2023-10-20 15:25:42 +00:00
xor a, a
ldh [hLineClearCt], a
2023-10-20 14:28:11 +00:00
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
2023-10-13 09:20:28 +00:00
ENDC