📄 main.asm
字号:
;作者:仙人指路,QQ 13633292, QQ群 28287492
;******************************************************************************
.386
.model flat, stdcall
option casemap:none
;******************************************************************************
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;通用控件库
include comctl32.inc
includelib comctl32.lib
include <data.inc>
;******************************************************************************
.code
;******************************************************************************
;--------------------------------------
; 把10*20的方块矩阵画到窗口上
;--------------------------------------
_MapToWnd proc
ret
_MapToWnd endp
;--------------------------------------
; 随机数
;--------------------------------------
_Rand proc uses ebx ecx edx, first, second
local stime: SYSTEMTIME
;取得随机数的种子,当然,可用别的方法代替
invoke GetLocalTime, addr stime ;当前本地时间
movzx eax, stime.wMilliseconds ;现在时间的毫秒
;mov ebx, 3
;mul ebx
movzx ebx, stime.wSecond
add eax, ebx
;invoke GetTickCount ; 程序开始运行时间的毫秒数
mov ecx, 23 ; X = ecx = 23
mul ecx ; eax = eax * X
add eax, 97 ; eax = eax + Y (Y = 97)
mov ecx, second ; ecx = 上限
sub ecx, first ; ecx = 上限 - 下限
inc ecx ; Z = ecx + 1 (得到了范围)
xor edx, edx ; edx = 0
div ecx ; eax = eax mod Z (余数在edx里面)
add edx, first ; 修正产生的随机数的范围
mov eax, edx ; eax = Rand_Number
ret
_Rand endp
;--------------------------------------
;索引号转换成窗口坐标
;方格左上角的坐标
;Cell是表示小方格的全局变量,
;i-行, j-列
;--------------------------------------
_IndexToCoord proc i, j
mov eax, 20
mul j
add eax, 2
mov Cell.left, eax ;求坐标X [x] = 2 + j * 20
mov eax, 20
mul i
add eax, 2
mov Cell.top, eax ;求坐标Y [y] = 2 + i * 20
mov eax, 20
mul j
add eax, 21 ;考虑到有间隔线
mov Cell.right, eax ;求坐标X
mov eax, 20
mul i
add eax, 21
mov Cell.bottom, eax ;求坐标Y
ret
_IndexToCoord endp
Test_IndexToCoord proc
invoke _IndexToCoord, 19, 9
;mov eax, 'K'
invoke wsprintf, addr DgBuffer, addr DgMsg, Cell.left, Cell.top, Cell.right, Cell.bottom
invoke MessageBox, NULL, addr DgBuffer, addr DgCaption, MB_OK
ret
Test_IndexToCoord endp
;--------------------------------------
;游戏矩阵在(i,j)处的符号
;返回al
;--------------------------------------
_GetMapChar proc uses edx, i, j
mov eax, i
mov edx, 10
mul edx
add eax, j
mov edx, offset Map
add edx, eax ; edx = Map + j * 10 + i
mov al, byte ptr [edx]
movzx eax, al
ret
_GetMapChar endp
;--------------------------------------
;设置游戏矩阵在(i,j)处的符号设置成'Q'
;返回al
;--------------------------------------
_SetMapChar proc uses edx, i, j
mov eax, i
mov edx, 10
mul edx
add eax, j
mov edx, offset Map
add edx, eax ; edx = Map + j * 10 + i
mov al, 'Q'
mov byte ptr [edx], al
ret
_SetMapChar endp
;--------------------------------------
; WM_PAINT事件
;--------------------------------------
_OnPaint proc
local ps: PAINTSTRUCT
local hdc
invoke BeginPaint, HMainWnd, addr ps
mov hdc, eax
;求需要重画的区域的长和宽
mov eax, ps.rcPaint.right
sub eax, ps.rcPaint.left
mov ecx, ps.rcPaint.bottom
sub ecx, ps.rcPaint.top
;把内存设备拷贝到窗口上
invoke BitBlt,
hdc, ps.rcPaint.left, ps.rcPaint.top, eax, ecx,
HMemWnd, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY
invoke EndPaint, HMainWnd, addr ps
ret
_OnPaint endp
;--------------------------------------
; 在缓冲区画图
;--------------------------------------
_DrawView proc uses ebx ecx esi
local i, j
;画背景是蓝色
invoke FillRect, HMemWnd, addr Panel, BlueBrush
;画10*20个方格
mov i, 0
.while i < 20 ;总共有20行
mov j, 0
.while j < 10 ;10列
invoke _IndexToCoord, i, j ;改变Cell的四角坐标
;(i,j)处的符号
invoke _GetMapChar, i, j
.if al == ' ' ;画空白方块
invoke FillRect, HMemWnd, addr Cell, BlackBrush
;invoke MessageBox, NULL, offset DgMsg, offset DgCaption, MB_OK
.elseif al == 'Q' ;画墙根
invoke StretchBlt, \
HMemWnd, Cell.left, Cell.top, CellWidth, CellHeight, \
HDCO, 0, 0, 35, 35, \
SRCCOPY
;invoke MessageBox, NULL, offset DgMsg, offset DgCaption, MB_OK
.endif
inc j
.endw
inc i
.endw
;画正在下落的东西,共4个方块,
.if IsDrawCooky
mov i, 0
mov esi, offset Dots
.while i < 4
mov ebx, [esi]
add esi, 4
mov ecx, [esi]
add esi, 4
invoke _IndexToCoord, ebx, ecx
invoke StretchBlt, \
HMemWnd, Cell.left, Cell.top, 20, 20, \
HDCX, 0, 0, 35, 35, \
SRCCOPY
inc i
.endw
.endif
ret
_DrawView endp
Test_WndCreate proc
invoke wsprintf, addr DgBuffer, addr DgMsg, HMemWnd
invoke MessageBox, NULL, addr DgBuffer, addr DgCaption, MB_OK
ret
Test_WndCreate endp
;--------------------------------------
; WM_CREATE消息
;--------------------------------------
_CreateOther proc
local hbmp1, hbmp2, hbmp3 ;位图句柄
local hdc
invoke GetDC, HMainWnd ;这样得到的窗口设备和BeginPaint得到的设备不一样
mov hdc, eax ;所以不能这样用
;画板的矩形大小
mov Panel.left, 0
mov Panel.top, 0
mov Panel.right, PanelWidth
mov Panel.bottom, PanelHeight
;正在下落的方块图片
invoke LoadBitmap, HInstance, IDB_BITMAP2
mov hbmp1, eax
invoke CreateCompatibleDC, hdc
mov HDCX, eax
invoke SelectObject, HDCX, hbmp1
;已经落到底的方块图片
invoke LoadBitmap, HInstance, IDB_BITMAP8
mov hbmp2, eax
invoke CreateCompatibleDC, hdc
mov HDCO, eax
invoke SelectObject, HDCO, hbmp2
;窗口图像的缓冲区
invoke CreateCompatibleDC, hdc
mov HMemWnd, eax
invoke CreateCompatibleBitmap, hdc, PanelWidth, PanelHeight
mov hbmp3, eax
invoke SelectObject, HMemWnd, hbmp3
;画空白小方格的画刷
invoke GetStockObject, BLACK_BRUSH
mov BlackBrush, eax
;画蓝色背景的画刷
invoke CreateSolidBrush, 0FF0000H
mov BlueBrush, eax
;释放
invoke DeleteObject, hbmp1
invoke DeleteObject, hbmp2
invoke DeleteObject, hbmp3
invoke ReleaseDC, HMainWnd, hdc
;先把图画到内存设备中
invoke _DrawView
;状态栏
invoke CreateStatusWindow, \
WS_CHILD or WS_VISIBLE, \
0, \
HMainWnd, \
ID_STATUSBAR
mov HStatus, eax
mov Score, 0
invoke wsprintf, addr StatusBuffer, addr StatusMsg, Score
invoke SendMessage, HStatus, SB_SETTEXT, 0, addr StatusBuffer
ret
_CreateOther endp
;--------------------------------------
; WM_CLOSE消息
;--------------------------------------
_OnClose proc
invoke DeleteObject, BlueBrush
invoke DeleteDC, HMemWnd
invoke DestroyWindow, HMainWnd
invoke PostQuitMessage, NULL
_OnClose endp
;--------------------------------------
; 开始新游戏
;--------------------------------------
_NewGame proc
.if IsTimer
mov IsTimer, FALSE
invoke KillTimer, HMainWnd, ID_TIMER1
.endif
mov al, ' ' ;把矩阵清空
mov edi, offset Map
mov ecx, 200
rep stosb
invoke _DrawView ;重新画游戏
invoke InvalidateRect, HMainWnd, NULL, FALSE
invoke SetTimer, HMainWnd, ID_TIMER1, INTERVAL, NULL
mov IsTimer, TRUE
mov IsDrawCooky, TRUE
mov Act, XNEW
;状态栏
mov Score, 0
invoke wsprintf, addr StatusBuffer, addr StatusMsg, Score
invoke SendMessage, HStatus, SB_SETTEXT, 0, addr StatusBuffer
ret
_NewGame endp
;--------------------------------------
; 游戏结束,一切静止
;--------------------------------------
_GameOver proc
.if IsTimer
mov IsTimer, FALSE
invoke KillTimer, HMainWnd, ID_TIMER1
.endif
mov IsTimer, FALSE
mov IsDrawCooky, FALSE
mov Act, 0
ret
_GameOver endp
;--------------------------------------
; 产生新的要下落的物件,如果有放置的空间返回True,如果没有空间返回False
;--------------------------------------
_NewCooky proc uses ebx edi
local x, y ;游戏矩阵上的位置(行,列)
local i, j ;物件4*4方格上的位置
local pos ;(i, j)位置的的地址
local count ;计数, 达到4个方格就退出
local CanNew ;是否有位置产生
mov CanNew, TRUE
invoke _Rand, 0, 6 ;得到随机的将要落下的物件
mov CookyNo, eax ;把现在是哪一个方块和方块的位置保存起来
mov Pose, 0
mov CookyTop, 0 ;把方块左上角的位置保存起来
mov CookyLeft, 4
;;mov eax, 0
mov ebx, 64
mul ebx
mov esi, eax
add esi, offset Box ;esi是存放物件结构的数据的地址
mov x, 0
mov i, 0
mov count, 1
mov edi, offset Dots ;把行列位置放到edi指定的地方去
.while i < 4
mov j, 0
mov y, 4
.while j < 4
mov bl, [esi]
.if bl == 'X'
;先判断矩阵上是否有位置
invoke _GetMapChar, x, y
.if al == 'Q'
mov CanNew, FALSE
jmp @F
.endif
mov edx, x ;行
mov [edi], edx ;还要这样赋值
add edi, 4
mov edx, y ;列
mov [edi], edx
add edi, 4
inc count
.if count > 4 ;已经够了4个方格, 怕不小心Dots越界
jmp @F
.endif
.endif
inc j
inc y
inc esi
.endw
inc i
inc x
.endw
@@:
.if CanNew
mov IsDrawCooky, TRUE
invoke _DrawView ;重新画游戏
invoke InvalidateRect, HMainWnd, NULL, FALSE
mov Act, XDOWN
.else
invoke _GameOver
invoke wsprintf, addr StatusBuffer, addr GameOver, Score
invoke MessageBox, NULL, addr StatusBuffer, addr TipCaption, MB_OK
.endif
ret
_NewCooky endp
Test_NewCooky proc uses ecx
local x1, y1, x2, y2, x3, y3, x4, y4
mov ecx, offset Dots
mov eax, [ecx]
mov x1, eax
add ecx, 4
mov eax, [ecx]
mov y1, eax
add ecx, 4
mov eax, [ecx]
mov x2, eax
add ecx, 4
mov eax, [ecx]
mov y2, eax
add ecx, 4
mov eax, [ecx]
mov x3, eax
add ecx, 4
mov eax, [ecx]
mov y3, eax
add ecx, 4
mov eax, [ecx]
mov x4, eax
add ecx, 4
mov eax, [ecx]
mov y4, eax
invoke wsprintf, addr DgBuffer, addr DgMsg, x1, y1, x2, y2, x3, y3, x4, y4
invoke MessageBox, NULL, addr DgBuffer, addr DgCaption, MB_OK
invoke _NewCooky ;产生新物件
mov IsDrawCooky, TRUE
invoke _DrawView ;重新画游戏
invoke InvalidateRect, HMainWnd, NULL, FALSE
ret
Test_NewCooky endp
;--------------------------------------
; 消去行
;--------------------------------------
_RemoveRow proc uses ebx ecx edx esi edi
local i ;循环每一行用
local j ;转移每一个元素用
local full_num ;有多少行被消去了
local addr_from, addr_to ;消去一行,自上往下填充,起始地址和目标地址
local line_flag ;指向每一行是否已满的标志
local mov_num ;移动个数
invoke KillTimer, HMainWnd, ID_TIMER1
mov full_num, 0
mov i, 0 ;i表示行号
mov edx, offset RowFull ;这个地方为什么非得用寄存器, 内存变量为啥就是不行
mov addr_from, offset Map
sub addr_from, 1
mov addr_to, offset Map
add addr_to, 9
mov mov_num, 10
.while i < 20
mov bl, byte ptr [edx]
.if bl == 'Y' ;消去一行
;invoke MessageBox, NULL, addr DgMsg, addr DgCaption, MB_OK
;注意,我本来在这里打算这么用,结果程序执行的莫名其妙,只好用笨方法了
;mov esi, addr_from
;mov edi, addr_to
;mov ecx, mov_num
;std
;rep movsb
mov j, 0
mov esi, addr_from
mov edi, addr_to
mov ebx, mov_num
.while j < ebx
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -