/* * WineMine (main.c) * * Copyright 2000 Joshua Thielen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "main.h" #include "dialog.h" #include "resource.h" #ifdef DUMB_DEBUG #include #define DEBUG(x) fprintf(stderr,x) #else #define DEBUG(x) #endif static const TCHAR szAppName[] = TEXT("WineMine"); int WINAPI _tWinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR cmdline, int cmdshow ) { MSG msg; WNDCLASS wc; HWND hWnd; HACCEL haccel; wc.style = 0; wc.lpfnWndProc = MainProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(IDI_WINEMINE) ); wc.hCursor = LoadCursor( NULL, (LPCTSTR)IDI_APPLICATION ); wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); wc.lpszMenuName = MAKEINTRESOURCE(IDM_WINEMINE); wc.lpszClassName = szAppName; if ( !RegisterClass(&wc) ) return 1; hWnd = CreateWindow( szAppName, szAppName, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL ); if (!hWnd) return 1; ShowWindow( hWnd, cmdshow ); UpdateWindow( hWnd ); haccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDA_WINEMINE) ); SetTimer( hWnd, ID_TIMER, 1000, NULL ); while( GetMessage(&msg, NULL, 0, 0) ) { if ( !TranslateAccelerator(hWnd, haccel, &msg) ) TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static BOARD board; switch(msg) { case WM_CREATE: board.hInst = ((LPCREATESTRUCT) lParam)->hInstance; board.hWnd = hWnd; InitBoard( &board ); CreateBoard( &board ); return 0; case WM_PAINT: { HDC hDC; HDC hMemDC; PAINTSTRUCT ps; DEBUG("WM_PAINT\n"); hDC = BeginPaint( hWnd, &ps ); hMemDC = CreateCompatibleDC(hDC); DrawBoard( hDC, hMemDC, &ps, &board ); DeleteDC( hMemDC ); EndPaint( hWnd, &ps ); return 0; } case WM_MOVE: DEBUG("WM_MOVE\n"); board.Pos.x = (LONG) LOWORD(lParam); board.Pos.y = (LONG) HIWORD(lParam); return 0; case WM_DESTROY: SaveBoard( &board ); DestroyBoard( &board ); PostQuitMessage( 0 ); return 0; case WM_TIMER: if( board.Status == PLAYING ) { board.uTime++; RedrawWindow( hWnd, &board.TimerRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); } return 0; case WM_LBUTTONDOWN: DEBUG("WM_LBUTTONDOWN\n"); if( wParam & MK_RBUTTON ) msg = WM_MBUTTONDOWN; TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); SetCapture( hWnd ); return 0; case WM_LBUTTONUP: DEBUG("WM_LBUTTONUP\n"); if( wParam & MK_RBUTTON ) msg = WM_MBUTTONUP; TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); ReleaseCapture(); return 0; case WM_RBUTTONDOWN: DEBUG("WM_RBUTTONDOWN\n"); if( wParam & MK_LBUTTON ) { board.Press.x = 0; board.Press.y = 0; msg = WM_MBUTTONDOWN; } TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); return 0; case WM_RBUTTONUP: DEBUG("WM_RBUTTONUP\n"); if( wParam & MK_LBUTTON ) msg = WM_MBUTTONUP; TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); return 0; case WM_MBUTTONDOWN: DEBUG("WM_MBUTTONDOWN\n"); TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); return 0; case WM_MBUTTONUP: DEBUG("WM_MBUTTONUP\n"); TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); return 0; case WM_MOUSEMOVE: { if( (wParam & MK_LBUTTON) && (wParam & MK_RBUTTON) ) msg = WM_MBUTTONDOWN; else if( wParam & MK_LBUTTON ) msg = WM_LBUTTONDOWN; else return 0; TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); return 0; } case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_NEW: CreateBoard( &board ); return 0; case IDM_MARKQ: { HMENU hMenu; hMenu = GetMenu( hWnd ); board.bMark = !board.bMark; if( board.bMark ) CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED ); else CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED ); return 0; } case IDM_BEGINNER: SetDifficulty( &board, BEGINNER ); CreateBoard( &board ); return 0; case IDM_ADVANCED: SetDifficulty( &board, ADVANCED ); CreateBoard( &board ); return 0; case IDM_EXPERT: SetDifficulty( &board, EXPERT ); CreateBoard( &board ); return 0; case IDM_CUSTOM: SetDifficulty( &board, CUSTOM ); CreateBoard( &board ); return 0; case IDM_EXIT: SendMessage( hWnd, WM_CLOSE, 0, 0); return 0; case IDM_TIMES: DialogBoxParam( board.hInst, MAKEINTRESOURCE(IDD_TIMES), hWnd, TimesDlgProc, (LPARAM) &board); return 0; case IDM_ABOUT: { TCHAR szOtherStuff[255]; LoadString( board.hInst, IDS_ABOUT, szOtherStuff, sizeof(szOtherStuff) / sizeof(TCHAR) ); ShellAbout( hWnd, szAppName, szOtherStuff, (HICON)SendMessage(hWnd, WM_GETICON, ICON_BIG, 0) ); return 0; } default: DEBUG("Unknown WM_COMMAND command message received\n"); break; } } return( DefWindowProc( hWnd, msg, wParam, lParam )); } void InitBoard( BOARD *pBoard ) { HMENU hMenu; pBoard->hMinesBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_MINES); pBoard->hFacesBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_FACES); pBoard->hLedsBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_LEDS); LoadBoard( pBoard ); if( pBoard->Pos.x < GetSystemMetrics( SM_CXFIXEDFRAME ) ) pBoard->Pos.x = GetSystemMetrics( SM_CXFIXEDFRAME ); if( pBoard->Pos.x > (GetSystemMetrics( SM_CXSCREEN ) - GetSystemMetrics( SM_CXFIXEDFRAME ))) { pBoard->Pos.x = GetSystemMetrics( SM_CXSCREEN ) - GetSystemMetrics( SM_CXFIXEDFRAME ); } if( pBoard->Pos.y < (GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFIXEDFRAME ))) { pBoard->Pos.y = GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFIXEDFRAME ); } if( pBoard->Pos.y > (GetSystemMetrics( SM_CYSCREEN ) - GetSystemMetrics( SM_CYFIXEDFRAME ))) { pBoard->Pos.y = GetSystemMetrics( SM_CYSCREEN ) - GetSystemMetrics( SM_CYFIXEDFRAME ); } hMenu = GetMenu( pBoard->hWnd ); CheckMenuItem( hMenu, IDM_BEGINNER + pBoard->Difficulty, MF_CHECKED ); if( pBoard->bMark ) CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED ); else CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED ); CheckLevel( pBoard ); } static DWORD LoadDWord(HKEY hKey, TCHAR *szKeyName, DWORD dwDefaultValue) { DWORD dwSize; DWORD dwValue; dwSize = sizeof(DWORD); if( RegQueryValueEx( hKey, szKeyName, NULL, NULL, (LPBYTE) &dwValue, &dwSize ) == ERROR_SUCCESS ) return dwValue; return dwDefaultValue; } void LoadBoard( BOARD *pBoard ) { DWORD dwSize; HKEY hKey; TCHAR szData[16]; TCHAR szKeyName[8]; TCHAR szNobody[15]; UCHAR i; RegOpenKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, KEY_QUERY_VALUE, &hKey ); pBoard->Pos.x = (LONG) LoadDWord( hKey, TEXT("Xpos"), GetSystemMetrics(SM_CXFIXEDFRAME) ); pBoard->Pos.y = (LONG) LoadDWord( hKey, TEXT("Ypos"), GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFIXEDFRAME) ); pBoard->uRows = (ULONG) LoadDWord( hKey, TEXT("Height"), BEGINNER_ROWS ); pBoard->uCols = (ULONG) LoadDWord( hKey, TEXT("Width"), BEGINNER_COLS ); pBoard->uMines = (ULONG) LoadDWord( hKey, TEXT("Mines"), BEGINNER_MINES ); pBoard->Difficulty = (DIFFICULTY) LoadDWord( hKey, TEXT("Difficulty"), BEGINNER ); pBoard->bMark = (BOOL) LoadDWord( hKey, TEXT("Mark"), TRUE ); LoadString( pBoard->hInst, IDS_NOBODY, szNobody, sizeof(szNobody) / sizeof(TCHAR) ); for( i = 0; i < 3; i++ ) { // As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys wsprintf( szKeyName, TEXT("Name%d"), i + 1 ); dwSize = sizeof(szData); if( RegQueryValueEx( hKey, szKeyName, NULL, NULL, (LPBYTE)szData, (LPDWORD) &dwSize ) == ERROR_SUCCESS ) _tcsncpy( pBoard->szBestName[i], szData, sizeof(szData) / sizeof(TCHAR) ); else _tcscpy( pBoard->szBestName[i], szNobody); } for( i = 0; i < 3; i++ ) { wsprintf( szKeyName, TEXT("Time%d"), i + 1 ); pBoard->uBestTime[i] = LoadDWord( hKey, szKeyName, 999 ); } RegCloseKey(hKey); } void SaveBoard( BOARD *pBoard ) { DWORD dwValue; HKEY hKey; UCHAR i; TCHAR szData[16]; TCHAR szKeyName[8]; if( RegCreateKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL ) != ERROR_SUCCESS) return; RegSetValueEx( hKey, TEXT("Xpos"), 0, REG_DWORD, (LPBYTE) &pBoard->Pos.x, sizeof(DWORD) ); RegSetValueEx( hKey, TEXT("Ypos"), 0, REG_DWORD, (LPBYTE) &pBoard->Pos.y, sizeof(DWORD) ); RegSetValueEx( hKey, TEXT("Difficulty"), 0, REG_DWORD, (LPBYTE) &pBoard->Difficulty, sizeof(DWORD) ); RegSetValueEx( hKey, TEXT("Height"), 0, REG_DWORD, (LPBYTE) &pBoard->uRows, sizeof(DWORD) ); RegSetValueEx( hKey, TEXT("Width"), 0, REG_DWORD, (LPBYTE) &pBoard->uCols, sizeof(DWORD) ); RegSetValueEx( hKey, TEXT("Mines"), 0, REG_DWORD, (LPBYTE) &pBoard->uMines, sizeof(DWORD) ); RegSetValueEx( hKey, TEXT("Mark"), 0, REG_DWORD, (LPBYTE) &pBoard->bMark, sizeof(DWORD) ); for( i = 0; i < 3; i++ ) { // As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys wsprintf( szKeyName, TEXT("Name%u"), i + 1); _tcsncpy( szData, pBoard->szBestName[i], sizeof(szData) / sizeof(TCHAR) ); RegSetValueEx( hKey, szKeyName, 0, REG_SZ, (LPBYTE)szData, (_tcslen(szData) + 1) * sizeof(TCHAR) ); } for( i = 0; i < 3; i++ ) { wsprintf( szKeyName, TEXT("Time%u"), i + 1); dwValue = pBoard->uBestTime[i]; RegSetValueEx( hKey, szKeyName, 0, REG_DWORD, (LPBYTE)(LPDWORD)&dwValue, sizeof(DWORD) ); } RegCloseKey(hKey); } void DestroyBoard( BOARD *pBoard ) { DeleteObject( pBoard->hFacesBMP ); DeleteObject( pBoard->hLedsBMP ); DeleteObject( pBoard->hMinesBMP ); } void SetDifficulty( BOARD *pBoard, DIFFICULTY Difficulty ) { HMENU hMenu; switch(Difficulty) { case BEGINNER: pBoard->uCols = BEGINNER_COLS; pBoard->uRows = BEGINNER_ROWS; pBoard->uMines = BEGINNER_MINES; break; case ADVANCED: pBoard->uCols = ADVANCED_COLS; pBoard->uRows = ADVANCED_ROWS; pBoard->uMines = ADVANCED_MINES; break; case EXPERT: pBoard->uCols = EXPERT_COLS; pBoard->uRows = EXPERT_ROWS; pBoard->uMines = EXPERT_MINES; break; case CUSTOM: if( DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_CUSTOM), pBoard->hWnd, CustomDlgProc, (LPARAM) pBoard) != IDOK ) return; break; } hMenu = GetMenu(pBoard->hWnd); CheckMenuItem( hMenu, IDM_BEGINNER + pBoard->Difficulty, MF_UNCHECKED ); pBoard->Difficulty = Difficulty; CheckMenuItem( hMenu, IDM_BEGINNER + Difficulty, MF_CHECKED ); } void CreateBoard( BOARD *pBoard ) { ULONG uLeft, uTop, uBottom, uRight, uWndX, uWndY, uWndWidth, uWndHeight; pBoard->uBoxesLeft = pBoard->uCols * pBoard->uRows - pBoard->uMines; pBoard->uNumFlags = 0; CreateBoxes( pBoard ); pBoard->uWidth = pBoard->uCols * MINE_WIDTH + BOARD_WMARGIN * 2; pBoard->uHeight = pBoard->uRows * MINE_HEIGHT + LED_HEIGHT + BOARD_HMARGIN * 3; uWndX = pBoard->Pos.x - GetSystemMetrics( SM_CXFIXEDFRAME ); uWndY = pBoard->Pos.y - GetSystemMetrics( SM_CYMENU ) - GetSystemMetrics( SM_CYCAPTION ) - GetSystemMetrics( SM_CYFIXEDFRAME ); uWndWidth = pBoard->uWidth + GetSystemMetrics( SM_CXFIXEDFRAME ) * 2; uWndHeight = pBoard->uHeight + GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFIXEDFRAME ) * 2; /* setting the mines rectangle boundary */ uLeft = BOARD_WMARGIN; uTop = BOARD_HMARGIN * 2 + LED_HEIGHT; uRight = uLeft + pBoard->uCols * MINE_WIDTH; uBottom = uTop + pBoard->uRows * MINE_HEIGHT; SetRect( &pBoard->MinesRect, uLeft, uTop, uRight, uBottom ); /* setting the face rectangle boundary */ uLeft = pBoard->uWidth / 2 - FACE_WIDTH / 2; uTop = BOARD_HMARGIN; uRight = uLeft + FACE_WIDTH; uBottom = uTop + FACE_HEIGHT; SetRect( &pBoard->FaceRect, uLeft, uTop, uRight, uBottom ); /* setting the timer rectangle boundary */ uLeft = BOARD_WMARGIN; uTop = BOARD_HMARGIN; uRight = uLeft + LED_WIDTH * 3; uBottom = uTop + LED_HEIGHT; SetRect( &pBoard->CounterRect, uLeft, uTop, uRight, uBottom ); /* setting the counter rectangle boundary */ uLeft = pBoard->uWidth - BOARD_WMARGIN - LED_WIDTH * 3; uTop = BOARD_HMARGIN; uRight = pBoard->uWidth - BOARD_WMARGIN; uBottom = uTop + LED_HEIGHT; SetRect( &pBoard->TimerRect, uLeft, uTop, uRight, uBottom ); pBoard->Status = WAITING; pBoard->FaceBmp = SMILE_BMP; pBoard->uTime = 0; MoveWindow( pBoard->hWnd, uWndX, uWndY, uWndWidth, uWndHeight, TRUE ); RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE ); } void CheckLevel( BOARD *pBoard ) { if( pBoard->uRows < BEGINNER_ROWS ) pBoard->uRows = BEGINNER_ROWS; if( pBoard->uRows > MAX_ROWS ) pBoard->uRows = MAX_ROWS; if( pBoard->uCols < BEGINNER_COLS ) pBoard->uCols = BEGINNER_COLS; if( pBoard->uCols > MAX_COLS ) pBoard->uCols = MAX_COLS; if( pBoard->uMines < BEGINNER_MINES ) pBoard->uMines = BEGINNER_MINES; if( pBoard->uMines > pBoard->uCols * pBoard->uRows - 1 ) pBoard->uMines = pBoard->uCols * pBoard->uRows - 1; } void CreateBoxes( BOARD *pBoard ) { LONG i, j; ULONG uCol, uRow; srand( (unsigned int)time( NULL ) ); /* Create the boxes... * We actually create them with an empty border, * so special care doesn't have to be taken on the edges */ for( uCol = 0; uCol <= pBoard->uCols + 1; uCol++ ) { for( uRow = 0; uRow <= pBoard->uRows + 1; uRow++ ) { pBoard->Box[uCol][uRow].bIsPressed = FALSE; pBoard->Box[uCol][uRow].bIsMine = FALSE; pBoard->Box[uCol][uRow].uFlagType = NORMAL; pBoard->Box[uCol][uRow].uNumMines = 0; } } /* create mines */ i = 0; while( (ULONG)i < pBoard->uMines ) { uCol = (ULONG)(pBoard->uCols * (float)rand() / RAND_MAX + 1); uRow = (ULONG)(pBoard->uRows * (float)rand() / RAND_MAX + 1); if( !pBoard->Box[uCol][uRow].bIsMine ) { i++; pBoard->Box[uCol][uRow].bIsMine = TRUE; } } /* * Now we label the remaining boxes with the * number of mines surrounding them. */ for( uCol = 1; uCol < pBoard->uCols + 1; uCol++ ) { for( uRow = 1; uRow < pBoard->uRows + 1; uRow++ ) { for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { if( pBoard->Box[uCol + i][uRow + j].bIsMine ) { pBoard->Box[uCol][uRow].uNumMines++; } } } } } } void DrawMines ( HDC hdc, HDC hMemDC, BOARD *pBoard ) { HGDIOBJ hOldObj; ULONG uCol, uRow; hOldObj = SelectObject (hMemDC, pBoard->hMinesBMP); for( uRow = 1; uRow <= pBoard->uRows; uRow++ ) { for( uCol = 1; uCol <= pBoard->uCols; uCol++ ) { DrawMine( hdc, hMemDC, pBoard, uCol, uRow, FALSE ); } } SelectObject( hMemDC, hOldObj ); } void DrawMine( HDC hdc, HDC hMemDC, BOARD *pBoard, ULONG uCol, ULONG uRow, BOOL bIsPressed ) { MINEBMP_OFFSET offset = BOX_BMP; if( uCol == 0 || uCol > pBoard->uCols || uRow == 0 || uRow > pBoard->uRows ) return; if( pBoard->Status == GAMEOVER ) { if( pBoard->Box[uCol][uRow].bIsMine ) { switch( pBoard->Box[uCol][uRow].uFlagType ) { case FLAG: offset = FLAG_BMP; break; case COMPLETE: offset = EXPLODE_BMP; break; case QUESTION: /* fall through */ case NORMAL: offset = MINE_BMP; } } else { switch( pBoard->Box[uCol][uRow].uFlagType ) { case QUESTION: offset = QUESTION_BMP; break; case FLAG: offset = WRONG_BMP; break; case NORMAL: offset = BOX_BMP; break; case COMPLETE: /* Do nothing */ break; default: DEBUG("Unknown FlagType during game over in DrawMine\n"); break; } } } else { /* WAITING or PLAYING */ switch( pBoard->Box[uCol][uRow].uFlagType ) { case QUESTION: if( !bIsPressed ) offset = QUESTION_BMP; else offset = QPRESS_BMP; break; case FLAG: offset = FLAG_BMP; break; case NORMAL: if( !bIsPressed ) offset = BOX_BMP; else offset = MPRESS_BMP; break; case COMPLETE: /* Do nothing */ break; default: DEBUG("Unknown FlagType while playing in DrawMine\n"); break; } } if( pBoard->Box[uCol][uRow].uFlagType == COMPLETE && !pBoard->Box[uCol][uRow].bIsMine ) offset = (MINEBMP_OFFSET) pBoard->Box[uCol][uRow].uNumMines; BitBlt( hdc, (uCol - 1) * MINE_WIDTH + pBoard->MinesRect.left, (uRow - 1) * MINE_HEIGHT + pBoard->MinesRect.top, MINE_WIDTH, MINE_HEIGHT, hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY ); } void DrawLeds( HDC hDC, HDC hMemDC, BOARD *pBoard, LONG nNumber, LONG x, LONG y ) { HGDIOBJ hOldObj; UCHAR i; ULONG uLED[3]; LONG nCount; nCount = nNumber; if( nCount < 1000 ) { if( nCount >= 0 ) { uLED[0] = nCount / 100 ; nCount -= uLED[0] * 100; } else { uLED[0] = 10; /* negative sign */ nCount = -nCount; } uLED[1] = nCount / 10; nCount -= uLED[1] * 10; uLED[2] = nCount; } else { for( i = 0; i < 3; i++ ) uLED[i] = 10; } /* use unlit led if not playing */ /* if( pBoard->Status == WAITING ) for( i = 0; i < 3; i++ ) uLED[i] = 11;*/ hOldObj = SelectObject (hMemDC, pBoard->hLedsBMP); for( i = 0; i < 3; i++ ) { BitBlt( hDC, i * LED_WIDTH + x, y, LED_WIDTH, LED_HEIGHT, hMemDC, 0, uLED[i] * LED_HEIGHT, SRCCOPY); } SelectObject( hMemDC, hOldObj ); } void DrawFace( HDC hDC, HDC hMemDC, BOARD *pBoard ) { HGDIOBJ hOldObj; hOldObj = SelectObject (hMemDC, pBoard->hFacesBMP); BitBlt( hDC, pBoard->FaceRect.left, pBoard->FaceRect.top, FACE_WIDTH, FACE_HEIGHT, hMemDC, 0, pBoard->FaceBmp * FACE_HEIGHT, SRCCOPY); SelectObject( hMemDC, hOldObj ); } void DrawBoard( HDC hDC, HDC hMemDC, PAINTSTRUCT *ps, BOARD *pBoard ) { RECT TempRect; if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->CounterRect) ) DrawLeds( hDC, hMemDC, pBoard, pBoard->uMines - pBoard->uNumFlags, pBoard->CounterRect.left, pBoard->CounterRect.top ); if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->TimerRect ) ) DrawLeds( hDC, hMemDC, pBoard, pBoard->uTime, pBoard->TimerRect.left, pBoard->TimerRect.top); if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->FaceRect ) ) DrawFace( hDC, hMemDC, pBoard ); if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->MinesRect ) ) DrawMines( hDC, hMemDC, pBoard ); } void TestBoard( HWND hWnd, BOARD *pBoard, LONG x, LONG y, int msg ) { POINT pt; ULONG uCol, uRow; pt.x = x; pt.y = y; if( PtInRect( &pBoard->MinesRect, pt ) && pBoard->Status != GAMEOVER && pBoard->Status != WON ) TestMines( pBoard, pt, msg ); else { UnpressBoxes( pBoard, pBoard->Press.x, pBoard->Press.y ); pBoard->Press.x = 0; pBoard->Press.y = 0; } if( pBoard->uBoxesLeft == 0 ) { // MG - 2006-02-21 // mimic MS minesweeper behaviour - when autocompleting a board, flag mines pBoard->Status = WON; for( uCol = 0; uCol <= pBoard->uCols + 1; uCol++ ) { for( uRow = 0; uRow <= pBoard->uRows + 1; uRow++ ) { if(pBoard->Box[uCol][uRow].bIsMine) { pBoard->Box[uCol][uRow].uFlagType = FLAG; } } } pBoard->uNumFlags = pBoard->uMines; RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); if( pBoard->Difficulty != CUSTOM && pBoard->uTime < pBoard->uBestTime[pBoard->Difficulty] ) { pBoard->uBestTime[pBoard->Difficulty] = pBoard->uTime; DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_CONGRATS), hWnd, CongratsDlgProc, (LPARAM) pBoard); DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_TIMES), hWnd, TimesDlgProc, (LPARAM) pBoard); } } TestFace( pBoard, pt, msg ); } void TestMines( BOARD *pBoard, POINT pt, int msg ) { BOOL bDraw = TRUE; ULONG uCol, uRow; uCol = (pt.x - pBoard->MinesRect.left) / MINE_WIDTH + 1; uRow = (pt.y - pBoard->MinesRect.top ) / MINE_HEIGHT + 1; switch (msg) { case WM_LBUTTONDOWN: if( pBoard->Press.x != uCol || pBoard->Press.y != uRow ) { UnpressBox( pBoard, pBoard->Press.x, pBoard->Press.y ); pBoard->Press.x = uCol; pBoard->Press.y = uRow; PressBox( pBoard, uCol, uRow ); } bDraw = FALSE; break; case WM_LBUTTONUP: if( pBoard->Press.x != uCol || pBoard->Press.y != uRow ) UnpressBox( pBoard, pBoard->Press.x, pBoard->Press.y ); pBoard->Press.x = 0; pBoard->Press.y = 0; if( pBoard->Box[uCol][uRow].uFlagType != FLAG ) pBoard->Status = PLAYING; CompleteBox( pBoard, uCol, uRow ); break; case WM_MBUTTONDOWN: PressBoxes( pBoard, uCol, uRow ); bDraw = FALSE; break; case WM_MBUTTONUP: if( pBoard->Press.x != uCol || pBoard->Press.y != uRow ) UnpressBoxes( pBoard, pBoard->Press.x, pBoard->Press.y ); pBoard->Press.x = 0; pBoard->Press.y = 0; CompleteBoxes( pBoard, uCol, uRow ); break; case WM_RBUTTONDOWN: AddFlag( pBoard, uCol, uRow ); pBoard->Status = PLAYING; break; default: DEBUG("Unknown message type received in TestMines\n"); break; } if(bDraw) RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); } void TestFace( BOARD *pBoard, POINT pt, int msg ) { if( pBoard->Status == PLAYING || pBoard->Status == WAITING ) { if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ) pBoard->FaceBmp = OOH_BMP; else pBoard->FaceBmp = SMILE_BMP; } else if( pBoard->Status == GAMEOVER ) pBoard->FaceBmp = DEAD_BMP; else if( pBoard->Status == WON ) pBoard->FaceBmp = COOL_BMP; if( PtInRect( &pBoard->FaceRect, pt ) ) { if( msg == WM_LBUTTONDOWN ) pBoard->FaceBmp = SPRESS_BMP; if( msg == WM_LBUTTONUP ) CreateBoard( pBoard ); } RedrawWindow( pBoard->hWnd, &pBoard->FaceRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); } void CompleteBox( BOARD *pBoard, ULONG uCol, ULONG uRow ) { CHAR i, j; if( pBoard->Box[uCol][uRow].uFlagType != COMPLETE && pBoard->Box[uCol][uRow].uFlagType != FLAG && uCol > 0 && uCol < pBoard->uCols + 1 && uRow > 0 && uRow < pBoard->uRows + 1 ) { pBoard->Box[uCol][uRow].uFlagType = COMPLETE; if( pBoard->Box[uCol][uRow].bIsMine ) { pBoard->FaceBmp = DEAD_BMP; pBoard->Status = GAMEOVER; } else if( pBoard->Status != GAMEOVER ) pBoard->uBoxesLeft--; if( pBoard->Box[uCol][uRow].uNumMines == 0 ) { for( i = -1; i <= 1; i++ ) for( j = -1; j <= 1; j++ ) CompleteBox( pBoard, uCol + i, uRow + j ); } } } void CompleteBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ) { CHAR i, j; ULONG uNumFlags = 0; if( pBoard->Box[uCol][uRow].uFlagType == COMPLETE ) { for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { if( pBoard->Box[uCol + i][uRow + j].uFlagType == FLAG ) uNumFlags++; } } if( uNumFlags == pBoard->Box[uCol][uRow].uNumMines ) { for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { if( pBoard->Box[uCol + i][uRow + j].uFlagType != FLAG ) CompleteBox( pBoard, uCol + i, uRow + j ); } } } } } void AddFlag( BOARD *pBoard, ULONG uCol, ULONG uRow ) { if( pBoard->Box[uCol][uRow].uFlagType != COMPLETE ) { switch( pBoard->Box[uCol][uRow].uFlagType ) { case FLAG: if( pBoard->bMark ) pBoard->Box[uCol][uRow].uFlagType = QUESTION; else pBoard->Box[uCol][uRow].uFlagType = NORMAL; pBoard->uNumFlags--; break; case QUESTION: pBoard->Box[uCol][uRow].uFlagType = NORMAL; break; default: pBoard->Box[uCol][uRow].uFlagType = FLAG; pBoard->uNumFlags++; } } } void PressBox( BOARD *pBoard, ULONG uCol, ULONG uRow ) { HDC hDC; HGDIOBJ hOldObj; HDC hMemDC; hDC = GetDC( pBoard->hWnd ); hMemDC = CreateCompatibleDC(hDC); hOldObj = SelectObject (hMemDC, pBoard->hMinesBMP); DrawMine( hDC, hMemDC, pBoard, uCol, uRow, TRUE ); SelectObject( hMemDC, hOldObj ); DeleteDC( hMemDC ); ReleaseDC( pBoard->hWnd, hDC ); } void PressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ) { CHAR i, j; for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { pBoard->Box[uCol + i][uRow + j].bIsPressed = TRUE; PressBox( pBoard, uCol + i, uRow + j ); } } for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { if( !pBoard->Box[pBoard->Press.x + i][pBoard->Press.y + j].bIsPressed ) UnpressBox( pBoard, pBoard->Press.x + i, pBoard->Press.y + j ); } } for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { pBoard->Box[uCol + i][uRow + j].bIsPressed = FALSE; PressBox( pBoard, uCol + i, uRow + j ); } } pBoard->Press.x = uCol; pBoard->Press.y = uRow; } void UnpressBox( BOARD *pBoard, ULONG uCol, ULONG uRow ) { HDC hDC; HGDIOBJ hOldObj; HDC hMemDC; hDC = GetDC( pBoard->hWnd ); hMemDC = CreateCompatibleDC( hDC ); hOldObj = SelectObject( hMemDC, pBoard->hMinesBMP ); DrawMine( hDC, hMemDC, pBoard, uCol, uRow, FALSE ); SelectObject( hMemDC, hOldObj ); DeleteDC( hMemDC ); ReleaseDC( pBoard->hWnd, hDC ); } void UnpressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ) { CHAR i, j; for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { UnpressBox( pBoard, uCol + i, uRow + j ); } } }