File indexing completed on 2024-04-28 07:51:20

0001 /*
0002     KBlackBox - A simple game inspired by an emacs module
0003 
0004     SPDX-FileCopyrightText: 1999-2000 Robert Cimrman <cimrman3@students.zcu.cz>
0005     SPDX-FileCopyrightText: 2007 Nicolas Roffet <nicolas-kde@roffet.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "kbbmainwindow.h"
0011 
0012 #include <QFile>
0013 #include <QHBoxLayout>
0014 #include <QPointer>
0015 #include <QWidget>
0016 
0017 #include <KActionCollection>
0018 #include <KConfigDialog>
0019 #include <KLocalizedString>
0020 #include <KMessageBox>
0021 
0022 #include <KGameClock>
0023 #include <KGameHighScoreDialog>
0024 #include <KGameStandardAction>
0025 
0026 #include <QStatusBar>
0027 #include <QIcon>
0028 #include <QStandardPaths>
0029 
0030 #include "kbbgamedoc.h"
0031 #include "kbbgraphicsitemtutorialmarker.h"
0032 #include "kbblevelconfigurationwidget.h"
0033 #include "kbbprefs.h"
0034 #include "kbbscalablegraphicwidget.h"
0035 #include "kbbthememanager.h"
0036 #include "kbbtutorial.h"
0037 
0038 
0039 
0040 //
0041 // Constructor / Destructor
0042 //
0043 
0044 KBBMainWindow::KBBMainWindow()
0045 {
0046     m_boardEnabled = false;
0047 
0048     //Read configuration options
0049     m_customBallNumber = KBBPrefs::balls();
0050     m_customColumns = KBBPrefs::columns();
0051     m_customRows = KBBPrefs::rows();
0052 
0053 
0054     // Status bar
0055     //QT5 statusBar()->insertPermanentItem("", SRUN, 1);
0056     //QT5 statusBar()->insertPermanentItem(i18n("Time: 00:00"), STIME, 1);
0057     //QT5 statusBar()->insertPermanentItem(i18n("Size: 00 x 00"), SSIZE);
0058     //QT5 statusBar()->setItemAlignment(SRUN, Qt::AlignLeft | Qt::AlignVCenter);
0059 
0060 
0061     // Difficulty
0062     KGameDifficulty::global()->addStandardLevelRange(
0063         KGameDifficultyLevel::VeryEasy, KGameDifficultyLevel::ExtremelyHard,
0064         KGameDifficultyLevel::Medium //default
0065     );
0066     KGameDifficulty::global()->addLevel(new KGameDifficultyLevel(
0067         1000, QByteArray("config"), i18nc("Difficulty level title", "Configurable")
0068     ));
0069     KGameDifficultyGUI::init(this);
0070     connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, this, &KBBMainWindow::levelChanged);
0071 
0072 
0073     // Menu "Game"
0074     KGameStandardAction::gameNew(this, &KBBMainWindow::newGame, actionCollection());
0075     m_pauseAction = KGameStandardAction::pause(this, &KBBMainWindow::pause, actionCollection());
0076     QAction* tutorial = actionCollection()->addAction( QStringLiteral( "game_tutorial" ));
0077     tutorial->setText(i18n("Start Tutorial"));
0078     tutorial->setIcon(QIcon::fromTheme( QStringLiteral( "footprint" )));
0079     tutorial->setToolTip(i18n("Start tutorial"));
0080     tutorial->setWhatsThis(i18n("<qt>The <b>tutorial</b> is a fast, user friendly and interactive way to learn the rules of the game. Start it if you do not know them!</qt>"));
0081     connect(tutorial, &QAction::triggered, this, &KBBMainWindow::startTutorial);
0082     KGameStandardAction::quit(this, &QWidget::close, actionCollection());
0083     QAction* sandbox = actionCollection()->addAction( QStringLiteral( "game_sandbox" ));
0084     sandbox->setText(i18n("New Sandbox Game"));
0085     sandbox->setToolTip(i18n("Start a new sandbox game"));
0086     sandbox->setWhatsThis(i18n("<qt><p>In a <b>sandbox game</b>, the solution is displayed at the beginning of the game. This is useful to understand the game principles.</p><p>However: after a while, it is not really fun and you should try to start a real game!</p></qt>"));
0087     connect(sandbox, &QAction::triggered, this, &KBBMainWindow::startSandbox);
0088     KGameStandardAction::highscores(this, &KBBMainWindow::showHighscores, actionCollection());
0089 
0090     // Menu "Move"
0091     m_doneAction = actionCollection()->addAction( QStringLiteral( "move_done" ));
0092     m_doneAction->setText(i18nc("This is the last action of a game to check the result, when the user is done.", "Done!"));
0093     m_doneAction->setWhatsThis(i18n("<qt><ul><li>First, you have to place all the balls on the black box. To guess the correct positions of the balls and see how they interact with laser beams, you should use the lasers that are positioned around the black box.</li><li><b>When you think you are done</b>, you should click here.</li></ul><p>Note that it is only possible to click here if you have placed the correct number of balls.</p></qt>"));
0094     m_doneAction->setIcon(QIcon::fromTheme( QStringLiteral( "dialog-ok" )));
0095     connect(m_doneAction, &QAction::triggered, this, &KBBMainWindow::done);
0096     m_solveAction = KGameStandardAction::solve(this, &KBBMainWindow::solve, actionCollection());
0097     m_solveAction->setToolTip(i18n("Give up the game"));
0098     m_solveAction->setWhatsThis(i18n("<qt><p>Choose \"<b>Solve</b>\" if you want to give up the current game. The solution will be displayed.</p><p>If you placed all the balls and do not want to give up, choose \"Done!\".</p></qt>"));
0099 
0100     // Menu "Settings"
0101     KStandardAction::preferences(this, &KBBMainWindow::settingsDialog, actionCollection());
0102     m_toggleCursorAction = actionCollection()->addAction( QStringLiteral( "toggle_cursor" ));
0103     m_toggleCursorAction->setText(i18n("Enable highlight under mouse"));
0104     m_toggleCursorAction->setCheckable(true);
0105     const KConfigGroup group = KSharedConfig::openConfig()->group(QStringLiteral("default"));
0106     m_toggleCursorAction->setChecked(group.readEntry<bool>("highlight_enabled", true));
0107     connect(m_toggleCursorAction, &QAction::triggered, this, &KBBMainWindow::toggleCursor);
0108 
0109     // Theme manager
0110     QString svgzFile = KBBPrefs::theme();
0111     if (!QFile(svgzFile).exists())
0112         svgzFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("pics/kblackbox.svgz"));
0113     m_themeManager = new KBBThemeManager(svgzFile);
0114 
0115 
0116     // Tutorial widget
0117     m_tutorial = new KBBTutorial(this);
0118 
0119 
0120     // Board
0121     m_gameDoc = new KBBGameDoc(this, m_tutorial);
0122     connect(m_gameDoc, &KBBGameDoc::updateStats, this, &KBBMainWindow::updateStats);
0123     connect(m_gameDoc, &KBBGameDoc::isRunning, this, &KBBMainWindow::setRunning);
0124 
0125 
0126     // Game widget
0127     m_gameWidget = new KBBScalableGraphicWidget(m_gameDoc, m_themeManager, m_doneAction);
0128     m_tutorial->setGameWidget(m_gameWidget, new KBBGraphicsItemTutorialMarker(m_gameWidget, m_themeManager, KBBTutorial::COLUMNS, KBBTutorial::ROWS));
0129 
0130 
0131     // Central Widget
0132     m_centralWidget = new QWidget(this);
0133     QHBoxLayout *widgetLayout = new QHBoxLayout();
0134     widgetLayout->setContentsMargins(0, 0, 0, 0);
0135     m_centralWidget->setLayout(widgetLayout);
0136     widgetLayout->addWidget(m_gameWidget);
0137     widgetLayout->addWidget(m_tutorial);
0138     setCentralWidget(m_centralWidget);
0139 
0140 
0141     // Keyboard only
0142     QAction * action = actionCollection()->addAction( QStringLiteral(  "move_down" ) );
0143     action->setText( i18n("Move Down") );
0144     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveDown);
0145     KActionCollection::setDefaultShortcut(action, Qt::Key_Down);
0146     addAction(action);
0147 
0148     action = actionCollection()->addAction( QStringLiteral(  "move_up" ) );
0149     action->setText( i18n("Move Up") );
0150     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveUp);
0151     KActionCollection::setDefaultShortcut(action,Qt::Key_Up);
0152     addAction(action);
0153 
0154     action = actionCollection()->addAction( QStringLiteral(  "move_left" ) );
0155     action->setText( i18n("Move Left") );
0156     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveLeft);
0157     KActionCollection::setDefaultShortcut(action, Qt::Key_Left);
0158     addAction(action);
0159 
0160     action = actionCollection()->addAction( QStringLiteral(  "move_right" ) );
0161     action->setText( i18n("Move Right") );
0162     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveRight);
0163     KActionCollection::setDefaultShortcut(action, Qt::Key_Right);
0164     addAction(action);
0165 
0166     action = actionCollection()->addAction( QStringLiteral( "switch_ball" ));
0167     action->setText(i18n("Switch Ball or Shoot Laser"));
0168     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardEnter);
0169     KActionCollection::setDefaultShortcut(action, Qt::Key_Return);
0170     addAction(action);
0171 
0172     action = actionCollection()->addAction( QStringLiteral( "switch_marker" ));
0173     action->setText(i18n("Switch Marker"));
0174     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardSpace);
0175     KActionCollection::setDefaultShortcut(action, Qt::Key_Space);
0176     addAction(action);
0177 
0178 
0179     m_gameClock = new KGameClock(this, KGameClock::MinSecOnly);
0180     connect(m_gameClock, &KGameClock::timeChanged, this, &KBBMainWindow::updateStats);
0181     connect(m_gameClock, &KGameClock::timeChanged, m_gameDoc, &KBBGameDoc::timeChanged);
0182 
0183 
0184     levelChanged();
0185 
0186     setupGUI();
0187 
0188     if (m_toggleCursorAction->isChecked())
0189         toggleCursor();
0190 
0191     // start a new game
0192     startGame(false);
0193 }
0194 
0195 
0196 KBBMainWindow::~KBBMainWindow()
0197 {
0198     KBBPrefs::self()->save();
0199 
0200     delete m_gameWidget;
0201     delete m_themeManager;
0202 }
0203 
0204 
0205 
0206 //
0207 // Public slots
0208 //
0209 
0210 void KBBMainWindow::levelChanged()
0211 {
0212     KGameDifficultyLevel::StandardLevel level = KGameDifficulty::globalLevel();
0213     switch(level) {
0214         case KGameDifficultyLevel::VeryEasy:
0215             m_ballNumber = 2;
0216             m_columns = 6;
0217             m_rows = 6;
0218             break;
0219         case KGameDifficultyLevel::Easy:
0220         default:
0221             m_ballNumber = 4;
0222             m_columns = 8;
0223             m_rows = 8;
0224             level = KGameDifficultyLevel::Medium;
0225             break;
0226         case KGameDifficultyLevel::Medium:
0227             m_ballNumber = 6;
0228             m_columns = 10;
0229             m_rows = 10;
0230             break;
0231         case KGameDifficultyLevel::Hard:
0232             m_ballNumber = 8;
0233             m_columns = 12;
0234             m_rows = 12;
0235             break;
0236         case KGameDifficultyLevel::VeryHard:
0237             m_ballNumber = 11;
0238             m_columns = 14;
0239             m_rows = 10;
0240             break;
0241         case KGameDifficultyLevel::ExtremelyHard:
0242             m_ballNumber = 15;
0243             m_columns = 20;
0244             m_rows = 12;
0245             break;
0246         case KGameDifficultyLevel::Custom:
0247             m_gameWidget->popupText(i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Note: You can change<br />the parameters of<br />custom games in the<br />Settings dialog."));
0248             break;
0249     }
0250 
0251     m_level = level;
0252     startGame(m_sandboxMode);
0253 }
0254 
0255 
0256 void KBBMainWindow::setRunning(bool r)
0257 {
0258     // Difficulty
0259     KGameDifficulty::global()->setGameRunning(r);
0260 
0261     // Clock
0262     if (r) {
0263         m_gameClock->resume();
0264         m_gameDoc->timeChanged(); // It's important to end the current seconde before pausing so that the player cannot cheat with pause.
0265     } else
0266         m_gameClock->pause();
0267 
0268     // Pause
0269     m_pauseAction->setEnabled(r);
0270 }
0271 
0272 
0273 void KBBMainWindow::updateStats()
0274 {
0275     int ballsLeftToPlace = m_gameDoc->numberOfBallsToPlace() - m_gameDoc->numberOfBallsPlaced();
0276 
0277     m_doneAction->setEnabled(m_solveAction->isEnabled() && (ballsLeftToPlace==0));
0278 
0279     if (ballsLeftToPlace<0)
0280         m_doneAction->setToolTip(i18np("First, you need to remove 1 ball from the black box.", "First, you need to remove %1 balls from the black box.", -ballsLeftToPlace));
0281     else if (ballsLeftToPlace==0) {
0282         m_doneAction->setToolTip(i18n("To check if you successfully guessed the ball positions, click here!"));
0283     } else if (ballsLeftToPlace>0) {
0284         m_doneAction->setToolTip(i18np("You need to place 1 more ball on the black box.", "You need to place %1 more balls on the black box.", ballsLeftToPlace));
0285     }
0286 
0287     if (!m_boardEnabled)
0288         m_doneAction->setToolTip(i18n("Game over."));
0289     if (m_pauseAction->isChecked())
0290         m_doneAction->setToolTip(i18n("Game paused."));
0291 
0292 
0293     // Status bar
0294     if (m_tutorial->isVisible()) {
0295         //QT5 statusBar()->changeItem(i18n("Tutorial"), SRUN );
0296     }
0297     if (!m_tutorial->isVisible()) {
0298         if (m_boardEnabled) {
0299             if (ballsLeftToPlace<0) {
0300                 //QT5 statusBar()->changeItem((i18np("1 ball too many!", "%1 balls too many!", -ballsLeftToPlace)), SRUN);
0301             } else if (ballsLeftToPlace==0) {
0302                 //QT5 statusBar()->changeItem(i18n("No more balls to place"), SRUN);
0303             } else if (ballsLeftToPlace>0) {
0304                 //QT5 statusBar()->changeItem(i18np("1 ball to place", "%1 balls to place", ballsLeftToPlace), SRUN);
0305             }
0306         } else {
0307             //QT5 statusBar()->changeItem(i18n("Game over"), SRUN );
0308         }
0309     }
0310 
0311     //QT5 statusBar()->changeItem(i18n("Time: %1", m_gameClock->timeString()), STIME);
0312 
0313     //QT5 statusBar()->changeItem( i18n("Size: %1 x %2", m_gameDoc->columns(), m_gameDoc->rows()), SSIZE );
0314 
0315 
0316     // 2. Info Widget
0317     m_gameWidget->setScore(m_gameDoc->score());
0318 }
0319 
0320 
0321 
0322 //
0323 // Private slots
0324 //
0325 
0326 void KBBMainWindow::done()
0327 {
0328     if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
0329         KMessageBox::information(this, i18n("Clicking on \"Done!\" is the normal way to check the positions of the balls at the end of the game. However, it is not possible in the tutorial to end the game before you reached the last step.\nPlease first finish the tutorial."), i18n("Check positions"));
0330     } else {
0331         solving();
0332 
0333         const int score = m_gameDoc->score();
0334         QString s;
0335         if (score <= (m_ballNumber*35)) {
0336             s = i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Your final score is: %1.<br />You did really well!", score);
0337             if (m_sandboxMode)
0338                 s += QLatin1String("<br /><br />") + i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "But it does not count<br />because <b>it is the sandbox!</b>");
0339         } else
0340             s = i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Your final score is: %1.<br />I guess you need more practice.", score);
0341 
0342         if ((!m_tutorial->isVisible()) && (!m_sandboxMode) && (KGameDifficulty::globalLevel() != KGameDifficultyLevel::Custom) && (score<KBBGameDoc::SCORE_LOST)) {
0343             QPointer<KGameHighScoreDialog> scoreDialog = new KGameHighScoreDialog(KGameHighScoreDialog::Score | KGameHighScoreDialog::Name, this);
0344             scoreDialog->initFromDifficulty(KGameDifficulty::global());
0345 
0346             KGameHighScoreDialog::FieldInfo scoreInfo;
0347             scoreInfo[KGameHighScoreDialog::Score].setNum(score);
0348             if(scoreDialog->addScore(scoreInfo, KGameHighScoreDialog::LessIsMore) != 0)
0349                 scoreDialog->exec();
0350             delete scoreDialog;
0351         }
0352 
0353         m_gameWidget->popupText(s);
0354     }
0355 }
0356 
0357 
0358 void KBBMainWindow::newGame()
0359 {
0360     if (mayAbortGame())
0361         startGame(false);
0362 }
0363 
0364 
0365 void KBBMainWindow::pause(bool state)
0366 {
0367     if (state) {
0368         m_gameClock->pause();
0369         m_gameWidget->popupText(i18n("Game paused.<br />Press \"%1\" to resume.", m_pauseAction->shortcut().toString(QKeySequence::NativeText)), 0);
0370     } else {
0371         m_gameClock->resume();
0372         m_gameWidget->popupText(QString());
0373     }
0374     m_solveAction->setEnabled(!state);
0375 
0376     updateStats();
0377     m_gameWidget->setPause(state);
0378 }
0379 
0380 
0381 void KBBMainWindow::settingsChanged()
0382 {
0383     m_customBallNumber = m_levelConfig->balls();
0384     m_customColumns = m_levelConfig->columns();
0385     m_customRows = m_levelConfig->rows();
0386 
0387     if (m_level==KGameDifficultyLevel::Custom) {
0388         bool mayRestart = true;
0389         if (m_gameDoc->gameReallyStarted())
0390             if (KMessageBox::questionTwoActions(this,
0391                 i18n("Do you want to cancel the current custom game and start a new one with the new parameters?"), QString(),
0392                 KGuiItem(i18nc("@action:button", "Start New Game"), QStringLiteral("view-refresh")),
0393                 KStandardGuiItem::cont())
0394                 == KMessageBox::SecondaryAction)
0395                 mayRestart = false;
0396 
0397         if (mayRestart)
0398             startGame(m_sandboxMode);
0399     }
0400 }
0401 
0402 
0403 void KBBMainWindow::settingsDialog()
0404 {
0405     if (!KConfigDialog::showDialog(QStringLiteral("settings"))) {
0406         KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), KBBPrefs::self());
0407         m_levelConfig = new KBBLevelConfigurationWidget(dialog, m_customBallNumber, m_customColumns, m_customRows, m_themeManager);
0408         dialog->addPage(m_levelConfig, i18n("Custom Game"), QStringLiteral("games-config-custom"));
0409         connect(dialog, &KConfigDialog::settingsChanged, this, &KBBMainWindow::settingsChanged);
0410                 //QT5 dialog->setHelp(QString(), "kblackbox");
0411         dialog->show();
0412     }
0413 }
0414 
0415 
0416 void KBBMainWindow::showHighscores()
0417 {
0418     KGameHighScoreDialog scoreDialog(KGameHighScoreDialog::Score | KGameHighScoreDialog::Name, this);
0419     scoreDialog.initFromDifficulty(KGameDifficulty::global());
0420     scoreDialog.exec();
0421 }
0422 
0423 
0424 void KBBMainWindow::solve()
0425 {
0426     if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
0427         KMessageBox::information(this, i18n("Sorry, you may not give up during the tutorial."), i18n("Solve"));
0428     } else {
0429         if (m_gameDoc->numberOfBallsToPlace()==m_gameDoc->numberOfBallsPlaced()) {
0430             if (KMessageBox::warningContinueCancel(this, i18n("You placed all the balls. Great!\nYou should now click on \"Done!\" to end the game and check if you guessed correctly.\nSo, do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue)
0431                 solving();
0432         } else if (KMessageBox::warningContinueCancel(this, i18np("You should place %1 ball!\n", "You should place %1 balls!\n", m_gameDoc->numberOfBallsToPlace()) + i18np("You have placed %1.\n", "You have placed %1.\n", m_gameDoc->numberOfBallsPlaced()) + i18n("Do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue)
0433             solving();
0434     }
0435 }
0436 
0437 
0438 void KBBMainWindow::startSandbox()
0439 {
0440     if (mayAbortGame()) {
0441         startGame(true);
0442         m_gameWidget->popupText(i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Note: In the sandbox mode,<br />the solution is already displayed.<br />Have fun!"));
0443     }
0444 }
0445 
0446 
0447 void KBBMainWindow::startTutorial()
0448 {
0449     if (mayAbortGame()) {
0450         m_gameDoc->startTutorial();
0451         m_solveAction->setEnabled(true);
0452         m_pauseAction->setChecked(false);
0453         KGameDifficulty::global()->setEditable(false);
0454 
0455         // Reset clock but don't start it yet.
0456         m_gameClock->restart();
0457         m_gameClock->pause();
0458 
0459         updateStats();
0460     }
0461 }
0462 
0463 void KBBMainWindow::toggleCursor()
0464 {
0465     m_gameWidget->toggleCursor();
0466 
0467     KConfigGroup group = KSharedConfig::openConfig()->group(QStringLiteral("default"));
0468     group.writeEntry<bool>("highlight_enabled", m_toggleCursorAction->isChecked());
0469 }
0470 
0471 
0472 //
0473 // Private
0474 //
0475 
0476 bool KBBMainWindow::mayAbortGame()
0477 {
0478     bool mayAbort = true;
0479 
0480     if (m_gameDoc->gameReallyStarted())
0481         mayAbort = ( KMessageBox::warningContinueCancel(nullptr, i18n("This will be the end of the current game!"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::Continue );
0482 
0483     return mayAbort;
0484 }
0485 
0486 
0487 void KBBMainWindow::solving()
0488 {
0489     m_boardEnabled = false;
0490     m_solveAction->setEnabled(false);
0491     m_doneAction->setEnabled(false);
0492     m_gameDoc->gameOver();
0493     m_gameWidget->solve(false);
0494     updateStats();
0495 }
0496 
0497 
0498 void KBBMainWindow::startGame(bool sandboxMode)
0499 {
0500     if (m_level==KGameDifficultyLevel::Custom) {
0501         m_ballNumber = m_customBallNumber;
0502         m_columns = m_customColumns;
0503         m_rows = m_customRows;
0504     }
0505 
0506     m_boardEnabled = true;
0507     m_sandboxMode = sandboxMode;
0508 
0509     m_solveAction->setEnabled(true);
0510     m_pauseAction->setChecked(false);
0511     KGameDifficulty::global()->setEditable(true);
0512     m_tutorial->hide();
0513     m_gameDoc->newGame(m_ballNumber, m_columns, m_rows);
0514     m_gameWidget->newGame(m_columns, m_rows, m_ballNumber);
0515     if (m_sandboxMode)
0516         m_gameWidget->solve(true);
0517 
0518     // Reset clock but don't start it yet.
0519     m_gameClock->restart();
0520     m_gameClock->pause();
0521 
0522     updateStats();
0523 }
0524 
0525 #include "moc_kbbmainwindow.cpp"