File indexing completed on 2024-10-13 03:44:25

0001 /*
0002     SPDX-FileCopyrightText: 2004-2005 Andi Peredri <andi@ukr.net>
0003     SPDX-FileCopyrightText: 2007 Simon Hürlimann <simon.huerlimann@huerlisi.ch>
0004     SPDX-FileCopyrightText: 2007-2008 Fela Winkelmolen <fela.kde@gmail.com>
0005     SPDX-FileCopyrightText: 2013 Ashwin Rajeev <ashwin_rajeev@hotmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "mainwindow.h"
0011 
0012 #include <KConfigDialog>
0013 #include <KLocalizedString>
0014 #include <KStandardAction>
0015 #include <QAction>
0016 #include <KToggleAction>
0017 #include <KActionCollection>
0018 #include <KGameStandardAction>
0019 #include <QStatusBar>
0020 
0021 #include <KGameDifficulty>
0022 #include <KGameHighScoreDialog>
0023 #include <KGameThemeSelector>
0024 #include <KGameRenderer>
0025 #include <KGameClock>
0026 #include <KGameSound>
0027 
0028 #include <ctime>
0029 #include <cmath>
0030 
0031 #include "ui_general.h"
0032 #include "ui_customgame.h"
0033 
0034 #include "globals.h"
0035 #include "gameview.h"
0036 #include "settings.h"
0037 #include "abstractgrid.h"
0038 
0039 class GeneralConfig : public QWidget
0040 {
0041 public:
0042     GeneralConfig(QWidget *parent)
0043         : QWidget(parent)
0044     {
0045         ui.setupUi(this);
0046     }
0047 private:
0048     Ui::GeneralConfig ui;
0049 };
0050 
0051 class CustomGameConfig : public QWidget
0052 {
0053 public:
0054     CustomGameConfig(QWidget *parent)
0055         : QWidget(parent)
0056     {
0057         ui.setupUi(this);
0058     }
0059 private:
0060     Ui::CustomGameConfig ui;
0061 };
0062 
0063 MainWindow::MainWindow(QWidget *parent)
0064     : KXmlGuiWindow(parent), m_clickCount(0),
0065       m_view(new GameView(this))
0066 {
0067     connect(m_view, &GameView::gameOver, this, &MainWindow::gameOver);
0068     connect(m_view, &GameView::rotationStarted, this, &MainWindow::rotationStarted);
0069     connect(this, SIGNAL(pause(QVariant)), m_view->rootObject(), SLOT(pause(QVariant)));
0070 
0071     m_movesLabel = new QLabel(this);
0072     m_movesLabel->setAlignment(Qt::AlignCenter);
0073     m_timeLabel = new QLabel(this);
0074     m_timeLabel->setAlignment(Qt::AlignCenter);
0075 
0076     statusBar()->insertPermanentWidget(0, m_movesLabel, 1);
0077     statusBar()->insertPermanentWidget(1, m_timeLabel, 1);
0078 
0079     // Difficulty
0080     KGameDifficulty::global()->addStandardLevelRange(
0081         KGameDifficultyLevel::Easy, KGameDifficultyLevel::VeryHard
0082     );
0083     KGameDifficulty::global()->addLevel(
0084                 new KGameDifficultyLevel(100, QByteArray("Custom"), i18n("Custom"))
0085     );
0086     KGameDifficultyGUI::init(this);
0087     connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, this, &MainWindow::startNewGame);
0088 
0089 
0090     setCentralWidget(m_view);
0091 
0092     setupActions();
0093 
0094     setupGUI();
0095 
0096     setAutoSaveSettings();
0097 
0098     m_gameClock = new KGameClock(this, KGameClock::FlexibleHourMinSec);
0099     connect(m_gameClock, &KGameClock::timeChanged, this, &MainWindow::updateStatusBar);
0100 
0101     m_soundStart = new KGameSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/start.wav")), this);
0102     m_soundWin = new KGameSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/win.wav")), this);
0103 
0104 
0105     startNewGame();
0106 }
0107 
0108 void MainWindow::setupActions()
0109 {
0110     // Game
0111     KGameStandardAction::gameNew(this, &MainWindow::startNewGame,
0112                                  actionCollection());
0113 
0114     m_pauseAction = KGameStandardAction::pause(this, &MainWindow::pauseGame,
0115                                                actionCollection());
0116     connect(KGameDifficulty::global(), &KGameDifficulty::gameRunningChanged, m_pauseAction,
0117             &QAction::setEnabled);
0118 
0119     QAction *action = KGameStandardAction::solve(m_view, &GameView::solve, actionCollection());
0120     connect(KGameDifficulty::global(), &KGameDifficulty::gameRunningChanged, action, &QAction::setEnabled);
0121 
0122     KGameStandardAction::highscores(this, &MainWindow::showHighscores,
0123                                     actionCollection());
0124 
0125     KGameStandardAction::quit(this, &MainWindow::close, actionCollection());
0126 
0127     // Settings
0128     KStandardAction::preferences(this, &MainWindow::configureSettings, actionCollection());
0129 
0130     action = new QAction(i18n("&Unlock All"), this);
0131     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(unlockAll()));
0132     connect(KGameDifficulty::global(), &KGameDifficulty::gameRunningChanged, action, &QAction::setEnabled);
0133     actionCollection()->addAction( QStringLiteral( "unlock_all" ), action);
0134 
0135     action = new QAction(i18n("Keyboard: Field right"), this);
0136     KActionCollection::setDefaultShortcut(action, Qt::Key_Right);
0137     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoRight()));
0138     actionCollection()->addAction( QStringLiteral( "kb_go_right" ), action);
0139 
0140     action = new QAction(i18n("Keyboard: Field left"),this);
0141     KActionCollection::setDefaultShortcut(action, Qt::Key_Left);
0142     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoLeft()));
0143     actionCollection()->addAction( QStringLiteral( "kb_go_left" ), action);
0144 
0145     action = new QAction(i18n("Keyboard: Field up"),this);
0146     KActionCollection::setDefaultShortcut(action, Qt::Key_Up);
0147     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoUp()));
0148     actionCollection()->addAction( QStringLiteral( "kb_go_up" ), action);
0149 
0150     action = new QAction(i18n("Keyboard: Field down"),this);
0151     KActionCollection::setDefaultShortcut(action, Qt::Key_Down);
0152     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoDown()));
0153     actionCollection()->addAction( QStringLiteral( "kb_go_down" ), action);
0154 
0155     action = new QAction(i18n("Keyboard: Turn clockwise"),this);
0156     KActionCollection::setDefaultShortcut(action, Qt::Key_Return);
0157     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(rotateClockwise()));
0158     actionCollection()->addAction( QStringLiteral( "kb_turn_clockwise" ), action);
0159 
0160     action = new QAction(i18n("Keyboard: Turn counterclockwise"),this);
0161     KActionCollection::setDefaultShortcut(action, Qt::CTRL | Qt::Key_Return);
0162     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(rotateCounterclockwise()));
0163     actionCollection()->addAction( QStringLiteral( "kb_turn_counterclockwise" ), action);
0164 
0165     action = new QAction(i18n("Keyboard: Toggle lock"),this);
0166     KActionCollection::setDefaultShortcut(action, Qt::Key_Space);
0167     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(toggleLock()));
0168     actionCollection()->addAction( QStringLiteral( "kb_lock" ), action);
0169 }
0170 
0171 void MainWindow::configureSettings()
0172 {
0173     if (KConfigDialog::showDialog(QStringLiteral("settings")))
0174         return;
0175     KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), Settings::self());
0176     dialog->addPage(new GeneralConfig(dialog), i18n("General"), QStringLiteral("games-config-options"));
0177     dialog->addPage(new KGameThemeSelector(m_view->getProvider()), i18n("Theme"), QStringLiteral("games-config-theme"));
0178     dialog->addPage(new CustomGameConfig(dialog), i18n("Custom Game"), QStringLiteral("games-config-custom"));
0179     connect(dialog, &KConfigDialog::settingsChanged, m_view, &GameView::updateSettings);
0180 //    dialog->setHelp(QString(),QLatin1String("knetwalk"));
0181     dialog->show();
0182 }
0183 
0184 void MainWindow::showHighscores()
0185 {
0186     KGameHighScoreDialog scoreDialog(KGameHighScoreDialog::Name | KGameHighScoreDialog::Time, this);
0187     scoreDialog.addField(KGameHighScoreDialog::Custom1, i18n("Moves Penalty"), QStringLiteral( "moves" ));
0188     scoreDialog.initFromDifficulty(KGameDifficulty::global());
0189     scoreDialog.exec();
0190 }
0191 
0192 void MainWindow::startNewGame()
0193 {
0194     if(Settings::playSounds())
0195         m_soundStart->start();
0196 
0197     const KGameDifficultyLevel::StandardLevel l = KGameDifficulty::globalLevel();
0198 
0199     bool isWrapped = (l == KGameDifficultyLevel::VeryHard);
0200     if (KGameDifficulty::globalLevel() == KGameDifficultyLevel::Custom)
0201         isWrapped = Settings::wrapping();
0202     const QSize size = boardSize();
0203     m_view->startNewGame(size.width(), size.height(), (Wrapping)isWrapped);
0204     m_clickCount = -m_view->minimumMoves();
0205     m_gameClock->restart();
0206 
0207     if(m_pauseAction->isChecked())
0208     {
0209         m_pauseAction->setChecked(false);
0210     }
0211     KGameDifficulty::global()->setGameRunning(true);
0212 
0213     updateStatusBar();
0214 }
0215 
0216 void MainWindow::gameOver(const QVariant &msg)
0217 {
0218     m_gameClock->pause();
0219     KGameDifficulty::global()->setGameRunning(false);
0220 
0221     if (msg.toString() != QLatin1String("won"))
0222         return;
0223 
0224     if(Settings::playSounds())
0225         m_soundWin->start();
0226 
0227     //=== calculate the score ====//
0228 
0229     double penalty = m_gameClock->seconds() / 2.0 * (m_clickCount/2 + 1);
0230 
0231     // normalize the penalty
0232     penalty = std::sqrt(penalty/m_view->cellCount());
0233 
0234     int score = static_cast<int>(100.0 / penalty);
0235 
0236     // create the new scoreInfo
0237     KGameHighScoreDialog::FieldInfo scoreInfo;
0238     scoreInfo[KGameHighScoreDialog::Score].setNum(score);
0239     scoreInfo[KGameHighScoreDialog::Custom1].setNum(m_clickCount/2);
0240     scoreInfo[KGameHighScoreDialog::Time] = m_gameClock->timeString();
0241 
0242     // show the new dialog and add the new score to it
0243     KGameHighScoreDialog scoreDialog(KGameHighScoreDialog::Name | KGameHighScoreDialog::Time, this);
0244     scoreDialog.addField(KGameHighScoreDialog::Custom1, i18n("Moves Penalty"), QStringLiteral( "moves" ));
0245     scoreDialog.initFromDifficulty(KGameDifficulty::global());
0246     bool madeIt = scoreDialog.addScore(scoreInfo);
0247     if (!madeIt) {
0248         QString comment = i18np("Your score was %1, you did not make it to the high score list.",
0249                                 "Your score was %1, you did not make it to the high score list.", score);
0250         scoreDialog.setComment(comment);
0251     }
0252     scoreDialog.exec();
0253 }
0254 
0255 void MainWindow::rotationStarted()
0256 {
0257     m_clickCount++;
0258     updateStatusBar();
0259 }
0260 
0261 void MainWindow::pauseGame(bool paused)
0262 {
0263     pause(paused);
0264     if(paused) {
0265         m_gameClock->pause();
0266     } else {
0267         m_gameClock->resume();
0268     }
0269 }
0270 
0271 void MainWindow::updateStatusBar()
0272 {
0273     QString moves = i18nc("Number of mouse clicks", "Moves: %1", m_clickCount);
0274     QString time = i18nc("Time elapsed", "Time: %1", m_gameClock->timeString());
0275     m_movesLabel->setText(moves);
0276     m_timeLabel->setText(time);
0277 }
0278 
0279 QSize MainWindow::boardSize()
0280 {
0281     switch (KGameDifficulty::globalLevel()) {
0282     case KGameDifficultyLevel::Easy: return QSize(5, 5);
0283     case KGameDifficultyLevel::Medium: return QSize(7, 7);
0284     case KGameDifficultyLevel::Hard: return QSize(9, 9);
0285     case KGameDifficultyLevel::Custom: return QSize(Settings::width(), Settings::height());
0286     default: return QSize(9, 9);
0287     }
0288 }
0289 
0290 #include "moc_mainwindow.cpp"