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