'TETRIS2 by Timothy Peters
'
'feel free to use anything you want

'misc
CONST directory$ = ""
CONST true% = -1
CONST false% = 0
CONST ms& = 800
CONST speedconstant& = 10
CONST speedadvance% = 2000
CONST minspeed& = 90000
CONST maxspeed& = 5000
CONST screenwidth% = 80
CONST screenheight% = 25
CONST centerx% = screenwidth% \ 2
CONST centery% = screenheight% \ 2
CONST boardx% = screenwidth / 2 - 9
CONST boardy% = 2
CONST boardwidth% = 10
CONST boardheight% = 23
CONST maxblocks% = 10
CONST maxlevel% = 30
CONST maxhighscores% = 20
CONST maxpuzzles% = 40

'game modes
CONST normalgame% = 1
CONST puzzlegame% = 2
CONST highscoretable% = 3
CONST quitgame% = 4
CONST versusgame% = 3

'block colors
CONST nothing% = 0
CONST red% = 1
CONST yellow% = 2
CONST blue% = 3
CONST gray% = 4

'block type
CONST noblock% = 0
CONST longblock% = 1
CONST lblock% = 2
CONST lblocka% = 3
CONST zblock% = 4
CONST zblocka% = 5
CONST tblock% = 6
CONST yblock% = 7
CONST iblock% = 8
CONST iblocka% = 9
CONST cornerblock% = 10

'scoring
CONST blockmult% = 5
CONST breakmult% = 100
CONST additionalbase% = 2
CONST blastbonus& = 50000
CONST cascadebase% = 2
CONST colorclearbonus& = 1000
CONST endbonus& = 1000000

TYPE blocktype
    char AS INTEGER
    group AS INTEGER
    clr AS INTEGER
    fixed AS INTEGER
    flashing AS INTEGER
    landed AS INTEGER
    flag AS INTEGER
END TYPE

TYPE playertype
    x AS INTEGER
    y AS INTEGER
    block AS INTEGER
    blocklanded AS INTEGER
    singled AS INTEGER
END TYPE

TYPE blockinfotype
    x AS INTEGER
    y AS INTEGER
END TYPE

TYPE highscoretype
    initials AS STRING * 3   ' player's initials
    lastlevel AS INTEGER     ' last level completed
    score AS LONG            ' final score
    cascadescore AS INTEGER  ' how many cascades
    blastscore AS INTEGER    ' how many block blasts
    speedscore AS INTEGER    ' how fast player usually completes levels
    wins AS INTEGER          ' for 2 player
END TYPE

TYPE chartype
    char AS INTEGER
    clr AS INTEGER
END TYPE

TYPE cursortype
    boundx AS INTEGER
    boundy AS INTEGER
    boundwidth AS INTEGER
    boundheight AS INTEGER
    x AS INTEGER
    y AS INTEGER
    info AS blocktype
END TYPE

'GAME PROCEDURES
DECLARE SUB initgame ()
DECLARE SUB shutdown ()
DECLARE SUB initblocktemplates ()
DECLARE SUB displayscreen ()
DECLARE SUB playcontinuousgame ()
DECLARE SUB playpuzzlegame ()
DECLARE SUB playversusgame ()
DECLARE SUB savepuzzlegame (filename AS STRING)
DECLARE SUB loadpuzzlegame (filename AS STRING)
DECLARE SUB savepuzzles ()
DECLARE SUB copyrightscreen ()
DECLARE SUB titlescreen ()
DECLARE SUB titlemenu ()
DECLARE SUB levelselect ()
DECLARE SUB displaybar (x AS INTEGER, y AS INTEGER, length AS INTEGER, max AS INTEGER, current AS INTEGER)
DECLARE SUB helpmenu ()
DECLARE SUB displaynormalbanner (x AS INTEGER, y AS INTEGER)
DECLARE SUB displaypuzzlebanner (x AS INTEGER, y AS INTEGER)
DECLARE SUB gamecompleted ()

'BOARD PROCEDURES
DECLARE SUB initboard ()
DECLARE SUB loadboard ()
DECLARE SUB displayboard ()
DECLARE SUB displaypuzzleboard ()
DECLARE SUB blastblocks ()
DECLARE SUB blastfixedblocks ()
DECLARE SUB blastcolor ()
DECLARE SUB blastall ()
DECLARE SUB killall ()
DECLARE SUB dropblocks ()
DECLARE SUB pauseboard (message$)
DECLARE SUB addtogroup (x AS INTEGER, y AS INTEGER)
DECLARE FUNCTION testformatches% ()

'BLOCK PROCEDURES
DECLARE SUB initblock (block() AS blocktype, b AS INTEGER)
DECLARE SUB loadblock (block() AS blocktype, p AS INTEGER)
DECLARE SUB getnewblock ()
DECLARE SUB displayblock (block() AS blocktype, x AS INTEGER, y AS INTEGER, solid AS INTEGER)
DECLARE SUB displayactiveblock ()
DECLARE SUB eraseblock ()
DECLARE SUB moveblock (xd AS INTEGER, yd AS INTEGER)
DECLARE SUB dropcurrentblock ()
DECLARE SUB addtoboard ()
DECLARE SUB rotateright ()
DECLARE SUB rotateleft ()
DECLARE SUB centerblock ()
DECLARE SUB separateblockgroups ()
DECLARE SUB getnextgroup ()
DECLARE FUNCTION canmove% (x AS INTEGER, y AS INTEGER)
DECLARE FUNCTION candrop% ()
DECLARE FUNCTION outofbounds% (x AS INTEGER, y AS INTEGER) 'not used
DECLARE FUNCTION newestgroup% (steps AS INTEGER)
DECLARE FUNCTION getnextpiece% () ' for puzzle mode

'PLAYER PROCEDURES
DECLARE SUB initplayer ()
DECLARE SUB displayscore ()

'HIGHSCORE PROCEDURES
DECLARE SUB inithighscores ()
DECLARE SUB displayhighscoretable ()
DECLARE SUB addhighscore (position AS INTEGER)
DECLARE SUB savehighscores ()
DECLARE SUB loadhighscores ()

'SCREEN PROCEDURES
DECLARE SUB displaytitle (x AS INTEGER, y AS INTEGER, showbox AS INTEGER)
DECLARE SUB initplayerscore ()
DECLARE SUB displayhighscores ()
DECLARE SUB displaynextblock ()
DECLARE SUB displaylevel ()

'MISC PROCEDURES
DECLARE SUB clearscreen (char AS INTEGER, fc AS INTEGER, bc AS INTEGER)
DECLARE SUB putchar (x AS INTEGER, y AS INTEGER, char AS INTEGER, fc AS INTEGER, bc AS INTEGER)
DECLARE SUB putstring (x AS INTEGER, y AS INTEGER, txt AS STRING, fc AS INTEGER, bc AS INTEGER, center AS INTEGER)
DECLARE SUB putstringf (x AS INTEGER, y AS INTEGER, txt AS STRING, fc AS STRING, bc AS STRING, center AS INTEGER)
DECLARE SUB box (doublewide AS INTEGER, x2 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, fc AS INTEGER, bc AS INTEGER, filled AS INTEGER, shadowed AS INTEGER)
DECLARE SUB voidbox (x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER, char AS INTEGER, fc AS INTEGER, bc AS INTEGER)
DECLARE SUB shadow (x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER)
DECLARE SUB fadeout ()
DECLARE SUB fadetowhite ()
DECLARE SUB displaydatawindow (x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER, title AS STRING, clr AS INTEGER)
DECLARE SUB displaybignumber (x AS INTEGER, y AS INTEGER, number AS INTEGER, clr AS INTEGER)
DECLARE SUB storescreen ()
DECLARE SUB restorescreen ()
DECLARE SUB flashscreen ()
DECLARE SUB rest (delay AS INTEGER, cancel AS INTEGER)
DECLARE FUNCTION rand% (value AS INTEGER)
DECLARE FUNCTION power& (radix AS LONG, exponent AS INTEGER)
DECLARE FUNCTION formatl$ (value AS LONG, digits AS INTEGER)
DECLARE FUNCTION formati$ (value AS INTEGER, digits AS INTEGER)
DECLARE FUNCTION getinput$ (x AS INTEGER, y AS INTEGER, maxlength AS INTEGER)
DECLARE FUNCTION highscoreposition% ()
DECLARE FUNCTION tryagain% ()
DECLARE FUNCTION getdirectory$ ()

'EDITOR PROCEDURES
DECLARE SUB runeditor ()
DECLARE SUB initeditor ()
DECLARE SUB displayeditor ()
DECLARE SUB displayboard ()
DECLARE SUB selectpieces ()
DECLARE SUB displaypieces ()
DECLARE SUB setgroups ()
DECLARE SUB savepuzzle (filename AS STRING)
DECLARE SUB loadpuzzle (filename AS STRING)
DECLARE SUB clearpuzzle ()

DECLARE SUB initcursor ()
DECLARE SUB displaycursor ()
DECLARE SUB erasecursor ()
DECLARE SUB movecursor (xdir AS INTEGER, ydir AS INTEGER)

DECLARE SUB displayselectcursor ()
DECLARE SUB initpiece (p AS INTEGER, b AS INTEGER)
DECLARE SUB changecolor (p AS INTEGER, b AS INTEGER)

'MENU PROCEDURES
DECLARE SUB displaymenu (menu() AS STRING, title AS STRING, x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER, clr AS INTEGER)
DECLARE SUB displaycurrent (x AS INTEGER, y AS INTEGER, wide AS INTEGER, old AS INTEGER, new AS INTEGER)
DECLARE FUNCTION getselection% (menu() AS STRING, x AS INTEGER, y AS INTEGER, wide AS INTEGER)

DEF SEG = &HB800
'ON ERROR GOTO errorhandler


DIM SHARED levelcolor(maxlevels) AS INTEGER
DIM SHARED blockcolor(4) AS INTEGER
DIM SHARED titlecolors(10) AS STRING
DIM SHARED fadecolors(3, 15) AS INTEGER
DIM SHARED blocktemplates(maxblocks, 4, 4) AS blocktype
DIM SHARED blockgroups(1000, 4) AS blockinfotype
DIM SHARED highscores(3, maxhighscores) AS highscoretype
DIM SHARED store(screenwidth, screenheight) AS chartype
DIM SHARED puzzlelevels(maxpuzzles) AS STRING
DIM SHARED randomcount AS INTEGER

DIM SHARED board(boardwidth, boardheight) AS blocktype
DIM SHARED currentblock(4, 4) AS blocktype
DIM SHARED nextblock(4, 4) AS blocktype
DIM SHARED puzzlepieces(8, 4, 4) AS blocktype
DIM SHARED nextgroup AS INTEGER
DIM SHARED currentlevel AS INTEGER, currentpuzzle AS INTEGER
DIM SHARED lastpuzzle AS INTEGER
DIM SHARED currentspeed AS LONG
DIM SHARED redblast AS INTEGER, yellowblast AS INTEGER, blueblast AS INTEGER
DIM SHARED redout AS INTEGER, yellowout AS INTEGER, blueout AS INTEGER
DIM SHARED player1 AS playertype
DIM SHARED player1score AS highscoretype
DIM SHARED highlightscore AS INTEGER

DIM SHARED score AS LONG
DIM SHARED bonus AS INTEGER
DIM SHARED numsets AS INTEGER
DIM SHARED numcascades AS INTEGER

DIM SHARED gamemode AS INTEGER
DIM SHARED background AS INTEGER, backcolor AS INTEGER
DIM SHARED quit AS INTEGER, gameover AS INTEGER
DIM SHARED erroroccurred AS INTEGER
DIM SHARED fadelevel AS INTEGER

DIM SHARED mainmenu(4) AS STRING

DIM endgame AS INTEGER

'EDITOR VARIABLES

DIM SHARED groupmode AS INTEGER
DIM SHARED cursor AS cursortype
DIM SHARED selectcursor AS cursortype
DIM SHARED currentpiece AS INTEGER
DIM SHARED types(8) AS INTEGER

'MENU VARIABLES
DIM SHARED selection AS INTEGER

erroroccurred = false

WIDTH 80, 25
RANDOMIZE TIMER
CLS
initgame
gamemode = normalgame
currentlevel = 1

endgame = false

FOR t& = 1 TO 100000
    keyinput$ = INKEY$
    IF UCASE$(keyinput$) = "R" THEN
        runeditor
        EXIT FOR
    ELSEIF keyinput$ <> "" THEN
        EXIT FOR
    END IF
NEXT

rest 500, false
copyrightscreen
titlescreen

WHILE NOT endgame
    titlemenu

    SELECT CASE selection
        CASE normalgame
            gamemode = normalgame
            levelselect
            IF NOT quit THEN
                playcontinuousgame
                storescreen
                addhighscore highscoreposition
                player1score.score = 0
                restorescreen
                rest 100, false
                fadeout
                displayhighscoretable
            ELSE
                quit = false
            END IF
        CASE puzzlegame
            gamemode = puzzlegame
            levelselect
            IF NOT quit THEN playpuzzlegame ELSE quit = false
        CASE highscoretable
            displayhighscoretable
        CASE quitgame
            fadeout
            endgame = true
        CASE ELSE
            fadeout
            endgame = true
    END SELECT
WEND

FOR t& = 1 TO 100000
    keyinput$ = INKEY$
    IF UCASE$(keyinput$) = "R" THEN
        runeditor
        EXIT FOR
    ELSEIF keyinput$ <> "" THEN
        EXIT FOR
    END IF
NEXT

shutdown
SYSTEM

errorhandler:
    erroroccurred = true
RESUME NEXT

SUB addhighscore (position AS INTEGER)
    DIM initials AS STRING
    DIM index AS INTEGER

    WHILE INKEY$ <> "": WEND

    IF position = -1 THEN EXIT SUB

    displaydatawindow screenwidth \ 2 - 10, screenheight \ 2 - 3, 20, 2, "NEW HIGHSCORE!", 3
    putstring screenwidth \ 2 + 2, screenheight \ 2, "ENTER INITIALS:", 7, 0, true
    initials = getinput(screenwidth \ 2, screenheight \ 2 + 1, 3)

    FOR index = maxhighscores - 1 TO position STEP -1
        highscores(normalgame, index + 1) = highscores(normalgame, index)
    NEXT

    highscores(normalgame, position).initials = initials
    highscores(normalgame, position).score = player1score.score
    highscores(normalgame, position).lastlevel = player1score.lastlevel
    highlightscore = position

    WHILE INKEY$ <> "": WEND
END SUB

