1 /*
2     Copyright 2004-2005 Andi Peredri <andi@ukr.net>
3     Copyright 2007 Simon Hürlimann <simon.huerlimann@huerlisi.ch>
4     Copyright 2007-2008 Fela Winkelmolen <fela.kde@gmail.com>
5     Copyright 2013 Ashwin Rajeev<ashwin_rajeev@hotmail.com>
6 
7     This program is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "mainwindow.h"
22 
23 #include <KConfigDialog>
24 #include <KLocalizedString>
25 #include <KStandardAction>
26 #include <QAction>
27 #include <KToggleAction>
28 #include <KActionCollection>
29 #include <KStandardGameAction>
30 #include <KMessageBox>
31 #include <QStatusBar>
32 
33 #include <KgDifficulty>
34 #include <KgThemeSelector>
35 #include <KGameRenderer>
36 #include <KScoreDialog>
37 #include <KGameClock>
38 #include <KgSound>
39 
40 #include <ctime>
41 #include <cmath>
42 
43 #include "ui_general.h"
44 #include "ui_customgame.h"
45 
46 #include "globals.h"
47 #include "gameview.h"
48 #include "settings.h"
49 #include "abstractgrid.h"
50 
51 class GeneralConfig : public QWidget
52 {
53 public:
GeneralConfig(QWidget * parent)54     GeneralConfig(QWidget *parent)
55         : QWidget(parent)
56     {
57         ui.setupUi(this);
58     }
59 private:
60     Ui::GeneralConfig ui;
61 };
62 
63 class CustomGameConfig : public QWidget
64 {
65 public:
CustomGameConfig(QWidget * parent)66     CustomGameConfig(QWidget *parent)
67         : QWidget(parent)
68     {
69         ui.setupUi(this);
70     }
71 private:
72     Ui::CustomGameConfig ui;
73 };
74 
MainWindow(QWidget * parent)75 MainWindow::MainWindow(QWidget *parent)
76     : KXmlGuiWindow(parent), m_clickCount(0),
77       m_view(new GameView(this))
78 {
79     connect(m_view, &GameView::gameOver, this, &MainWindow::gameOver);
80     connect(m_view, &GameView::rotationStarted, this, &MainWindow::rotationStarted);
81     connect(this, SIGNAL(pause(QVariant)), m_view->rootObject(), SLOT(pause(QVariant)));
82 
83     m_movesLabel = new QLabel(this);
84     m_movesLabel->setAlignment(Qt::AlignCenter);
85     m_timeLabel = new QLabel(this);
86     m_timeLabel->setAlignment(Qt::AlignCenter);
87 
88     statusBar()->insertPermanentWidget(0, m_movesLabel, 1);
89     statusBar()->insertPermanentWidget(1, m_timeLabel, 1);
90 
91     // Difficulty
92     Kg::difficulty()->addStandardLevelRange(
93         KgDifficultyLevel::Easy, KgDifficultyLevel::VeryHard
94     );
95     Kg::difficulty()->addLevel(
96                 new KgDifficultyLevel(100, QByteArray("Custom"), i18n("Custom"))
97     );
98     KgDifficultyGUI::init(this);
99     connect(Kg::difficulty(), &KgDifficulty::currentLevelChanged, this, &MainWindow::startNewGame);
100 
101 
102     setCentralWidget(m_view);
103 
104     setupActions();
105 
106     setupGUI();
107 
108     setAutoSaveSettings();
109 
110     m_gameClock = new KGameClock(this, KGameClock::MinSecOnly);
111     connect(m_gameClock, &KGameClock::timeChanged, this, &MainWindow::updateStatusBar);
112 
113     m_soundStart = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/start.wav")), this);
114     m_soundWin = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/win.wav")), this);
115 
116 
117     startNewGame();
118 }
119 
setupActions()120 void MainWindow::setupActions()
121 {
122     // Game
123     KStandardGameAction::gameNew(this, SLOT(startNewGame()),
124                                  actionCollection());
125 
126     m_pauseAction = KStandardGameAction::pause(this, SLOT(pauseGame(bool)),
127                                                actionCollection());
128     connect(Kg::difficulty(), &KgDifficulty::gameRunningChanged, m_pauseAction,
129             &QAction::setEnabled);
130 
131     QAction *action = KStandardGameAction::solve(m_view, SLOT(solve()), actionCollection());
132     connect(Kg::difficulty(), &KgDifficulty::gameRunningChanged, action, &QAction::setEnabled);
133 
134     KStandardGameAction::highscores(this, SLOT(showHighscores()),
135                                     actionCollection());
136 
137     KStandardGameAction::quit(this, SLOT(close()), actionCollection());
138 
139     // Settings
140     KStandardAction::preferences(this, SLOT(configureSettings()), actionCollection());
141 
142     action = new QAction(i18n("&Unlock All"), this);
143     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(unlockAll()));
144     connect(Kg::difficulty(), &KgDifficulty::gameRunningChanged, action, &QAction::setEnabled);
145     actionCollection()->addAction( QStringLiteral( "unlock_all" ), action);
146 
147     action = new QAction(i18n("Keyboard: Field right"), this);
148     actionCollection()->setDefaultShortcut(action, Qt::Key_Right);
149     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoRight()));
150     actionCollection()->addAction( QStringLiteral( "kb_go_right" ), action);
151 
152     action = new QAction(i18n("Keyboard: Field left"),this);
153     actionCollection()->setDefaultShortcut(action, Qt::Key_Left);
154     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoLeft()));
155     actionCollection()->addAction( QStringLiteral( "kb_go_left" ), action);
156 
157     action = new QAction(i18n("Keyboard: Field up"),this);
158     actionCollection()->setDefaultShortcut(action, Qt::Key_Up);
159     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoUp()));
160     actionCollection()->addAction( QStringLiteral( "kb_go_up" ), action);
161 
162     action = new QAction(i18n("Keyboard: Field down"),this);
163     actionCollection()->setDefaultShortcut(action, Qt::Key_Down);
164     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(kbGoDown()));
165     actionCollection()->addAction( QStringLiteral( "kb_go_down" ), action);
166 
167     action = new QAction(i18n("Keyboard: Turn clockwise"),this);
168     actionCollection()->setDefaultShortcut(action, Qt::Key_Return);
169     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(rotateClockwise()));
170     actionCollection()->addAction( QStringLiteral( "kb_turn_clockwise" ), action);
171 
172     action = new QAction(i18n("Keyboard: Turn counterclockwise"),this);
173     actionCollection()->setDefaultShortcut(action, Qt::CTRL | Qt::Key_Return);
174     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(rotateCounterclockwise()));
175     actionCollection()->addAction( QStringLiteral( "kb_turn_counterclockwise" ), action);
176 
177     action = new QAction(i18n("Keyboard: Toggle lock"),this);
178     actionCollection()->setDefaultShortcut(action, Qt::Key_Space);
179     connect(action, SIGNAL(triggered()), m_view->rootObject(), SLOT(toggleLock()));
180     actionCollection()->addAction( QStringLiteral( "kb_lock" ), action);
181 }
182 
configureSettings()183 void MainWindow::configureSettings()
184 {
185     if (KConfigDialog::showDialog(QStringLiteral("settings")))
186         return;
187     KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), Settings::self());
188     dialog->addPage(new GeneralConfig(dialog), i18n("General"), QStringLiteral("games-config-options"));
189     dialog->addPage(new KgThemeSelector(m_view->getProvider()), i18n("Theme"), QStringLiteral("games-config-theme"));
190     dialog->addPage(new CustomGameConfig(dialog), i18n("Custom Game"), QStringLiteral("games-config-custom"));
191     connect(dialog, &KConfigDialog::settingsChanged, m_view, &GameView::updateSettings);
192 //    dialog->setHelp(QString(),QLatin1String("knetwalk"));
193     dialog->show();
194 }
195 
showHighscores()196 void MainWindow::showHighscores()
197 {
198     KScoreDialog scoreDialog(KScoreDialog::Name | KScoreDialog::Time, this);
199     scoreDialog.addField(KScoreDialog::Custom1, i18n("Moves Penalty"), QStringLiteral( "moves" ));
200     scoreDialog.initFromDifficulty(Kg::difficulty());
201     scoreDialog.exec();
202 }
203 
startNewGame()204 void MainWindow::startNewGame()
205 {
206     if(Settings::playSounds())
207         m_soundStart->start();
208 
209     const KgDifficultyLevel::StandardLevel l = Kg::difficultyLevel();
210 
211     bool isWrapped = (l == KgDifficultyLevel::VeryHard);
212     if (Kg::difficultyLevel() == KgDifficultyLevel::Custom)
213         isWrapped = Settings::wrapping();
214     const QSize size = boardSize();
215     m_view->startNewGame(size.width(), size.height(), (Wrapping)isWrapped);
216     m_clickCount = -m_view->minimumMoves();
217     m_gameClock->restart();
218 
219     if(m_pauseAction->isChecked())
220     {
221         m_pauseAction->setChecked(false);
222     }
223     Kg::difficulty()->setGameRunning(true);
224 
225     updateStatusBar();
226 }
227 
gameOver(const QVariant & msg)228 void MainWindow::gameOver(const QVariant &msg)
229 {
230     m_gameClock->pause();
231     Kg::difficulty()->setGameRunning(false);
232 
233     if (msg.toString() != QLatin1String("won"))
234         return;
235 
236     if(Settings::playSounds())
237         m_soundWin->start();
238 
239     //=== calculate the score ====//
240 
241     double penalty = m_gameClock->seconds() / 2.0 * (m_clickCount/2 + 1);
242 
243     // normalize the penalty
244     penalty = std::sqrt(penalty/m_view->cellCount());
245 
246     int score = static_cast<int>(100.0 / penalty);
247 
248     // create the new scoreInfo
249     KScoreDialog::FieldInfo scoreInfo;
250     scoreInfo[KScoreDialog::Score].setNum(score);
251     scoreInfo[KScoreDialog::Custom1].setNum(m_clickCount/2);
252     scoreInfo[KScoreDialog::Time] = m_gameClock->timeString();
253 
254     // show the new dialog and add the new score to it
255     KScoreDialog scoreDialog(KScoreDialog::Name | KScoreDialog::Time, this);
256     scoreDialog.addField(KScoreDialog::Custom1, i18n("Moves Penalty"), QStringLiteral( "moves" ));
257     scoreDialog.initFromDifficulty(Kg::difficulty());
258     bool madeIt = scoreDialog.addScore(scoreInfo);
259     if (!madeIt) {
260         QString comment = i18np("Your score was %1, you did not make it to the high score list.",
261                                 "Your score was %1, you did not make it to the high score list.", score);
262         scoreDialog.setComment(comment);
263     }
264     scoreDialog.exec();
265 }
266 
rotationStarted()267 void MainWindow::rotationStarted()
268 {
269     m_clickCount++;
270     updateStatusBar();
271 }
272 
pauseGame(bool paused)273 void MainWindow::pauseGame(bool paused)
274 {
275     pause(paused);
276     if(paused) {
277         m_gameClock->pause();
278     } else {
279         m_gameClock->resume();
280     }
281 }
282 
updateStatusBar()283 void MainWindow::updateStatusBar()
284 {
285     QString moves = i18nc("Number of mouse clicks", "Moves: %1", m_clickCount);
286     QString time = i18nc("Time elapsed", "Time: %1", m_gameClock->timeString());
287     m_movesLabel->setText(moves);
288     m_timeLabel->setText(time);
289 }
290 
boardSize()291 QSize MainWindow::boardSize()
292 {
293     switch (Kg::difficultyLevel()) {
294     case KgDifficultyLevel::Easy: return QSize(5, 5);
295     case KgDifficultyLevel::Medium: return QSize(7, 7);
296     case KgDifficultyLevel::Hard: return QSize(9, 9);
297     case KgDifficultyLevel::Custom: return QSize(Settings::width(), Settings::height());
298     default: return QSize(9, 9);
299     }
300 }
301 
302