00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "kbbmainwindow.h"
00031
00032
00033
00034 #include <QFile>
00035 #include <QHBoxLayout>
00036 #include <QWidget>
00037
00038
00039 #include <KActionCollection>
00040 #include <KConfigDialog>
00041 #include <KGameClock>
00042 #include <KGameDifficulty>
00043 #include <KGamePopupItem>
00044 #include <KGlobal>
00045 #include <KLocale>
00046 #include <KMessageBox>
00047 #include <KScoreDialog>
00048 #include <KStandardDirs>
00049 #include <KStandardGameAction>
00050 #include <KStatusBar>
00051 #include <KToggleAction>
00052
00053
00054 #include "kbbgamedoc.h"
00055 #include "kbbgraphicsitemtutorialmarker.h"
00056 #include "kbblevelconfigurationwidget.h"
00057 #include "kbbprefs.h"
00058 #include "kbbscalablegraphicwidget.h"
00059 #include "kbbthememanager.h"
00060 #include "kbbtutorial.h"
00061
00062
00063
00064
00065
00066
00067
00068 KBBMainWindow::KBBMainWindow()
00069 {
00070 m_boardEnabled = false;
00071
00072
00073 m_customBallNumber = KBBPrefs::balls();
00074 m_customColumns = KBBPrefs::columns();
00075 m_customRows = KBBPrefs::rows();
00076
00077
00078
00079 statusBar()->insertPermanentItem("", SRUN, 1);
00080 statusBar()->insertPermanentItem(i18n("Time: 00:00"), STIME, 1);
00081 statusBar()->insertPermanentItem(i18n("Size: 00 x 00"), SSIZE);
00082 statusBar()->setItemAlignment(SRUN, Qt::AlignLeft | Qt::AlignVCenter);
00083
00084
00085
00086 KGameDifficulty::init(this, this, SLOT(levelChanged(KGameDifficulty::standardLevel)));
00087 KGameDifficulty::addStandardLevel(KGameDifficulty::VeryEasy);
00088 KGameDifficulty::addStandardLevel(KGameDifficulty::Easy);
00089 KGameDifficulty::addStandardLevel(KGameDifficulty::Medium);
00090 KGameDifficulty::addStandardLevel(KGameDifficulty::Hard);
00091 KGameDifficulty::addStandardLevel(KGameDifficulty::VeryHard);
00092 KGameDifficulty::addStandardLevel(KGameDifficulty::ExtremelyHard);
00093 KGameDifficulty::addStandardLevel(KGameDifficulty::Configurable);
00094
00095
00096
00097 KStandardGameAction::gameNew(this, SLOT(newGame()), actionCollection());
00098 m_pauseAction = KStandardGameAction::pause(this, SLOT(pause(bool)), actionCollection());
00099 QAction* tutorial = actionCollection()->addAction("game_tutorial");
00100 tutorial->setText(i18n("Start Tutorial"));
00101 tutorial->setIcon(KIcon("footprint"));
00102 tutorial->setToolTip(i18n("Start tutorial"));
00103 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>"));
00104 connect(tutorial, SIGNAL(triggered(bool)), SLOT(startTutorial()));
00105 KStandardGameAction::quit(this, SLOT(close()), actionCollection());
00106 QAction* sandbox = actionCollection()->addAction("game_sandbox");
00107 sandbox->setText(i18n("New Sandbox Game"));
00108 sandbox->setToolTip(i18n("Start a new sandbox game"));
00109 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>"));
00110 connect(sandbox, SIGNAL(triggered(bool)), SLOT(startSandbox()));
00111 KStandardGameAction::highscores(this, SLOT(showHighscores()), actionCollection());
00112
00113
00114 m_doneAction = actionCollection()->addAction("move_done");
00115 m_doneAction->setText(i18nc("This is the last action of a game to check the result, when the user is done.", "Done!"));
00116 m_doneAction->setWhatsThis(i18n("<qt><ul><li>First, you have to place all the balls on the black box. To guess the right possitions of the balls and see how they interact with laser beams, you should use the lasers that are disposed 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 possible to click here only if you placed the right number of balls.</p></qt>"));
00117 m_doneAction->setIcon(KIcon("dialog-ok"));
00118 connect(m_doneAction, SIGNAL(triggered(bool)), SLOT(done()));
00119 m_solveAction = KStandardGameAction::solve(this, SLOT(solve()), actionCollection());
00120 m_solveAction->setToolTip(i18n("Give up the game"));
00121 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>"));
00122
00123
00124 KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection());
00125
00126
00127
00128 QString svgzFile = KBBPrefs::theme();
00129 if (!QFile(svgzFile).exists())
00130 svgzFile = KStandardDirs::locate("appdata", "pics/kblackbox.svgz");
00131 m_themeManager = new KBBThemeManager(svgzFile);
00132
00133
00134
00135 m_tutorial = new KBBTutorial(this);
00136
00137
00138
00139 m_gameDoc = new KBBGameDoc(this, m_tutorial);
00140 connect(m_gameDoc, SIGNAL(updateStats()), this, SLOT(updateStats()) );
00141 connect(m_gameDoc, SIGNAL(isRunning(bool)), SLOT(setRunning(bool)));
00142
00143
00144
00145 m_gameWidget = new KBBScalableGraphicWidget(m_gameDoc, m_themeManager, m_doneAction);
00146 m_tutorial->setGameWidget(m_gameWidget, new KBBGraphicsItemTutorialMarker(m_gameWidget, m_themeManager, KBBTutorial::COLUMNS, KBBTutorial::ROWS));
00147
00148
00149
00150 m_centralWidget = new QWidget(this);
00151 QHBoxLayout *widgetLayout = new QHBoxLayout();
00152 widgetLayout->setMargin(0);
00153 m_centralWidget->setLayout(widgetLayout);
00154 widgetLayout->addWidget(m_gameWidget);
00155 widgetLayout->addWidget(m_tutorial);
00156 setCentralWidget(m_centralWidget);
00157
00158
00159
00160 QAction* action = actionCollection()->addAction( "move_down" );
00161 action->setText( i18n("Move Down") );
00162 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveDown()));
00163 action->setShortcut(Qt::Key_Down);
00164 addAction(action);
00165
00166 action = actionCollection()->addAction( "move_up" );
00167 action->setText( i18n("Move Up") );
00168 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveUp()));
00169 action->setShortcut(Qt::Key_Up);
00170 addAction(action);
00171
00172 action = actionCollection()->addAction( "move_left" );
00173 action->setText( i18n("Move Left") );
00174 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveLeft()));
00175 action->setShortcut(Qt::Key_Left);
00176 addAction(action);
00177
00178 action = actionCollection()->addAction( "move_right" );
00179 action->setText( i18n("Move Right") );
00180 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveRight()));
00181 action->setShortcut(Qt::Key_Right);
00182 addAction(action);
00183
00184 action = actionCollection()->addAction("switch_ball");
00185 action->setText(i18n("Switch Ball or Shoot Laser"));
00186 connect(action, SIGNAL(triggered(bool)), m_gameWidget, SLOT(keyboardEnter()));
00187 action->setShortcut(Qt::Key_Return);
00188 addAction(action);
00189
00190 action = actionCollection()->addAction("switch_marker");
00191 action->setText(i18n("Switch Marker"));
00192 connect(action, SIGNAL(triggered(bool)), m_gameWidget, SLOT(keyboardSpace()));
00193 action->setShortcut(Qt::Key_Space);
00194 addAction(action);
00195
00196
00197 m_gameClock = new KGameClock(this, KGameClock::MinSecOnly);
00198 connect(m_gameClock, SIGNAL(timeChanged(const QString&)), SLOT(updateStats()));
00199 connect(m_gameClock, SIGNAL(timeChanged(const QString&)), m_gameDoc, SLOT(timeChanged()));
00200
00201
00202 levelChanged((KGameDifficulty::standardLevel) (KBBPrefs::level()));
00203 KGameDifficulty::setLevel(m_level);
00204
00205
00206 setupGUI();
00207
00208
00209 startGame(false);
00210 }
00211
00212
00213 KBBMainWindow::~KBBMainWindow()
00214 {
00215 KBBPrefs::self()->writeConfig();
00216
00217 delete m_gameWidget;
00218 delete m_themeManager;
00219 }
00220
00221
00222
00223
00224
00225
00226
00227 void KBBMainWindow::levelChanged(KGameDifficulty::standardLevel level)
00228 {
00229 switch(level) {
00230 case KGameDifficulty::VeryEasy:
00231 m_ballNumber = 2;
00232 m_columns = 6;
00233 m_rows = 6;
00234 break;
00235 case KGameDifficulty::Easy:
00236 default:
00237 m_ballNumber = 4;
00238 m_columns = 8;
00239 m_rows = 8;
00240 level = KGameDifficulty::Medium;
00241 break;
00242 case KGameDifficulty::Medium:
00243 m_ballNumber = 6;
00244 m_columns = 10;
00245 m_rows = 10;
00246 break;
00247 case KGameDifficulty::Hard:
00248 m_ballNumber = 8;
00249 m_columns = 12;
00250 m_rows = 12;
00251 break;
00252 case KGameDifficulty::VeryHard:
00253 m_ballNumber = 11;
00254 m_columns = 14;
00255 m_rows = 10;
00256 break;
00257 case KGameDifficulty::ExtremelyHard:
00258 m_ballNumber = 15;
00259 m_columns = 20;
00260 m_rows = 12;
00261 break;
00262 case KGameDifficulty::Configurable:
00263 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 />setting dialog."));
00264 break;
00265 }
00266
00267 m_level = level;
00268 KBBPrefs::setLevel((int)(m_level));
00269 startGame(m_sandboxMode);
00270 }
00271
00272
00273 void KBBMainWindow::setRunning(bool r)
00274 {
00275
00276 KGameDifficulty::setRunning(r);
00277
00278
00279 if (r) {
00280 m_gameClock->resume();
00281 m_gameDoc->timeChanged();
00282 } else
00283 m_gameClock->pause();
00284
00285
00286 m_pauseAction->setEnabled(r);
00287 }
00288
00289
00290 void KBBMainWindow::updateStats()
00291 {
00292 int ballsLeftToPlace = m_gameDoc->numberOfBallsToPlace() - m_gameDoc->numberOfBallsPlaced();
00293
00294 m_doneAction->setEnabled(m_solveAction->isEnabled() && (ballsLeftToPlace==0));
00295
00296 if (ballsLeftToPlace<0)
00297 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));
00298 else if (ballsLeftToPlace==0) {
00299 m_doneAction->setToolTip(i18n("To check if you successful guessed the ball positions, click here!"));
00300 } else if (ballsLeftToPlace>0) {
00301 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));
00302 }
00303
00304 if (!m_boardEnabled)
00305 m_doneAction->setToolTip(i18n("Game over."));
00306 if (m_pauseAction->isChecked())
00307 m_doneAction->setToolTip(i18n("Game paused."));
00308
00309
00310
00311 if (m_tutorial->isVisible())
00312 statusBar()->changeItem(i18n("Tutorial"), SRUN );
00313 if (!m_tutorial->isVisible()) {
00314 if (m_boardEnabled) {
00315 if (ballsLeftToPlace<0) {
00316 statusBar()->changeItem((i18np("1 ball too much!", "%1 balls too much!", -ballsLeftToPlace)), SRUN);
00317 } else if (ballsLeftToPlace==0) {
00318 statusBar()->changeItem(i18n("No more ball to place"), SRUN);
00319 } else if (ballsLeftToPlace>0) {
00320 statusBar()->changeItem(i18np("1 ball to place", "%1 balls to place", ballsLeftToPlace), SRUN);
00321 }
00322 } else
00323 statusBar()->changeItem(i18n("Game over"), SRUN );
00324 }
00325
00326 statusBar()->changeItem(i18n("Time: %1", m_gameClock->timeString()), STIME);
00327
00328 statusBar()->changeItem( i18n("Size: %1 x %2", m_gameDoc->columns(), m_gameDoc->rows()), SSIZE );
00329
00330
00331
00332 m_gameWidget->setScore(m_gameDoc->score());
00333 }
00334
00335
00336
00337
00338
00339
00340
00341 void KBBMainWindow::done()
00342 {
00343 if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
00344 KMessageBox::sorry(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"));
00345 } else {
00346 solving();
00347
00348 const int score = m_gameDoc->score();
00349 QString s;
00350 if (score <= (m_ballNumber*35)) {
00351 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);
00352 if (m_sandboxMode)
00353 s += QString("<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>");
00354 } else
00355 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);
00356
00357 if ((!m_tutorial->isVisible()) && (!m_sandboxMode) && (KGameDifficulty::level() != KGameDifficulty::Configurable) && (score<KBBGameDoc::SCORE_LOST)) {
00358 KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
00359 scoreDialog.addLocalizedConfigGroupNames(KGameDifficulty::localizedLevelStrings());
00360 scoreDialog.setConfigGroup(KGameDifficulty::localizedLevelString());
00361
00362 KScoreDialog::FieldInfo scoreInfo;
00363 scoreInfo[KScoreDialog::Score].setNum(score);
00364 if(scoreDialog.addScore(scoreInfo, KScoreDialog::LessIsMore) != 0)
00365 scoreDialog.exec();
00366 }
00367
00368 m_gameWidget->popupText(s);
00369 }
00370 }
00371
00372
00373 void KBBMainWindow::newGame()
00374 {
00375 if (mayAbortGame())
00376 startGame(false);
00377 }
00378
00379
00380 void KBBMainWindow::pause(bool state)
00381 {
00382 if (state) {
00383 m_gameClock->pause();
00384 m_gameWidget->popupText(i18n("Game paused.<br />Press \"%1\" to resume.", m_pauseAction->shortcut().toString(QKeySequence::NativeText)), 0);
00385 } else {
00386 m_gameClock->resume();
00387 m_gameWidget->popupText("");
00388 }
00389 m_solveAction->setEnabled(!state);
00390
00391 updateStats();
00392 m_gameWidget->setPause(state);
00393 }
00394
00395
00396 void KBBMainWindow::settingsChanged()
00397 {
00398 m_customBallNumber = m_levelConfig->balls();
00399 m_customColumns = m_levelConfig->columns();
00400 m_customRows = m_levelConfig->rows();
00401
00402 if (m_level==KGameDifficulty::Configurable) {
00403 bool mayRestart = true;
00404 if (m_gameDoc->gameReallyStarted())
00405 if (KMessageBox::questionYesNo(this, i18n("Do you want to cancel the current custom game and start a new one with the new parameters?"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::No)
00406 mayRestart = false;
00407
00408 if (mayRestart)
00409 startGame(m_sandboxMode);
00410 }
00411 }
00412
00413
00414 void KBBMainWindow::settingsDialog()
00415 {
00416 if (!KConfigDialog::showDialog("settings")) {
00417 KConfigDialog *dialog = new KConfigDialog(this, "settings", KBBPrefs::self());
00418 m_levelConfig = new KBBLevelConfigurationWidget(dialog, m_customBallNumber, m_customColumns, m_customRows, m_themeManager);
00419 dialog->addPage(m_levelConfig, i18n("Custom Game"), "games-config-custom");
00420 connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(settingsChanged()));
00421 dialog->setHelp(QString(), "kblackbox");
00422 dialog->show();
00423 }
00424 }
00425
00426
00427 void KBBMainWindow::showHighscores()
00428 {
00429 KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
00430 scoreDialog.addLocalizedConfigGroupNames(KGameDifficulty::localizedLevelStrings());
00431 scoreDialog.setConfigGroup( KGameDifficulty::localizedLevelString() );
00432 scoreDialog.exec();
00433 }
00434
00435
00436 void KBBMainWindow::solve()
00437 {
00438 if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
00439 KMessageBox::sorry(this, i18n("Sorry, you may not give up the tutorial."), i18n("Solve"));
00440 } else {
00441 if (m_gameDoc->numberOfBallsToPlace()==m_gameDoc->numberOfBallsPlaced()) {
00442 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 right.\nSo, do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue)
00443 solving();
00444 } 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)
00445 solving();
00446 }
00447 }
00448
00449
00450 void KBBMainWindow::startSandbox()
00451 {
00452 if (mayAbortGame()) {
00453 startGame(true);
00454 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!"));
00455 }
00456 }
00457
00458
00459 void KBBMainWindow::startTutorial()
00460 {
00461 if (mayAbortGame()) {
00462 m_gameDoc->startTutorial();
00463 m_solveAction->setEnabled(true);
00464 m_pauseAction->setChecked(false);
00465 KGameDifficulty::setEnabled(false);
00466
00467
00468 m_gameClock->restart();
00469 m_gameClock->pause();
00470
00471 updateStats();
00472 }
00473 }
00474
00475
00476
00477
00478
00479
00480
00481 bool KBBMainWindow::mayAbortGame()
00482 {
00483 bool mayAbort = true;
00484
00485 if (m_gameDoc->gameReallyStarted())
00486 mayAbort = ( KMessageBox::warningContinueCancel(0, i18n("This will be the end of the current game!"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::Continue );
00487
00488 return mayAbort;
00489 }
00490
00491
00492 void KBBMainWindow::solving()
00493 {
00494 m_boardEnabled = false;
00495 m_solveAction->setEnabled(false);
00496 m_doneAction->setEnabled(false);
00497 m_gameDoc->gameOver();
00498 m_gameWidget->solve(false);
00499 updateStats();
00500 }
00501
00502
00503 void KBBMainWindow::startGame(bool sandboxMode)
00504 {
00505 if (m_level==KGameDifficulty::Configurable) {
00506 m_ballNumber = m_customBallNumber;
00507 m_columns = m_customColumns;
00508 m_rows = m_customRows;
00509 }
00510
00511 m_boardEnabled = true;
00512 m_sandboxMode = sandboxMode;
00513
00514 m_solveAction->setEnabled(true);
00515 m_pauseAction->setChecked(false);
00516 KGameDifficulty::setEnabled(true);
00517 m_tutorial->hide();
00518 m_gameDoc->newGame(m_ballNumber, m_columns, m_rows);
00519 m_gameWidget->newGame(m_columns, m_rows, m_ballNumber);
00520 if (m_sandboxMode)
00521 m_gameWidget->solve(true);
00522
00523
00524 m_gameClock->restart();
00525 m_gameClock->pause();
00526
00527 updateStats();
00528 }
00529
00530
00531
00532 #include "kbbmainwindow.moc"