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