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