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