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