/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "MainWindow.h" #include "FXMessageBox.h" #include "fxdefs.h" #include // rand #include #include FXDEFMAP(MainWindow) MainWindow_Map[]= { //________Message_Type____________ID____________Message_Handler_______ FXMAPFUNC(SEL_COMMAND, MainWindow::UI_Tile, MainWindow::on_Tile_Click), FXMAPFUNC(SEL_COMMAND, MainWindow::UI_New, MainWindow::on_New_Click), FXMAPFUNC(SEL_RIGHTBUTTONPRESS, MainWindow::UI_Tile, MainWindow::on_Tile_Right_Click), FXMAPFUNC(SEL_TIMEOUT, MainWindow::UI_Timer_Tick, MainWindow::on_Timer_Tick), FXMAPFUNC(SEL_KEYPRESS, 0, MainWindow::on_Key_Press), FXMAPFUNC(SEL_KEYRELEASE, 0, MainWindow::on_Key_Release), }; FXIMPLEMENT(MainWindow, FXMainWindow, MainWindow_Map, ARRAYNUMBER(MainWindow_Map)) MainWindow::MainWindow(FXApp *a) : FXMainWindow(a, "foxminesweeper", nullptr, nullptr, DECOR_ALL, 0,0, 500, 500) { board = new Board(); matrix = nullptr; app = a; create_ui(); int w = 9; int h = 9; int minecount = 10; game_count = 0; game_over = false; new_game(w, h, minecount); } MainWindow::~MainWindow() { delete board; } long MainWindow::on_Timer_Tick(FXObject *sender, FXSelector sel, void *data) { seconds++; time_label->setText(FXStringVal(seconds)); getApp()->addTimeout(this, UI_Timer_Tick, 1000, nullptr); return 1; } void MainWindow::create() { FXMainWindow::create(); bomb_icon->create(); flag_icon->create(); empty_icon->create(); tile_1_icon->create(); tile_2_icon->create(); tile_3_icon->create(); tile_4_icon->create(); tile_5_icon->create(); tile_6_icon->create(); tile_7_icon->create(); tile_8_icon->create(); show(PLACEMENT_SCREEN); } void MainWindow::create_ui() { contents=new FXHorizontalFrame(this, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y); // LEFT pane to contain the canvas canvasFrame=new FXVerticalFrame(contents, FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT, 0, 0, 0, 0, 10, 10, 10, 10); // Label above the canvas new FXLabel(canvasFrame,"foxminesweeper", NULL, JUSTIFY_CENTER_X|LAYOUT_FILL_X); time_label = new FXLabel(canvasFrame, "0", nullptr, LAYOUT_CENTER_X); // Horizontal divider line new FXHorizontalSeparator(canvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X); scroll_area = new FXScrollWindow(canvasFrame, FX::SCROLLERS_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN); scroll_area->setBackColor(canvasFrame->getBackColor()); // RIGHT pane for the buttons buttonFrame=new FXVerticalFrame(contents, FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT, 0, 0, 0, 0, 10, 10, 10, 10); // Label above the buttons new FXLabel(buttonFrame, "New Game", NULL, JUSTIFY_CENTER_X|LAYOUT_FILL_X); // Horizontal divider line new FXHorizontalSeparator(buttonFrame, SEPARATOR_RIDGE|LAYOUT_FILL_X); // new / exit width_input_frame = new FXHorizontalFrame(buttonFrame, LAYOUT_SIDE_TOP|LAYOUT_FILL_X); height_input_frame = new FXHorizontalFrame(buttonFrame, LAYOUT_SIDE_TOP|LAYOUT_FILL_X); mine_input_frame = new FXHorizontalFrame(buttonFrame, LAYOUT_SIDE_TOP|LAYOUT_FILL_X); new FXLabel(width_input_frame, "Width: ", NULL, LAYOUT_BOTTOM|LAYOUT_FILL_X); width_input = new FXTextField(width_input_frame, 3, NULL, 0, TEXTFIELD_NORMAL|TEXTFIELD_INTEGER); width_input->setText("9"); new FXLabel(height_input_frame, "Height: ", NULL, LAYOUT_BOTTOM|LAYOUT_FILL_X); height_input = new FXTextField(height_input_frame, 3, NULL, 0, TEXTFIELD_NORMAL|TEXTFIELD_INTEGER); height_input->setText("9"); new FXLabel(mine_input_frame, "Mines: ", NULL, LAYOUT_BOTTOM|LAYOUT_FILL_X); mine_input = new FXTextField(mine_input_frame, 3, NULL, 0, TEXTFIELD_NORMAL|TEXTFIELD_INTEGER); mine_input->setText("10"); new FXButton(buttonFrame, "&New Game", nullptr, this, MainWindow::UI_New, BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(buttonFrame, "&Exit", nullptr, app, FXApp::ID_QUIT, BUTTON_NORMAL|LAYOUT_FILL_X|LAYOUT_BOTTOM); /* create icons */ bomb_icon = new FXGIFIcon(app, bomb, IMAGE_KEEP); flag_icon = new FXGIFIcon(app, flag, IMAGE_KEEP); empty_icon = new FXGIFIcon(app, empty, IMAGE_KEEP); tile_1_icon = new FXGIFIcon(app, tile_1, IMAGE_KEEP); tile_2_icon = new FXGIFIcon(app, tile_2, IMAGE_KEEP); tile_3_icon = new FXGIFIcon(app, tile_3, IMAGE_KEEP); tile_4_icon = new FXGIFIcon(app, tile_4, IMAGE_KEEP); tile_5_icon = new FXGIFIcon(app, tile_5, IMAGE_KEEP); tile_6_icon = new FXGIFIcon(app, tile_6, IMAGE_KEEP); tile_7_icon = new FXGIFIcon(app, tile_7, IMAGE_KEEP); tile_8_icon = new FXGIFIcon(app, tile_8, IMAGE_KEEP); } void MainWindow::new_game(int w, int h, int minecount) { tile_buttons.clear(); game_count++; seconds = 0; ticking = false; //delete board; delete matrix; puts("starting new game.."); int tilecount = w*h; printf("tile count will be %d\n", tilecount); //board = new Board(width, height, minecount); board->new_game(w, h, minecount); matrix = new FXMatrix(scroll_area, w, MATRIX_BY_COLUMNS|LAYOUT_CENTER_Y|LAYOUT_CENTER_X); if (game_count > 1) matrix->create(); for (int i = 0; i < tilecount; i++) { std::unique_ptr b(new FXButton(matrix, "", nullptr, this, UI_Tile)); b->setIcon(empty_icon); if (game_count > 1) b->create(); tile_buttons.push_back(std::move(b)); } draw_buttons(); } void MainWindow::draw_buttons() { int x, y; FXButton *button; Tile *tile; for(auto b = tile_buttons.begin(); b != tile_buttons.end(); ++b) { button = (*b).get(); x = matrix->colOfChild(button); y = matrix->rowOfChild(button); if (x < 0 || y < 0) return; tile = board->get_tile_at(x, y); if (tile->is_flagged()) { button->setIcon(flag_icon); button->setFrameStyle(0); continue; } if (!tile->is_revealed()) { button->setIcon(empty_icon); button->setFrameStyle(BUTTON_NORMAL); continue; } if (tile->is_mine()) { button->setIcon(bomb_icon); button->setFrameStyle(0); button->setBackColor(FXRGB(255, 0, 0)); // make mines red } else { switch (tile->get_neighbor_mine_count()) { case 1: button->setIcon(tile_1_icon); break; case 2: button->setIcon(tile_2_icon); break; case 3: button->setIcon(tile_3_icon); break; case 4: button->setIcon(tile_4_icon); break; case 5: button->setIcon(tile_5_icon); break; case 6: button->setIcon(tile_6_icon); break; case 7: button->setIcon(tile_7_icon); break; case 8: button->setIcon(tile_8_icon); break; default: button->setIcon(empty_icon); break; } button->setFrameStyle(0); } } } long MainWindow::on_Tile_Click(FXObject *sender, FXSelector sel, void *data) { puts("click"); if (!ticking) { app->addTimeout(this, UI_Timer_Tick, 1000); ticking = true; } if (game_over == true) { puts("game is over"); return 1; } int x = 0, y = 0; Tile *tile; FXButton *button = dynamic_cast(sender); if (!button) { printf("button is null"); return 1; } puts("running click methods"); button->killFocus(); // let user control with keyboard? x = matrix->colOfChild(button); y = matrix->rowOfChild(button); tile = board->get_tile_at(x, y); if (tile->is_revealed()) return 1; if (tile->is_flagged()) return 1; printf("left clicked on: x=%d, y=%d\n", x, y); board->reveal_tile_at(x, y); if (!board->is_game_running() && !board->is_game_won()) { /* lost */ draw_buttons(); app->removeTimeout(this, UI_Timer_Tick); puts("you lose the game"); app->beep(); FXMessageBox::information(app, FX::MBOX_OK, "Game Over", "You lost in %ld seconds.", seconds); game_over = true; ticking = false; } else if (!board->is_game_running() && board->is_game_won()) { /* won */ draw_buttons(); app->removeTimeout(this, UI_Timer_Tick); puts("you won the game"); app->beep(); FXMessageBox::information(app, FX::MBOX_OK, "Game Over", "You won in %ld seconds.", seconds); game_over = true; ticking = false; } button->setFrameStyle(0); draw_buttons(); return 1; } long MainWindow::on_Tile_Right_Click(FXObject *sender, FXSelector sel, void *data) { if (!ticking) { app->addTimeout(this, UI_Timer_Tick, 1000); ticking = true; } if (game_over == true) return 1; int x = 0, y = 0; Tile *tile; FXButton *button = dynamic_cast(sender); if (!button) return 0; x = matrix->colOfChild(button); y = matrix->rowOfChild(button); tile = board->get_tile_at(x, y); if (tile->is_revealed()) return 1; printf("right click on: %d, %d\n", tile->get_x(), tile->get_y()); tile->toggle_flag(Tile::FLAGGED); printf("%d\n", tile->is_flagged()); draw_buttons(); return 1; } long MainWindow::on_New_Click(FXObject *sender, FXSelector sel, void *data) { if (board) { int w, h, m; w = FXIntVal(width_input->getText()); h = FXIntVal(height_input->getText()); m = FXIntVal(mine_input->getText()); if (w == 0 || h == 0 || m == 0) { /* all fields need values */ app->beep(); FXMessageBox::information(app, FX::MBOX_OK, "Invalid game options", "All fields need positive (non-zero) integer values"); return 1; } if (m >= (w*h)) { /* cannot have >= mines that tiles */ app->beep(); FXMessageBox::information(app, FX::MBOX_OK, "Invalid game options", "You cannot have the same or more mines than tiles (mines >= (width*height))"); return 1; } printf("new game width = %d height = %d mines = %d\n", w, h, m); game_over = false; time_label->setText("0"); app->removeTimeout(this, UI_Timer_Tick); new_game(w, h, m); } return 1; } long MainWindow::on_Key_Press(FXObject *sender, FXSelector sel, void *data) { FXEvent* event=(FXEvent*)data; switch(event->code) { /* show mine positons while left control held */ case KEY_Control_L: FXButton *button; Tile *tile; int x, y; for(auto b = tile_buttons.begin(); b != tile_buttons.end(); ++b) { button = (*b).get(); x = matrix->colOfChild(button); y = matrix->rowOfChild(button); tile = board->get_tile_at(x, y); if (tile->is_mine()) { button->setBackColor(FXRGB(0, 255, 0)); // make mines green } } break; default: this->onKeyPress(sender, sel, data); break; } return 1; } long MainWindow::on_Key_Release(FXObject *sender, FXSelector sel, void *data) { FXEvent* event=(FXEvent*)data; switch(event->code) { case KEY_Control_L: FXButton *button; Tile *tile; int x, y; for(auto b = tile_buttons.begin(); b != tile_buttons.end(); ++b) { button = (*b).get(); x = matrix->colOfChild(button); y = matrix->rowOfChild(button); tile = board->get_tile_at(x, y); if (tile->is_mine()) { button->setBackColor(canvasFrame->getBackColor()); // make mines normal } } break; default: this->onKeyPress(sender, sel, data); break; } return 1; }