SUB addtoboard
    STATIC blockcount AS INTEGER
    DIM x AS INTEGER, y AS INTEGER
    DIM px AS INTEGER, py AS INTEGER

    px = player1.x
    py = player1.y

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            IF py + y - 1 >= 1 THEN
                IF currentblock(x, y).clr <> 0 AND currentblock(x, y).landed = true THEN
                    IF blockgroups(currentblock(x, y).group, 1).x = -1 THEN
                        blockgroups(currentblock(x, y).group, 1).x = px + x - 1
                        blockgroups(currentblock(x, y).group, 1).y = py + y - 1
                    ELSEIF blockgroups(currentblock(x, y).group, 2).x = -1 THEN
                        blockgroups(currentblock(x, y).group, 2).x = px + x - 1
                        blockgroups(currentblock(x, y).group, 2).y = py + y - 1
                    ELSEIF blockgroups(currentblock(x, y).group, 3).x = -1 THEN
                        blockgroups(currentblock(x, y).group, 3).x = px + x - 1
                        blockgroups(currentblock(x, y).group, 3).y = py + y - 1
                    ELSE
                        blockgroups(currentblock(x, y).group, 4).x = px + x - 1
                        blockgroups(currentblock(x, y).group, 4).y = py + y - 1
                    END IF

                    blockcount = blockcount + 1
                    board(px + x - 1, py + y - 1) = currentblock(x, y)
                    currentblock(x, y).clr = 0
                END IF
            ELSEIF currentblock(x, y).clr <> 0 THEN
                killall
                gameover = true
                EXIT SUB
            END IF
        NEXT
    NEXT

    IF blockcount >= 4 THEN
        player1.blocklanded = true
        blockcount = 0
    ELSEIF blockcount = 3 THEN
        player1.singled = true
    END IF
END SUB

SUB addtogroup (x AS INTEGER, y AS INTEGER)
    DIM g AS INTEGER

    g = board(x, y).group

    IF blockgroups(g, 1).x = -1 THEN
        blockgroups(g, 1).x = x
        blockgroups(g, 1).y = y
    ELSEIF blockgroups(g, 2).x = -1 THEN
        blockgroups(g, 2).x = x
        blockgroups(g, 2).y = y
    ELSEIF blockgroups(g, 3).x = -1 THEN
        blockgroups(g, 3).x = x
        blockgroups(g, 3).y = y
    ELSE
        blockgroups(g, 4).x = x
        blockgroups(g, 4).y = y
    END IF
END SUB

SUB blastall
    DIM x AS INTEGER, y AS INTEGER
    DIM blast AS INTEGER

    blast = false
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr = red THEN
                board(x, y).flag = true
                blast = true
            END IF
        NEXT
    NEXT

    IF blast THEN
        blastblocks
        rest 300, false
    END IF
  
    blast = false
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr = yellow THEN
                board(x, y).flag = true
                blast = true
            END IF
        NEXT
    NEXT

    IF blast THEN
        blastblocks
        rest 300, false
    END IF

    blast = false
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr = blue THEN
                board(x, y).flag = true
                blast = true
            END IF
        NEXT
    NEXT

    IF blast THEN
        blastblocks
        rest 300, false
    END IF
END SUB

SUB blastblocks
    DIM x AS INTEGER, y AS INTEGER
    DIM g AS INTEGER, b AS INTEGER
    DIM good AS INTEGER

    good = false

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).flag = true THEN
                board(x, y).char = 178
                good = true
            END IF
        NEXT
    NEXT

    IF good THEN rest 100, false

    displayboard
    rest 50, false
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).flag = true THEN
                board(x, y).char = 177
            END IF
        NEXT
    NEXT
    displayboard
    rest 50, false
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).flag = true THEN
                board(x, y).char = 176
            END IF
        NEXT
    NEXT
    displayboard
    rest 50, false
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).flag = true THEN
                board(x, y).char = 32
            END IF
        NEXT
    NEXT

    displayboard
   
    FOR g = 1 TO 1000
        FOR b = 1 TO 4
            x = blockgroups(g, b).x
          
            IF x <> -1 THEN
                y = blockgroups(g, b).y
               
                IF board(x, y).flag = true THEN
                    blockgroups(g, b).x = -1
                    board(x, y).clr = 0
                END IF
            END IF
        NEXT
    NEXT
   
    separateblockgroups

    ' erase fixed blocks
    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr <> 0 AND board(x, y).flag = true AND board(x, y).fixed = true THEN
                IF board(x, y).flashing = true THEN
                    IF board(x, y).clr = red THEN
                        redout = true
                    ELSEIF board(x, y).clr = yellow THEN
                        yellowout = true
                    ELSEIF board(x, y).clr = blue THEN
                        blueout = true
                    END IF
                END IF

                board(x, y).clr = 0
            END IF
        NEXT
    NEXT
    displayboard
END SUB

SUB blastcolor
    DIM x AS INTEGER, y AS INTEGER
    DIM blast AS INTEGER

    blast = false

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF ((board(x, y).clr = red AND redblast) OR (board(x, y).clr = yellow AND yellowblast) OR (board(x, y).clr = blue AND blueblast)) AND board(x, y).fixed = false THEN
                blast = true
                board(x, y).flag = true
            END IF
        NEXT
    NEXT

    IF blast THEN
        flashscreen
        rest 300, false
        blastblocks
        rest 100, false
    END IF
END SUB

SUB blastfixedblocks
    DIM x AS INTEGER, y AS INTEGER
    DIM clr AS INTEGER
    DIM blast AS INTEGER

    blast = false

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            board(x, y).flag = false
            clr = board(x, y).clr
            IF clr <> 0 AND board(x, y).fixed = true AND NOT board(x, y).flashing THEN
                IF redout AND clr = red THEN
                    blast = true
                    board(x, y).flag = true
                ELSEIF yellowout AND clr = yellow THEN
                    blast = true
                    board(x, y).flag = true
                ELSEIF blueout AND clr = blue THEN
                    blast = true
                    board(x, y).flag = true
                END IF
            END IF
        NEXT
    NEXT

    player1score.score = player1score.score + score
    score = 0

    IF blast THEN
        rest 300, false
        FOR y = 1 TO boardheight
            FOR x = 1 TO boardwidth
                IF board(x, y).flag = true THEN
                    board(x, y).char = 178
                END IF
            NEXT
        NEXT
        displayboard
        rest 50, false
        FOR y = 1 TO boardheight
            FOR x = 1 TO boardwidth
                IF board(x, y).flag = true THEN
                    board(x, y).char = 177
                END IF
            NEXT
        NEXT
        displayboard
        rest 50, false
        FOR y = 1 TO boardheight
            FOR x = 1 TO boardwidth
                IF board(x, y).flag = true THEN
                    board(x, y).char = 176
                END IF
            NEXT
        NEXT
        displayboard
        rest 50, false
        FOR y = 1 TO boardheight
            FOR x = 1 TO boardwidth
                IF board(x, y).flag = true THEN
                    board(x, y).char = 32
                END IF
            NEXT
        NEXT

        displayboard

        ' erase fixed blocks
        FOR y = 1 TO boardheight
            FOR x = 1 TO boardwidth
                IF board(x, y).clr <> 0 AND board(x, y).flag = true AND board(x, y).fixed = true THEN
                    IF board(x, y).flashing = true THEN
                        IF board(x, y).clr = red THEN
                            player1score.score = player1score.score + colorclearbonus
                            redout = true
                        ELSEIF board(x, y).clr = yellow THEN
                            player1score.score = player1score.score + colorclearbonus
                            yellowout = true
                        ELSEIF board(x, y).clr = blue THEN
                            player1score.score = player1score.score + colorclearbonus
                            blueout = true
                        END IF
                    END IF

                    board(x, y).clr = 0
                END IF
            NEXT
        NEXT
        displayboard
        IF gamemode = normalgame THEN displayscore
    END IF
END SUB

SUB box (doublewide AS INTEGER, x1 AS INTEGER, y1 AS INTEGER, wide AS INTEGER, high AS INTEGER, fc AS INTEGER, bc AS INTEGER, filled AS INTEGER, shadowed AS INTEGER)
    DIM tl AS INTEGER, tr AS INTEGER, bl AS INTEGER, br AS INTEGER
    DIM h AS INTEGER, v AS INTEGER
    DIM x2 AS INTEGER, y2 AS INTEGER
    DIM x AS INTEGER, y AS INTEGER

    x2 = x1 + wide - 1
    y2 = y1 + high - 1

    IF doublewide = false THEN
        tl = 218
        tr = 191
        bl = 192
        br = 217
        v = 179
        h = 196
    ELSE
        tl = 201
        tr = 187
        bl = 200
        br = 188
        v = 186
        h = 205
    END IF

    IF x1 >= 1 AND y1 >= 1 AND x1 <= screenwidth AND y1 <= screenheight THEN putchar x1, y1, tl, fc, bc
    IF x1 >= 1 AND y2 >= 1 AND x1 <= screenwidth AND y2 <= screenheight THEN putchar x1, y2, bl, fc, bc
    IF x2 >= 1 AND y1 >= 1 AND x2 <= screenwidth AND y1 <= screenheight THEN putchar x2, y1, tr, fc, bc
    IF x2 >= 1 AND y2 >= 1 AND x2 <= screenwidth AND y2 <= screenheight THEN putchar x2, y2, br, fc, bc
   
    FOR x = x1 + 1 TO x2 - 1
        y = y1
        IF x >= 1 AND y >= 1 AND x <= screenwidth AND y <= screenheight THEN putchar x, y, h, fc, bc
        y = y2
        IF x >= 1 AND y >= 1 AND x <= screenwidth AND y <= screenheight THEN putchar x, y, h, fc, bc
    NEXT

    FOR y = y1 + 1 TO y2 - 1
        x = x1
        IF x >= 1 AND y >= 1 AND x <= screenwidth AND y <= screenheight THEN putchar x, y, v, fc, bc
        x = x2
        IF x >= 1 AND y >= 1 AND x <= screenwidth AND y <= screenheight THEN putchar x, y, v, fc, bc
    NEXT

    IF filled THEN
        voidbox x1 + 1, y1 + 1, wide - 2, high - 2, 32, 0, 0
    END IF

    IF shadowed THEN
        shadow x1 + wide, y1 + 1, 1, high
        shadow x1 + 1, y1 + high, wide, 1
    END IF
END SUB

FUNCTION candrop%
    DIM x AS INTEGER, y AS INTEGER
    DIM xi AS INTEGER, yi AS INTEGER
    DIM g AS INTEGER, g1 AS INTEGER, g2 AS INTEGER, g3 AS INTEGER

    x = player1.x
    y = player1.y + 1

    candrop% = true
    
    g1 = -1
    g2 = -1
    g3 = -1

    FOR yi = 1 TO 4
        FOR xi = 1 TO 4
            IF y + yi - 1 >= 1 THEN
                IF currentblock(xi, yi).clr <> 0 THEN
                    IF y + yi - 1 <= boardheight THEN
                        IF board(x + xi - 1, y + yi - 1).clr <> 0 THEN
                            currentblock(xi, yi).landed = true
                            IF g1 = -1 THEN
                                g1 = currentblock(xi, yi).group
                            ELSEIF g2 = -1 THEN
                                g2 = currentblock(xi, yi).group
                            ELSE
                                g3 = currentblock(xi, yi).group
                            END IF
                            candrop% = false
                        END IF
                    ELSE
                        currentblock(xi, yi).landed = true
                        IF g1 = -1 THEN
                            g1 = currentblock(xi, yi).group
                        ELSEIF g2 = -1 THEN
                            g2 = currentblock(xi, yi).group
                        ELSE
                            g3 = currentblock(xi, yi).group
                        END IF
                        candrop% = false
                    END IF
                END IF
            END IF
        NEXT
    NEXT

    FOR yi = 1 TO 4
        FOR xi = 1 TO 4
            g = currentblock(xi, yi).group
            IF currentblock(xi, yi).landed = false AND (g = g1 OR g = g2 OR g = g3) THEN
                currentblock(xi, yi).landed = true
            END IF
        NEXT
    NEXT
END FUNCTION

FUNCTION canmove% (x AS INTEGER, y AS INTEGER)
    DIM xi AS INTEGER, yi AS INTEGER

    canmove% = true

    FOR yi = 1 TO 4
        FOR xi = 1 TO 4
            IF currentblock(xi, yi).clr <> 0 THEN
                IF x + xi - 1 >= 1 AND x + xi - 1 <= boardwidth AND y + yi - 1 <= boardheight THEN
                    IF y + yi - 1 >= 1 THEN
                        IF board(x + xi - 1, y + yi - 1).clr <> 0 THEN
                            canmove% = false
                        END IF
                    END IF
                ELSE
                    canmove% = false
                END IF
            END IF
        NEXT
    NEXT
END FUNCTION

SUB centerblock
    DIM x AS INTEGER, y AS INTEGER
    DIM tx AS SINGLE, ty AS SINGLE
    DIM tempblock(4, 4) AS blocktype

    FOR x = 1 TO 4
        FOR y = 1 TO 4
            IF currentblock(x, y).clr <> 0 THEN EXIT FOR
        NEXT
        IF y <= 4 THEN EXIT FOR
    NEXT
      
    x1 = x - 1

    FOR x = 4 TO 1 STEP -1
        FOR y = 1 TO 4
            IF currentblock(x, y).clr <> 0 THEN EXIT FOR
        NEXT
        IF y <= 4 THEN EXIT FOR
    NEXT
     
    x2 = x + 1

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            IF currentblock(x, y).clr <> 0 THEN EXIT FOR
        NEXT
        IF x <= 4 THEN EXIT FOR
    NEXT
     
    y1 = y - 1

    FOR y = 4 TO 1 STEP -1
        FOR x = 1 TO 4
            IF currentblock(x, y).clr <> 0 THEN EXIT FOR
        NEXT
        IF x <= 4 THEN EXIT FOR
    NEXT
    
    y2 = y + 1

    tx = (x2 + x1) / 2
    ty = (y2 + y1) / 2
 
    IF ABS(tx - 2.5) >= 1 THEN tx = SGN(2.5 - tx) ELSE tx = 0
    IF ABS(ty - 2.5) >= 1 THEN ty = SGN(2.5 - ty) ELSE ty = 0

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            IF x - tx >= 1 AND x - tx <= 4 AND y - ty >= 1 AND y - ty <= 4 THEN
                tempblock(x, y) = currentblock(x - tx, y - ty)
            ELSE
                tempblock(x, y).clr = 0
            END IF
        NEXT
    NEXT

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            currentblock(x, y) = tempblock(x, y)
        NEXT
    NEXT

    player1.x = player1.x - tx
    player1.y = player1.y - ty

    displayactiveblock
END SUB

SUB changecolor (p AS INTEGER, b AS INTEGER)
    DIM x AS INTEGER, y AS INTEGER
    DIM bi AS INTEGER

    bi = 0

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            IF puzzlepieces(p, x, y).clr <> 0 THEN
                bi = bi + 1
                IF bi = b THEN
                    puzzlepieces(p, x, y).clr = puzzlepieces(p, x, y).clr + 1
                    IF puzzlepieces(p, x, y).clr > blue THEN
                        puzzlepieces(p, x, y).clr = red
                    END IF
                END IF
            END IF
        NEXT
    NEXT
END SUB

SUB clearpuzzle
    DIM p AS INTEGER, x AS INTEGER, y AS INTEGER

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            board(x, y).clr = 0
            board(x, y).char = 0
            board(x, y).group = 0
            board(x, y).fixed = false
            board(x, y).flashing = false
        NEXT
    NEXT

    FOR p = 1 TO 8
        FOR y = 1 TO 4
            FOR x = 1 TO 4
                puzzlepieces(p, x, y).clr = 0
                puzzlepieces(p, x, y).char = 219
                puzzlepieces(p, x, y).group = 0
                puzzlepieces(p, x, y).fixed = false
                puzzlepieces(p, x, y).flashing = false
            NEXT
        NEXT

        types(p) = noblock
    NEXT
END SUB

