PROCEDURE reset_figure
STORE 1 TO THIS.dY, THIS.dX
ENDPROC
FUNCTION get_color ()
DO CASE
CASE INLIST (THIS.mode, 1,11)
RETURN RGB (c1,c0,c0)
CASE THIS.mode = 2
RETURN RGB (c1,c1,c0)
CASE INLIST (THIS.mode, 3,31,32,33)
RETURN RGB (c1,c0,c1)
CASE INLIST (THIS.mode, 4,41)
RETURN RGB (c0,c1,c1)
CASE INLIST (THIS.mode, 5,51)
RETURN RGB (c0,c1,c0)
CASE INLIST (THIS.mode, 6,61,62,63)
RETURN RGB (c0,c0,c1)
CASE INLIST (THIS.mode, 7,71,72,73)
RETURN RGB (c0,c0,c0)
OTHER
RETURN RGB (c1,c1,c1)
ENDCASE
ENDFUNC
PROCEDURE set_state (numColor, numOwner)
LOCAL ii
FOR ii=1 TO tetris
WITH ThisForm.d.arr [ THIS.dY+THIS.arrY[ii], THIS.dX+THIS.arrX[ii] ]
.BackColor = numColor
.Owner = numOwner
ENDWITH
ENDFOR
ENDPROC
PROCEDURE set_owner (numOwner)
LOCAL ii
FOR ii=1 TO tetris
WITH ThisForm.d.arr [ THIS.dY+THIS.arrY[ii], THIS.dX+THIS.arrX[ii] ]
.Owner = numOwner
ENDWITH
ENDFOR
ENDPROC
PROCEDURE conflict (dY,dX, allowedMode)
LOCAL ii
FOR ii=1 TO tetris
IF Not (BETW(dY+THIS.dY+THIS.arrY[ii], 1, bucketHeight);
And BETW(dX+THIS.dX+THIS.arrX[ii], 1, bucketWidth))
RETURN .T.
ENDIF
WITH ThisForm.d.arr [ dY+THIS.dY+THIS.arrY[ii], dX+THIS.dX+THIS.arrX[ii] ]
IF Not (.Owner=0 Or .Owner=THIS.mode Or .Owner=allowedMode)
RETURN .T.
ENDIF
ENDWITH
ENDFOR
RETURN .F.
ENDPROC
FUNCTION move_ (dY,dX)
IF THIS.Conflict (dY,dX,0)
RETURN .F.
ELSE
THIS.set_free
THIS.dY = THIS.dY + dY
THIS.dX = THIS.dX + dX
THIS.set_visible
RETURN .T.
ENDIF
ENDPROC
PROCEDURE AddSquees
LOCAL lnY, lnX, lcName
FOR lnY=1 TO bucketHeight
FOR lnX=1 TO bucketWidth
lcName = STRTRAN('arr'+STR(lnY,2) + '_' + STR(lnX,2), ' ','0')
THIS.AddObject (lcName, 'sqee')
THIS.arr [lnY,lnX] = EVAL('THIS.'+lcName)
WITH THIS.arr [lnY,lnX]
.left = (lnX-1) * sqee_width
.top = (lnY-1) * sqee_height
.Owner = 0
.visible = .T.
ENDWITH
ENDFOR
ENDFOR
ENDPROC
PROCEDURE RemoveSquees
LOCAL lnY, lnX, lcName
FOR lnY=1 TO bucketHeight
FOR lnX=1 TO bucketWidth
lcName = STRTRAN('arr'+STR(lnY,2) + '_' + STR(lnX,2), ' ','0')
THIS.RemoveObject (lcName)
ENDFOR
ENDFOR
ENDPROC
FUNCTION init_figure
THIS.current_mode = INT (RAND() * THIS.max_mode) + 1
IF NOT BETW(THIS.current_mode, 1,THIS.max_mode)
THIS.current_mode = 1
ENDIF
WITH THIS.ff [THIS.current_mode]
.reset_figure
IF .conflict (0,0,0)
RETURN .F.
ENDIF
.set_visible
ENDWITH
RETURN .T.
ENDFUNC
FUNCTION debris_line (num) && if there is at least one line of debris
LOCAL ii
FOR ii=1 TO bucketWidth
IF THIS.arr [num, ii].Owner <> -1
RETURN .F.
ENDIF
ENDFOR
RETURN .T.
ENDFUNC
FUNCTION find_debris_line
LOCAL jj
FOR jj=bucketHeight TO 1 STEP -1
IF THIS.debris_line (jj)
RETURN jj
ENDIF
ENDFOR
RETURN 0
ENDFUNC
PROCEDURE shake_debris
LOCAL num, jj, ii, savedColor
num = THIS.find_debris_line()
IF num = 0
RETURN
ENDIF
* release line
FOR ii=1 TO bucketWidth
THIS.arr[num, ii].Owner = 0
THIS.arr[num, ii].BackColor = THIS.BackColor
ENDFOR
* drop all other lines
FOR jj=num-1 TO 1 STEP -1
FOR ii=1 TO bucketWidth
IF THIS.arr[jj,ii].Owner = -1
savedColor = THIS.arr [jj, ii].BackColor
THIS.arr [jj, ii].BackColor = THIS.BackColor
THIS.arr [jj, ii].Owner = 0
THIS.arr [jj+1, ii].BackColor = savedColor
THIS.arr [jj+1, ii].Owner = -1
ENDIF
ENDFOR
ENDFOR
ENDPROC
IF Not obj.Conflict (0,0,THIS.current_mode)
THIS.ff [THIS.current_mode].set_free
THIS.current_mode = obj.mode
THIS.ff [THIS.current_mode].set_visible
RETURN .T.
ELSE
RETURN .F.
ENDIF
ENDPROC
PROCEDURE rotate
WITH THIS.ff [THIS.current_mode]
DO WHILE .T.
IF THIS.rotate_figure (.turned_clockwise, .turned_clockwise_dY, .turned_clockwise_dX)
EXIT
ELSE
IF Not .move_right()
EXIT
ENDIF
ENDIF
ENDDO
ENDWITH
ENDPROC
DEFINE CLASS frm As Form
Caption = 'Tetris'
MaxButton = .F.
BorderStyle = 2
KeyPreview = .T.
ADD OBJECT d As bucket
ADD OBJECT t As Timer
PROCEDURE Init
WITH THIS.d
STORE 0 TO .top, .left
THIS.Width = .Width
THIS.Height = .Height
ENDWITH
THIS.d.init_figure
THIS.t.Interval = dropInterval && setting speed
ENDPROC
PROCEDURE Destroy
THIS.d.RemoveSquees
ENDPROC
PROCEDURE KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
DO CASE
CASE nKeyCode=27
THIS.release
CASE nKeyCode=keyLeft
THIS.d.ff [THIS.d.current_mode].move_left
CASE nKeyCode=keyRight
THIS.d.ff [THIS.d.current_mode].move_right
CASE nKeyCode=keyDrop
DO WHILE THIS.d.ff [THIS.d.current_mode].move_down()
ENDDO
CASE nKeyCode=keyRotate
THIS.d.rotate
ENDCASE
ENDPROC
PROCEDURE t.Timer
LOCAL obj
WITH ThisForm.d
obj = .ff [.current_mode]
IF Not obj.move_down()
obj.set_debris
IF .init_figure()
obj = .ff [.current_mode]
ELSE
ThisForm.release && here you lost
ENDIF
ENDIF
.shake_debris
ENDWITH
ENDPROC
ENDDEFINE