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