SUB clearscreen (char AS INTEGER, fc AS INTEGER, bc AS INTEGER)
    DIM o AS INTEGER

    FOR o = 0 TO 7999 STEP 2
        POKE o, char
        POKE o + 1, bc * 16 + fc
    NEXT
END SUB

SUB copyrightscreen
    putstring centerx, centery - 1, "TETRIS 2 IS A REGISTERED", 8, 0, true
    putstring centerx, centery, "TRADEMARK OF NINTENDO", 8, 0, true
    putstring centerx, centery + 1, " ALL RIGHTS RESERVED", 8, 0, true
    rest 100, false
    putstring centerx, centery - 1, "TETRIS 2 IS A REGISTERED", 7, 0, true
    putstring centerx, centery, "TRADEMARK OF NINTENDO", 7, 0, true
    putstring centerx, centery + 1, " ALL RIGHTS RESERVED", 7, 0, true
    rest 100, false
    putstring centerx, centery - 1, "TETRIS 2 IS A REGISTERED", 15, 0, true
    putstring centerx, centery, "TRADEMARK OF NINTENDO", 15, 0, true
    putstring centerx, centery + 1, " ALL RIGHTS RESERVED", 15, 0, true
    rest 2000, true
    fadeout
END SUB

SUB displayactiveblock
    DIM x AS INTEGER, y AS INTEGER
    DIM xi AS INTEGER, yi AS INTEGER
    DIM c AS INTEGER, ch AS INTEGER

    x = player1.x
    y = player1.y

    FOR yi = 1 TO 4
        FOR xi = 1 TO 4
            IF x + xi - 1 >= 1 AND x + xi - 1 <= boardwidth AND y + yi - 1 >= 1 AND y + yi - 1 <= boardheight THEN
                IF currentblock(xi, yi).clr <> 0 THEN
                    putchar boardx + (x + xi - 2) * 2, boardy + y + yi - 2, currentblock(xi, yi).char, blockcolor(currentblock(xi, yi).clr), 0
                    putchar boardx + (x + xi - 2) * 2 + 1, boardy + y + yi - 2, currentblock(xi, yi).char, blockcolor(currentblock(xi, yi).clr), 0
                ELSE
                    c = board(x + xi - 1, y + yi - 1).clr
                    IF c <> 0 THEN
                        IF board(x + xi - 1, y + yi - 1).fixed = false THEN
                            putchar boardx + (x + xi - 2) * 2, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c) - 8, 0
                            putchar boardx + (x + xi - 2) * 2 + 1, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c) - 8, 0
                        ELSE
                            putchar boardx + (x + xi - 2) * 2, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c), blockcolor(c) - 8 * (NOT board(x + xi - 1, y + yi - 1).flashing)
                            putchar boardx + (x + xi - 2) * 2 + 1, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c), blockcolor(c) - 8 * (NOT board(x + xi - 1, y + yi - 1).flashing)
                        END IF
                    ELSE
                        putchar boardx + (x + xi - 2) * 2, boardy + (y + yi - 2), 32, 0, 0
                        putchar boardx + (x + xi - 2) * 2 + 1, boardy + (y + yi - 2), 32, 0, 0
                    END IF
                END IF
            END IF
        NEXT
    NEXT
END SUB

SUB displaybar (x AS INTEGER, y AS INTEGER, length AS INTEGER, max AS INTEGER, current AS INTEGER)
    DIM index AS INTEGER
    DIM realcurrent AS INTEGER

    FOR index = x TO x + length - 1
        putchar index, y, 205, 7, 0
    NEXT
    putchar x - 1, y, 32, 0, 0
    putchar x + length, y, 32, 0, 0

    IF current > 1 THEN
        realcurrent = current * 2 * length \ max
    ELSE
        realcurrent = 0
    END IF

    IF current <> -1 THEN
        IF realcurrent MOD 2 = 1 THEN
            putstring x + realcurrent \ 2, y, "", 14, 0, false
        ELSE
            putstring x + realcurrent \ 2 - 1, y, "", 14, 0, false
        END IF
    END IF
END SUB

SUB displaybignumber (x AS INTEGER, y AS INTEGER, number AS INTEGER, clr AS INTEGER)
    DIM index AS INTEGER
    DIM numstring AS STRING

    numstring = LTRIM$(STR$(number))
    IF LEN(numstring) = 1 THEN numstring = "0" + numstring

    FOR index = 0 TO LEN(numstring) - 1
        SELECT CASE VAL(MID$(numstring, index + 1, 1))
            CASE 0
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, " ", clr, 0, false
                putstring x + index * 5, y + 2, " ", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 1
                putstring x + index * 5, y + 0, "  ", clr, 0, false
                putstring x + index * 5, y + 1, " ", clr, 0, false
                putstring x + index * 5, y + 2, "   ", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 2
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, "  ", clr, 0, false
                putstring x + index * 5, y + 2, " ", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 3
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, " ", clr, 0, false
                putstring x + index * 5, y + 2, " ", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 4
                putstring x + index * 5, y + 0, " ", clr, 0, false
                putstring x + index * 5, y + 1, "", clr, 0, false
                putstring x + index * 5, y + 2, "", clr, 0, false
                putstring x + index * 5, y + 3, "  ", clr, 0, false
            CASE 5
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, " ", clr, 0, false
                putstring x + index * 5, y + 2, " ", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 6
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, " ", clr, 0, false
                putstring x + index * 5, y + 2, "", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 7
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, "  ", clr, 0, false
                putstring x + index * 5, y + 2, "  ", clr, 0, false
                putstring x + index * 5, y + 3, "   ", clr, 0, false
            CASE 8
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, "", clr, 0, false
                putstring x + index * 5, y + 2, " ", clr, 0, false
                putstring x + index * 5, y + 3, "", clr, 0, false
            CASE 9
                putstring x + index * 5, y + 0, "", clr, 0, false
                putstring x + index * 5, y + 1, " ", clr, 0, false
                putstring x + index * 5, y + 2, "", clr, 0, false
                putstring x + index * 5, y + 3, "  ", clr, 0, false
        END SELECT
    NEXT
END SUB

SUB displayblock (block() AS blocktype, x AS INTEGER, y AS INTEGER, solid AS INTEGER)
    DIM xi AS INTEGER, yi AS INTEGER
    DIM ch AS INTEGER

    FOR yi = 1 TO 4
        FOR xi = 1 TO 4
            IF block(xi, yi).clr > 0 OR solid = true THEN
                IF x + xi - 1 >= 1 AND x + xi - 1 <= screenwidth AND y + yi - 1 >= 2 AND y + yi - 1 <= screenheight THEN
                    putchar x + xi * 2 - 2, y + yi - 1, block(xi, yi).char, blockcolor(block(xi, yi).clr), 0
                    putchar x + xi * 2 - 1, y + yi - 1, block(xi, yi).char, blockcolor(block(xi, yi).clr), 0
                END IF
            END IF
        NEXT
    NEXT
END SUB

SUB displayboard
    DIM x AS INTEGER, y AS INTEGER, c AS INTEGER

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            c = board(x, y).clr

            IF c <> 0 THEN
                IF board(x, y).fixed = false THEN
                    putchar boardx + x * 2 - 2, boardy + y - 1, board(x, y).char, blockcolor(c) - 8, 0
                    putchar boardx + x * 2 - 1, boardy + y - 1, board(x, y).char, blockcolor(c) - 8, 0
                ELSE
                    putchar boardx + x * 2 - 2, boardy + y - 1, board(x, y).char, blockcolor(c), blockcolor(c) - 8 * (NOT board(x, y).flashing)
                    putchar boardx + x * 2 - 1, boardy + y - 1, board(x, y).char, blockcolor(c), blockcolor(c) - 8 * (NOT board(x, y).flashing)
                END IF
            ELSE
                putchar boardx + x * 2 - 2, boardy + y - 1, 32, 0, 0
                putchar boardx + x * 2 - 1, boardy + y - 1, 32, 0, 0
            END IF
        NEXT
    NEXT
END SUB

SUB displaycurrent (x AS INTEGER, y AS INTEGER, wide AS INTEGER, old AS INTEGER, new AS INTEGER)
    DIM index AS INTEGER

    FOR index = x TO x + wide - 1
        putchar index, old, SCREEN(old, index), 1, 0
    NEXT
    FOR index = x TO x + wide - 1
        putchar index, new, SCREEN(new, index), 12, 1
    NEXT
END SUB

