mirror of
https://github.com/irmen/prog8.git
synced 2024-11-23 07:32:10 +00:00
08ac459a41
When it returns true it means run the system IRQ handler afterwards. When it returns false, the system handler is NOT ran afterwards.
730 lines
22 KiB
Lua
730 lines
22 KiB
Lua
; TehTriz - a Tetris clone. Commander X16 version.
|
|
;
|
|
; features:
|
|
; holding area
|
|
; wall kick rotations
|
|
; shows next piece
|
|
; staged speed increase
|
|
; simple sound effects (Vera PSG)
|
|
|
|
%import syslib
|
|
%import textio
|
|
%import math
|
|
%import test_stack
|
|
%import psg
|
|
|
|
main {
|
|
|
|
const ubyte boardOffsetX = 14
|
|
const ubyte boardOffsetY = 3
|
|
const ubyte boardWidth = 10
|
|
const ubyte boardHeight = 20
|
|
const ubyte startXpos = boardOffsetX + 3
|
|
const ubyte startYpos = boardOffsetY - 2
|
|
uword lines
|
|
uword score
|
|
ubyte xpos
|
|
ubyte ypos
|
|
ubyte nextBlock
|
|
ubyte speedlevel
|
|
ubyte holding
|
|
bool holdingAllowed
|
|
ubyte ticks_since_previous_action
|
|
ubyte ticks_since_previous_move
|
|
|
|
sub start() {
|
|
void cx16.screen_mode(3, false) ; low res
|
|
txt.color2(7,0) ; make sure correct screen colors are (re)set
|
|
txt.clear_screen()
|
|
|
|
; gimmick: make a mirrored R
|
|
cx16.vpoke(1,$f000+sc:'r'*8+0, %00111110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+1, %01100110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+2, %01100110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+3, %00111110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+4, %00011110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+5, %00110110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+6, %01100110)
|
|
cx16.vpoke(1,$f000+sc:'r'*8+7, %00000000)
|
|
|
|
sound.init()
|
|
newGame()
|
|
drawBoard()
|
|
gameOver()
|
|
|
|
newgame:
|
|
newGame()
|
|
drawBoard()
|
|
spawnNextBlock()
|
|
|
|
waitkey:
|
|
sys.waitvsync()
|
|
ticks_since_previous_action++
|
|
ticks_since_previous_move++
|
|
if ticks_since_previous_action==0
|
|
ticks_since_previous_action=255
|
|
if ticks_since_previous_move==0
|
|
ticks_since_previous_move=255
|
|
|
|
ubyte time_lo = lsb(cbm.RDTIM16())
|
|
if time_lo>=(60-4*speedlevel) {
|
|
cbm.SETTIM(0,0,0)
|
|
|
|
drawBlock(xpos, ypos, true) ; hide block
|
|
if blocklogic.noCollision(xpos, ypos+1) {
|
|
; slowly move the block down
|
|
ypos++
|
|
drawBlock(xpos, ypos, false) ; show block on new position
|
|
} else {
|
|
; block can't move further down!
|
|
; check if the game area is full, if not, spawn the next block at the top.
|
|
if blocklogic.isGameOver(xpos, ypos) {
|
|
gameOver()
|
|
goto newgame
|
|
} else {
|
|
sound.blockrotate()
|
|
checkForLines()
|
|
spawnNextBlock()
|
|
score++
|
|
}
|
|
}
|
|
|
|
drawScore()
|
|
|
|
; txt.plot(0,0)
|
|
; test_stack.test()
|
|
}
|
|
|
|
ubyte key=cbm.GETIN()
|
|
keypress(key)
|
|
joystick(cx16.joystick_get2(1))
|
|
|
|
goto waitkey
|
|
|
|
}
|
|
|
|
sub move_left() {
|
|
drawBlock(xpos, ypos, true)
|
|
if blocklogic.noCollision(xpos-1, ypos) {
|
|
xpos--
|
|
}
|
|
drawBlock(xpos, ypos, false)
|
|
}
|
|
|
|
sub move_right() {
|
|
drawBlock(xpos, ypos, true)
|
|
if blocklogic.noCollision(xpos+1, ypos) {
|
|
xpos++
|
|
}
|
|
drawBlock(xpos, ypos, false)
|
|
}
|
|
|
|
sub move_down_faster() {
|
|
drawBlock(xpos, ypos, true)
|
|
if blocklogic.noCollision(xpos, ypos+1) {
|
|
ypos++
|
|
}
|
|
drawBlock(xpos, ypos, false)
|
|
}
|
|
|
|
sub drop_down_immediately() {
|
|
drawBlock(xpos, ypos, true)
|
|
ubyte dropypos
|
|
for dropypos in ypos+1 to boardOffsetY+boardHeight-1 {
|
|
if not blocklogic.noCollision(xpos, dropypos) {
|
|
dropypos-- ; the furthest down that still fits
|
|
break
|
|
}
|
|
}
|
|
|
|
if dropypos>ypos {
|
|
ypos = dropypos
|
|
sound.blockdrop()
|
|
drawBlock(xpos, ypos, false)
|
|
checkForLines()
|
|
spawnNextBlock()
|
|
score++
|
|
drawScore()
|
|
}
|
|
}
|
|
|
|
sub rotate_counterclockwise() {
|
|
drawBlock(xpos, ypos, true)
|
|
if blocklogic.canRotateCCW(xpos, ypos) {
|
|
blocklogic.rotateCCW()
|
|
sound.blockrotate()
|
|
}
|
|
else if blocklogic.canRotateCCW(xpos-1, ypos) {
|
|
xpos--
|
|
blocklogic.rotateCCW()
|
|
sound.blockrotate()
|
|
}
|
|
else if blocklogic.canRotateCCW(xpos+1, ypos) {
|
|
xpos++
|
|
blocklogic.rotateCCW()
|
|
sound.blockrotate()
|
|
}
|
|
drawBlock(xpos, ypos, false)
|
|
}
|
|
|
|
sub rotate_clockwise() {
|
|
drawBlock(xpos, ypos, true)
|
|
if blocklogic.canRotateCW(xpos, ypos) {
|
|
blocklogic.rotateCW()
|
|
sound.blockrotate()
|
|
}
|
|
else if blocklogic.canRotateCW(xpos-1, ypos) {
|
|
xpos--
|
|
blocklogic.rotateCW()
|
|
sound.blockrotate()
|
|
}
|
|
else if blocklogic.canRotateCW(xpos+1, ypos) {
|
|
xpos++
|
|
blocklogic.rotateCW()
|
|
sound.blockrotate()
|
|
}
|
|
drawBlock(xpos, ypos, false)
|
|
}
|
|
|
|
sub hold_block() {
|
|
if holdingAllowed {
|
|
sound.swapping()
|
|
if holding<7 {
|
|
drawBlock(xpos, ypos, true)
|
|
ubyte newholding = blocklogic.currentBlockNum
|
|
swapBlock(holding)
|
|
holding = newholding
|
|
holdingAllowed = false
|
|
} else {
|
|
holding = blocklogic.currentBlockNum
|
|
drawBlock(xpos, ypos, true)
|
|
spawnNextBlock()
|
|
}
|
|
drawHoldBlock()
|
|
}
|
|
}
|
|
|
|
sub keypress(ubyte key) {
|
|
when key {
|
|
157, ',' -> move_left()
|
|
29, '/' -> move_right()
|
|
17, '.' -> move_down_faster()
|
|
145, ' ' -> drop_down_immediately()
|
|
'z' -> rotate_counterclockwise()
|
|
'x' -> rotate_clockwise()
|
|
'c' -> hold_block()
|
|
}
|
|
}
|
|
|
|
sub joystick(uword joy) {
|
|
; note: we don't process simultaneous button presses
|
|
when joy {
|
|
%1111111111111101 -> {
|
|
if ticks_since_previous_move > 5 {
|
|
move_left()
|
|
ticks_since_previous_move = 0
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
%1111111111111110 -> {
|
|
if ticks_since_previous_move > 5 {
|
|
move_right()
|
|
ticks_since_previous_move = 0
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
%1111111111111011 -> {
|
|
if ticks_since_previous_move > 5 {
|
|
move_down_faster()
|
|
ticks_since_previous_move = 0
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
%1111111101111111 -> {
|
|
if ticks_since_previous_action > 200 {
|
|
drop_down_immediately()
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
%1111111110111111 -> {
|
|
if ticks_since_previous_action > 20 {
|
|
rotate_counterclockwise()
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
%1011111111111111, %0111111111111111 -> {
|
|
if ticks_since_previous_action > 20 {
|
|
rotate_clockwise()
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
%1111111111110111 -> {
|
|
if ticks_since_previous_action > 60 {
|
|
hold_block()
|
|
ticks_since_previous_action = 0
|
|
}
|
|
}
|
|
$ffff -> {
|
|
; no button pressed, reset timers to allow button tapping
|
|
ticks_since_previous_move = 255
|
|
ticks_since_previous_action = 255
|
|
}
|
|
}
|
|
}
|
|
|
|
sub checkForLines() {
|
|
; check if line(s) are full -> flash/clear line(s) + add score + move rest down
|
|
ubyte[boardHeight] complete_lines
|
|
ubyte num_lines=0
|
|
ubyte linepos
|
|
sys.memset(complete_lines, len(complete_lines), 0)
|
|
for linepos in boardOffsetY to boardOffsetY+boardHeight-1 {
|
|
if blocklogic.isLineFull(linepos) {
|
|
complete_lines[num_lines]=linepos
|
|
num_lines++
|
|
ubyte x
|
|
for x in boardOffsetX to boardOffsetX+boardWidth-1
|
|
txt.setcc2(x, linepos, 160, 1)
|
|
}
|
|
}
|
|
if num_lines {
|
|
if num_lines>3 {
|
|
sound.lineclear_big()
|
|
sys.wait(25) ; slight delay to flash the line
|
|
}
|
|
else {
|
|
sound.lineclear()
|
|
sys.wait(15) ; slight delay to flash the line
|
|
}
|
|
for linepos in complete_lines
|
|
if linepos and blocklogic.isLineFull(linepos)
|
|
blocklogic.collapse(linepos)
|
|
lines += num_lines
|
|
uword[] scores = [10, 25, 50, 100] ; can never clear more than 4 lines at once
|
|
score += scores[num_lines-1]
|
|
speedlevel = 1+lsb(lines/10)
|
|
drawScore()
|
|
}
|
|
}
|
|
|
|
sub gameOver() {
|
|
sound.gameover()
|
|
txt.plot(7, 7)
|
|
txt.chrout('U')
|
|
txt.print("────────────────────────")
|
|
txt.chrout('I')
|
|
txt.plot(7, 8)
|
|
txt.print("│*** g a m e o v e r ***│")
|
|
txt.plot(7, 9)
|
|
txt.chrout('J')
|
|
txt.print("────────────────────────")
|
|
txt.chrout('K')
|
|
|
|
txt.plot(7, 18)
|
|
txt.chrout('U')
|
|
txt.print("────────────────────────")
|
|
txt.chrout('I')
|
|
txt.plot(7, 19)
|
|
txt.print("│ f1/start for new game │")
|
|
txt.plot(7, 20)
|
|
txt.chrout('J')
|
|
txt.print("────────────────────────")
|
|
txt.chrout('K')
|
|
|
|
ubyte key
|
|
do {
|
|
; endless loop until user presses F1 or Start button to restart the game
|
|
cx16.r0 = cx16.joystick_get2(1)
|
|
if cx16.r0 & %0000000000010000 == 0
|
|
break
|
|
key = cbm.GETIN()
|
|
} until key==133
|
|
}
|
|
|
|
sub newGame() {
|
|
lines = 0
|
|
score = 0
|
|
xpos = startXpos
|
|
ypos = startYpos
|
|
speedlevel = 1
|
|
nextBlock = math.rnd() % 7
|
|
holding = 255
|
|
holdingAllowed = true
|
|
ticks_since_previous_action = 0
|
|
ticks_since_previous_move = 0
|
|
}
|
|
|
|
sub swapBlock(ubyte newblock) {
|
|
blocklogic.newCurrentBlock(newblock)
|
|
xpos = startXpos
|
|
ypos = startYpos
|
|
drawBlock(xpos, ypos, false)
|
|
}
|
|
|
|
sub spawnNextBlock() {
|
|
swapBlock(nextBlock)
|
|
nextBlock = (math.rnd() + lsb(cbm.RDTIM16())) % 7
|
|
drawNextBlock()
|
|
holdingAllowed = true
|
|
}
|
|
|
|
sub drawBoard() {
|
|
txt.clear_screen()
|
|
txt.color(7)
|
|
txt.plot(1,1)
|
|
txt.print("* tehtriz *")
|
|
txt.color(5)
|
|
txt.plot(6,4)
|
|
txt.print("hold:")
|
|
txt.plot(2,22)
|
|
txt.print("speed: ")
|
|
txt.plot(28,3)
|
|
txt.print("next:")
|
|
txt.plot(28,10)
|
|
txt.print("lines:")
|
|
txt.plot(28,14)
|
|
txt.print("score:")
|
|
txt.color(12)
|
|
txt.plot(27,18)
|
|
txt.print("controls:")
|
|
txt.color(11)
|
|
txt.plot(28,19)
|
|
txt.print(",/ move")
|
|
txt.plot(28,20)
|
|
txt.print("zx rotate")
|
|
txt.plot(29,21)
|
|
txt.print(". descend")
|
|
txt.plot(27,22)
|
|
txt.print("spc drop")
|
|
txt.plot(29,23)
|
|
txt.print("c hold")
|
|
txt.plot(25,24)
|
|
txt.print("or joystick #1")
|
|
|
|
txt.setcc(boardOffsetX-1, boardOffsetY-2, 255, 0) ; invisible barrier
|
|
txt.setcc(boardOffsetX-1, boardOffsetY-3, 255, 0) ; invisible barrier
|
|
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-2, 255, 0) ; invisible barrier
|
|
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-3, 255, 0) ; invisible barrier
|
|
|
|
txt.setcc(boardOffsetX-1, boardOffsetY-1, 108, 12)
|
|
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-1, 123, 12)
|
|
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-1, 123, 12)
|
|
txt.setcc(boardOffsetX-1, boardOffsetY+boardHeight, 124, 12)
|
|
txt.setcc(boardOffsetX+boardWidth, boardOffsetY+boardHeight, 126, 12)
|
|
ubyte i
|
|
for i in boardOffsetX+boardWidth-1 downto boardOffsetX {
|
|
txt.setcc(i, boardOffsetY-3, 255, 0) ; invisible barrier
|
|
txt.setcc(i, boardOffsetY+boardHeight, 69, 11)
|
|
}
|
|
for i in boardOffsetY+boardHeight-1 downto boardOffsetY {
|
|
txt.setcc(boardOffsetX-1, i, 89, 11)
|
|
txt.setcc(boardOffsetX+boardWidth, i, 84, 11)
|
|
}
|
|
|
|
ubyte[] colors = [6,8,7,5,4]
|
|
for i in len(colors)-1 downto 0 {
|
|
ubyte x
|
|
for x in 5 downto 0 {
|
|
txt.setcc(6+x-i, 11+2*i, 102, colors[i])
|
|
}
|
|
}
|
|
drawScore()
|
|
}
|
|
|
|
sub drawScore() {
|
|
txt.color(1)
|
|
txt.plot(30,11)
|
|
txt.print_uw(lines)
|
|
txt.plot(30,15)
|
|
txt.print_uw(score)
|
|
txt.plot(9,22)
|
|
txt.print_ub(speedlevel)
|
|
}
|
|
|
|
sub drawNextBlock() {
|
|
const ubyte nextBlockXpos = 29
|
|
const ubyte nextBlockYpos = 5
|
|
ubyte x
|
|
for x in nextBlockXpos+3 downto nextBlockXpos {
|
|
txt.setcc2(x, nextBlockYpos, ' ', 0)
|
|
txt.setcc2(x, nextBlockYpos+1, ' ', 0)
|
|
}
|
|
|
|
; reuse the normal block draw routine (because we can't manipulate array pointers yet)
|
|
ubyte prev = blocklogic.currentBlockNum
|
|
blocklogic.newCurrentBlock(nextBlock)
|
|
drawBlock(nextBlockXpos, nextBlockYpos, false)
|
|
blocklogic.newCurrentBlock(prev)
|
|
}
|
|
|
|
sub drawHoldBlock() {
|
|
const ubyte holdBlockXpos = 7
|
|
const ubyte holdBlockYpos = 6
|
|
ubyte x
|
|
for x in holdBlockXpos+3 downto holdBlockXpos {
|
|
txt.setcc2(x, holdBlockYpos, '@', 0)
|
|
txt.setcc2(x, holdBlockYpos+1, '@', 0)
|
|
}
|
|
if holding < 7 {
|
|
; reuse the normal block draw routine (because we can't manipulate array pointers yet)
|
|
ubyte prev = blocklogic.currentBlockNum
|
|
blocklogic.newCurrentBlock(holding)
|
|
drawBlock(holdBlockXpos, holdBlockYpos, false)
|
|
blocklogic.newCurrentBlock(prev)
|
|
}
|
|
}
|
|
|
|
sub drawBlock(ubyte x, ubyte y, bool erase) {
|
|
ubyte char = 79 ; top left edge
|
|
if erase
|
|
char = 32 ; space
|
|
ubyte @zp i
|
|
for i in 15 downto 0 {
|
|
ubyte @zp c=blocklogic.currentBlock[i]
|
|
if c {
|
|
if erase
|
|
c=0
|
|
else {
|
|
ubyte edge = blocklogic.edgecolors[c]
|
|
c <<= 4
|
|
c |= edge
|
|
}
|
|
txt.setcc2((i&3)+x, (i/4)+y, char, c)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
blocklogic {
|
|
|
|
ubyte currentBlockNum
|
|
ubyte[16] currentBlock
|
|
ubyte[16] rotated
|
|
|
|
; the 7 tetrominos
|
|
ubyte[] blockI = [0,0,0,0, ; blue note: special rotation (around matrix center)
|
|
6,6,6,6,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
ubyte[] blockJ = [5,0,0,0, ; green
|
|
5,5,5,0,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
ubyte[] blockL = [0,0,2,0, ; red
|
|
2,2,2,0,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
ubyte[] blockO = [0,12,12,0, ; grey ; note: no rotation (square)
|
|
0,12,12,0,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
ubyte[] blockS = [0,11,11,0, ; dark grey
|
|
11,11,0,0,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
ubyte[] blockT = [0,9,0,0, ; brown
|
|
9,9,9,0,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
ubyte[] blockZ = [4,4,0,0, ; purple
|
|
0,4,4,0,
|
|
0,0,0,0,
|
|
0,0,0,0]
|
|
|
|
ubyte[16] edgecolors = [11, 1, 10, 0, 10, 13, 14, 1, 7, 7, 11, 12, 15, 1, 1, 1] ; highlighed colors for the edges
|
|
|
|
uword[] blocks = [&blockI, &blockJ, &blockL, &blockO, &blockS, &blockT, &blockZ]
|
|
|
|
sub newCurrentBlock(ubyte block) {
|
|
currentBlockNum = block
|
|
sys.memcopy(blocks[block], currentBlock, len(currentBlock))
|
|
}
|
|
|
|
sub rotateCW() {
|
|
; rotates the current block clockwise.
|
|
if currentBlockNum==0 {
|
|
; the 'I' block rotates a 4x4 matrix around the center
|
|
rotated[0] = currentBlock[12]
|
|
rotated[1] = currentBlock[8]
|
|
rotated[2] = currentBlock[4]
|
|
rotated[3] = currentBlock[0]
|
|
rotated[4] = currentBlock[13]
|
|
rotated[5] = currentBlock[9]
|
|
rotated[6] = currentBlock[5]
|
|
rotated[7] = currentBlock[1]
|
|
rotated[8] = currentBlock[14]
|
|
rotated[9] = currentBlock[10]
|
|
rotated[10] = currentBlock[6]
|
|
rotated[11] = currentBlock[2]
|
|
rotated[12] = currentBlock[15]
|
|
rotated[13] = currentBlock[11]
|
|
rotated[14] = currentBlock[7]
|
|
rotated[15] = currentBlock[3]
|
|
sys.memcopy(rotated, currentBlock, len(currentBlock))
|
|
}
|
|
else if currentBlockNum!=3 {
|
|
; rotate all blocks (except 3, the square) around their center square in a 3x3 matrix
|
|
sys.memset(rotated, len(rotated), 0)
|
|
rotated[0] = currentBlock[8]
|
|
rotated[1] = currentBlock[4]
|
|
rotated[2] = currentBlock[0]
|
|
rotated[4] = currentBlock[9]
|
|
rotated[5] = currentBlock[5]
|
|
rotated[6] = currentBlock[1]
|
|
rotated[8] = currentBlock[10]
|
|
rotated[9] = currentBlock[6]
|
|
rotated[10] = currentBlock[2]
|
|
sys.memcopy(rotated, currentBlock, len(currentBlock))
|
|
}
|
|
}
|
|
|
|
sub rotateCCW() {
|
|
; rotates the current block counterclockwise.
|
|
if currentBlockNum==0 {
|
|
; the 'I' block rotates a 4x4 matrix around the center
|
|
rotated[0] = currentBlock[3]
|
|
rotated[1] = currentBlock[7]
|
|
rotated[2] = currentBlock[11]
|
|
rotated[3] = currentBlock[15]
|
|
rotated[4] = currentBlock[2]
|
|
rotated[5] = currentBlock[6]
|
|
rotated[6] = currentBlock[10]
|
|
rotated[7] = currentBlock[14]
|
|
rotated[8] = currentBlock[1]
|
|
rotated[9] = currentBlock[5]
|
|
rotated[10] = currentBlock[9]
|
|
rotated[11] = currentBlock[13]
|
|
rotated[12] = currentBlock[0]
|
|
rotated[13] = currentBlock[4]
|
|
rotated[14] = currentBlock[8]
|
|
rotated[15] = currentBlock[12]
|
|
sys.memcopy(rotated, currentBlock, len(currentBlock))
|
|
}
|
|
else if currentBlockNum!=3 {
|
|
; rotate all blocks (except 3, the square) around their center square in a 3x3 matrix
|
|
sys.memset(rotated, len(rotated), 0)
|
|
rotated[0] = currentBlock[2]
|
|
rotated[1] = currentBlock[6]
|
|
rotated[2] = currentBlock[10]
|
|
rotated[4] = currentBlock[1]
|
|
rotated[5] = currentBlock[5]
|
|
rotated[6] = currentBlock[9]
|
|
rotated[8] = currentBlock[0]
|
|
rotated[9] = currentBlock[4]
|
|
rotated[10] = currentBlock[8]
|
|
sys.memcopy(rotated, currentBlock, len(currentBlock))
|
|
}
|
|
}
|
|
|
|
; For movement checking it is not needed to clamp the x/y coordinates,
|
|
; because we have to check for brick collisions anyway.
|
|
; The full play area is bordered by (in)visible characters that will collide.
|
|
; Collision is determined by reading the screen data directly.
|
|
|
|
sub canRotateCW(ubyte xpos, ubyte ypos) -> bool {
|
|
rotateCW()
|
|
bool nocollision = noCollision(xpos, ypos)
|
|
rotateCCW()
|
|
return nocollision
|
|
}
|
|
|
|
sub canRotateCCW(ubyte xpos, ubyte ypos) -> bool {
|
|
rotateCCW()
|
|
bool nocollision = noCollision(xpos, ypos)
|
|
rotateCW()
|
|
return nocollision
|
|
}
|
|
|
|
sub noCollision(ubyte xpos, ubyte ypos) -> bool {
|
|
ubyte @zp i
|
|
for i in 15 downto 0 {
|
|
if currentBlock[i] and txt.getchr(xpos + (i&3), ypos+i/4)!=32
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
sub isGameOver(ubyte xpos, ubyte ypos) -> bool {
|
|
main.drawBlock(xpos, ypos, true)
|
|
bool result = ypos==main.startYpos and not noCollision(xpos, ypos+1)
|
|
main.drawBlock(xpos, ypos, false)
|
|
return result
|
|
}
|
|
|
|
sub isLineFull(ubyte ypos) -> bool {
|
|
ubyte x
|
|
for x in main.boardOffsetX to main.boardOffsetX+main.boardWidth-1 {
|
|
if txt.getchr(x, ypos)==32
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
sub collapse(ubyte ypos) {
|
|
while ypos>main.startYpos+1 {
|
|
ubyte x
|
|
for x in main.boardOffsetX+main.boardWidth-1 downto main.boardOffsetX {
|
|
ubyte char = txt.getchr(x, ypos-1)
|
|
ubyte color = txt.getclr(x, ypos-1)
|
|
txt.setcc2(x, ypos, char, color)
|
|
}
|
|
ypos--
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
sound {
|
|
sub init() {
|
|
cx16.vpoke(1, $f9c2, %00111111) ; volume max, no channels
|
|
psg.silent()
|
|
sys.set_irq(&psg.envelopes_irq)
|
|
}
|
|
|
|
sub blockrotate() {
|
|
; soft click/"tschk" sound
|
|
psg.freq(0, 15600)
|
|
psg.voice(0, psg.LEFT | psg.RIGHT, 32, psg.NOISE, 0)
|
|
psg.envelope(0, 32, 200, 1, 100)
|
|
}
|
|
|
|
sub blockdrop() {
|
|
; swish
|
|
psg.freq(1, 4600)
|
|
psg.voice(1, psg.LEFT | psg.RIGHT, 32, psg.NOISE, 0)
|
|
psg.envelope(1, 32, 200, 5, 20)
|
|
}
|
|
|
|
sub swapping() {
|
|
; beep
|
|
psg.freq(2, 1500)
|
|
psg.voice(2, psg.LEFT | psg.RIGHT, 32, psg.TRIANGLE, 0)
|
|
psg.envelope(2, 40, 100, 6, 10)
|
|
}
|
|
|
|
sub lineclear() {
|
|
; explosion
|
|
psg.freq(3, 1400)
|
|
psg.voice(3, psg.LEFT | psg.RIGHT, 63, psg.NOISE, 0)
|
|
psg.envelope(3, 63, 100, 8, 10)
|
|
}
|
|
|
|
sub lineclear_big() {
|
|
; big explosion
|
|
psg.freq(4, 2500)
|
|
psg.voice(4, psg.LEFT | psg.RIGHT, 63, psg.NOISE, 0)
|
|
psg.envelope(4, 63, 100, 20, 10)
|
|
}
|
|
|
|
sub gameover() {
|
|
; attempt at buzz/boing
|
|
psg.freq(5, 300)
|
|
psg.freq(6, 600)
|
|
psg.voice(5, psg.LEFT | psg.RIGHT, 0, psg.SAWTOOTH, 0)
|
|
psg.voice(6, psg.LEFT | psg.RIGHT, 0, psg.TRIANGLE, 0)
|
|
psg.envelope(5, 50, 100, 30, 10)
|
|
psg.envelope(6, 50, 100, 30, 10)
|
|
}
|
|
}
|