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