SUB displaycursor
    IF NOT groupmode THEN
        IF cursor.info.clr <> 0 THEN
            IF cursor.info.fixed = false THEN
                putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 219, blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
                putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 219, blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
            ELSE
                putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 254, blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
                putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 254, blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
            END IF
        ELSE
            putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 219, 8, 8
            putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 219, 8, 8
        END IF
    ELSE
        IF cursor.info.fixed = false THEN
            putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, cursor.info.group, blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
            putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, cursor.info.group, blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
        ELSE
            putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, ASC("0"), blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
            putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, ASC("0"), blockcolor(cursor.info.clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
        END IF
    END IF
END SUB

SUB displaydatawindow (x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER, title AS STRING, clr AS INTEGER)
    box false, x, y, wide + 2, 3, clr + 8, clr, false, false
    voidbox x + 1, y + 1, wide, 1, 32, 0, clr
    putstring x + (wide + 2) / 2, y + 1, title, 15, clr, true
    box true, x, y + 2, wide + 2, high + 2, 15, clr, false, false
    voidbox x + 1, y + 3, wide, high, 32, 0, 0
    shadow x + wide + 2, y + 1, 1, high + 4
    shadow x + 1, y + high + 4, wide + 1, 1
END SUB

SUB displayeditor
    DIM p AS INTEGER

    'display background
    voidbox 1, 1, screenwidth, screenheight, 219, 8, 0

    'display board
    box true, boardx - 1, boardy - 1, boardwidth * 2 + 2, boardheight + 2, 15, 7, true, true

    'display 'TETRIS 2'
    box true, 2, 1, 26, 14, 7, 0, true, true
    displaytitle 3, 2, false
    putstring 3, 13, "EDITOR", 1, 0, false
    putstring 9, 13, "EDITOR", 2, 0, false
    putstring 15, 13, "EDITOR", 3, 0, false
    putstring 21, 13, "EDITOR", 4, 0, false

    'display 'next' boxes
    FOR p = 1 TO 8
        box p = currentpiece, 55 + 13 * -SGN(p > 4), 2 + 6 * ((p - 1) MOD 4), 10, 6, 7 + 1 * SGN(p = currentpiece), 0, true, false
    NEXT

    'display info
    displaydatawindow 2, 16, 24, 5, "INFO", 0
END SUB

SUB displayhighscores
    DIM p AS INTEGER
  
    FOR p = 1 TO 3
        putstring boardx + boardwidth * 2 + 5, boardy + 12 + p, highscores(normalgame, p).initials, 8, 0, false
        putstring boardx + boardwidth * 2 + 10, boardy + 12 + p, formatl(highscores(normalgame, p).score, 12), 5, 0, false
        putstring boardx + boardwidth * 2 + 24, boardy + 12 + p, formati(highscores(normalgame, p).lastlevel, 2), 4, 0, false
    NEXT
END SUB

SUB displayhighscoretable
    DIM index AS INTEGER, flash AS INTEGER

    fadelevel = 1
    clearscreen rand(256), rand(16) - 1, rand(8) - 1

    box true, 23, 1, screenwidth - 47, screenheight, 1, 0, false, false
    voidbox 24, 2, screenwidth - 49, screenheight - 2, 32, 0, 0
    shadow 22, 1, 1, screenheight
    shadow screenwidth - 24, 1, 1, screenheight

    putstring screenwidth \ 2, 2, "HIGH SCORES", 9, 0, true
   
    putstring 30, 3, "INI", 7, 0, false
    putstring 40, 3, "SCORE", 13, 0, false
    putstring 49, 3, "LVL", 12, 0, false

    FOR index = 1 TO maxhighscores
        IF highlightscore = index THEN flash = 8 ELSE flash = 0
        putstring 27, 4 + index, LTRIM$(STR$(index)) + ".", 14, 0, false
        putstring 30, 4 + index, highscores(normalgame, index).initials, 11, flash, false
        putstring 36, 4 + index, formatl(highscores(normalgame, index).score, 12), 5, flash, false
        putstring 50, 4 + index, formati(highscores(normalgame, index).lastlevel, 2), 4, flash, false
    NEXT

    SLEEP
    IF UCASE$(INKEY$) = "C" THEN
        inithighscores
        savehighscores
        displayhighscoretable
    END IF
    fadeout
END SUB

SUB displaylevel
    IF gamemode = normalgame THEN
        displaybignumber boardx + boardwidth * 2 + 17, 5, currentlevel, 12
    ELSEIF gamemode = puzzlegame THEN
        displaybignumber boardx + boardwidth * 2 + 17, 5, currentpuzzle, 12
    END IF
END SUB

SUB displaymenu (menu() AS STRING, title AS STRING, x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER, clr AS INTEGER)
    DIM index AS INTEGER
    DIM midx AS INTEGER

    midx = x + wide \ 2 + 1

    displaydatawindow x, y, wide, high, title, clr

    FOR index = 1 TO UBOUND(menu)
        putstring midx, y + 3 + index, menu(index), 1, 0, true
    NEXT
END SUB

SUB displaynextblock
    IF gamemode = normalgame THEN
        displayblock nextblock(), boardx + boardwidth * 2 + 4, 5, true
    ELSE
        IF currentpiece < 8 THEN
            IF currentpiece > 0 THEN
                IF types(currentpiece + 1) <> noblock THEN
                    displayblock nextblock(), boardx + boardwidth * 2 + 4, 5, true
                ELSE
                    voidbox boardx + boardwidth * 2 + 4, 5, 8, 4, 219, 0, 0
                END IF
            ELSE
                displayblock nextblock(), boardx + boardwidth * 2 + 4, 5, true
            END IF
        ELSE
            voidbox boardx + boardwidth * 2 + 4, 5, 8, 4, 219, 0, 0
        END IF
    END IF
END SUB

SUB displaynormalbanner (x AS INTEGER, y AS INTEGER)
    box false, x, y, 31, 7, 12, 4, false, true
    putstring x + 1, y + 1, "           ", 12, 4, false
    putstring x + 1, y + 2, "            ", 12, 4, false
    putstring x + 1, y + 3, "            ", 12, 4, false
    putstring x + 1, y + 4, "          ", 12, 4, false
    putstring x + 1, y + 5, "             ", 12, 4, false
END SUB

SUB displaypieces
    DIM tempblock(4, 4) AS blocktype
    DIM x AS INTEGER, y AS INTEGER, p AS INTEGER
    DIM t AS INTEGER

    FOR p = 1 TO 8
        FOR y = 1 TO 4
            FOR x = 1 TO 4
                tempblock(x, y) = puzzlepieces(p, x, y)
            NEXT
        NEXT
        t = p
        IF p >= 5 THEN t = t - 4
        t = t - 1
        displayblock tempblock(), 56 + (p \ 5) * 13, 3 + t * 6, true
    NEXT
END SUB

SUB displaypuzzlebanner (x AS INTEGER, y AS INTEGER)
    box false, x, y, 28, 7, 9, 1, false, true
    putstring x + 1, y + 1, "          ", 9, 1, false
    putstring x + 1, y + 2, "              ", 9, 1, false
    putstring x + 1, y + 3, "           ", 9, 1, false
    putstring x + 1, y + 4, "               ", 9, 1, false
    putstring x + 1, y + 5, "         ", 9, 1, false
END SUB

SUB displaypuzzleboard
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF NOT groupmode THEN
                IF board(x, y).clr <> 0 THEN
                    IF board(x, y).fixed = false THEN
                        putchar boardx + x * 2 - 2, boardy + y - 1, 219, blockcolor(board(x, y).clr) - 8, 0
                        putchar boardx + x * 2 - 1, boardy + y - 1, 219, blockcolor(board(x, y).clr) - 8, 0
                    ELSE
                        putchar boardx + x * 2 - 2, boardy + y - 1, 254, blockcolor(board(x, y).clr), blockcolor(board(x, y).clr) - 8 * (NOT board(x, y).flashing)
                        putchar boardx + x * 2 - 1, boardy + y - 1, 254, blockcolor(board(x, y).clr), blockcolor(board(x, y).clr) - 8 * (NOT board(x, y).flashing)
                    END IF
                ELSE
                    putchar boardx + x * 2 - 2, boardy + y - 1, 32, 0, 0
                    putchar boardx + x * 2 - 1, boardy + y - 1, 32, 0, 0
                END IF
            ELSE
                IF board(x, y).clr <> 0 THEN
                    IF board(x, y).fixed = false THEN
                        putchar boardx + x * 2 - 2, boardy + y - 1, board(x, y).group, blockcolor(board(x, y).clr) - 8, 0
                        putchar boardx + x * 2 - 1, boardy + y - 1, board(x, y).group, blockcolor(board(x, y).clr) - 8, 0
                    ELSE
                        putchar boardx + x * 2 - 2, boardy + y - 1, ASC("0"), blockcolor(board(x, y).clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
                        putchar boardx + x * 2 - 1, boardy + y - 1, ASC("0"), blockcolor(board(x, y).clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
                    END IF
                ELSE
                    putchar boardx + x * 2 - 2, boardy + y - 1, 32, 0, 0
                    putchar boardx + x * 2 - 1, boardy + y - 1, 32, 0, 0
                END IF
            END IF
        NEXT
    NEXT
    displaycursor
END SUB

SUB displayscore
    putstring boardx + boardwidth * 2 + 16, boardy + 21, formatl(player1score.score, 12), 14, 0, true
END SUB

SUB displayscreen
    DIM bc AS INTEGER

    'display background color
    voidbox 1, 1, screenwidth, screenheight, background, currentlevel MOD 16, backcolor
   
    'display board
    box true, boardx - 1, boardy - 1, boardwidth * 2 + 2, boardheight + 2, 15, 7, true, false
    'display board shadow
    shadow boardx + boardwidth * 2 + 1, 2, 1, screenheight - 1

    'display level box
    displaydatawindow boardx + boardwidth * 2 + 15, 2, 11, 4, "LEVEL", 4

    'display next box
    displaydatawindow boardx + boardwidth * 2 + 3, 2, 8, 4, "NEXT", 5
     
    IF gamemode = normalgame THEN
        'display highscore box
        displaydatawindow boardx + boardwidth * 2 + 3, boardy + 9, 23, 4, "HIGH SCORES", 1
        'display highscore key
        putstring boardx + boardwidth * 2 + 5, boardy + 12, "INI", 7, 0, false
        putstring boardx + boardwidth * 2 + 13, boardy + 12, "SCORE", 13, 0, false
        putstring boardx + boardwidth * 2 + 23, boardy + 12, "LVL", 12, 0, false

        'display current score
        displaydatawindow boardx + boardwidth * 2 + 6, boardy + 18, 18, 1, "CURRENT SCORE", 2
    END IF

    'display 'TETRIS 2'
    box true, 2, 1, 26, 12, 7, 0, false, false
    displaytitle 3, 2, false
    'display title shadow
    shadow 28, 2, 1, 12
    shadow 3, 13, 25, 1
END SUB

SUB displayselectcursor
    DIM x AS INTEGER, y AS INTEGER

    putchar selectcursor.boundx + selectcursor.x - 1, selectcursor.boundy + selectcursor.y - 1, 219, blockcolor(puzzlepieces(currentpiece, selectcursor.x, selectcursor.y).clr), 0
END SUB

SUB displaytitle (x AS INTEGER, y AS INTEGER, showbox AS INTEGER)
    DIM xi AS INTEGER, yi AS INTEGER

    putstring x, y + 0, "      ", 8, 0, false
    putstring x, y + 1, "                 ", 8, 0, false
    putstring x, y + 2, "             ", 8, 0, false
    putstring x, y + 3, "                 ", 8, 0, false
    putstring x, y + 4, "           ", 8, 0, false
    putstring x, y + 5, "                ", 8, 0, false
    putstring x, y + 6, "                ", 8, 0, false
    putstring x, y + 7, "                ", 8, 0, false
    putstring x, y + 8, "          ", 8, 0, false
    putstring x, y + 9, "          ", 8, 0, false

    FOR yi = 0 TO 9
        FOR xi = 0 TO 23
            putchar x + xi, y + yi, SCREEN(y + yi, x + xi), VAL(MID$(titlecolors(yi), xi + 1, 1)) + 8, 0
        NEXT
    NEXT

    IF showbox THEN box true, x - 1, y - 1, 26, 12, 15, 7, false, true
END SUB

SUB dropblocks
    DIM x AS INTEGER, y AS INTEGER, g AS INTEGER, b AS INTEGER
    DIM drop AS INTEGER, repeat AS INTEGER

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr <> 0 AND board(x, y).fixed = false THEN
                board(x, y).landed = false
            ELSEIF board(x, y).clr <> 0 AND board(x, y).fixed = true THEN
                board(x, y).landed = true
            END IF
        NEXT
    NEXT
  
    repeat = true

    WHILE repeat
        rest 30, false

        FOR y = 1 TO boardheight
            FOR x = 1 TO boardwidth
                board(x, y).flag = false
            NEXT
        NEXT
       
        repeat = false

        y = boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr <> 0 AND board(x, y).fixed = false THEN
                g = board(x, y).group

                FOR b = 1 TO 4
                    IF blockgroups(g, b).x <> -1 THEN
                        board(blockgroups(g, b).x, blockgroups(g, b).y).landed = true
                        board(blockgroups(g, b).x, blockgroups(g, b).y).flag = true
                    END IF
                NEXT
            END IF
        NEXT

        FOR y = boardheight - 1 TO 1 STEP -1
            FOR x = 1 TO boardwidth
                IF board(x, y).clr <> 0 AND board(x, y).fixed = false AND board(x, y).flag = false THEN
                    drop = true
                   
                    g = board(x, y).group
                
                    FOR b = 1 TO 4
                        IF blockgroups(g, b).x <> -1 AND blockgroups(g, b).y < boardheight THEN
                            IF board(blockgroups(g, b).x, blockgroups(g, b).y + 1).clr <> 0 AND board(blockgroups(g, b).x, blockgroups(g, b).y + 1).landed = true THEN
                                drop = false
                            END IF

                            board(blockgroups(g, b).x, blockgroups(g, b).y).flag = true
                        END IF
                    NEXT

                    IF drop = false THEN
                        FOR b = 1 TO 4
                            IF blockgroups(g, b).x <> -1 THEN
                                board(blockgroups(g, b).x, blockgroups(g, b).y).landed = true
                            END IF
                        NEXT
                    ELSE
                        repeat = true
                    END IF
                END IF
            NEXT
        NEXT

        FOR y = boardheight - 1 TO 1 STEP -1
            FOR x = 1 TO boardwidth
                IF board(x, y).clr <> 0 AND board(x, y).fixed = false AND board(x, y).landed = false THEN
                    board(x, y + 1) = board(x, y)
                    board(x, y).clr = 0

                    g = board(x, y + 1).group

                    FOR b = 1 TO 4
                        IF blockgroups(g, b).x = x AND blockgroups(g, b).y = y THEN
                            blockgroups(g, b).y = blockgroups(g, b).y + 1
                        END IF
                    NEXT
                END IF
            NEXT
        NEXT
        displayboard
    WEND
    rest 50, false
END SUB

SUB dropcurrentblock
    IF canmove(player1.x, player1.y + 1) THEN
        eraseblock
        player1.y = player1.y + 1
        displayactiveblock
    END IF
END SUB

SUB eraseblock
    DIM x AS INTEGER, y AS INTEGER
    DIM xi AS INTEGER, yi AS INTEGER
    DIM c AS INTEGER, ch AS INTEGER

    x = player1.x
    y = player1.y

    FOR yi = 1 TO 4
        FOR xi = 1 TO 4
            IF x + xi - 1 >= 1 AND x + xi - 1 <= boardwidth AND y + yi - 1 >= 1 AND y + yi - 1 <= boardheight THEN
                c = board(x + xi - 1, y + yi - 1).clr

                IF c <> 0 THEN
                    IF board(x + xi - 1, y + yi - 1).fixed = false THEN
                        putchar boardx + (x + xi - 2) * 2, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c) - 8, 0
                        putchar boardx + (x + xi - 2) * 2 + 1, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c) - 8, 0
                    ELSE
                        putchar boardx + (x + xi - 2) * 2, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c), blockcolor(c) - 8 * (NOT board(x + xi - 1, y + yi - 1).flashing)
                        putchar boardx + (x + xi - 2) * 2 + 1, boardy + (y + yi - 2), board(x + xi - 1, y + yi - 1).char, blockcolor(c), blockcolor(c) - 8 * (NOT board(x + xi - 1, y + yi - 1).flashing)
                    END IF
                ELSE
                    putchar boardx + (x + xi - 2) * 2, boardy + (y + yi - 2), 32, 0, 0
                    putchar boardx + (x + xi - 2) * 2 + 1, boardy + (y + yi - 2), 32, 0, 0
                END IF
            END IF
        NEXT
    NEXT
END SUB

