FastASMTetris.asm
Compile옵션 및 FastASM의 라이브러리와 전체 코드를 불러오는 일종의 make 역할을 하는 파일입니다.
include "%FastASM%\Init.inc"
!_
Purpose: FastASM Tetris
_!
Option WindowsForms
Option Resource On
Option Buffer Memory
;Option Debug On
;Option Error On
;Option DebugProcedure On
!_
Purpose: Header
_!
include "%FastASM%\FastASM.inc"
include "frmMain.inc"
!_
Purpose: Code
_!
include "%FastASM%\FastASM.asm"
include "frmMain.asm"
Run frmMain
; Buffer Type File size
;------------------------
; NoBuffer 0000264D
; MemoryBuffer 00002F35
; BitmapBuffer 0000279C
; Compile optimization time
; 1, 0.8 0.7 0.6
frmMain.inc
클래스와 변수들을 정의한 해더파일입니다.
변수들이 전역과 Class가 섞여 있는데 초칭기 FastASM은 클래스를 지원하지 않았었기에 전부 전역으로 변수를 정의했었고 Class 지원을 하면서 Class 테스트를 위한 코드 수정을 하다보니 이런 모양이 됐습니다.
!_
Purpose: frmMain
_!
TIMER_ID = 1 ; 타이머 ID
PADDING_ALL = 50 ; 상하좌우 여백
BLOCK_SHADOW = 3 ; 테트리스 블록 테두리 두께
Enum eDoBlock, \ ; 블록 출력 , 이동, 저장 구분 열거자
Paint, Move, Save, Next
Enum eMove, \ ; 블록 이동 구분 열거자
Rotate, Left, Right, Down
MATRIX_SPACE = 7 ; 테트리스 배열 공백
MATRIX_GHOST = 8 ; 테트리스 배열 고스트
MATRIX_ROWS = 20 ; 테트리스 배열 높이
MATRIX_COLS = 10 ; 테트리스 배열 넓이
MATRIX_BLOCK = 30 ; 테트리스 블록 크기
; 테트리스 배열 좌표
MATRIX_LEFT = PADDING_ALL
MATRIX_TOP = PADDING_ALL
MATRIX_RIGHT = MATRIX_LEFT + MATRIX_COLS * MATRIX_BLOCK
MATRIX_BOTTOM = MATRIX_TOP + MATRIX_ROWS * MATRIX_BLOCK
BORDER_WIDTH = 8 ; 테트리스 배열 테두리 두께
COLOR_BORDER = $667799 ; 테트리스 배열 테두리 색
Dim BrushBorder As Long := COLOR_BORDER ; 테트리스 배열 테두리 브러시
; 테트리스 배열 및 테두리 좌표
Dim RectMatrix As RECT := MATRIX_LEFT, MATRIX_TOP, MATRIX_RIGHT, MATRIX_BOTTOM
Dim RectBorder As RECT := MATRIX_LEFT - BORDER_WIDTH, MATRIX_TOP - BORDER_WIDTH, \
MATRIX_RIGHT + BORDER_WIDTH, MATRIX_BOTTOM + BORDER_WIDTH
TETRIS_FONT_SIZE= 30 ; 출력 폰트 크기
SCORE_UNIT = 10 ; 점수 단위
GOAL_LINE = 5 ; 레벨당 클리어 라인 시작 개수
; 게임 정보 좌표
INFO_RIGHT = MATRIX_RIGHT + MATRIX_BLOCK * 5
Dim RectInfo As RECT := MATRIX_RIGHT + MATRIX_BLOCK, MATRIX_TOP, INFO_RIGHT, MATRIX_BOTTOM
; 0.8초를 시작으로 매 레벨 12% 씩 속도가 빨라진다. 타이머에서 사용시 4 를 곱해서 사용한다 .
Dim abLevelSpeed(20) As Byte := 200,176,155,136,120,106,93,82,72,63,56,49,43,38,33,29,26,23,20,18
; 윈도우 클라이언트 크기
CLIENT_WIDTH = INFO_RIGHT + PADDING_ALL
CLIENT_HEIGHT = MATRIX_BOTTOM + PADDING_ALL
INIT_BLOCK = 20 ; 초기화면 블록 크기
; 초기화면, TETRIS 글자하나의 크기는 3 * 5
Dim abGameInit(21)(5) As Byte := \
0,0,0,7,1,1,1,7,0,0,0,7,2,2,7,7,3,7,7,5,5,\
7,0,7,7,1,7,7,7,7,0,7,7,2,7,2,7,3,7,5,7,7,\
7,0,7,7,1,1,1,7,7,0,7,7,2,2,7,7,3,7,7,5,7,\
7,0,7,7,1,7,7,7,7,0,7,7,2,7,2,7,3,7,7,7,5,\
7,0,7,7,1,1,1,7,7,0,7,7,2,7,2,7,3,7,5,5,7
; 초기화면 그래픽 TETRIS 출력용 좌표
INIT_LEFT = (CLIENT_WIDTH - (INIT_BLOCK * abGameInit.Cols)) / 2
INIT_TOP = CLIENT_HEIGHT / 2 - (INIT_BLOCK * abGameInit.Rows)
; 초기화면의 문자열 출력용 좌표
Dim RectInit As RECT := 0, INIT_TOP-TETRIS_FONT_SIZE*2, CLIENT_WIDTH,INIT_TOP+TETRIS_FONT_SIZE*12
; 블록 종류별 brush, pen
Dim aBrushBlock(7) As Long := $EAEA00,$02FBFF,$00CC30,$FF3101,$FF9933,$3366FF,$CC33CC
Dim aBrushBorder(7) As Long := $FDB100,$00B9CE,$239841,$B41F00,$D26900,$0026B4,$8A208A
; 블록 종류별 모양 데이터
Dim awBlock(4)(aBrushBlock.Cols) As Short := \
0000011001100000b,0000011001100000b,0000011001100000b,0000011001100000b,\ ; O
0000111100000000b,0010001000100010b,0000111100000000b,0010001000100010b,\ ; I
0000001101100000b,0010001100010000b,0000001101100000b,0010001100010000b,\ ; S
0000011000110000b,0001001100100000b,0000011000110000b,0001001100100000b,\ ; Z
0000011101000000b,0010001000110000b,0001011100000000b,0110001000100000b,\ ; L
0000011100010000b,0011001000100000b,0100011100000000b,0010001001100000b,\ ; J
0000011100100000b,0010001100100000b,0010011100000000b,0010011000100000b ; T
; 파일 크기를 줄이기 위해 문자열을 1바이트 문자열로 정의한다.
Dim sGameOver As StringA := "GAME OVER"
Dim sGamePause As StringA := "PAUSE"
Dim sGameInfo As StringA := "NEXT",13,13,13,13,13,\
"LEVEL",13,"%d" ,13,13,\
"SCORE",13,"%d" ,13,13,\
"GOAL",13,"%d" ,13,13,\
"HELP",13,"<F1>"
Dim sGameInit As StringA := "FastASM",13,13,13,13,13,13,13,13,13,13,\
"Press enter key to start"
Dim sGameHelpTitle As StringA:= "About"
Dim sGameHelp As StringA := "ENTER",Keys.Tab, "Start",13,\
"ESC",Keys.Tab,"Stop" ,13,\
"PAUSE",Keys.Tab,"Pause" ,13,\
"G",Keys.Tab,"Ghost" ,13,13,\
"UP",Keys.Tab,"Rotate" ,13,\
"LEFT",Keys.Tab,"Move Left" ,13,\
"RIGHT",Keys.Tab,"Move Right" ,13,\
"DOWN",Keys.Tab,"Soft Drop" ,13,\
"SPACE",Keys.Tab,"Hard Drop" ,13,13,\
; 블록 드랍 소리
_BLOCK_DROP_INST = 118
_BLOCK_DROP_NOTE = 20
_BLOCK_DROP_TIME = 30
; 레벨업 소리
_BLOCK_LEVEL_INST = 55
_BLOCK_LEVEL_NOTE = 50
_BLOCK_LEVEL_TIME = 1000
; 줄 클리어 소리
_BLOCK_CLEAR_INST = 127
_BLOCK_CLEAR_NOTE = 10
_BLOCK_CLEAR_TIME = 200
; 블록 정보
Class clsBlock
Dim Type As Long ; 블록 종류
Dim NextType As Long ; 다음 블록 종류
Dim Rotate As Long ; 블록 회전 순서
Dim X As Long ; 블록 X좌표
Dim Y As Long ; 블록 Y좌표
End Class
; 게임 정보
Enum eGame, \
Init, Run, Pause, Over
Class clsGame
Dim Level As Long
Dim Score As Long
Dim GoalLine As Long
Dim bStatus As Byte
End Class
Class frmMain Form
Dim Block As clsBlock
Dim Game As clsGame
Dim bGhost As Byte
Dim abMatrix(MATRIX_COLS)(MATRIX_ROWS) As Byte ; 테트리스 배열
Static FontName As String := "Fixedsys"
Static Name As String := "FastASMTetris"
Static Text As String := "FastASM Tetris"
End Class
frmMain.asm
클래스 함수를 정의한 코드 파일입니다.
초창기 클래스는 함수 상속을 지원하지 않았으나 비용없이 상속이 가능하게 구현한 후 상속받은 함수를 사용하기도 합니다.
코드 부분 또한 클래스를 지원하지 않을 때 제작된 걸 클래스에 맞춰 테스트 용도로 수정한 코드라 객체지향을 제대로 보여주는데는 많이 부족합니다.
!_
Purpose: frmMain
_!
Class frmMain
Sub WindowProc
Begin
BEGIN_MSG_MAP
ON_PAINT
ON_KEYDOWN
ON_TIMER
ON_CREATE
ON_DESTROY
END_MSG_MAP
End Sub
!_
Purpose: 생성자
_!
Sub Tiny New
Begin
Let rMe:WndProc(0) := frmMain.WindowProc
Let rMe:psName(0) := frmMain.Name
Let rMe:psText(0) := frmMain.Text
Let rMe:Style(0) := WS_VISIBLE or \
WS_CAPTION or \
WS_THICKFRAME or \
WS_SYSMENU or \
WS_MAXIMIZEBOX or \
WS_MINIMIZEBOX or \
WS_CLIPCHILDREN or \
WS_CLIPSIBLINGS
Let rMe:BackColor(0) := Color.Black
Let rMe:Cursor(0) := Cursors.Default
Let rMe:Icon(0) := IDI_FASTASMTETRIS
Let rMe:BitmapFont(0) := Font.Bitmap.Default
Let rMe:BitmapFontSize(0):= 2
;Let rMe:psFontName(0) := frmMain.FontName
;Let rMe:FontSize(0) := TETRIS_FONT_SIZE
Let rMe:ForeColor(0) := Color.White
Let rTemp := CW_USEDEFAULT
Let rMe:Left(0) := rTemp
Let rMe:Top(0) := rTemp
Let rMe:iClientWidth(0) := CLIENT_WIDTH
Let rMe:iClientHeight(0) := CLIENT_HEIGHT
Let rMe:bBufferMode(0) := eBufferMode.Fit
Let rMe:BufferWidth(0) := CLIENT_WIDTH
Let rMe:BufferHeight(0) := CLIENT_HEIGHT
; Class
Let rMe:bGhost(0) := True
End Sub
!_
Purpose: 사용할 자원을 생성한다.
_!
Sub frmMain_CREATE
Dim .rLoop As Long rbx
Begin
; 미디출력을 준비한다 .
Midi.Out.Open
if Project.Buffer <> eDoubleBuffer.Memory
; brush와 pen 을 생성한다 .
Get BrushBorder(0) := Brush.Solid BrushBorder(0)
For Each .rLoop In aBrushBlock
Get aBrushBorder(.rLoop):= Brush.Solid aBrushBorder(.rLoop)
Get aBrushBlock(.rLoop) := Brush.Solid aBrushBlock(.rLoop)
Next
end if
End Sub
!_
Purpose: 사용한 자원을 반환하고 종료한다.
_!
Sub frmMain_DESTROY
Dim .rLoop As Long rbx
Begin
; 미디출력을 종료한다 .
Midi.Out.Close
if Project.Buffer <> eDoubleBuffer.Memory
; 사용한 brush 와 pen을 해제한다.
Brush.Dispose BrushBorder(0)
For Each .rLoop In aBrushBlock
Brush.Dispose aBrushBorder(.rLoop)
Brush.Dispose aBrushBlock(.rLoop)
Next
end if
Application.Exit
End Sub
!_
Purpose: 키입력을 처리한다 .
_!
Sub frmMain_KEYDOWN, .Key, .KeyData
Dim .rbKey As Byte rParam1L
Begin
Select .rbKey
Case Keys.F1 ; 도움말
MessageBoxA.Show sGameHelp, sGameHelpTitle
Case Keys.Escape ; 게임 종료
If rMe:Game.bStatus(0) = eGame.Init
Me OnClose
Return
Else If rMe:Game.bStatus(0) = eGame.Run
Timer.Stop TIMER_ID
End If
Let rMe:Game.bStatus(0) := eGame.Init
Me Refresh
Case Keys.Enter ; 게임 시작
Me StartGame
Case Keys.Pause ; 게임 정지
If rMe:Game.bStatus(0) = eGame.Run
Let rMe:Game.bStatus(0) := eGame.Pause
Timer.Stop TIMER_ID
Else If rMe:Game.bStatus(0) = eGame.Pause
Let rMe:Game.bStatus(0) := eGame.Run
Me StartTimer
End If
Me Refresh
Case "G"
Let rMe:bGhost(0) ^= 1
Me Refresh
Case Keys.Up ; 블록 회전
Me MoveBlock, eMove.Rotate
Case Keys.Down ; 블록 아래
Me MoveBlock, eMove.Down
Case Keys.Left ; 블록 왼쪽
Me MoveBlock, eMove.Left
Case Keys.Right ; 블록 오른쪽
Me MoveBlock, eMove.Right
Case Keys.Space ; 블록 바닥
If rMe:Game.bStatus(0) = eGame.Run
Me DropBlock
If rRetL = False Return
Me SaveBlock
End If
End Select
End Sub
!_
Purpose: WM_TIMER, 오직 블록을 아래로 내릴 때만 이벤트가 발생한다.
_!
Sub frmMain_TIMER, .TimerID
Begin
Me MoveBlock, eMove.Down
End Sub
!_
Purpose: 타이머를 실행한다 . 블록이 떨어지는 속도는 레벨당 1 바이트로 저장하기 위해
밀리세컨드 / 4 단위로 저장되어 있다 . 읽은 후 4를 곱해서 타이머를 실행한다 .
_!
Sub StartTimer
Begin
Le3 rTemp := rMe:Game.Level(0) --1; 레벨은 1부터 , 레벨속도 데이터는 0부터 시작하기 때문에
; 레벨에서 1 을 뺀후 데이터를 읽는다.
Let rTemp <= abLevelSpeed(rTemp)
Let rTemp <<= 2
Timer.Start TIMER_ID, rTemp
End Sub
!_
Purpose: 게임화면을 출력한다.
_!
Sub frmMain_PAINT, .e As rbx
Begin
; 배경을 지운다.
Graphics.Clear .e
; 시작화면 또는 실행화면을 출력한다.
If rMe:Game.bStatus(0) = eGame.Init
Me PaintMatrix, .e, abGameInit, abGameInit.Cols - 1, abGameInit.Rows - 1, \
INIT_LEFT, INIT_TOP, INIT_BLOCK
Graphics.DrawStringA .e, sGameInit, sGameInit.Length, RectInit, DT_CENTER
Else
Me PaintGame, .e
End If
Let rRet := 0
End Sub
!_
Purpose: 실행화면을 출력한다.
_!
Sub PaintGame, .e As rbx
Dim .rGhost As Long r12
Dim .sBuffer(sGameInfo.Length + 30) As Char
Begin
; 테트리스 배열 테두리를 그린다.
Graphics.FillRectangle .e, BrushBorder(0), RectBorder
Graphics.FillRectangle .e, rMe:hBackColor(0), RectMatrix
; 테트리스 배열을 그린다 .
Me PaintMatrix, .e, ByRef rMe:abMatrix(0),frmMain.abMatrix.Cols-1,frmMain.abMatrix.Rows-1,\
MATRIX_LEFT, MATRIX_TOP, MATRIX_BLOCK
; 고스트를 출력한다.
If rMe:bGhost(0) <> False And rMe:Game.bStatus(0) = eGame.Run
Let .rGhost := rMe:Block.Y(0)
Me DropBlock
Me DoBlock, .e, eDoBlock.Paint, True
Let rMe:Block.Y(0) := .rGhost
End If
Me DoBlock, .e, eDoBlock.Next, False ; 다음 블록을 출력한다 .
Me DoBlock, .e, eDoBlock.Paint, False ; 블록을 출력한다.
; 정지 또는 게임오버시 문자열을 출력한다.
Let rParam2 := 0
If rMe:Game.bStatus(0) = eGame.Over
Let rParam2 := sGameOver
Let rParam3 := sGameOver.Length
Else If rMe:Game.bStatus(0) = eGame.Pause
Let rParam2 := sGamePause
Let rParam3 := sGamePause.Length
End If
If rParam2 <> 0
Graphics.DrawStringA .e, rParam2, rParam3, RectBorder, \
DT_CENTER or DT_VCENTER or DT_SINGLELINE
End If
; 게임 진행상황을 출력한다.
String.FormatA ByRef .sBuffer(0), sGameInfo, rMe:Game.Level(0), rMe:Game.Score(0), \
rMe:Game.GoalLine(0)
Graphics.DrawStringA .e, ByRef .sBuffer(0), rRet, RectInfo, DT_CENTER
End Sub
!_
Purpose: 게임을 시작한다 .
_!
Sub StartGame
Begin
If rMe:Game.bStatus(0) <> eGame.Run
; 데이터를 초기화한다 .
Array.Clear rMe:abMatrix, 0, 0, MATRIX_SPACE
Let rTemp := 0
Let rMe:Game.Level(0) := 1
Let rMe:Game.GoalLine(0) := GOAL_LINE
Let rMe:Game.Score(0) := rTemp
Let rMe:Block.NextType(0):= rTemp
Let rMe:Game.bStatus(0) := eGame.Run
; 타이머를 실행한다.
Me StartTimer
; 블록을 생성한다.
Get rMe:Block.NextType(0) := Me CreateBlockType
Me CreateBlock
End If
End Sub
!_
Purpose: 배열을 출력한다 .
_!
Sub PaintMatrix, .e As r14, .pbMatrix, .Cols, .Rows, .Left, .Top, .Size
Dim .rIndex As Long rbx
Dim .rCols As Long rdi
Dim .rRows As Long r13
Dim .rX As Long rsi
Dim .rY As Long r12
Begin
Let .rIndex := .pbMatrix
Let .rCols := .Cols
Let .rRows := .Rows
For .rY = 0 To .rRows
For .rX = 0 To .rCols
Let rTemp <= byte [.rIndex]
If rTempL <> MATRIX_SPACE
Me PaintSquare, .e, rTemp, .rX, .rY, .Left(0), .Top(0), .Size(0), False
End If
Let .rIndex++
Next
Next
End Sub
!_
Purpose: 블록의 사각형 하나를 출력한다.
_!
Sub PaintSquare, .e As r12, .Color, .X, .Y, .Left, .Top, .Size, .IsGhost
Dim .rLeft As Long rdi
Dim .rTop As Long rsi
Dim .rColor As Long r13
Dim .rSize As Long rbx
Begin
If .X >= 0 And .Y >=0
Let .rColor := .Color
Let .rSize := .Size(0)
Le3 .rLeft := .X ** .rSize
Let .rLeft += .Left(0)
Le3 .rTop := .Y ** .rSize
Let .rTop += .Top(0)
Graphics.DrawRectangle .e, aBrushBorder(.rColor), .rLeft, .rTop, \
ByRef [.rLeft + .rSize], ByRef [.rTop + .rSize]
Let rTemp := rMe:hBackColor(0)
If .IsGhost(0) = False Then rTemp := aBrushBlock(.rColor)
Graphics.DrawRectangle .e, rTemp, ByRef [.rLeft+BLOCK_SHADOW], ByRef [.rTop+BLOCK_SHADOW],\
ByRef [.rLeft + .rSize - BLOCK_SHADOW], ByRef [.rTop + .rSize - BLOCK_SHADOW]
End If
End Sub
!_
Purpose: 블록 타입을 랜덤하게 생성한다.
_!
Sub Tiny CreateBlockType
Dim .riBlockTypeCount As Long ecx
Dim .riBlockType As Long edx
Begin
Random.Int
Let .riBlockTypeCount := awBlock.Rows
div .riBlockTypeCount
Let rRet := 0
Let rRetE := .riBlockType
End Sub
!_
Purpose: 블록을 생성한다 .
_!
Sub CreateBlock
Dim .rBlockType As Long rcx
Begin
; 같은 블록이 3번 연속해서 나오지 않도록 한다.
Do
Me CreateBlockType
Loop Until rRet <> rMe:Block.NextType(0) Or rRet <> rMe:Block.Type(0)
Let .rBlockType := rRet
Let rMe:Block.Type(0) := rMe:Block.NextType(0)
Let rMe:Block.NextType(0):= .rBlockType
Let rMe:Block.Rotate(0) := 0
Let rMe:Block.Y(0) := -1
Let rMe:Block.X(0) := (frmMain.abMatrix.Cols - 4) / 2
; 블록을 생성할 자리가 없으면 게임을 종료한다 .
Me DoBlock, 0, eDoBlock.Move, False
If rRetL = False
Let rMe:Game.bStatus(0) := eGame.Over
Timer.Stop TIMER_ID
End If
Me Refresh
End Sub
!_
Purpose: 블록을 출력하거나 이동 가능을 검토하거나 테트리스 배열에 저장한다 .
Returns: True- 이동가능, False- 이동불가
_!
Sub DoBlock, .e As r13, .Mod, .IsGhost As r15
Dim .rX As Long rsi ; 블록 사각형 X좌표
Dim .rY As Long rdi ; 블록 사각형 Y좌표
Dim .rBlockType As Long rcx ; 블록 타입
Dim .rBlockTypeL As Byte cl ; 블록 타입 ( 테트리스 배열 저장용 1 바이트 변수)
Dim .rBlockRotate As Long rdx ; 블록 회전
Dim .rwBlock As Short r14w ; 블록 데이터
Dim .rBlockCol As Long r12 ; 블록 모양을 읽어오기 위한 루프
Dim .rbBlockRow As Byte bh
Dim .rbMode As Byte bl
Begin
Let .rbMode := CByte .Mod
If .rbMode = eDoBlock.Next
Let .rY := 0
Let .rBlockType := rMe:Block.NextType(0)
Let .rBlockRotate := 0
Else
Let .rY := rMe:Block.Y(0)
Let .rBlockType := rMe:Block.Type(0)
Let .rBlockRotate := rMe:Block.Rotate(0)
End If
Let .rwBlock := awBlock(.rBlockRotate)(.rBlockType)
For .rbBlockRow = 0 To 3
Let .rX := rMe:Block.X(0)
For .rBlockCol = 0 To 3
Let .rwBlock <<= 1
If CARRY?
Select .rbMode
Case eDoBlock.Paint ; 블록을 출력한다.
Me PaintSquare, .e, rMe:Block.Type(0), .rX, .rY, \
MATRIX_LEFT, MATRIX_TOP, MATRIX_BLOCK, .IsGhost
Case eDoBlock.Next ; 다음 블록을 출력한다 .
; TODO: 테트리스 타입에 따라 출력하는 위치를 하드코딩으로 조정했다 .
; 블록 모양이 변경되면 수정해야 한다.
Let rTemp := 0
Let rTempE := RectInfo.left(0)
If rMe:Block.NextType(0) > 1
Let rTempE -= MATRIX_BLOCK / 2
End If
Me PaintSquare, .e, rMe:Block.NextType(0), .rBlockCol, .rY, \
rTemp, MATRIX_TOP + TETRIS_FONT_SIZE, MATRIX_BLOCK, False
Case eDoBlock.Move ; 블록을 이동할 수 있는지 검토한다.
If .rY >= 0
If .rX>=frmMain.abMatrix.Cols Or .rX<0 Or .rY>=frmMain.abMatrix.Rows
GoTo .CantMove
End If
If rMe:abMatrix(.rX)(.rY) <> MATRIX_SPACE GoTo .CantMove
End If
Case eDoBlock.Save ; 블록을 테트리스 배열에 저장한다.
Let rMe:abMatrix(.rX)(.rY) := .rBlockTypeL
End Select
End If
Let .rX++
Next
Let .rY++
Next
Let rRetL := True
Return
.CantMove:
Let rRetL := False
End Sub
!_
Purpose: 블록을 이동시킨다 .
_!
Sub MoveBlock, .Direction
Dim .rbDirection As Byte bl
Dim .rBlockX As Long rdi
Dim .rBlockY As Long rsi
Dim .rBlockRotate As Long r12
Begin
If rMe:Game.bStatus(0) <> eGame.Run Return
Let .rbDirection := CByte .Direction
Let .rBlockX := rMe:Block.X(0)
Let .rBlockY := rMe:Block.Y(0)
Let .rBlockRotate := rMe:Block.Rotate(0)
Select .rbDirection
Case eMove.Rotate
Let rMe:Block.Rotate(0)++
Let rMe:Block.Rotate(0) %= awBlock.Cols
Case eMove.Left
Let rMe:Block.X(0)--
Case eMove.Right
Let rMe:Block.X(0)++
Case eMove.Down
Let rMe:Block.Y(0)++
End Select
Me DoBlock, 0, eDoBlock.Move, False
If rRetL = False
Let rMe:Block.X(0) := .rBlockX
Let rMe:Block.Y(0) := .rBlockY
Let rMe:Block.Rotate(0) := .rBlockRotate
If .rbDirection = eMove.Down
Me SaveBlock
End If
Else
Me Refresh
End If
End Sub
!_
Purpose: 블록을 떨어뜨린다 .
_!
Sub DropBlock
Begin
; 블록을 떨어뜨린다 .
For rMe:Block.Y(0) To frmMain.abMatrix.Rows - 1
Me DoBlock, 0, eDoBlock.Move, False
If rRetL = False Exit For
Next
Let rMe:Block.Y(0)--
; 시작위치부터 블록을 내릴수 없으면 게임을 종료한다.
Let rRetL := True
If rMe:Block.Y(0) < 0
Let rMe:Game.bStatus(0) := eGame.Over
Timer.Stop TIMER_ID
Let rRetL := False
End If
End Sub
!_
Purpose: 블록을 저장하고 클리어한다.
_!
Sub SaveBlock
Dim .rIndex As Long r12
Dim .rX As Long rdi
Dim .rSource As Long rsi
Dim .rDest As Long rdi
Dim .rScore As Long rbx
Begin
; 블록을 저장한다.
Me DoBlock, 0, eDoBlock.Save, False
; 완성된 줄을 지운다 .
; 블록의 크기는 4칸이므로 블록이 저장된 위치부터 아래로 4 칸까지 검토하고 지운다 .
Let .rScore := SCORE_UNIT
Le3 .rIndex := rMe:Block.Y(0) ** frmMain.abMatrix.Cols
For rMe:Block.Y(0) To frmMain.abMatrix.Rows - 1
Let rTemp := 0
For .rX = 0 To frmMain.abMatrix.Cols-1
If rMe:abMatrix(.rIndex) < MATRIX_SPACE
Let rTemp++
End If
Let .rIndex++
Next
If rTemp = frmMain.abMatrix.Cols
; 완성된 줄을 지운다 .
Let rDest := ByRef rMe:abMatrix(.rIndex - 1)
Le3 rSource := rDest -- frmMain.abMatrix.Cols
Le3 rCount := rMe:Block.Y(0) ** frmMain.abMatrix.Cols
std
rep movsb
cld
; 맨 윗줄을 지운다 .
Array.Clear rMe:abMatrix, 0, frmMain.abMatrix.Cols, MATRIX_SPACE
; ; 점수를 증가시킨다 . 점수 획득은 2의 배수로 증가한다.
Let rMe:Game.Score(0) += .rScore
Let .rScore *= 2
; 레벨마다 할당된 줄을 클리어하면 레벨을 높인다.
Let rMe:Game.GoalLine(0)--
If rMe:Game.GoalLine(0) = 0
Le3 rMe:Game.GoalLine(0) := rMe:Game.Level(0) ++ GOAL_LINE
If rMe:Game.Level(0) < abLevelSpeed.Length
Let rMe:Game.Level(0)++
Timer.Stop TIMER_ID
Me StartTimer
Midi.Out.Play _BLOCK_LEVEL_INST, _BLOCK_LEVEL_NOTE, _BLOCK_LEVEL_TIME
End If
End If
End If
Next
If .rScore = SCORE_UNIT
Midi.Out.Play _BLOCK_DROP_INST, _BLOCK_DROP_NOTE, _BLOCK_DROP_TIME
Else
Midi.Out.Play _BLOCK_CLEAR_INST, _BLOCK_CLEAR_NOTE, _BLOCK_CLEAR_TIME
End If
; 새 블록을 생성한다 .
Me CreateBlock
End Sub
End Class