// 最後更新:2010-12-18
//
#include <graphics.h>
#include <conio.h>
#include <time.h> /////////////////////////////////////////////
// 定義常量、枚舉量、結構體、全局變量
/////////////////////////////////////////////#define WIDTH 10 // 遊戲區寬度
#define HEIGHT 22 // 遊戲區高度
#define SIZE 20 // 每個遊戲區單位的實際像素// 定義操作類型
enum CTRL
{
CTRL_ROTATE, // 方塊旋轉
CTRL_LEFT, CTRL_RIGHT, CTRL_DOWN, // 方塊左、右、下移動
CTRL_SINK, // 方塊沈底
CTRL_QUIT // 退出遊戲
};// 定義繪制方塊的方法
enum DRAW
{
SHOW, // 顯示方塊
HIDE, // 隱藏方塊
FIX // 固定方塊
};// 定義七種俄羅斯方塊
struct BLOCK
{
WORD dir[4]; // 方塊的四個旋轉狀態
COLORREF color; // 方塊的顏色
} g_Blocks[7] = { {0x0F00, 0x4444, 0x0F00, 0x4444, RED}, // I
{0x0660, 0x0660, 0x0660, 0x0660, BLUE}, // 口
{0x4460, 0x02E0, 0x0622, 0x0740, MAGENTA}, // L
{0x2260, 0x0E20, 0x0644, 0x0470, YELLOW}, // 反L
{0x0C60, 0x2640, 0x0C60, 0x2640, CYAN}, // Z
{0x0360, 0x4620, 0x0360, 0x4620, GREEN}, // 反Z
{0x4E00, 0x4C40, 0x0E40, 0x4640, BROWN}}; // T// 定義當前方塊、下壹個方塊的信息
struct BLOCKINFO
{
byte id; // 方塊 ID
char x, y; // 方塊在遊戲區中的坐標
byte dir:2; // 方向
} g_CurBlock, g_NextBlock;// 定義遊戲區
BYTE g_World[WIDTH][HEIGHT] = {0}; /////////////////////////////////////////////
// 函數聲明
/////////////////////////////////////////////void Init(); // 初始化遊戲
void Quit(); // 退出遊戲
void NewGame(); // 開始新遊戲
void GameOver(); // 結束遊戲
CTRL GetControl(bool _onlyresettimer = false); // 獲取控制命令
void DispatchControl(CTRL _ctrl); // 分發控制命令
void NewBlock(); // 生成新的方塊
bool CheckBlock(BLOCKINFO _block); // 檢測指定方塊是否可以放下
void DrawBlock(BLOCKINFO _block, DRAW _draw = SHOW); // 畫方塊
void OnRotate(); // 旋轉方塊
void OnLeft(); // 左移方塊
void OnRight(); // 右移方塊
void OnDown(); // 下移方塊
void OnSink(); // 沈底方塊 /////////////////////////////////////////////
// 函數定義
/////////////////////////////////////////////// 主函數
void main()
{
Init(); CTRL c;
while(true)
{
c = GetControl();
DispatchControl(c); // 按退出時,顯示對話框咨詢用戶是否退出
if (c == CTRL_QUIT)
{
HWND wnd = GetHWnd();
if (MessageBox(wnd, "您要退出遊戲嗎?", "提醒", MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
Quit();
}
}
}
// 初始化遊戲
void Init()
{
initgraph(640, 480);
srand((unsigned)time(NULL)); // 顯示操作說明
setfont(14, 0, "宋體");
outtextxy(20, 330, "操作說明");
outtextxy(20, 350, "上:旋轉");
outtextxy(20, 370, "左:左移");
outtextxy(20, 390, "右:右移");
outtextxy(20, 410, "下:下移");
outtextxy(20, 430, "空格:沈底");
outtextxy(20, 450, "ESC:退出"); // 設置坐標原點
setorigin(220, 20); // 繪制遊戲區邊界
rectangle(-1, -1, WIDTH * SIZE, HEIGHT * SIZE);
rectangle((WIDTH + 1) * SIZE - 1, -1, (WIDTH + 5) * SIZE, 4 * SIZE); // 開始新遊戲
NewGame();
}
// 退出遊戲
void Quit()
{
closegraph();
exit(0);
}
// 開始新遊戲
void NewGame()
{
// 清空遊戲區
setfillstyle(BLACK);
bar(0, 0, WIDTH * SIZE - 1, HEIGHT * SIZE - 1);
ZeroMemory(g_World, WIDTH * HEIGHT); // 生成下壹個方塊
g_NextBlock.id = rand() % 7;
g_NextBlock.dir = rand() % 4;
g_NextBlock.x = WIDTH + 1;
g_NextBlock.y = HEIGHT - 1; // 獲取新方塊
NewBlock();
}
// 結束遊戲
void GameOver()
{
HWND wnd = GetHWnd();
if (MessageBox(wnd, "遊戲結束。\n您想重新來壹局嗎?", "遊戲結束", MB_YESNO | MB_ICONQUESTION) == IDYES)
NewGame();
else
Quit();
}
// 獲取控制命令
CTRL GetControl(bool _onlyresettimer)
{
static DWORD oldtime = GetTickCount(); // 重置計時器
if (_onlyresettimer)
{
oldtime = GetTickCount();
return CTRL_DOWN; // 僅僅為了重置計時器,隨便返回壹個值
} // 獲取控制值
while(true)
{
// 如果超時,自動下落壹格