SUB erasecursor
    IF NOT groupmode THEN
        IF board(cursor.x, cursor.y).clr <> 0 THEN
            IF board(cursor.x, cursor.y).fixed = false THEN
                putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 219, blockcolor(board(cursor.x, cursor.y).clr) - 8, 0
                putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 219, blockcolor(board(cursor.x, cursor.y).clr) - 8, 0
            ELSE
                putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 254, blockcolor(board(cursor.x, cursor.y).clr), blockcolor(board(cursor.x, cursor.y).clr) - 8 * (NOT board(cursor.x, cursor.y).flashing)
                putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 254, blockcolor(board(cursor.x, cursor.y).clr), blockcolor(board(cursor.x, cursor.y).clr) - 8 * (NOT board(cursor.x, cursor.y).flashing)
            END IF
        ELSE
            putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 32, 0, 0
            putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 32, 0, 0
        END IF
    ELSE
        IF board(cursor.x, cursor.y).clr <> 0 THEN
            IF board(cursor.x, cursor.y).fixed = false THEN
                putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, board(cursor.x, cursor.y).group, blockcolor(board(cursor.x, cursor.y).clr) - 8, 0
                putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, board(cursor.x, cursor.y).group, blockcolor(board(cursor.x, cursor.y).clr) - 8, 0
            ELSE
                putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, ASC("0"), blockcolor(board(cursor.x, cursor.y).clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
                putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, ASC("0"), blockcolor(board(cursor.x, cursor.y).clr), blockcolor(cursor.info.clr) - 8 * (NOT cursor.info.flashing)
            END IF
        ELSE
            putchar cursor.boundx + cursor.x * 2 - 2, cursor.boundy + cursor.y - 1, 32, 0, 0
            putchar cursor.boundx + cursor.x * 2 - 1, cursor.boundy + cursor.y - 1, 32, 0, 0
        END IF
    END IF
END SUB

SUB fadeout
    DIM x AS INTEGER, y AS INTEGER, c AS INTEGER

    FOR y = 1 TO screenheight
        FOR x = 1 TO screenwidth
            c = SCREEN(y, x, 1) MOD 16
            IF c >= 8 THEN c = c - 8 ELSE c = 0
            IF SCREEN(y, x, 1) >= 8 THEN putchar x, y, SCREEN(y, x), c, 0
        NEXT
    NEXT
    IF fadelevel < 3 THEN
        rest 50, false
        fadelevel = fadelevel + 1
        FOR y = 1 TO screenheight
            FOR x = 1 TO screenwidth
                putchar x, y, SCREEN(y, x), SCREEN(y, x, 1), 0
            NEXT
        NEXT

        IF fadelevel < 3 THEN
            rest 50, false
            fadelevel = fadelevel + 1
            FOR y = 1 TO screenheight
                FOR x = 1 TO screenwidth
                    putchar x, y, SCREEN(y, x), SCREEN(y, x, 1), 0
                NEXT
            NEXT
        END IF
    END IF
    fadelevel = 3
END SUB

SUB fadetowhite
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO screenheight
        FOR x = 1 TO screenwidth
            putchar x, y, SCREEN(y, x), (SCREEN(y, x, 1) MOD 8) + 8, 7
        NEXT
    NEXT
    rest 100, false
    FOR y = 1 TO screenheight
        FOR x = 1 TO screenwidth
            putchar x, y, SCREEN(y, x), 7, 7
        NEXT
    NEXT
    rest 100, false
    FOR y = 1 TO screenheight
        FOR x = 1 TO screenwidth
            putchar x, y, 219, 15, 7
        NEXT
    NEXT
END SUB

SUB flashscreen
    DIM x AS INTEGER, y AS INTEGER
    DIM fc AS INTEGER, bc AS INTEGER, clr AS INTEGER
    DIM index AS INTEGER

    FOR index = 1 TO 2
        storescreen
        FOR y = 0 TO boardheight - 1
            FOR x = 0 TO boardwidth - 1
                IF board(x, y).clr = 0 THEN
                    putchar x * 2 + boardx, y + boardy, 219, 15, 0
                    putchar x * 2 + 1 + boardx, y + boardy, 219, 15, 0
                END IF
            NEXT
        NEXT
        rest 100, false
        restorescreen
    NEXT
END SUB

FUNCTION formati$ (value AS INTEGER, digits AS INTEGER)
    DIM outstring AS STRING

    outstring = LTRIM$(STR$(value))

    IF LEN(outstring) < digits THEN
        outstring = STRING$(digits - LEN(outstring), "0") + outstring
    END IF

    formati$ = outstring
END FUNCTION

FUNCTION formatl$ (value AS LONG, digits AS INTEGER)
    DIM outstring AS STRING

    outstring = LTRIM$(STR$(value))

    IF LEN(outstring) < digits THEN
        outstring = STRING$(digits - LEN(outstring), "0") + outstring
    END IF

    formatl$ = outstring
END FUNCTION

SUB gamecompleted
    IF gamemode = normalgame THEN
        clearscreen ASC("T"), 12, 4
        displaytitle 3, 2, true
        displaytitle 54, 2, true
        displaytitle 3, 15, true
        displaytitle 54, 15, true
        box true, centerx - 10, centery - 5, 21, 12, 12, 4, true, true
        putstringf centerx, centery - 3, "Congratulations!!", "bcdefghijihgfedcb", "aaaaaaaaaaaaaaaaa", true
        rest 1000, false
        putstring centerx, centery - 2, "You've completed", 15, 0, true
        rest 1000, false
        putstring centerx, centery - 1, "NORMAL  MODE", 12, 4, true
        rest 2000, false
        putstring centerx, centery + 1, "Bonus:   +" + formatl(endbonus, -1), 10, 4, true
        rest 1000, false
        player1score.score = player1score.score + endbonus
        putstring centerx, centery + 3, "Your final score:", 15, 0, true
        rest 1000, false
        putstring centerx, centery + 4, formatl(player1score.score, 12), 10, 4, true
        rest 1000, true
    ELSEIF gamemode = puzzlegame THEN
        clearscreen ASC("T"), 9, 1
        displaytitle 3, 2, true
        displaytitle 54, 2, true
        displaytitle 3, 15, true
        displaytitle 54, 15, true
        box true, centerx - 10, centery - 5, 21, 8, 9, 1, true, true
        putstringf centerx, centery - 3, "Congratulations!!", "bcdefghijihgfedcb", "aaaaaaaaaaaaaaaaa", true
        rest 1000, false
        putstring centerx, centery - 2, "You've completed", 15, 0, true
        rest 1000, false
        putstring centerx, centery, "PUZZLE  MODE", 9, 1, true
        rest 10000, true
    END IF
END SUB

FUNCTION getinput$ (x AS INTEGER, y AS INTEGER, maxlength AS INTEGER)
    DIM done AS INTEGER
    DIM outstring AS STRING
    DIM keyinput AS STRING

    putstring x, y, outstring, 14, 0, false
    putstring x + LEN(outstring), y, STRING$(maxlength - LEN(outstring), " "), 0, 0, false

    WHILE NOT done
        keyinput = INKEY$

        SELECT CASE keyinput
            CASE "A" TO "z"
                IF LEN(outstring) < maxlength THEN
                    outstring = outstring + UCASE$(keyinput)
                    putstring x, y, outstring, 14, 0, false
                    putstring x + LEN(outstring), y, STRING$(maxlength - LEN(outstring), " "), 0, 0, false
                END IF
            CASE CHR$(8)
                IF LEN(outstring) > 1 THEN
                    outstring = LEFT$(outstring, LEN(outstring) - 1)
                ELSE
                    outstring = ""
                END IF
                putstring x, y, outstring, 14, 0, false
                putstring x + LEN(outstring), y, STRING$(maxlength - LEN(outstring), " "), 0, 0, false
            CASE CHR$(13)
                done = true
            CASE CHR$(27)
                outstring = ""
                done = true
        END SELECT
    WEND

    getinput$ = outstring
END FUNCTION

SUB getnewblock
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            currentblock(x, y) = nextblock(x, y)
        NEXT
    NEXT

    player1.block = rand(10)
    initblock nextblock(), player1.block
    displaynextblock
END SUB

SUB getnextgroup
    DIM index AS INTEGER
    DIM advance AS INTEGER

    advance = true
   
    WHILE advance
        advance = false
        nextgroup = nextgroup + 1
        IF nextgroup > 1000 THEN nextgroup = nextgroup - 1000

        FOR index = 1 TO 4
            IF blockgroups(nextgroup, index).x <> -1 THEN advance = true
        NEXT
    WEND
END SUB

FUNCTION getnextpiece%
    DIM x AS INTEGER, y AS INTEGER
  
    IF currentpiece < 8 THEN
        IF types(currentpiece + 1) <> noblock THEN
            FOR y = 1 TO 4
                FOR x = 1 TO 4
                    currentblock(x, y) = nextblock(x, y)
                NEXT
            NEXT
          
            currentpiece = currentpiece + 1
            IF currentpiece < 8 THEN
                IF types(currentpiece + 1) <> noblock THEN
                    loadblock nextblock(), currentpiece + 1
                END IF
            END IF

            displaynextblock

            getnextpiece% = true
        ELSE
            getnextpiece% = false
        END IF
    ELSE
        getnextpiece% = false
    END IF
END FUNCTION

FUNCTION getselection% (menu() AS STRING, x AS INTEGER, y AS INTEGER, wide AS INTEGER)
    DIM size AS INTEGER
    DIM old AS INTEGER, current AS INTEGER
    DIM done AS INTEGER
    DIM keyinput AS STRING
   
    size = UBOUND(menu)
    current = y

    displaycurrent x, y, wide, current, current

    done = false
    WHILE NOT done
        keyinput = INKEY$

        SELECT CASE keyinput
            CASE CHR$(0) + CHR$(72)
                old = current
                current = current - 1
                IF current < y THEN current = current + size
                displaycurrent x, y, wide, old, current
            CASE CHR$(0) + CHR$(80)
                old = current
                current = current + 1
                IF current = y + size THEN current = current - size
                displaycurrent x, y, wide, old, current
            CASE CHR$(13)
                getselection = current - y + 1
                done = true
            CASE CHR$(27)
                getselection% = -1
                done = true
        END SELECT
    WEND
END FUNCTION

SUB helpmenu
    fadelevel = 2
    clearscreen 63, 14, 6
    rest 500, false
    fadelevel = 1
    clearscreen 63, 14, 6
    SLEEP
    fadeout
END SUB

FUNCTION highscoreposition%
    DIM position AS INTEGER

    position = maxhighscores + 1

    WHILE player1score.score > highscores(normalgame, position - 1).score AND position > 1
        position = position - 1
    WEND

    IF position <= maxhighscores THEN
        highscoreposition% = position
    ELSE
        highscoreposition% = -1
    END IF
END FUNCTION

SUB initblock (block() AS blocktype, b AS INTEGER)
    DIM x AS INTEGER, y AS INTEGER, c AS INTEGER
    DIM lastc AS INTEGER, sim AS INTEGER
    DIM g1 AS INTEGER, g2 AS INTEGER, g3 AS INTEGER
    DIM increase AS INTEGER, allfour AS INTEGER

    increase = 1

    sim = 0
    lastc = -1

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            IF blocktemplates(b, x, y).clr = 1 THEN
                IF sim < 1 THEN
                    c = rand(3)
                    IF c = lastc THEN
                        sim = sim + 1
                    ELSE
                        lastc = c
                    END IF
                ELSE
                    allfour = rand(1)
                    IF allfour <> 1 THEN
                        c = lastc
                        WHILE c = lastc
                            c = rand(3)
                        WEND
                        sim = 0
                        lastc = c
                    END IF
                END IF
                                
                IF allfour <> 1 OR b <> 1 THEN
                    block(x, y).clr = c
                ELSE
                    block(x, y).clr = c
                END IF

                block(x, y).char = blocktemplates(b, x, y).char
                block(x, y).group = newestgroup(blocktemplates(b, x, y).group + 1)
                
                IF increase - 1 < blocktemplates(b, x, y).group THEN
                    increase = blocktemplates(b, x, y).group + 1
                END IF
                block(x, y).fixed = false
                block(x, y).flashing = false
            ELSE
                block(x, y).clr = 0
            END IF
        NEXT
    NEXT

    WHILE increase > 0
        getnextgroup
        increase = increase - 1
    WEND

    player1.singled = false
END SUB

SUB initblocktemplates
    DIM b AS INTEGER, x AS INTEGER, y AS INTEGER

' long block
    blocktemplates(longblock, 2, 1).clr = 1
    blocktemplates(longblock, 2, 1).group = 0
    blocktemplates(longblock, 2, 2).clr = 1
    blocktemplates(longblock, 2, 2).group = 0
    blocktemplates(longblock, 2, 3).clr = 1
    blocktemplates(longblock, 2, 3).group = 0
    blocktemplates(longblock, 2, 4).clr = 1
    blocktemplates(longblock, 2, 4).group = 0
  
' l-block
    blocktemplates(lblock, 2, 1).clr = 1
    blocktemplates(lblock, 2, 1).group = 0
    blocktemplates(lblock, 2, 2).clr = 1
    blocktemplates(lblock, 2, 2).group = 0
    blocktemplates(lblock, 2, 3).clr = 1
    blocktemplates(lblock, 2, 3).group = 0
    blocktemplates(lblock, 3, 3).clr = 1
    blocktemplates(lblock, 3, 3).group = 0
' l-block a
    blocktemplates(lblocka, 3, 1).clr = 1
    blocktemplates(lblocka, 3, 1).group = 0
    blocktemplates(lblocka, 3, 2).clr = 1
    blocktemplates(lblocka, 3, 2).group = 0
    blocktemplates(lblocka, 3, 3).clr = 1
    blocktemplates(lblocka, 3, 3).group = 0
    blocktemplates(lblocka, 2, 3).clr = 1
    blocktemplates(lblocka, 2, 3).group = 0
' z-block
    blocktemplates(zblock, 1, 2).clr = 1
    blocktemplates(zblock, 1, 2).group = 0
    blocktemplates(zblock, 2, 2).clr = 1
    blocktemplates(zblock, 2, 2).group = 0
    blocktemplates(zblock, 2, 3).clr = 1
    blocktemplates(zblock, 2, 3).group = 0
    blocktemplates(zblock, 3, 3).clr = 1
    blocktemplates(zblock, 3, 3).group = 0
' z-block a
    blocktemplates(zblocka, 1, 3).clr = 1
    blocktemplates(zblocka, 1, 3).group = 0
    blocktemplates(zblocka, 2, 3).clr = 1
    blocktemplates(zblocka, 2, 3).group = 0
    blocktemplates(zblocka, 2, 2).clr = 1
    blocktemplates(zblocka, 2, 2).group = 0
    blocktemplates(zblocka, 3, 2).clr = 1
    blocktemplates(zblocka, 3, 2).group = 0
' t-block
    blocktemplates(tblock, 1, 2).clr = 1
    blocktemplates(tblock, 1, 2).group = 0
    blocktemplates(tblock, 2, 2).clr = 1
    blocktemplates(tblock, 2, 2).group = 0
    blocktemplates(tblock, 3, 2).clr = 1
    blocktemplates(tblock, 3, 2).group = 0
    blocktemplates(tblock, 2, 3).clr = 1
    blocktemplates(tblock, 2, 3).group = 0
' y-block
    blocktemplates(yblock, 1, 2).clr = 1
    blocktemplates(yblock, 1, 2).group = 1
    blocktemplates(yblock, 3, 2).clr = 1
    blocktemplates(yblock, 3, 2).group = 2
    blocktemplates(yblock, 2, 3).clr = 1
    blocktemplates(yblock, 2, 3).group = 0
    blocktemplates(yblock, 2, 4).clr = 1
    blocktemplates(yblock, 2, 4).group = 0
' i-block
    blocktemplates(iblock, 1, 1).clr = 1
    blocktemplates(iblock, 1, 1).group = 1
    blocktemplates(iblock, 2, 2).clr = 1
    blocktemplates(iblock, 2, 2).group = 0
    blocktemplates(iblock, 2, 3).clr = 1
    blocktemplates(iblock, 2, 3).group = 0
    blocktemplates(iblock, 2, 4).clr = 1
    blocktemplates(iblock, 2, 4).group = 0
' i-block a
    blocktemplates(iblocka, 3, 1).clr = 1
    blocktemplates(iblocka, 3, 1).group = 1
    blocktemplates(iblocka, 2, 2).clr = 1
    blocktemplates(iblocka, 2, 2).group = 0
    blocktemplates(iblocka, 2, 3).clr = 1
    blocktemplates(iblocka, 2, 3).group = 0
    blocktemplates(iblocka, 2, 4).clr = 1
    blocktemplates(iblocka, 2, 4).group = 0
' corner block
    blocktemplates(cornerblock, 2, 3).clr = 1
    blocktemplates(cornerblock, 2, 3).group = 1
    blocktemplates(cornerblock, 2, 4).clr = 1
    blocktemplates(cornerblock, 2, 4).group = 1
    blocktemplates(cornerblock, 3, 2).clr = 1
    blocktemplates(cornerblock, 3, 2).group = 0
    blocktemplates(cornerblock, 4, 2).clr = 1
    blocktemplates(cornerblock, 4, 2).group = 0
                               
    FOR b = 1 TO maxblocks
        FOR y = 1 TO 4
            FOR x = 1 TO 4
                blocktemplates(b, x, y).char = 219
            NEXT
        NEXT
    NEXT
END SUB

SUB initboard
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            board(x, y).clr = 0
            board(x, y).char = 32
            board(x, y).group = 0
            board(x, y).fixed = false
            board(x, y).flashing = false
        NEXT
    NEXT
   
    FOR x = 1 TO 1000
        FOR y = 1 TO 4
            blockgroups(x, y).x = -1
        NEXT
    NEXT

    IF gamemode = normalgame THEN
        currentspeed = minspeed - (currentlevel / maxlevel) * (minspeed - maxspeed)
    ELSEIF gamemode = puzzlegame THEN
        currentspeed = minspeed
    END IF
   
    redblast = false
    yellowblast = false
    blueblast = false
    redout = false
    yellowout = false
    blueout = false

    background = rand(256)
    backcolor = rand(8) - 1
    IF backcolor = currentlevel MOD 16 THEN
        backcolor = backcolor + 1
        IF backcolor = 8 THEN backcolor = 0
    END IF

    IF gamemode = normalgame THEN
        score = 0
        numsets = 0
        numcascades = 0
    END IF
END SUB

SUB initcursor
    cursor.x = 1
    cursor.y = 1
    cursor.boundx = boardx
    cursor.boundy = boardy
    cursor.boundwidth = boardwidth
    cursor.boundheight = boardheight
    cursor.info.clr = 1
    cursdor.info.char = 219
    cursor.info.group = 1
    cursor.info.fixed = true
    cursor.info.flashing = false
END SUB

SUB initeditor
    DIM x AS INTEGER, y AS INTEGER, p AS INTEGER

    fadelevel = 1

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            board(x, y).clr = 0
            board(x, y).char = 32
            board(x, y).group = 1
            board(x, y).fixed = false
            board(x, y).flashing = false
        NEXT
    NEXT

    FOR x = 1 TO 1000
        FOR y = 1 TO 4
            blockgroups(x, y).x = -1
        NEXT
    NEXT

    FOR p = 1 TO 8
        FOR y = 1 TO 4
            FOR x = 1 TO 4
                puzzlepieces(p, x, y).clr = 0
                puzzlepieces(p, x, y).char = 219
                puzzlepieces(p, x, y).group = 0
                puzzlepieces(p, x, y).fixed = false
                puzzlepieces(p, x, y).flashing = false
            NEXT
        NEXT

        types(p) = noblock
    NEXT

    groupmode = false
    currentpiece = 1
END SUB

SUB initgame
    DIM index AS INTEGER, b AS INTEGER, r AS INTEGER
    DIM x AS INTEGER, y AS INTEGER

    FOR index = 1 TO maxlevels
        levelcolor(index) = (index MOD 15) + 1
    NEXT

    FOR b = 1 TO maxblocks
        FOR r = 1 TO 4
            FOR y = 1 TO 4
                FOR x = 1 TO 4
                    blocktemplates(b, x, y).clr = 0
                NEXT
            NEXT
        NEXT
    NEXT

    FOR index = 1 TO maxpuzzles
        puzzlelevels(index) = "LVL" + LTRIM$(STR$(index))
    NEXT

    blockcolor(0) = 0
    blockcolor(1) = 12
    blockcolor(2) = 10
    blockcolor(3) = 9
    blockcolor(4) = 8

    titlecolors(0) = "666011102220444055503330"
    titlecolors(1) = "066611110224444555033300"
    titlecolors(2) = "066011000224444055033330"
    titlecolors(3) = "066011100224444055033330"
    titlecolors(4) = "066011100224444055503330"
    titlecolors(5) = "006001104777777105033300"
    titlecolors(6) = "000000046666666610000000"
    titlecolors(7) = "000000004666666100000000"
    titlecolors(8) = "000004777777777777100000"
    titlecolors(9) = "000004777777777777100000"

    fadecolors(1, 0) = 0
    fadecolors(1, 1) = 1
    fadecolors(1, 2) = 2
    fadecolors(1, 3) = 3
    fadecolors(1, 4) = 4
    fadecolors(1, 5) = 5
    fadecolors(1, 6) = 6
    fadecolors(1, 7) = 7
    fadecolors(1, 8) = 8
    fadecolors(1, 9) = 9
    fadecolors(1, 10) = 10
    fadecolors(1, 11) = 11
    fadecolors(1, 12) = 12
    fadecolors(1, 13) = 13
    fadecolors(1, 14) = 14
    fadecolors(1, 15) = 15
    fadecolors(2, 0) = 0
    fadecolors(2, 1) = 0
    fadecolors(2, 2) = 0
    fadecolors(2, 3) = 1
    fadecolors(2, 4) = 8
    fadecolors(2, 5) = 8
    fadecolors(2, 6) = 0
    fadecolors(2, 7) = 8
    fadecolors(2, 8) = 0
    fadecolors(2, 9) = 1
    fadecolors(2, 10) = 2
    fadecolors(2, 11) = 3
    fadecolors(2, 12) = 4
    fadecolors(2, 13) = 5
    fadecolors(2, 14) = 6
    fadecolors(2, 15) = 7
    fadecolors(3, 0) = 0
    fadecolors(3, 1) = 0
    fadecolors(3, 2) = 0
    fadecolors(3, 3) = 0
    fadecolors(3, 4) = 0
    fadecolors(3, 5) = 0
    fadecolors(3, 6) = 0
    fadecolors(3, 7) = 0
    fadecolors(3, 8) = 0
    fadecolors(3, 9) = 0
    fadecolors(3, 10) = 0
    fadecolors(3, 11) = 0
    fadecolors(3, 12) = 0
    fadecolors(3, 13) = 0
    fadecolors(3, 14) = 0
    fadecolors(3, 15) = 0
    fadelevel = 1

    lastpuzzle = 20

    mainmenu(1) = "1 PLAYER CONTINUOUS"
    mainmenu(2) = "1 PLAYER PUZZLE"
    mainmenu(3) = "VIEW HIGH SCORES"
    mainmenu(4) = "QUIT"

    initblocktemplates
    initplayerscore
    loadhighscores
END SUB

SUB inithighscores
    DIM m AS INTEGER, p AS INTEGER

    FOR m = 1 TO 3
        FOR p = 1 TO maxhighscores
            highscores(m, p).initials = "???"
            highscores(m, p).lastlevel = 0
            highscores(m, p).score = (maxhighscores + 1 - p) * (1000000 / maxhighscores)
            highscores(m, p).cascadescore = 0
            highscores(m, p).speedscore = 0
            highscores(m, p).blastscore = 0
            highscores(m, p).wins = 0
        NEXT
    NEXT

    highlightscore = 0
END SUB

SUB initpiece (p AS INTEGER, b AS INTEGER)
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            puzzlepieces(p, x, y).char = 219
            puzzlepieces(p, x, y).group = 254 - blocktemplates(b, x, y).group - 3 * (p - 1)
            puzzlepieces(p, x, y).fixed = false
            puzzlepieces(p, x, y).flashing = false

            IF b <> noblock THEN
                IF blocktemplates(b, x, y).clr = 1 THEN
                    puzzlepieces(p, x, y).clr = red
                ELSE
                    puzzlepieces(p, x, y).clr = 0
                END IF
            ELSE
                puzzlepieces(p, x, y).clr = 0
            END IF
        NEXT
    NEXT
END SUB

SUB initplayer
    player1.x = boardwidth \ 2 - 1
    player1.y = -3
    player1.block = 0
END SUB

SUB initplayerscore
    player1score.initials = "   "
    player1score.lastlevel = 0
    player1score.score = 0
    player1score.speedscore = 0
    player1score.cascadescore = 0
    player1score.blastscore = 0
END SUB

SUB killall
    DIM x AS INTEGER, y AS INTEGER

    voidbox boardx + boardwidth * 2 + 4, 5, 8, 4, 32, 0, 0

    FOR y = boardheight TO 1 STEP -1
        FOR x = 1 TO boardwidth
            putchar boardx + x * 2 - 2, boardy + y - 1, 254, 8, 0
            putchar boardx + x * 2 - 1, boardy + y - 1, 254, 8, 0
        NEXT
        rest 50, false
    NEXT

    putstring 36, 11, "GAME  OVER", 15, 16, false
    rest 1000, true
END SUB

SUB levelselect
    DIM done AS INTEGER
    DIM keyinput AS STRING
    DIM current AS INTEGER
    DIM puzzle AS INTEGER
    DIM max AS INTEGER
   
    FOR t& = 1 TO 100000
        keyinput = UCASE$(INKEY$)
        IF keyinput = "M" THEN
            max = -1
            EXIT FOR
        END IF
    NEXT

    IF gamemode = normalgame THEN
        puzzle = false
        IF max <> -1 THEN max = 20 ELSE max = maxlevel
    ELSEIF gamemode = puzzlegame THEN
        puzzle = true
        IF max <> -1 THEN
            max = lastpuzzle
        ELSEIF lastpuzzle < 50 THEN
            max = maxpuzzles
        ELSE
            max = lastpuzzle
        END IF
    END IF

    current = 1

    fadelevel = 1
    clearscreen 15, 8, 0
    rest 100, false
    clearscreen 15, 1, 0
    rest 100, false
    clearscreen 15, 11, 1

    displaytitle 3, 2, true

    IF gamemode = normalgame THEN
        displaynormalbanner 2, 16
    ELSEIF gamemode = puzzlegame THEN
        displaypuzzlebanner 2, 16
    END IF

    displaydatawindow 38, 5, 26, 3, "CHOOSE LEVEL", 4
    displaybar 42, 9, 20, max, current
   
    box false, 44, 14, 14, 8, 8, 0, true, true
    displaybignumber 46, 16, current, 4

    done = false
    WHILE NOT done
        keyinput = INKEY$
        SELECT CASE keyinput
            CASE CHR$(0) + CHR$(75)
                IF current > 1 THEN
                    current = current - 1
                    displaybar 42, 9, 20, max, current
                    displaybignumber 46, 16, current, 4
                END IF
            CASE CHR$(0) + CHR$(77)
                IF current < max THEN
                    current = current + 1
                    displaybar 42, 9, 20, max, current
                    displaybignumber 46, 16, current, 4
                END IF
            CASE CHR$(13)
                quit = false
                done = true
            CASE CHR$(27)
                quit = true
                done = true
        END SELECT
    WEND

    IF NOT quit THEN
        currentlevel = current
        currentpuzzle = current
    END IF

    fadeout
END SUB

SUB loadblock (block() AS blocktype, p AS INTEGER)
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            block(x, y).clr = puzzlepieces(p, x, y).clr
            block(x, y).char = puzzlepieces(p, x, y).char
            block(x, y).group = puzzlepieces(p, x, y).group
            block(x, y).fixed = puzzlepieces(p, x, y).fixed
            block(x, y).flashing = puzzlepieces(p, x, y).flashing
        NEXT
    NEXT

    player1.singled = false
END SUB

SUB loadboard
    DIM i AS INTEGER, x AS INTEGER, y AS INTEGER
    DIM zone AS INTEGER

    zone = currentlevel * 12 \ maxlevel + 4

    FOR i = 1 TO currentlevel - 1
        DO
            x = rand(boardwidth)
            y = boardheight - rand(zone) + 1
        LOOP WHILE board(x, y).clr <> 0
        board(x, y).clr = red
        board(x, y).char = 254
        board(x, y).fixed = true
        board(x, y).flashing = false
        DO
            x = rand(boardwidth)
            y = boardheight - rand(zone) + 1
        LOOP WHILE board(x, y).clr <> 0
        board(x, y).clr = yellow
        board(x, y).char = 254
        board(x, y).fixed = true
        board(x, y).flashing = false
        DO
            x = rand(boardwidth)
            y = boardheight - rand(zone) + 1
        LOOP WHILE board(x, y).clr <> 0
        board(x, y).clr = blue
        board(x, y).char = 254
        board(x, y).fixed = true
        board(x, y).flashing = false
    NEXT

    y = boardheight

    DO
        x = rand(boardwidth)
    LOOP WHILE board(x, y).clr <> 0
    board(x, boardheight).clr = red
    board(x, boardheight).char = 254
    board(x, boardheight).fixed = true
    board(x, boardheight).flashing = true
    DO
        x = rand(boardwidth)
    LOOP WHILE board(x, y).clr <> 0
    board(x, boardheight).clr = yellow
    board(x, boardheight).char = 254
    board(x, boardheight).fixed = true
    board(x, boardheight).flashing = true
    DO
        x = rand(boardwidth)
    LOOP WHILE board(x, y).clr <> 0
    board(x, boardheight).clr = blue
    board(x, boardheight).char = 254
    board(x, boardheight).fixed = true
    board(x, boardheight).flashing = true

    nextgroup = 0
    getnextgroup
         
    initplayer
    getnewblock
END SUB

SUB loadhighscores
    DIM p AS INTEGER, i AS INTEGER, D AS INTEGER
    DIM temp AS STRING

    OPEN directory + "tetris2.hsc" FOR INPUT AS #1
    IF erroroccurred THEN
        erroroccurred = false
        inithighscores
        EXIT SUB
    END IF

    FOR p = 1 TO maxhighscores
        INPUT #1, highscores(normalgame, p).initials, highscores(normalgame, p).score, highscores(normalgame, p).lastlevel
    NEXT

    CLOSE #1
END SUB

SUB loadpuzzle (filename AS STRING)
    DIM x AS INTEGER, y AS INTEGER, p AS INTEGER

    OPEN directory + filename + ".PUZ" FOR INPUT AS #1

    IF erroroccurred THEN STOP
    nextgroup = -1

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            INPUT #1, board(x, y).clr, board(x, y).char, board(x, y).group, board(x, y).fixed, board(x, y).flashing
            IF board(x, y).group > nextgroup THEN nextgroup = board(x, y).group + 1
           
            IF board(x, y).clr <> 0 AND board(x, y).fixed = false THEN addtogroup x, y
        NEXT
    NEXT

    FOR p = 1 TO 8
        INPUT #1, types(p)
        FOR y = 1 TO 4
            FOR x = 1 TO 4
                INPUT #1, puzzlepieces(p, x, y).clr, puzzlepieces(p, x, y).char, puzzlepieces(p, x, y).group
            NEXT
        NEXT
    NEXT

    CLOSE #1
END SUB

SUB loadpuzzlegame (filename AS STRING)
    DIM index AS INTEGER

    OPEN directory + filename + ".GAM" FOR INPUT AS #1
        IF erroroccurred THEN EXIT SUB

        FOR index = 1 TO maxpuzzles
            INPUT #1, puzzlelevels(index)
        NEXT
    CLOSE #1
END SUB

SUB moveblock (xd AS INTEGER, yd AS INTEGER)
    IF canmove(player1.x + xd, player1.y + yd) THEN
        eraseblock
        player1.x = player1.x + xd
        player1.y = player1.y + yd
        displayactiveblock
    END IF
END SUB

SUB movecursor (xdir AS INTEGER, ydir AS INTEGER)
    IF cursor.x + xdir >= 1 AND cursor.y + ydir >= 1 AND cursor.x + xdir <= cursor.boundwidth AND cursor.y + ydir <= cursor.boundheight THEN
        erasecursor
        cursor.x = cursor.x + xdir
        cursor.y = cursor.y + ydir
        displaycursor
    END IF
END SUB

FUNCTION newestgroup% (steps AS INTEGER)
    DIM advance AS INTEGER
    DIM nextg AS INTEGER

    nextg = nextgroup

    WHILE steps > 0
        newestgroup% = nextg
        advance = true
        WHILE advance
            advance = false

            nextg = nextg + 1
            IF nextg > 1000 THEN nextg = nextg - 1000

            FOR index = 1 TO 4
                IF blockgroups(nextg, index).x <> -1 THEN advance = true
            NEXT
        WEND
        steps = steps - 1
    WEND
END FUNCTION

FUNCTION outofbounds% (x AS INTEGER, y AS INTEGER)
    IF x >= 1 AND y >= 1 AND x < boardwidth AND y < boardheight THEN
        outofbounds% = true
    ELSE
        outofbounds% = false
    END IF
END FUNCTION

SUB pauseboard (message$)
    DIM x AS INTEGER

    FOR y = 0 TO boardheight - 1
        putstring boardx, boardy + y, STRING$(boardwidth * 2, CHR$(176)), 8, 0, false
    NEXT
    displayblock currentblock(), boardx + player1.x * 2 - 2, boardy + player1.y - 1, false
    rest 100, false
    FOR y = 0 TO boardheight - 1
        putstring boardx, boardy + y, STRING$(boardwidth * 2, CHR$(176)), 6, 0, false
    NEXT
    displayblock currentblock(), boardx + player1.x * 2 - 2, boardy + player1.y - 1, false
    rest 100, false
    FOR y = 0 TO boardheight - 1
        putstring boardx, boardy + y, STRING$(boardwidth * 2, CHR$(176)), 14, 6, false
    NEXT
    displayblock currentblock(), boardx + player1.x * 2 - 2, boardy + player1.y - 1, false
   
    putstring boardx + boardwidth, boardy + boardheight \ 2 - 1, message$, 15, 30, true
    SLEEP
   
    FOR y = 0 TO boardheight - 1
        putstring boardx, boardy + y, STRING$(boardwidth * 2, CHR$(176)), 14, 6, false
    NEXT
    displayblock currentblock(), boardx + player1.x * 2 - 2, boardy + player1.y - 1, false
    rest 100, false
    FOR y = 0 TO boardheight - 1
        putstring boardx, boardy + y, STRING$(boardwidth * 2, CHR$(176)), 6, 0, false
    NEXT
    displayblock currentblock(), boardx + player1.x * 2 - 2, boardy + player1.y - 1, false
    rest 100, false
    FOR y = 0 TO boardheight - 1
        putstring boardx, boardy + y, STRING$(boardwidth * 2, CHR$(176)), 8, 0, false
    NEXT
    displayboard
    displayactiveblock
    WHILE INKEY$ <> "": WEND
END SUB

SUB playcontinuousgame
    DIM done AS INTEGER
    DIM keyinput AS STRING
    DIM count AS LONG, speedcount AS LONG
    DIM endcontinuous AS INTEGER
   
    quit = false
    gameover = false
    endcontinuous = false
    initboard
    fadelevel = 2
    clearscreen 32, 0, 0
    displayscreen
    rest 100, false
    fadelevel = 1
    clearscreen 32, 0, 0
    displayscreen
    loadboard

    displayhighscores
    displayboard
    displaynextblock
    displaylevel
    displayscore
         
    pauseboard "READY?"
    displayactiveblock
    getnewblock

    WHILE NOT endcontinuous
        done = false
        player1score.lastlevel = currentlevel

        WHILE NOT done
            keyinput = INKEY$

            SELECT CASE keyinput
                CASE CHR$(0) + CHR$(75)
                    moveblock -1, 0
                CASE CHR$(0) + CHR$(77)
                    moveblock 1, 0
                CASE CHR$(0) + CHR$(80)
                    moveblock 0, 1
                CASE ",", "<"
                    rotateleft
                    displayactiveblock
                CASE "/", "?"
                    rotateright
                    displayactiveblock
                CASE "p", "P"
                    pauseboard "PAUSED"
                CASE CHR$(27)
                    quit = true
                    done = true
                CASE CHR$(0) + CHR$(68)
                    CLS
                    SYSTEM
                CASE ELSE
                    keyinput = ""
            END SELECT
             
            IF quit THEN
                killall
                EXIT SUB
            END IF

            IF currentspeed > maxspeed THEN
                speedcount = speedcount + 1
                IF speedcount >= speedadvance THEN
                    currentspeed = currentspeed - 1
                    IF currentspeed < maxspeed THEN currentspeed = maxspeed
                    speedcount = 0
                END IF
            END IF

            count = count + 1
            IF count >= currentspeed THEN
                count = 0
               
                IF candrop THEN
                    dropcurrentblock
                ELSE
                    addtoboard
                   
                    IF gameover THEN
                        EXIT SUB
                    END IF
                    displayboard

                    IF player1.blocklanded THEN
                        player1.blocklanded = false
                        player1score.score = player1score.score + 4 * blockmult

                        numcascades = 0
                        score = 0
                        bonus = 0

                        WHILE testformatches = true
                            numcascades = numcascades + 1
                            
                            score = score * power(cascadebase, numcascades - 1)
                          
                            blastblocks
                            rest 100, false
                            player1score.score = player1score.score + score
                            displayscore

                            IF redblast OR yellowblast OR blueblast THEN
                                blastcolor
                            END IF

                            score = 0
                            blastfixedblocks
                            rest 300, false
                           
                            IF NOT redout OR NOT yellowout OR NOT blueout THEN
                                dropblocks
                            ELSE
                                blastall
                                done = true
                            END IF
                            score = 0
                        WEND
                       
                        displayscore
                        getnewblock
                        displaynextblock
                        initplayer
                       
                        WHILE INKEY$ <> "": WEND
                    ELSE
                        centerblock
                    END IF
                END IF
            END IF
        WEND

        currentlevel = currentlevel + 1
        IF currentlevel <= maxlevel THEN
            initboard
            loadboard
            pauseboard "READY?"
            displayhighscores
            displayboard
            displaynextblock
            displaylevel
            displayscore
                
            displayactiveblock
            getnewblock
        ELSE
            quit = true
            endcontinuous = true
            fadeout
            fadelevel = 1
            gamecompleted
        END IF
    WEND
END SUB

SUB playpuzzlegame
    DIM done AS INTEGER
    DIM keyinput AS STRING
    DIM count AS LONG
    DIM endpuzzle AS INTEGER
   
    fadelevel = 1

    endpuzzle = false
    quit = false
    gameover = false

    initboard
    loadpuzzle puzzlelevels(currentpuzzle)
   
    clearscreen 32, 0, 0
    displayscreen
    displayboard
   
    currentpiece = 0
    loadblock nextblock(), 1
   
    initplayer
    displayboard
    displaynextblock
    displaylevel
    pauseboard "READY?"

    null% = getnextpiece
   
    displayactiveblock

    WHILE NOT endpuzzle
        done = false
        player1score.lastlevel = currentlevel

        WHILE NOT done
            keyinput = INKEY$

            SELECT CASE keyinput
                CASE CHR$(0) + CHR$(75)
                    moveblock -1, 0
                CASE CHR$(0) + CHR$(77)
                    moveblock 1, 0
                CASE CHR$(0) + CHR$(80)
                    moveblock 0, 1
                CASE ",", "<"
                    rotateleft
                    displayactiveblock
                CASE "/", "?"
                    rotateright
                    displayactiveblock
                CASE "p", "P"
                    pauseboard "PAUSED"
                CASE CHR$(27)
                    quit = true
                    done = true
                CASE CHR$(0) + CHR$(68)
                    CLS
                    SYSTEM
                CASE ELSE
                    keyinput = ""
            END SELECT
            
            IF quit THEN
                killall

                IF NOT tryagain THEN
                    IF currentpuzzle > lastpuzzle AND currentpuzzle < maxpuzzles THEN
                        lastpuzzle = currentpuzzle
                    END IF
                    EXIT SUB
                ELSE
                    currentpuzzle = currentpuzzle - 1
                    done = true
                    quit = false
                END IF
            END IF

            count = count + 1
            IF count >= currentspeed THEN
                count = 0
             
                IF candrop THEN
                    dropcurrentblock
                ELSE
                    addtoboard
                    displayboard
                                                
                    IF gameover THEN
                        IF NOT tryagain THEN
                            IF currentpuzzle > lastpuzzle AND currentpuzzle < maxpuzzles THEN
                                lastpuzzle = currentpuzzle
                            END IF
                            EXIT SUB
                        ELSE
                            currentpuzzle = currentpuzzle - 1
                            done = true
                            gameover = false
                        END IF
                    END IF
                 
                    IF player1.blocklanded THEN
                        player1.blocklanded = false
                       
                        WHILE testformatches = true
                            blastblocks

                            IF redblast OR yellowblast OR blueblast THEN
                                blastcolor
                            END IF

                            blastfixedblocks
                            rest 300, false
                          
                            IF NOT redout OR NOT yellowout OR NOT blueout THEN
                                dropblocks
                            ELSE
                                blastall
                                done = true
                            END IF
                        WEND
                     
                        IF getnextpiece THEN
                            initplayer
                        ELSEIF NOT redout OR NOT yellowout OR NOT blueout THEN
                            displayboard
                            rest 500, false

                            killall

                            IF NOT tryagain THEN
                                gameover = true
                                IF currentpuzzle > lastpuzzle AND currentpuzzle < maxpuzzles THEN
                                    lastpuzzle = currentpuzzle
                                END IF
                                EXIT SUB
                            ELSE
                                currentpuzzle = currentpuzzle - 1
                                done = true
                                gameover = false
                            END IF
                        END IF
                      
                        WHILE INKEY$ <> "": WEND
                    ELSE
                        centerblock
                    END IF
                END IF
            END IF
        WEND

        IF NOT gameover THEN
            currentpuzzle = currentpuzzle + 1
            IF currentpuzzle <= maxpuzzles THEN
                initboard
                loadpuzzle puzzlelevels(currentpuzzle)
              
                clearscreen 32, 0, 0
                displayscreen
                displayboard
              
                currentpiece = 0
                loadblock nextblock(), 1
              
                initplayer
                pauseboard "READY?"
                displayboard
                displaynextblock
                displaylevel

                null% = getnextpiece
              
                displayactiveblock
            ELSE
                endpuzzle = true
                gamecompleted
            END IF
        ELSE
            endpuzzle = true
            'initboard
            'loadboard
        END IF
    WEND
END SUB

SUB playversusgame
END SUB

FUNCTION power& (radix AS LONG, exponent AS INTEGER)
    DIM index AS LONG, result AS LONG

    result = 1

    FOR index = 1 TO exponent
        result = result * radix
    NEXT

    power& = result
END FUNCTION

SUB putchar (x AS INTEGER, y AS INTEGER, char AS INTEGER, fc AS INTEGER, bc AS INTEGER)
    DIM b AS INTEGER, f AS INTEGER

    IF x >= 1 AND y >= 1 AND x <= screenwidth AND y <= screenheight THEN
        f = fadecolors(fadelevel, fc MOD 16)
        IF fadelevel = 1 THEN b = bc ELSE b = 0

        POKE (y - 1) * 160 + (x - 1) * 2, char
        POKE (y - 1) * 160 + (x - 1) * 2 + 1, b * 16 + f
    END IF
END SUB

SUB putstring (x AS INTEGER, y AS INTEGER, txt AS STRING, fc AS INTEGER, bc AS INTEGER, center AS INTEGER)
    DIM i AS INTEGER, xi AS INTEGER

    IF NOT center THEN xi = x ELSE xi = x - LEN(txt) / 2

    FOR i = 1 TO LEN(txt)
        putchar xi + i - 1, y, ASC(MID$(txt, i, 1)), fc, bc
    NEXT
END SUB

SUB putstringf (x AS INTEGER, y AS INTEGER, txt AS STRING, fc AS STRING, bc AS STRING, center AS INTEGER)
    DIM i AS INTEGER, xi AS INTEGER

    IF NOT center THEN xi = x ELSE xi = x - LEN(txt) / 2
    FOR i = 1 TO LEN(txt)
        putchar xi + i - 1, y, ASC(MID$(txt, i, 1)), ASC(MID$(fc, i, 1)) - 97, ASC(MID$(bc, i, 1)) - 97
    NEXT
END SUB

FUNCTION rand% (value AS INTEGER)
    rand% = INT(RND * value) + 1
END FUNCTION

SUB rest (delay AS INTEGER, cancel AS INTEGER)
    FOR t& = 1 TO delay * ms
        IF INKEY$ <> "" AND cancel THEN EXIT FOR
    NEXT
END SUB

SUB restorescreen
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO screenheight
        FOR x = 1 TO screenwidth
            putchar x, y, store(x, y).char, store(x, y).clr MOD 16, store(x, y).clr \ 16
        NEXT
    NEXT
END SUB

SUB rotateleft
    DIM temp(4, 4) AS blocktype
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            temp(x, y) = currentblock(5 - y, x)
        NEXT
    NEXT
    FOR y = 1 TO 4
        FOR x = 1 TO 4
            currentblock(x, y) = temp(x, y)
        NEXT
    NEXT

    IF canmove(player1.x, player1.y) = false THEN rotateright
END SUB

SUB rotateright
    DIM temp(4, 4) AS blocktype
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO 4
        FOR x = 1 TO 4
            temp(x, y) = currentblock(y, 5 - x)
        NEXT
    NEXT
    FOR y = 1 TO 4
        FOR x = 1 TO 4
            currentblock(x, y) = temp(x, y)
        NEXT
    NEXT

    IF canmove(player1.x, player1.y) = false THEN rotateleft
END SUB

SUB runeditor
    DIM done AS INTEGER
    DIM keyinput AS STRING
    DIM menu AS INTEGER

    initeditor
    initcursor

    displayeditor

    displaycursor

    done = false

    currentpuzzle = 1
    displaybignumber 11, 19, currentpuzzle, 12
    loadpuzzle puzzlelevels(currentpuzzle)
    IF erroroccurred THEN STOP

    displayboard
    displaypieces

    WHILE NOT done
        keyinput = INKEY$

        SELECT CASE keyinput
            CASE CHR$(0) + CHR$(72)
                movecursor 0, -1
            CASE CHR$(0) + CHR$(80)
                movecursor 0, 1
            CASE CHR$(0) + CHR$(75)
                movecursor -1, 0
            CASE CHR$(0) + CHR$(77)
                movecursor 1, 0
            CASE CHR$(32)
                IF NOT groupmode THEN
                    board(cursor.x, cursor.y).clr = cursor.info.clr
                    board(cursor.x, cursor.y).char = cursor.info.char
                    board(cursor.x, cursor.y).fixed = cursor.info.fixed
                    board(cursor.x, cursor.y).flashing = cursor.info.flashing
                ELSEIF board(cursor.x, cursor.y).clr <> 0 AND board(cursor.x, cursor.y).fixed = false THEN
                    board(cursor.x, cursor.y).group = cursor.info.group
                END IF
            CASE CHR$(8)
                IF NOT groupmode THEN board(cursor.x, cursor.y).clr = 0
                board(cursor.x, cursor.y).group = 0
            CASE CHR$(13)
                cursor.info = board(cursor.x, cursor.y)
                cursor.info.flashing = false
                displaycursor
            CASE "f"
                IF cursor.info.fixed = false THEN
                    cursor.info.fixed = true
                    board(cursor.x, cursor.y).group = 0
                ELSE
                    cursor.info.fixed = false
                    cursor.info.flashing = false
                    board(cursor.x, cursor.y).group = 0
                END IF
                displaycursor
            CASE "F"
                IF cursor.info.fixed = true THEN
                    IF cursor.info.flashing = false THEN cursor.info.flashing = true ELSE cursor.info.flashing = false
                    displaycursor
                END IF
            CASE ",", "<"
                IF NOT groupmode THEN
                    IF cursor.info.clr <> red THEN
                        cursor.info.clr = cursor.info.clr - 1
                    ELSE
                        cursor.info.clr = blue
                    END IF
                ELSE
                    IF cursor.info.group > 1 THEN
                        cursor.info.group = cursor.info.group - 1
                    END IF
                END IF

                displaycursor
            CASE "/", "?"
                IF NOT groupmode THEN
                    IF cursor.info.clr <> blue THEN
                        cursor.info.clr = cursor.info.clr + 1
                    ELSE
                        cursor.info.clr = red
                    END IF
                ELSE
                    IF cursor.info.group < 255 THEN cursor.info.group = cursor.info.group + 1
                END IF
                   
                displaycursor
            CASE CHR$(9)
                selectpieces
            CASE "`", "~"
                IF groupmode = false THEN groupmode = true ELSE groupmode = false
                displaypuzzleboard
            CASE "="
                savepuzzle puzzlelevels(currentpuzzle)
                currentpuzzle = currentpuzzle + 1
                IF currentpuzzle > maxpuzzles THEN currentpuzzle = 1
                loadpuzzle puzzlelevels(currentpuzzle)
                displayboard
                displaypieces
                displaycursor
                displaybignumber 11, 19, currentpuzzle, 12
            CASE "+"
                savepuzzle puzzlelevels(currentpuzzle)
                currentpuzzle = (currentpuzzle \ 10) * 10 + 10
                IF currentpuzzle > maxpuzzles THEN currentpuzzle = 10
                loadpuzzle puzzlelevels(currentpuzzle)
                displayboard
                displaypieces
                displaycursor
                displaybignumber 11, 19, currentpuzzle, 12
            CASE "-"
                savepuzzle puzzlelevels(currentpuzzle)
                currentpuzzle = currentpuzzle - 1
                IF currentpuzzle < 1 THEN currentpuzzle = maxpuzzles
                loadpuzzle puzzlelevels(currentpuzzle)
                displayboard
                displaypieces
                displaycursor
                displaybignumber 11, 19, currentpuzzle, 12
            CASE "_"
                savepuzzle puzzlelevels(currentpuzzle)
                currentpuzzle = (currentpuzzle \ 10) * 10 - 10
                IF currentpuzzle < 1 THEN currentpuzzle = maxpuzzles
                loadpuzzle puzzlelevels(currentpuzzle)
                displayboard
                displaypieces
                displaycursor
                displaybignumber 11, 19, currentpuzzle, 12
            CASE "c", "C"
                clearpuzzle
                displayboard
                displaypieces
                displaycursor
                displaybignumber 11, 19, currentpuzzle, 12
            CASE CHR$(27)
                savepuzzle puzzlelevels(currentpuzzle)
                done = true
        END SELECT
    WEND
    fadeout
END SUB

SUB savehighscores
    DIM p AS INTEGER, i AS INTEGER, D AS INTEGER
    DIM temp AS STRING

    OPEN directory + "tetris2.hsc" FOR OUTPUT AS #1

    FOR p = 1 TO maxhighscores
        WRITE #1, highscores(normalgame, p).initials, highscores(normalgame, p).score, highscores(normalgame, p).lastlevel
    NEXT

    CLOSE #1
END SUB

SUB savepuzzle (filename AS STRING)
    DIM x AS INTEGER, y AS INTEGER, p AS INTEGER

    OPEN directory + filename + ".PUZ" FOR OUTPUT AS #1

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr <> 0 THEN
                IF board(x, y).fixed = false THEN
                    board(x, y).char = 219
                ELSE
                    board(x, y).char = 254
                END IF
            END IF
            WRITE #1, board(x, y).clr, board(x, y).char, board(x, y).group, board(x, y).fixed, board(x, y).flashing
        NEXT
    NEXT

    FOR p = 1 TO 8
        WRITE #1, types(p)
        FOR y = 1 TO 4
            FOR x = 1 TO 4
                WRITE #1, puzzlepieces(p, x, y).clr, puzzlepieces(p, x, y).char, puzzlepieces(p, x, y).group
            NEXT
        NEXT
    NEXT

    CLOSE #1
END SUB

SUB savepuzzlegame (filename AS STRING)
    DIM index AS INTEGER

    OPEN directory + filename + ".GAM" FOR OUTPUT AS #1
        FOR index = 1 TO maxpuzzles
            WRITE #1, puzzlelevels(index)
        NEXT
    CLOSE #1
END SUB

SUB savepuzzles
    DIM index AS INTEGER

    FOR index = 1 TO maxpuzzles
        savepuzzle puzzlelevels(index)
    NEXT
END SUB

SUB selectpieces
    DIM done AS INTEGER
    DIM keyinput AS STRING

    selectcursor.boundx = 56
    selectcursor.boundy = 3
    selectcursor.boundwidth = 4
    selectcursor.boundheight = 4
    selectcursor.x = 1
    selectcursor.y = 1
  
    displayselectcursor

    done = false

    WHILE NOT done
        keyinput = INKEY$

        SELECT CASE keyinput
            CASE CHR$(0) + CHR$(72)
                IF currentpiece <> 1 AND currentpiece <> 5 THEN
                    box false, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 7, 0, true, false
                    currentpiece = currentpiece - 1
                    box true, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 6, 0, true, false
                END IF
                displaypieces
            CASE CHR$(0) + CHR$(80)
                IF currentpiece <> 4 AND currentpiece <> 8 THEN
                    box false, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 7, 0, true, false
                    currentpiece = currentpiece + 1
                    box true, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 6, 0, true, false
                END IF
                displaypieces
            CASE CHR$(0) + CHR$(75)
                IF currentpiece > 4 THEN
                    box false, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 7, 0, true, false
                    currentpiece = currentpiece - 4
                    box true, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 6, 0, true, false
                END IF
                displaypieces
            CASE CHR$(0) + CHR$(77)
                IF currentpiece < 5 THEN
                    box false, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 7, 0, true, false
                    currentpiece = currentpiece + 4
                    box true, 55 + 13 * -SGN(currentpiece > 4), 2 + 6 * ((currentpiece - 1) MOD 4), 10, 6, 6, 0, true, false
                    displaypieces
                END IF
                displaypieces
            CASE ",", "<"
                IF types(currentpiece) <> noblock THEN
                    types(currentpiece) = types(currentpiece) - 1
                ELSE
                    currenttype = cornerblock
                END IF

                initpiece currentpiece, types(currentpiece)
                displaypieces
            CASE "/", "?"
                IF types(currentpiece) <> cornerblock THEN
                    types(currentpiece) = types(currentpiece) + 1
                ELSE
                    types(currentpiece) = noblock
                END IF

                initpiece currentpiece, types(currentpiece)
                displaypieces
            CASE "1"
                changecolor currentpiece, 1
                displaypieces
            CASE "2"
                changecolor currentpiece, 2
                displaypieces
            CASE "3"
                changecolor currentpiece, 3
                displaypieces
            CASE "4"
                changecolor currentpiece, 4
                displaypieces
            CASE CHR$(9), CHR$(27)
                done = true
        END SELECT
    WEND
END SUB

SUB separateblockgroups
    DIM x AS INTEGER, y AS INTEGER, group AS INTEGER, b AS INTEGER
    DIM g AS INTEGER
    DIM separate AS INTEGER
    DIM flag(1000, 4) AS INTEGER

    FOR group = 1 TO 1000
        FOR b = 1 TO 4
            flag(group, b) = true
        NEXT
    NEXT

    FOR group = 1 TO 1000
        FOR b = 1 TO 4
            x = blockgroups(group, b).x

            IF x <> -1 AND flag(group, b) = true THEN
                y = blockgroups(group, b).y

                separate = true

                IF x > 1 THEN
                    IF board(x - 1, y).clr <> 0 AND board(x - 1, y).fixed = false AND board(x - 1, y).group = group THEN separate = false
                END IF
                IF x < boardwidth THEN
                    IF board(x + 1, y).clr <> 0 AND board(x + 1, y).fixed = false AND board(x + 1, y).group = group THEN separate = false
                END IF
                IF y > 1 THEN
                    IF board(x, y - 1).clr <> 0 AND board(x, y - 1).fixed = false AND board(x, y - 1).group = group THEN separate = false
                END IF
                IF y < boardheight THEN
                    IF board(x, y + 1).clr <> 0 AND board(x, y + 1).fixed = false AND board(x, y + 1).group = group THEN separate = false
                END IF
             
                IF separate = true THEN
                    x = blockgroups(group, b).x
                    blockgroups(group, b).x = -1

                    board(x, y).group = nextgroup
                    g = nextgroup
                    getnextgroup
                                
                    blockgroups(g, 1).x = x
                    blockgroups(g, 1).y = y
                   
                    flag(g, 1) = false
                    flag(g, 2) = false
                    flag(g, 3) = false
                    flag(g, 4) = false
                END IF
            END IF
        NEXT
    NEXT
END SUB

SUB setgroups
    DIM x AS INTEGER, y AS INTEGER
    DIM xi AS INTEGER, yi AS INTEGER
    DIM g AS INTEGER
    DIM separate AS INTEGER

    g = 1

    IF board(x, 1).clr <> 0 AND board(x, 1).fixed = false THEN
        board(x, 1).group = g
    END IF

    FOR x = 2 TO boardwidth
        IF board(x, 1).clr <> 0 AND board(x, 1).fixed = false THEN
            IF board(x, 1).clr = board(x - 1, 1).clr THEN
                board(x, 1).group = g
            ELSE
                g = g + 1
                board(x, 1).group = g
            END IF
        END IF
    NEXT

    g = g + 1

    FOR y = 2 TO boardheight
        FOR x = 1 TO boardwidth
            IF board(x, y).clr <> 0 AND board(x, y).fixed = false THEN
                IF board(x, y).clr = board(x, y - 1).clr THEN
                    board(x, y).group = board(x, y - 1).group
                ELSE
                    IF x > 1 THEN
                        IF board(x, y).clr = board(x - 1, y).clr THEN
                            board(x, y).group = board(x - 1, y).group
                        ELSE
                            board(x, y).group = g
                            g = g + 1
                        END IF
                    END IF
                END IF
            END IF
        NEXT
    NEXT
END SUB

SUB shadow (x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER)
    DIM xi AS INTEGER, yi AS INTEGER

    FOR yi = y TO y + high - 1
        FOR xi = x TO x + wide - 1
            IF xi >= 1 AND yi >= 1 AND xi <= screenwidth AND yi <= screenheight THEN
                putchar xi, yi, SCREEN(yi, xi), 8, 0
            END IF
        NEXT
    NEXT
END SUB

SUB shutdown
    savehighscores
END SUB

SUB storescreen
    DIM x AS INTEGER, y AS INTEGER

    FOR y = 1 TO screenheight
        FOR x = 1 TO screenwidth
            store(x, y).char = SCREEN(y, x)
            store(x, y).clr = SCREEN(y, x, 1)
        NEXT
    NEXT
END SUB

FUNCTION testformatches%
    DIM x AS INTEGER, y AS INTEGER, tx AS INTEGER, ty AS INTEGER, i AS INTEGER
    DIM break AS INTEGER
    DIM count AS INTEGER, clr AS INTEGER

    testformatches% = false
    numsets = 0

    redblast = false
    yellowblast = false
    blueblast = false

    FOR y = 1 TO boardheight
        FOR x = 1 TO boardwidth
            board(x, y).flag = false
        NEXT
    NEXT

    FOR y = boardheight TO 1 STEP -1
        count = 1
        tx = 1
        clr = board(1, y).clr
        IF board(1, y).fixed = true THEN
            break = false
        ELSE
            break = true
        END IF

        FOR x = 2 TO boardwidth
            IF board(x, y).clr <> clr THEN
                IF clr <> 0 AND count >= 3 AND break THEN
                    numsets = numsets + 1
                    score = score + count * breakmult * power(additionalbase, numsets - 1)
                    testformatches% = true
                    FOR i = tx TO x - 1
                        board(i, y).flag = true
                        IF clr = red AND board(i, y).flashing = true THEN redout = true
                        IF clr = yellow AND board(i, y).flashing = true THEN yellowout = true
                        IF clr = blue AND board(i, y).flashing = true THEN blueout = true
                    NEXT

                    IF count >= 6 THEN
                        IF clr = red THEN redblast = true
                        IF clr = yellow THEN yellowblast = true
                        IF clr = blue THEN blueblast = true
                    END IF
                END IF

                tx = x
                clr = board(x, y).clr
                count = 1
                IF board(x, y).fixed = true THEN
                    break = false
                ELSE
                    break = true
                END IF
            ELSE
                IF board(x, y).fixed = false THEN break = true
                count = count + 1
            END IF
        NEXT
        IF clr <> 0 AND count >= 3 AND break THEN
            numsets = numsets + 1
            IF gamemode = normalgame THEN
                score = score + count * breakmult * power(additionalbase, numsets - 1)
            END IF

            testformatches% = true
            FOR i = tx TO x - 1
                board(i, y).flag = true
                IF clr = red AND board(i, y).flashing = true THEN redout = true
                IF clr = yellow AND board(i, y).flashing = true THEN yellowout = true
                IF clr = blue AND board(i, y).flashing = true THEN blueout = true
            NEXT

            IF count >= 6 THEN
                IF clr = red THEN redblast = true
                IF clr = yellow THEN yellowblast = true
                IF clr = blue THEN blueblast = true
            END IF
        END IF
    NEXT

    FOR x = boardwidth TO 1 STEP -1
        count = 1
        ty = 1
        clr = board(x, 1).clr
        IF board(x, 1).fixed = true THEN
            break = false
        ELSE
            break = true
        END IF

        FOR y = 2 TO boardheight
            IF board(x, y).clr <> clr THEN
                IF clr <> 0 AND count >= 3 AND break THEN
                    numsets = numsets + 1
                    IF gamemode = normalgame THEN
                        score = score + count * breakmult * power(additionalbase, numsets - 1)
                    END IF
                    testformatches% = true
                    FOR i = ty TO y - 1
                        board(x, i).flag = true
                        IF clr = red AND board(x, i).flashing = true THEN redout = true
                        IF clr = yellow AND board(x, i).flashing = true THEN yellowout = true
                        IF clr = blue AND board(x, i).flashing = true THEN blueout = true
                    NEXT

                    IF count >= 6 THEN
                        IF clr = red THEN redblast = true
                        IF clr = yellow THEN yellowblast = true
                        IF clr = blue THEN blueblast = true
                    END IF
                END IF

                ty = y
                clr = board(x, y).clr
                count = 1
                IF board(x, y).fixed = true THEN
                    break = false
                ELSE
                    break = true
                END IF
            ELSE
                IF board(x, y).fixed = false THEN break = true
                count = count + 1
            END IF
        NEXT
        IF clr <> 0 AND count >= 3 AND break THEN
            IF gamemode = normalmode THEN
                score = score + count * breakmult * power(additionalbase, numsets - 1)
            END IF

            numsets = numsets + 1
            testformatches% = true
            FOR i = ty TO y - 1
                board(x, i).flag = true
                IF clr = red AND board(x, i).flashing = true THEN redout = true
                IF clr = yellow AND board(x, i).flashing = true THEN yellowout = true
                IF clr = blue AND board(x, i).flashing = true THEN blueout = true
            NEXT

            IF count >= 6 THEN
                IF clr = red THEN redblast = true
                IF clr = yellow THEN yellowblast = true
                IF clr = blue THEN blueblast = true
            END IF
        END IF
    NEXT

    IF redblast THEN player1score.score = player1score.score + blastbonus
    IF yellowblast THEN player1score.score = player1score.score + blastbonus
    IF blueblast THEN player1score.score = player1score.score + blastbonus
END FUNCTION

SUB titlemenu
    fadelevel = 2
    CLS
    box true, 27, 1, 26, 12, 15, 7, false, true
    displaytitle 28, 2, false
    displaymenu mainmenu(), "MAIN MENU", 28, 14, 22, 6, 1

    rest 100, false

    fadelevel = 1
    clearscreen 254, 8, 0
    box true, 27, 1, 26, 12, 15, 7, false, true
    displaytitle 28, 2, false
    displaymenu mainmenu(), "MAIN MENU", 28, 14, 22, 6, 1

    selection = getselection(mainmenu(), 30, 18, 20)
END SUB

SUB titlescreen
    fadelevel = 2
    displaytitle 28, 7, false
    putstringf 40, 18, "By Timothy Peters", "ooajjjjjjjakkkkkkkkkk", "aaaaaaaaaaaaaaaaaaaaa", true

    rest 100, false
    fadelevel = 1
    displaytitle 28, 7, false
    putstringf 40, 18, "By Timothy Peters", "ooajjjjjjjakkkkkkkkkk", "aaaaaaaaaaaaaaaaaaaaa", true
   
    rest 200, false
    fadelevel = 2
    box true, 26, 5, 27, 16, 15, 7, false, false
    rest 100, false
    fadelevel = 1
    box true, 26, 5, 27, 16, 15, 7, false, false
   
    rest 3000, true
    fadeout
    rest 100, false
END SUB

FUNCTION tryagain%
    DIM keyinput AS STRING

    box false, centerx - 9, centery - 1, 20, 3, 11, 3, false, false
    putstring centerx - 8, centery, " TRY AGAIN? (Y/N) ", 15, 3, false

    WHILE keyinput <> "Y" AND keyinput <> "N"
        keyinput = UCASE$(INKEY$)
    WEND

    IF keyinput = "Y" THEN tryagain% = true ELSE tryagain% = false
END FUNCTION

SUB voidbox (x AS INTEGER, y AS INTEGER, wide AS INTEGER, high AS INTEGER, char AS INTEGER, fc AS INTEGER, bc AS INTEGER)
    DIM xi AS INTEGER, yi AS INTEGER

    FOR yi = y TO y + high - 1
        FOR xi = x TO x + wide - 1
            putchar xi, yi, char, fc, bc
        NEXT
    NEXT
END SUB

