#include "stdafx.h" #include "Tetris.h" #define MAX_LOADSTRING 100 #define GAME_TIMER_ID 1 #define GAME_TIMER_INTERVAL 500 HINSTANCE hInst; TCHAR szTitle[MAX_LOADSTRING]; TCHAR szWindowClass[MAX_LOADSTRING]; ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); static void ResetGameTimer(HWND hWnd) { KillTimer(hWnd, GAME_TIMER_ID); SetTimer(hWnd, GAME_TIMER_ID, currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL, nullptr); } int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); HMODULE user32Module = GetModuleHandle(_T("user32.dll")); if (user32Module != nullptr) { typedef BOOL(WINAPI* SetProcessDPIAwareFunc)(); SetProcessDPIAwareFunc setProcessDPIAware = (SetProcessDPIAwareFunc)GetProcAddress(user32Module, "SetProcessDPIAware"); if (setProcessDPIAware != nullptr) { setProcessDPIAware(); } } LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TETRIS, szWindowClass, MAX_LOADSTRING); lstrcpy(szTitle, _T("\u4fc4\u7f57\u65af\u65b9\u5757")); MyRegisterClass(hInstance); if (!InitInstance(hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRIS)); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRIS)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = nullptr; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { RECT rect = { 0, 0, WINDOW_CLIENT_WIDTH, WINDOW_CLIENT_HEIGHT }; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); hInst = hInstance; HWND hWnd = CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, rect.right - rect.left, rect.bottom - rect.top, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: srand((unsigned int)time(nullptr)); ReturnToMainMenu(); ResetGameTimer(hWnd); InvalidateRect(hWnd, nullptr, FALSE); break; case WM_COMMAND: { int wmId = LOWORD(wParam); switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_TIMER: if (wParam == GAME_TIMER_ID) { bool shouldRefresh = false; if (feedbackState.visibleTicks > 0) { feedbackState.visibleTicks--; shouldRefresh = true; } if (currentMode == MODE_ROGUE && rogueStats.feverTicks > 0) { rogueStats.feverTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentMode == MODE_ROGUE && rogueStats.timeDilationTicks > 0 && currentScreen == SCREEN_PLAYING && !suspendFlag && !gameOverFlag) { rogueStats.timeDilationTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentMode == MODE_ROGUE && rogueStats.extremeSlowTicks > 0) { rogueStats.extremeSlowTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentMode == MODE_ROGUE && rogueStats.extremePlayerLevel > 0 && currentScreen == SCREEN_PLAYING && !suspendFlag && !gameOverFlag) { if (rogueStats.extremeDangerTicks > 0) { rogueStats.extremeDangerTicks--; } else { rogueStats.extremeDangerTicks = 30; if (rogueStats.extremeDangerLevel < 5) { rogueStats.extremeDangerLevel++; } currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); feedbackState.visibleTicks = 10; lstrcpyn(feedbackState.title, _T("极限压力升高"), sizeof(feedbackState.title) / sizeof(TCHAR)); lstrcpyn(feedbackState.detail, _T("30 秒内没有完成四消,危险等级提升,下落速度进一步加快。"), sizeof(feedbackState.detail) / sizeof(TCHAR)); shouldRefresh = true; } } if (currentMode == MODE_ROGUE && rogueStats.holdSlowTicks > 0) { rogueStats.holdSlowTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentScreen == SCREEN_PLAYING && !suspendFlag && !gameOverFlag) { if (currentMode == MODE_ROGUE) { int previousFallInterval = currentFallInterval; AdvanceRogueDifficulty(currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL); if (currentFallInterval != previousFallInterval) { ResetGameTimer(hWnd); } } if (currentMode == MODE_ROGUE && rogueStats.timeDilationLevel > 0 && rogueStats.timeDilationTicks <= 0) { int occupiedHeight = 0; int playableHeight = GetRoguePlayableHeight(); for (int y = 0; y < playableHeight; y++) { bool hasCell = false; for (int x = 0; x < nGameWidth; x++) { if (workRegion[y][x] != 0) { hasCell = true; break; } } if (hasCell) { occupiedHeight = playableHeight - y; break; } } if (occupiedHeight > 15) { rogueStats.timeDilationTicks = 8; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); feedbackState.visibleTicks = 10; lstrcpyn(feedbackState.title, _T("\u65f6\u95f4\u7f13\u6d41"), sizeof(feedbackState.title) / sizeof(TCHAR)); lstrcpyn(feedbackState.detail, _T("堆叠高度超过 15 行,接下来 8 秒下落速度降低 30%。"), sizeof(feedbackState.detail) / sizeof(TCHAR)); shouldRefresh = true; } } if (CanMoveDown()) { MoveDown(); } else { Fixing(); if (!gameOverFlag) { DeleteLines(); } } if (!gameOverFlag) { ComputeTarget(); } shouldRefresh = true; } if (shouldRefresh) { InvalidateRect(hWnd, nullptr, FALSE); } } break; case WM_SIZE: InvalidateRect(hWnd, nullptr, FALSE); break; case WM_KEYDOWN: if (currentScreen == SCREEN_MENU) { switch (wParam) { case VK_UP: case VK_LEFT: case 'W': case 'A': menuState.selectedIndex--; if (menuState.selectedIndex < 0) { menuState.selectedIndex = menuState.optionCount - 1; } InvalidateRect(hWnd, nullptr, FALSE); break; case VK_DOWN: case VK_RIGHT: case 'S': case 'D': menuState.selectedIndex++; if (menuState.selectedIndex >= menuState.optionCount) { menuState.selectedIndex = 0; } InvalidateRect(hWnd, nullptr, FALSE); break; case VK_RETURN: case VK_SPACE: if (menuState.selectedIndex == 0) { StartGameWithMode(MODE_CLASSIC); } else if (menuState.selectedIndex == 1) { StartGameWithMode(MODE_ROGUE); } else { OpenRulesScreen(); } ResetGameTimer(hWnd); InvalidateRect(hWnd, nullptr, FALSE); break; case VK_ESCAPE: DestroyWindow(hWnd); break; default: break; } break; } if (currentScreen == SCREEN_RULES) { switch (wParam) { case VK_ESCAPE: case VK_BACK: case 'M': ReturnToMainMenu(); InvalidateRect(hWnd, nullptr, FALSE); break; default: break; } break; } if (currentScreen == SCREEN_UPGRADE) { int upgradeColumnCount = upgradeUiState.optionCount <= 3 ? upgradeUiState.optionCount : 3; if (upgradeColumnCount < 1) { upgradeColumnCount = 1; } switch (wParam) { case VK_LEFT: case 'A': if (upgradeUiState.optionCount > 1) { int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount); if (upgradeUiState.selectedIndex > rowStart) { upgradeUiState.selectedIndex--; } else { int rowEnd = rowStart + upgradeColumnCount - 1; if (rowEnd >= upgradeUiState.optionCount) { rowEnd = upgradeUiState.optionCount - 1; } upgradeUiState.selectedIndex = rowEnd; } } InvalidateRect(hWnd, nullptr, FALSE); break; case VK_RIGHT: case 'D': if (upgradeUiState.optionCount > 1) { int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount); int rowEnd = rowStart + upgradeColumnCount - 1; if (rowEnd >= upgradeUiState.optionCount) { rowEnd = upgradeUiState.optionCount - 1; } if (upgradeUiState.selectedIndex < rowEnd) { upgradeUiState.selectedIndex++; } else { upgradeUiState.selectedIndex = rowStart; } } InvalidateRect(hWnd, nullptr, FALSE); break; case VK_UP: case 'W': if (upgradeUiState.selectedIndex >= upgradeColumnCount) { upgradeUiState.selectedIndex -= upgradeColumnCount; } InvalidateRect(hWnd, nullptr, FALSE); break; case VK_DOWN: case 'S': if (upgradeUiState.selectedIndex + upgradeColumnCount < upgradeUiState.optionCount) { upgradeUiState.selectedIndex += upgradeColumnCount; } InvalidateRect(hWnd, nullptr, FALSE); break; case VK_RETURN: case VK_SPACE: ConfirmUpgradeSelection(); ResetGameTimer(hWnd); InvalidateRect(hWnd, nullptr, FALSE); break; default: break; } break; } if (wParam == 'M') { ReturnToMainMenu(); InvalidateRect(hWnd, nullptr, FALSE); break; } if (wParam == 'R') { StartGameWithMode(currentMode); ResetGameTimer(hWnd); InvalidateRect(hWnd, nullptr, FALSE); break; } if (wParam == 'P') { suspendFlag = !suspendFlag; InvalidateRect(hWnd, nullptr, FALSE); break; } if (wParam == 'G') { targetFlag = !targetFlag; InvalidateRect(hWnd, nullptr, FALSE); break; } if (gameOverFlag || suspendFlag) { break; } switch (wParam) { case VK_LEFT: case 'A': if (CanMoveLeft()) { MoveLeft(); } break; case VK_RIGHT: case 'D': if (CanMoveRight()) { MoveRight(); } break; case VK_DOWN: case 'S': if (CanMoveDown()) { MoveDown(); } else { Fixing(); if (!gameOverFlag) { DeleteLines(); } } break; case VK_UP: case 'W': Rotate(); break; case VK_SPACE: DropDown(); Fixing(); if (!gameOverFlag) { DeleteLines(); } break; case 'C': case VK_SHIFT: HoldCurrentPiece(); break; case 'Z': UseBlackHole(); break; case 'X': UseScreenBomb(); break; case 'V': UseAirReshape(); break; default: break; } if (!gameOverFlag) { ComputeTarget(); } InvalidateRect(hWnd, nullptr, FALSE); break; case WM_ERASEBKGND: return 1; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT clientRect; GetClientRect(hWnd, &clientRect); HDC memDC = CreateCompatibleDC(hdc); HBITMAP memBitmap = CreateCompatibleBitmap( hdc, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top); HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap); TDrawScreen(memDC, hWnd); BitBlt( hdc, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, memDC, 0, 0, SRCCOPY); SelectObject(memDC, oldBitmap); DeleteObject(memBitmap); DeleteDC(memDC); EndPaint(hWnd, &ps); } break; case WM_DESTROY: KillTimer(hWnd, GAME_TIMER_ID); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: SetWindowText(hDlg, _T("\u5173\u4e8e\u4fc4\u7f57\u65af\u65b9\u5757")); return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }