Sync with trunk (48237)
[reactos.git] / base / applications / games / winemine / main.c
1 /*
2 * WineMine (main.c)
3 *
4 * Copyright 2000 Joshua Thielen <jt85296@ltu.edu>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <windows.h>
25 #include <tchar.h>
26 #include "main.h"
27 #include "dialog.h"
28 #include "resource.h"
29
30 #ifdef DUMB_DEBUG
31 #include <stdio.h>
32 #define DEBUG(x) fprintf(stderr,x)
33 #else
34 #define DEBUG(x)
35 #endif
36
37 static const TCHAR szAppName[] = TEXT("WineMine");
38
39
40 int WINAPI _tWinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR cmdline, int cmdshow )
41 {
42 MSG msg;
43 WNDCLASS wc;
44 HWND hWnd;
45 HACCEL haccel;
46
47 wc.style = 0;
48 wc.lpfnWndProc = MainProc;
49 wc.cbClsExtra = 0;
50 wc.cbWndExtra = 0;
51 wc.hInstance = hInst;
52 wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(IDI_WINEMINE) );
53 wc.hCursor = LoadCursor( NULL, (LPCTSTR)IDI_APPLICATION );
54 wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
55 wc.lpszMenuName = MAKEINTRESOURCE(IDM_WINEMINE);
56 wc.lpszClassName = szAppName;
57
58 if ( !RegisterClass(&wc) )
59 return 1;
60
61 hWnd = CreateWindow( szAppName, szAppName,
62 WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
63 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
64 NULL, NULL, hInst, NULL );
65
66 if (!hWnd)
67 return 1;
68
69 ShowWindow( hWnd, cmdshow );
70 UpdateWindow( hWnd );
71
72 haccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDA_WINEMINE) );
73 SetTimer( hWnd, ID_TIMER, 1000, NULL );
74
75 while( GetMessage(&msg, NULL, 0, 0) )
76 {
77 if ( !TranslateAccelerator(hWnd, haccel, &msg) )
78 TranslateMessage(&msg);
79
80 DispatchMessage(&msg);
81 }
82
83 return msg.wParam;
84 }
85
86 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
87 {
88 static BOARD board;
89
90 switch(msg)
91 {
92 case WM_CREATE:
93 board.hInst = ((LPCREATESTRUCT) lParam)->hInstance;
94 board.hWnd = hWnd;
95 InitBoard( &board );
96 CreateBoard( &board );
97 return 0;
98
99 case WM_PAINT:
100 {
101 HDC hDC;
102 HDC hMemDC;
103 PAINTSTRUCT ps;
104
105 DEBUG("WM_PAINT\n");
106 hDC = BeginPaint( hWnd, &ps );
107 hMemDC = CreateCompatibleDC(hDC);
108
109 DrawBoard( hDC, hMemDC, &ps, &board );
110
111 DeleteDC( hMemDC );
112 EndPaint( hWnd, &ps );
113
114 return 0;
115 }
116
117 case WM_MOVE:
118 DEBUG("WM_MOVE\n");
119 board.Pos.x = (LONG) LOWORD(lParam);
120 board.Pos.y = (LONG) HIWORD(lParam);
121 return 0;
122
123 case WM_DESTROY:
124 SaveBoard( &board );
125 DestroyBoard( &board );
126 PostQuitMessage( 0 );
127 return 0;
128
129 case WM_TIMER:
130 if( board.Status == PLAYING )
131 {
132 board.uTime++;
133 RedrawWindow( hWnd, &board.TimerRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
134 }
135 return 0;
136
137 case WM_LBUTTONDOWN:
138 DEBUG("WM_LBUTTONDOWN\n");
139
140 if( wParam & MK_RBUTTON )
141 msg = WM_MBUTTONDOWN;
142
143 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
144 SetCapture( hWnd );
145 return 0;
146
147 case WM_LBUTTONUP:
148 DEBUG("WM_LBUTTONUP\n");
149
150 if( wParam & MK_RBUTTON )
151 msg = WM_MBUTTONUP;
152
153 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
154 ReleaseCapture();
155 return 0;
156
157 case WM_RBUTTONDOWN:
158 DEBUG("WM_RBUTTONDOWN\n");
159
160 if( wParam & MK_LBUTTON )
161 {
162 board.Press.x = 0;
163 board.Press.y = 0;
164 msg = WM_MBUTTONDOWN;
165 }
166
167 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
168 return 0;
169
170 case WM_RBUTTONUP:
171 DEBUG("WM_RBUTTONUP\n");
172 if( wParam & MK_LBUTTON )
173 msg = WM_MBUTTONUP;
174 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
175 return 0;
176
177 case WM_MBUTTONDOWN:
178 DEBUG("WM_MBUTTONDOWN\n");
179 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
180 return 0;
181
182 case WM_MBUTTONUP:
183 DEBUG("WM_MBUTTONUP\n");
184 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
185 return 0;
186
187 case WM_MOUSEMOVE:
188 {
189 if( (wParam & MK_LBUTTON) && (wParam & MK_RBUTTON) )
190 msg = WM_MBUTTONDOWN;
191 else if( wParam & MK_LBUTTON )
192 msg = WM_LBUTTONDOWN;
193 else
194 return 0;
195
196 TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
197
198 return 0;
199 }
200
201 case WM_COMMAND:
202 switch(LOWORD(wParam))
203 {
204 case IDM_NEW:
205 CreateBoard( &board );
206 return 0;
207
208 case IDM_MARKQ:
209 {
210 HMENU hMenu;
211
212 hMenu = GetMenu( hWnd );
213 board.bMark = !board.bMark;
214
215 if( board.bMark )
216 CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
217 else
218 CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
219
220 return 0;
221 }
222
223 case IDM_BEGINNER:
224 SetDifficulty( &board, BEGINNER );
225 CreateBoard( &board );
226 return 0;
227
228 case IDM_ADVANCED:
229 SetDifficulty( &board, ADVANCED );
230 CreateBoard( &board );
231 return 0;
232
233 case IDM_EXPERT:
234 SetDifficulty( &board, EXPERT );
235 CreateBoard( &board );
236 return 0;
237
238 case IDM_CUSTOM:
239 SetDifficulty( &board, CUSTOM );
240 CreateBoard( &board );
241 return 0;
242
243 case IDM_EXIT:
244 SendMessage( hWnd, WM_CLOSE, 0, 0);
245 return 0;
246
247 case IDM_TIMES:
248 DialogBoxParam( board.hInst, MAKEINTRESOURCE(IDD_TIMES), hWnd, TimesDlgProc, (LPARAM) &board);
249 return 0;
250
251 case IDM_ABOUT:
252 {
253 TCHAR szOtherStuff[255];
254
255 LoadString( board.hInst, IDS_ABOUT, szOtherStuff, sizeof(szOtherStuff) / sizeof(TCHAR) );
256
257 ShellAbout( hWnd, szAppName, szOtherStuff, (HICON)SendMessage(hWnd, WM_GETICON, ICON_BIG, 0) );
258 return 0;
259 }
260
261 default:
262 DEBUG("Unknown WM_COMMAND command message received\n");
263 break;
264 }
265 }
266
267 return( DefWindowProc( hWnd, msg, wParam, lParam ));
268 }
269
270 void InitBoard( BOARD *pBoard )
271 {
272 HMENU hMenu;
273
274 pBoard->hMinesBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_MINES);
275 pBoard->hFacesBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_FACES);
276 pBoard->hLedsBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_LEDS);
277
278 LoadBoard( pBoard );
279
280 if( pBoard->Pos.x < GetSystemMetrics( SM_CXFIXEDFRAME ) )
281 pBoard->Pos.x = GetSystemMetrics( SM_CXFIXEDFRAME );
282
283 if( pBoard->Pos.x > (GetSystemMetrics( SM_CXSCREEN ) - GetSystemMetrics( SM_CXFIXEDFRAME )))
284 {
285 pBoard->Pos.x = GetSystemMetrics( SM_CXSCREEN )
286 - GetSystemMetrics( SM_CXFIXEDFRAME );
287 }
288
289 if( pBoard->Pos.y < (GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFIXEDFRAME )))
290 {
291 pBoard->Pos.y = GetSystemMetrics( SM_CYMENU ) +
292 GetSystemMetrics( SM_CYCAPTION ) +
293 GetSystemMetrics( SM_CYFIXEDFRAME );
294 }
295
296 if( pBoard->Pos.y > (GetSystemMetrics( SM_CYSCREEN ) - GetSystemMetrics( SM_CYFIXEDFRAME )))
297 {
298 pBoard->Pos.y = GetSystemMetrics( SM_CYSCREEN )
299 - GetSystemMetrics( SM_CYFIXEDFRAME );
300 }
301
302 hMenu = GetMenu( pBoard->hWnd );
303 CheckMenuItem( hMenu, IDM_BEGINNER + pBoard->Difficulty, MF_CHECKED );
304
305 if( pBoard->bMark )
306 CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
307 else
308 CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
309 CheckLevel( pBoard );
310 }
311
312 static DWORD LoadDWord(HKEY hKey, TCHAR *szKeyName, DWORD dwDefaultValue)
313 {
314 DWORD dwSize;
315 DWORD dwValue;
316
317 dwSize = sizeof(DWORD);
318
319 if( RegQueryValueEx( hKey, szKeyName, NULL, NULL, (LPBYTE) &dwValue, &dwSize ) == ERROR_SUCCESS )
320 return dwValue;
321
322 return dwDefaultValue;
323 }
324
325 void LoadBoard( BOARD *pBoard )
326 {
327 DWORD dwSize;
328 HKEY hKey;
329 TCHAR szData[16];
330 TCHAR szKeyName[8];
331 TCHAR szNobody[15];
332 UCHAR i;
333
334 RegOpenKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, KEY_QUERY_VALUE, &hKey );
335
336 pBoard->Pos.x = (LONG) LoadDWord( hKey, TEXT("Xpos"), GetSystemMetrics(SM_CXFIXEDFRAME) );
337 pBoard->Pos.y = (LONG) LoadDWord( hKey, TEXT("Ypos"), GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFIXEDFRAME) );
338 pBoard->uRows = (ULONG) LoadDWord( hKey, TEXT("Height"), BEGINNER_ROWS );
339 pBoard->uCols = (ULONG) LoadDWord( hKey, TEXT("Width"), BEGINNER_COLS );
340 pBoard->uMines = (ULONG) LoadDWord( hKey, TEXT("Mines"), BEGINNER_MINES );
341 pBoard->Difficulty = (DIFFICULTY) LoadDWord( hKey, TEXT("Difficulty"), BEGINNER );
342 pBoard->bMark = (BOOL) LoadDWord( hKey, TEXT("Mark"), TRUE );
343
344 LoadString( pBoard->hInst, IDS_NOBODY, szNobody, sizeof(szNobody) / sizeof(TCHAR) );
345
346 for( i = 0; i < 3; i++ )
347 {
348 // As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys
349 wsprintf( szKeyName, TEXT("Name%d"), i + 1 );
350 dwSize = sizeof(szData);
351
352 if( RegQueryValueEx( hKey, szKeyName, NULL, NULL, (LPBYTE)szData, (LPDWORD) &dwSize ) == ERROR_SUCCESS )
353 _tcsncpy( pBoard->szBestName[i], szData, sizeof(szData) / sizeof(TCHAR) );
354 else
355 _tcscpy( pBoard->szBestName[i], szNobody);
356 }
357
358 for( i = 0; i < 3; i++ )
359 {
360 wsprintf( szKeyName, TEXT("Time%d"), i + 1 );
361 pBoard->uBestTime[i] = LoadDWord( hKey, szKeyName, 999 );
362 }
363
364 RegCloseKey(hKey);
365 }
366
367 void SaveBoard( BOARD *pBoard )
368 {
369 DWORD dwValue;
370 HKEY hKey;
371 UCHAR i;
372 TCHAR szData[16];
373 TCHAR szKeyName[8];
374
375 if( RegCreateKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL ) != ERROR_SUCCESS)
376 return;
377
378 RegSetValueEx( hKey, TEXT("Xpos"), 0, REG_DWORD, (LPBYTE) &pBoard->Pos.x, sizeof(DWORD) );
379 RegSetValueEx( hKey, TEXT("Ypos"), 0, REG_DWORD, (LPBYTE) &pBoard->Pos.y, sizeof(DWORD) );
380 RegSetValueEx( hKey, TEXT("Difficulty"), 0, REG_DWORD, (LPBYTE) &pBoard->Difficulty, sizeof(DWORD) );
381 RegSetValueEx( hKey, TEXT("Height"), 0, REG_DWORD, (LPBYTE) &pBoard->uRows, sizeof(DWORD) );
382 RegSetValueEx( hKey, TEXT("Width"), 0, REG_DWORD, (LPBYTE) &pBoard->uCols, sizeof(DWORD) );
383 RegSetValueEx( hKey, TEXT("Mines"), 0, REG_DWORD, (LPBYTE) &pBoard->uMines, sizeof(DWORD) );
384 RegSetValueEx( hKey, TEXT("Mark"), 0, REG_DWORD, (LPBYTE) &pBoard->bMark, sizeof(DWORD) );
385
386 for( i = 0; i < 3; i++ )
387 {
388 // As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys
389 wsprintf( szKeyName, TEXT("Name%u"), i + 1);
390 _tcsncpy( szData, pBoard->szBestName[i], sizeof(szData) / sizeof(TCHAR) );
391 RegSetValueEx( hKey, szKeyName, 0, REG_SZ, (LPBYTE)szData, (_tcslen(szData) + 1) * sizeof(TCHAR) );
392 }
393
394 for( i = 0; i < 3; i++ )
395 {
396 wsprintf( szKeyName, TEXT("Time%u"), i + 1);
397 dwValue = pBoard->uBestTime[i];
398 RegSetValueEx( hKey, szKeyName, 0, REG_DWORD, (LPBYTE)(LPDWORD)&dwValue, sizeof(DWORD) );
399 }
400
401 RegCloseKey(hKey);
402 }
403
404 void DestroyBoard( BOARD *pBoard )
405 {
406 DeleteObject( pBoard->hFacesBMP );
407 DeleteObject( pBoard->hLedsBMP );
408 DeleteObject( pBoard->hMinesBMP );
409 }
410
411 void SetDifficulty( BOARD *pBoard, DIFFICULTY Difficulty )
412 {
413 HMENU hMenu;
414
415 switch(Difficulty)
416 {
417 case BEGINNER:
418 pBoard->uCols = BEGINNER_COLS;
419 pBoard->uRows = BEGINNER_ROWS;
420 pBoard->uMines = BEGINNER_MINES;
421 break;
422
423 case ADVANCED:
424 pBoard->uCols = ADVANCED_COLS;
425 pBoard->uRows = ADVANCED_ROWS;
426 pBoard->uMines = ADVANCED_MINES;
427 break;
428
429 case EXPERT:
430 pBoard->uCols = EXPERT_COLS;
431 pBoard->uRows = EXPERT_ROWS;
432 pBoard->uMines = EXPERT_MINES;
433 break;
434
435 case CUSTOM:
436 if( DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_CUSTOM), pBoard->hWnd, CustomDlgProc, (LPARAM) pBoard) != IDOK )
437 return;
438
439 break;
440 }
441
442 hMenu = GetMenu(pBoard->hWnd);
443 CheckMenuItem( hMenu, IDM_BEGINNER + pBoard->Difficulty, MF_UNCHECKED );
444 pBoard->Difficulty = Difficulty;
445 CheckMenuItem( hMenu, IDM_BEGINNER + Difficulty, MF_CHECKED );
446
447 }
448
449 void CreateBoard( BOARD *pBoard )
450 {
451 ULONG uLeft, uTop, uBottom, uRight, uWndX, uWndY, uWndWidth, uWndHeight;
452
453 pBoard->uBoxesLeft = pBoard->uCols * pBoard->uRows - pBoard->uMines;
454 pBoard->uNumFlags = 0;
455
456 CreateBoxes( pBoard );
457
458 pBoard->uWidth = pBoard->uCols * MINE_WIDTH + BOARD_WMARGIN * 2;
459
460 pBoard->uHeight = pBoard->uRows * MINE_HEIGHT + LED_HEIGHT
461 + BOARD_HMARGIN * 3;
462
463 uWndX = pBoard->Pos.x - GetSystemMetrics( SM_CXFIXEDFRAME );
464 uWndY = pBoard->Pos.y - GetSystemMetrics( SM_CYMENU )
465 - GetSystemMetrics( SM_CYCAPTION )
466 - GetSystemMetrics( SM_CYFIXEDFRAME );
467 uWndWidth = pBoard->uWidth + GetSystemMetrics( SM_CXFIXEDFRAME ) * 2;
468 uWndHeight = pBoard->uHeight
469 + GetSystemMetrics( SM_CYMENU )
470 + GetSystemMetrics( SM_CYCAPTION )
471 + GetSystemMetrics( SM_CYFIXEDFRAME ) * 2;
472
473 /* setting the mines rectangle boundary */
474 uLeft = BOARD_WMARGIN;
475 uTop = BOARD_HMARGIN * 2 + LED_HEIGHT;
476 uRight = uLeft + pBoard->uCols * MINE_WIDTH;
477 uBottom = uTop + pBoard->uRows * MINE_HEIGHT;
478 SetRect( &pBoard->MinesRect, uLeft, uTop, uRight, uBottom );
479
480 /* setting the face rectangle boundary */
481 uLeft = pBoard->uWidth / 2 - FACE_WIDTH / 2;
482 uTop = BOARD_HMARGIN;
483 uRight = uLeft + FACE_WIDTH;
484 uBottom = uTop + FACE_HEIGHT;
485 SetRect( &pBoard->FaceRect, uLeft, uTop, uRight, uBottom );
486
487 /* setting the timer rectangle boundary */
488 uLeft = BOARD_WMARGIN;
489 uTop = BOARD_HMARGIN;
490 uRight = uLeft + LED_WIDTH * 3;
491 uBottom = uTop + LED_HEIGHT;
492 SetRect( &pBoard->CounterRect, uLeft, uTop, uRight, uBottom );
493
494 /* setting the counter rectangle boundary */
495 uLeft = pBoard->uWidth - BOARD_WMARGIN - LED_WIDTH * 3;
496 uTop = BOARD_HMARGIN;
497 uRight = pBoard->uWidth - BOARD_WMARGIN;
498 uBottom = uTop + LED_HEIGHT;
499 SetRect( &pBoard->TimerRect, uLeft, uTop, uRight, uBottom );
500
501 pBoard->Status = WAITING;
502 pBoard->FaceBmp = SMILE_BMP;
503 pBoard->uTime = 0;
504
505 MoveWindow( pBoard->hWnd, uWndX, uWndY, uWndWidth, uWndHeight, TRUE );
506 RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE );
507 }
508
509 void CheckLevel( BOARD *pBoard )
510 {
511 if( pBoard->uRows < BEGINNER_ROWS )
512 pBoard->uRows = BEGINNER_ROWS;
513
514 if( pBoard->uRows > MAX_ROWS )
515 pBoard->uRows = MAX_ROWS;
516
517 if( pBoard->uCols < BEGINNER_COLS )
518 pBoard->uCols = BEGINNER_COLS;
519
520 if( pBoard->uCols > MAX_COLS )
521 pBoard->uCols = MAX_COLS;
522
523 if( pBoard->uMines < BEGINNER_MINES )
524 pBoard->uMines = BEGINNER_MINES;
525
526 if( pBoard->uMines > pBoard->uCols * pBoard->uRows - 1 )
527 pBoard->uMines = pBoard->uCols * pBoard->uRows - 1;
528 }
529
530 void CreateBoxes( BOARD *pBoard )
531 {
532 LONG i, j;
533 ULONG uCol, uRow;
534
535 srand( (unsigned int)time( NULL ) );
536
537 /* Create the boxes...
538 * We actually create them with an empty border,
539 * so special care doesn't have to be taken on the edges
540 */
541
542 for( uCol = 0; uCol <= pBoard->uCols + 1; uCol++ )
543 {
544 for( uRow = 0; uRow <= pBoard->uRows + 1; uRow++ )
545 {
546 pBoard->Box[uCol][uRow].bIsPressed = FALSE;
547 pBoard->Box[uCol][uRow].bIsMine = FALSE;
548 pBoard->Box[uCol][uRow].uFlagType = NORMAL;
549 pBoard->Box[uCol][uRow].uNumMines = 0;
550 }
551 }
552
553 /* create mines */
554 i = 0;
555 while( (ULONG)i < pBoard->uMines )
556 {
557 uCol = (ULONG)(pBoard->uCols * (float)rand() / RAND_MAX + 1);
558 uRow = (ULONG)(pBoard->uRows * (float)rand() / RAND_MAX + 1);
559
560 if( !pBoard->Box[uCol][uRow].bIsMine )
561 {
562 i++;
563 pBoard->Box[uCol][uRow].bIsMine = TRUE;
564 }
565 }
566
567 /*
568 * Now we label the remaining boxes with the
569 * number of mines surrounding them.
570 */
571 for( uCol = 1; uCol < pBoard->uCols + 1; uCol++ )
572 {
573 for( uRow = 1; uRow < pBoard->uRows + 1; uRow++ )
574 {
575 for( i = -1; i <= 1; i++ )
576 {
577 for( j = -1; j <= 1; j++ )
578 {
579 if( pBoard->Box[uCol + i][uRow + j].bIsMine )
580 {
581 pBoard->Box[uCol][uRow].uNumMines++;
582 }
583 }
584 }
585 }
586 }
587 }
588
589 void DrawMines ( HDC hdc, HDC hMemDC, BOARD *pBoard )
590 {
591 HGDIOBJ hOldObj;
592 ULONG uCol, uRow;
593 hOldObj = SelectObject (hMemDC, pBoard->hMinesBMP);
594
595 for( uRow = 1; uRow <= pBoard->uRows; uRow++ )
596 {
597 for( uCol = 1; uCol <= pBoard->uCols; uCol++ )
598 {
599 DrawMine( hdc, hMemDC, pBoard, uCol, uRow, FALSE );
600 }
601 }
602
603 SelectObject( hMemDC, hOldObj );
604 }
605
606 void DrawMine( HDC hdc, HDC hMemDC, BOARD *pBoard, ULONG uCol, ULONG uRow, BOOL bIsPressed )
607 {
608 MINEBMP_OFFSET offset = BOX_BMP;
609
610 if( uCol == 0 || uCol > pBoard->uCols || uRow == 0 || uRow > pBoard->uRows )
611 return;
612
613 if( pBoard->Status == GAMEOVER )
614 {
615 if( pBoard->Box[uCol][uRow].bIsMine )
616 {
617 switch( pBoard->Box[uCol][uRow].uFlagType )
618 {
619 case FLAG:
620 offset = FLAG_BMP;
621 break;
622 case COMPLETE:
623 offset = EXPLODE_BMP;
624 break;
625 case QUESTION:
626 /* fall through */
627 case NORMAL:
628 offset = MINE_BMP;
629 }
630 }
631 else
632 {
633 switch( pBoard->Box[uCol][uRow].uFlagType )
634 {
635 case QUESTION:
636 offset = QUESTION_BMP;
637 break;
638 case FLAG:
639 offset = WRONG_BMP;
640 break;
641 case NORMAL:
642 offset = BOX_BMP;
643 break;
644 case COMPLETE:
645 /* Do nothing */
646 break;
647 default:
648 DEBUG("Unknown FlagType during game over in DrawMine\n");
649 break;
650 }
651 }
652 }
653 else
654 { /* WAITING or PLAYING */
655 switch( pBoard->Box[uCol][uRow].uFlagType )
656 {
657 case QUESTION:
658 if( !bIsPressed )
659 offset = QUESTION_BMP;
660 else
661 offset = QPRESS_BMP;
662 break;
663 case FLAG:
664 offset = FLAG_BMP;
665 break;
666 case NORMAL:
667 if( !bIsPressed )
668 offset = BOX_BMP;
669 else
670 offset = MPRESS_BMP;
671 break;
672 case COMPLETE:
673 /* Do nothing */
674 break;
675 default:
676 DEBUG("Unknown FlagType while playing in DrawMine\n");
677 break;
678 }
679 }
680
681 if( pBoard->Box[uCol][uRow].uFlagType == COMPLETE && !pBoard->Box[uCol][uRow].bIsMine )
682 offset = (MINEBMP_OFFSET) pBoard->Box[uCol][uRow].uNumMines;
683
684 BitBlt( hdc,
685 (uCol - 1) * MINE_WIDTH + pBoard->MinesRect.left,
686 (uRow - 1) * MINE_HEIGHT + pBoard->MinesRect.top,
687 MINE_WIDTH, MINE_HEIGHT,
688 hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY );
689 }
690
691 void DrawLeds( HDC hDC, HDC hMemDC, BOARD *pBoard, LONG nNumber, LONG x, LONG y )
692 {
693 HGDIOBJ hOldObj;
694 UCHAR i;
695 ULONG uLED[3];
696 LONG nCount;
697
698 nCount = nNumber;
699
700 if( nCount < 1000 )
701 {
702 if( nCount >= 0 )
703 {
704 uLED[0] = nCount / 100 ;
705 nCount -= uLED[0] * 100;
706 }
707 else
708 {
709 uLED[0] = 10; /* negative sign */
710 nCount = -nCount;
711 }
712
713 uLED[1] = nCount / 10;
714 nCount -= uLED[1] * 10;
715 uLED[2] = nCount;
716 }
717 else
718 {
719 for( i = 0; i < 3; i++ )
720 uLED[i] = 10;
721 }
722
723 /* use unlit led if not playing */
724 /* if( pBoard->Status == WAITING )
725 for( i = 0; i < 3; i++ )
726 uLED[i] = 11;*/
727
728 hOldObj = SelectObject (hMemDC, pBoard->hLedsBMP);
729
730 for( i = 0; i < 3; i++ )
731 {
732 BitBlt( hDC,
733 i * LED_WIDTH + x,
734 y,
735 LED_WIDTH,
736 LED_HEIGHT,
737 hMemDC,
738 0,
739 uLED[i] * LED_HEIGHT,
740 SRCCOPY);
741 }
742
743 SelectObject( hMemDC, hOldObj );
744 }
745
746 void DrawFace( HDC hDC, HDC hMemDC, BOARD *pBoard )
747 {
748 HGDIOBJ hOldObj;
749
750 hOldObj = SelectObject (hMemDC, pBoard->hFacesBMP);
751
752 BitBlt( hDC,
753 pBoard->FaceRect.left,
754 pBoard->FaceRect.top,
755 FACE_WIDTH,
756 FACE_HEIGHT,
757 hMemDC, 0, pBoard->FaceBmp * FACE_HEIGHT, SRCCOPY);
758
759 SelectObject( hMemDC, hOldObj );
760 }
761
762 void DrawBoard( HDC hDC, HDC hMemDC, PAINTSTRUCT *ps, BOARD *pBoard )
763 {
764 RECT TempRect;
765
766 if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->CounterRect) )
767 DrawLeds( hDC, hMemDC, pBoard, pBoard->uMines - pBoard->uNumFlags,
768 pBoard->CounterRect.left,
769 pBoard->CounterRect.top );
770
771 if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->TimerRect ) )
772 DrawLeds( hDC, hMemDC, pBoard, pBoard->uTime,
773 pBoard->TimerRect.left,
774 pBoard->TimerRect.top);
775
776 if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->FaceRect ) )
777 DrawFace( hDC, hMemDC, pBoard );
778
779 if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->MinesRect ) )
780 DrawMines( hDC, hMemDC, pBoard );
781 }
782
783
784 void TestBoard( HWND hWnd, BOARD *pBoard, LONG x, LONG y, int msg )
785 {
786 POINT pt;
787 ULONG uCol, uRow;
788
789 pt.x = x;
790 pt.y = y;
791
792 if( PtInRect( &pBoard->MinesRect, pt ) && pBoard->Status != GAMEOVER && pBoard->Status != WON )
793 TestMines( pBoard, pt, msg );
794 else
795 {
796 UnpressBoxes( pBoard, pBoard->Press.x, pBoard->Press.y );
797 pBoard->Press.x = 0;
798 pBoard->Press.y = 0;
799 }
800
801 if( pBoard->uBoxesLeft == 0 )
802 {
803 // MG - 2006-02-21
804 // mimic MS minesweeper behaviour - when autocompleting a board, flag mines
805 pBoard->Status = WON;
806
807 for( uCol = 0; uCol <= pBoard->uCols + 1; uCol++ )
808 {
809 for( uRow = 0; uRow <= pBoard->uRows + 1; uRow++ )
810 {
811 if(pBoard->Box[uCol][uRow].bIsMine)
812 {
813 pBoard->Box[uCol][uRow].uFlagType = FLAG;
814 }
815 }
816 }
817
818 pBoard->uNumFlags = pBoard->uMines;
819 RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
820
821 if( pBoard->Difficulty != CUSTOM && pBoard->uTime < pBoard->uBestTime[pBoard->Difficulty] )
822 {
823 pBoard->uBestTime[pBoard->Difficulty] = pBoard->uTime;
824
825 DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_CONGRATS), hWnd, CongratsDlgProc, (LPARAM) pBoard);
826 DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_TIMES), hWnd, TimesDlgProc, (LPARAM) pBoard);
827 }
828 }
829
830 TestFace( pBoard, pt, msg );
831 }
832
833 void TestMines( BOARD *pBoard, POINT pt, int msg )
834 {
835 BOOL bDraw = TRUE;
836 ULONG uCol, uRow;
837
838 uCol = (pt.x - pBoard->MinesRect.left) / MINE_WIDTH + 1;
839 uRow = (pt.y - pBoard->MinesRect.top ) / MINE_HEIGHT + 1;
840
841 switch (msg)
842 {
843 case WM_LBUTTONDOWN:
844 if( pBoard->Press.x != uCol || pBoard->Press.y != uRow )
845 {
846 UnpressBox( pBoard, pBoard->Press.x, pBoard->Press.y );
847 pBoard->Press.x = uCol;
848 pBoard->Press.y = uRow;
849 PressBox( pBoard, uCol, uRow );
850 }
851
852 bDraw = FALSE;
853 break;
854
855 case WM_LBUTTONUP:
856 if( pBoard->Press.x != uCol || pBoard->Press.y != uRow )
857 UnpressBox( pBoard, pBoard->Press.x, pBoard->Press.y );
858
859 pBoard->Press.x = 0;
860 pBoard->Press.y = 0;
861
862 if( pBoard->Box[uCol][uRow].uFlagType != FLAG )
863 pBoard->Status = PLAYING;
864
865 CompleteBox( pBoard, uCol, uRow );
866 break;
867
868 case WM_MBUTTONDOWN:
869 PressBoxes( pBoard, uCol, uRow );
870 bDraw = FALSE;
871 break;
872
873 case WM_MBUTTONUP:
874 if( pBoard->Press.x != uCol || pBoard->Press.y != uRow )
875 UnpressBoxes( pBoard, pBoard->Press.x, pBoard->Press.y );
876
877 pBoard->Press.x = 0;
878 pBoard->Press.y = 0;
879 CompleteBoxes( pBoard, uCol, uRow );
880 break;
881
882 case WM_RBUTTONDOWN:
883 AddFlag( pBoard, uCol, uRow );
884 pBoard->Status = PLAYING;
885 break;
886
887 default:
888 DEBUG("Unknown message type received in TestMines\n");
889 break;
890 }
891
892 if(bDraw)
893 RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
894 }
895
896 void TestFace( BOARD *pBoard, POINT pt, int msg )
897 {
898 if( pBoard->Status == PLAYING || pBoard->Status == WAITING )
899 {
900 if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN )
901 pBoard->FaceBmp = OOH_BMP;
902 else pBoard->FaceBmp = SMILE_BMP;
903 }
904 else if( pBoard->Status == GAMEOVER )
905 pBoard->FaceBmp = DEAD_BMP;
906 else if( pBoard->Status == WON )
907 pBoard->FaceBmp = COOL_BMP;
908
909 if( PtInRect( &pBoard->FaceRect, pt ) )
910 {
911 if( msg == WM_LBUTTONDOWN )
912 pBoard->FaceBmp = SPRESS_BMP;
913
914 if( msg == WM_LBUTTONUP )
915 CreateBoard( pBoard );
916 }
917
918 RedrawWindow( pBoard->hWnd, &pBoard->FaceRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
919 }
920
921 void CompleteBox( BOARD *pBoard, ULONG uCol, ULONG uRow )
922 {
923 CHAR i, j;
924
925 if( pBoard->Box[uCol][uRow].uFlagType != COMPLETE &&
926 pBoard->Box[uCol][uRow].uFlagType != FLAG &&
927 uCol > 0 && uCol < pBoard->uCols + 1 &&
928 uRow > 0 && uRow < pBoard->uRows + 1 )
929 {
930 pBoard->Box[uCol][uRow].uFlagType = COMPLETE;
931
932 if( pBoard->Box[uCol][uRow].bIsMine )
933 {
934 pBoard->FaceBmp = DEAD_BMP;
935 pBoard->Status = GAMEOVER;
936 }
937 else if( pBoard->Status != GAMEOVER )
938 pBoard->uBoxesLeft--;
939
940 if( pBoard->Box[uCol][uRow].uNumMines == 0 )
941 {
942 for( i = -1; i <= 1; i++ )
943 for( j = -1; j <= 1; j++ )
944 CompleteBox( pBoard, uCol + i, uRow + j );
945 }
946 }
947 }
948
949 void CompleteBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow )
950 {
951 CHAR i, j;
952 ULONG uNumFlags = 0;
953
954 if( pBoard->Box[uCol][uRow].uFlagType == COMPLETE )
955 {
956 for( i = -1; i <= 1; i++ )
957 {
958 for( j = -1; j <= 1; j++ )
959 {
960 if( pBoard->Box[uCol + i][uRow + j].uFlagType == FLAG )
961 uNumFlags++;
962 }
963 }
964
965 if( uNumFlags == pBoard->Box[uCol][uRow].uNumMines )
966 {
967 for( i = -1; i <= 1; i++ )
968 {
969 for( j = -1; j <= 1; j++ )
970 {
971 if( pBoard->Box[uCol + i][uRow + j].uFlagType != FLAG )
972 CompleteBox( pBoard, uCol + i, uRow + j );
973 }
974 }
975 }
976 }
977 }
978
979 void AddFlag( BOARD *pBoard, ULONG uCol, ULONG uRow )
980 {
981 if( pBoard->Box[uCol][uRow].uFlagType != COMPLETE )
982 {
983 switch( pBoard->Box[uCol][uRow].uFlagType )
984 {
985 case FLAG:
986 if( pBoard->bMark )
987 pBoard->Box[uCol][uRow].uFlagType = QUESTION;
988 else
989 pBoard->Box[uCol][uRow].uFlagType = NORMAL;
990
991 pBoard->uNumFlags--;
992 break;
993
994 case QUESTION:
995 pBoard->Box[uCol][uRow].uFlagType = NORMAL;
996 break;
997
998 default:
999 pBoard->Box[uCol][uRow].uFlagType = FLAG;
1000 pBoard->uNumFlags++;
1001 }
1002 }
1003 }
1004
1005 void PressBox( BOARD *pBoard, ULONG uCol, ULONG uRow )
1006 {
1007 HDC hDC;
1008 HGDIOBJ hOldObj;
1009 HDC hMemDC;
1010
1011 hDC = GetDC( pBoard->hWnd );
1012 hMemDC = CreateCompatibleDC(hDC);
1013 hOldObj = SelectObject (hMemDC, pBoard->hMinesBMP);
1014
1015 DrawMine( hDC, hMemDC, pBoard, uCol, uRow, TRUE );
1016
1017 SelectObject( hMemDC, hOldObj );
1018 DeleteDC( hMemDC );
1019 ReleaseDC( pBoard->hWnd, hDC );
1020 }
1021
1022 void PressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow )
1023 {
1024 CHAR i, j;
1025
1026 for( i = -1; i <= 1; i++ )
1027 {
1028 for( j = -1; j <= 1; j++ )
1029 {
1030 pBoard->Box[uCol + i][uRow + j].bIsPressed = TRUE;
1031 PressBox( pBoard, uCol + i, uRow + j );
1032 }
1033 }
1034
1035 for( i = -1; i <= 1; i++ )
1036 {
1037 for( j = -1; j <= 1; j++ )
1038 {
1039 if( !pBoard->Box[pBoard->Press.x + i][pBoard->Press.y + j].bIsPressed )
1040 UnpressBox( pBoard, pBoard->Press.x + i, pBoard->Press.y + j );
1041 }
1042 }
1043
1044 for( i = -1; i <= 1; i++ )
1045 {
1046 for( j = -1; j <= 1; j++ )
1047 {
1048 pBoard->Box[uCol + i][uRow + j].bIsPressed = FALSE;
1049 PressBox( pBoard, uCol + i, uRow + j );
1050 }
1051 }
1052
1053 pBoard->Press.x = uCol;
1054 pBoard->Press.y = uRow;
1055 }
1056
1057 void UnpressBox( BOARD *pBoard, ULONG uCol, ULONG uRow )
1058 {
1059 HDC hDC;
1060 HGDIOBJ hOldObj;
1061 HDC hMemDC;
1062
1063 hDC = GetDC( pBoard->hWnd );
1064 hMemDC = CreateCompatibleDC( hDC );
1065 hOldObj = SelectObject( hMemDC, pBoard->hMinesBMP );
1066
1067 DrawMine( hDC, hMemDC, pBoard, uCol, uRow, FALSE );
1068
1069 SelectObject( hMemDC, hOldObj );
1070 DeleteDC( hMemDC );
1071 ReleaseDC( pBoard->hWnd, hDC );
1072 }
1073
1074 void UnpressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow )
1075 {
1076 CHAR i, j;
1077
1078 for( i = -1; i <= 1; i++ )
1079 {
1080 for( j = -1; j <= 1; j++ )
1081 {
1082 UnpressBox( pBoard, uCol + i, uRow + j );
1083 }
1084 }
1085 }