1 | /* |
2 | |
3 | Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) |
4 | Copyright (C) 2006-2009 Dmitry Suzdalev (dimsuz@gmail.com) |
5 | |
6 | This program is free software; you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by |
8 | the Free Software Foundation; either version 2 of the License, or |
9 | (at your option) any later version. |
10 | |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with this program; if not, write to the Free Software |
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "gamewidget.h" |
22 | #include "highscores.h" |
23 | #include "playfield.h" |
24 | #include "prefs.h" |
25 | |
26 | #include <QGraphicsView> |
27 | #include <QResizeEvent> |
28 | #include <QApplication> // for qApp->quit() |
29 | #include <QVBoxLayout> |
30 | #include <QTimer> // Next Level after N seconds |
31 | #include <kmessagebox.h> |
32 | #include <klocale.h> |
33 | #include <kstandarddirs.h> |
34 | #include <kconfig.h> |
35 | #include <kglobalsettings.h> |
36 | #include <kfiledialog.h> |
37 | #include <kdebug.h> |
38 | |
39 | GameWidget::GameWidget ( const QString& levelSet, QWidget *parent ) |
40 | : QWidget( parent ), m_allowAnyLevelSwitch( false ), m_moves(0), m_level(0) |
41 | { |
42 | m_highscore = new KAtomicHighscores(); |
43 | m_levelHighscore = 0; |
44 | |
45 | QVBoxLayout *top = new QVBoxLayout(this); |
46 | top->setMargin(0); |
47 | |
48 | // playfield |
49 | m_playField = new PlayField(this); |
50 | |
51 | m_view = new QGraphicsView(m_playField, this); |
52 | int defaultFieldSize = FIELD_SIZE*MIN_ELEM_SIZE; |
53 | // reserve some room for molecule preview |
54 | m_view->setMinimumSize( defaultFieldSize+defaultFieldSize/4, defaultFieldSize ); |
55 | m_view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); |
56 | m_view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); |
57 | m_view->setFrameStyle( QFrame::NoFrame ); |
58 | m_view->setCacheMode( QGraphicsView::CacheBackground ); |
59 | |
60 | // TODO uncomment DontSavePainterState optimization back after this bug in Qt 4.6 preview will be |
61 | // fixed (darktears promised it will get fixed in 4.6.0 release). Bug is about wrong coordinates |
62 | // for QPainter passed to QGS::drawForeground() function |
63 | m_view->setOptimizationFlags( QGraphicsView::DontClipPainter | |
64 | //QGraphicsView::DontSavePainterState | |
65 | QGraphicsView::DontAdjustForAntialiasing ); |
66 | |
67 | top->addWidget(m_view, 1); |
68 | |
69 | connect (m_playField, SIGNAL (gameOver(int)), SLOT(gameOver(int))); |
70 | connect (m_playField, SIGNAL (updateMoves(int)), SLOT(updateMoves(int))); |
71 | |
72 | // Next level after some seconds after ending the game. See gameOver, nextLevel and prevLevel |
73 | m_timer = new QTimer(this); |
74 | m_timer->setSingleShot(true); |
75 | connect (m_timer, SIGNAL(timeout()), this, SLOT(nextLevel())); |
76 | |
77 | setLevelSet(levelSet); |
78 | } |
79 | |
80 | GameWidget::~GameWidget() |
81 | { |
82 | delete m_highscore; |
83 | } |
84 | |
85 | |
86 | bool GameWidget::setLevelSet(const QString& levelSet) |
87 | { |
88 | if (m_levelSet.name() == levelSet) |
89 | { |
90 | kDebug() << "level set named" << levelSet << "is already loaded" ; |
91 | return true; |
92 | } |
93 | |
94 | bool res = m_levelSet.load(levelSet); |
95 | if (!res) |
96 | { |
97 | KMessageBox::error(this, i18n("Failed to load level set \"%1\". Check if it is installed on your computer." , levelSet)); |
98 | kDebug() << "failed to load levelset" << levelSet; |
99 | return false; |
100 | } |
101 | |
102 | int lastPlayed = lastPlayedLevel(); |
103 | int maxLevel = maxAccessibleLevel(); |
104 | |
105 | int startingLevel = qMin(lastPlayed, maxLevel); |
106 | switchToLevel(startingLevel); |
107 | |
108 | return true; |
109 | } |
110 | |
111 | const LevelSet& GameWidget::levelSet() const |
112 | { |
113 | return m_levelSet; |
114 | } |
115 | |
116 | QString GameWidget::currentMolecule() const |
117 | { |
118 | return m_playField->moleculeName(); |
119 | } |
120 | |
121 | void GameWidget::moveUp() |
122 | { |
123 | if(!m_playField->isLevelFinished()) |
124 | m_playField->moveSelectedAtom( PlayField::Up ); |
125 | } |
126 | |
127 | void GameWidget::moveDown() |
128 | { |
129 | if(!m_playField->isLevelFinished()) |
130 | m_playField->moveSelectedAtom( PlayField::Down ); |
131 | } |
132 | |
133 | void GameWidget::moveLeft() |
134 | { |
135 | if(!m_playField->isLevelFinished()) |
136 | m_playField->moveSelectedAtom( PlayField::Left ); |
137 | } |
138 | |
139 | void GameWidget::moveRight() |
140 | { |
141 | if(!m_playField->isLevelFinished()) |
142 | m_playField->moveSelectedAtom( PlayField::Right ); |
143 | } |
144 | |
145 | void GameWidget::gameOver(int moves) { |
146 | // writing this info only in normal mode |
147 | if ( !m_allowAnyLevelSwitch && |
148 | maxAccessibleLevel() < m_level+1 ) |
149 | { |
150 | saveMaxAccessibleLevel( m_level+1 ); |
151 | } |
152 | |
153 | QString message = i18n( "Level %1 finished. " , m_level ); |
154 | |
155 | if( m_highscore->addScore( moves , m_levelSet.name(), m_level ) ) // new highscore! |
156 | { |
157 | message += i18n("Congratulations! You have a new highscore!" ); |
158 | } |
159 | |
160 | m_playField->showMessage( message ); |
161 | emit statsChanged(m_level, moves, m_highscore->levelHighscore(m_levelSet.name(), m_level)); |
162 | if (!m_allowAnyLevelSwitch) |
163 | { |
164 | // reuse this signal to allow switching levels over toolbar |
165 | emit levelChanged(m_level); |
166 | // after 4 seconds, nextLevel |
167 | m_timer->start(4000); |
168 | } |
169 | } |
170 | |
171 | void GameWidget::updateMoves(int moves) |
172 | { |
173 | m_moves = moves; |
174 | emit statsChanged(m_level, moves, m_levelHighscore); |
175 | } |
176 | |
177 | void GameWidget::switchToLevel (int l) |
178 | { |
179 | const LevelData* levelData = m_levelSet.levelData(l); |
180 | if (levelData) |
181 | { |
182 | m_level=l; |
183 | |
184 | m_playField->setLevelData(levelData); |
185 | |
186 | m_levelHighscore = m_highscore->levelHighscore( m_levelSet.name(), m_level ); |
187 | |
188 | emit statsChanged(m_level, 0, m_levelHighscore); |
189 | emit levelChanged(m_level); |
190 | |
191 | saveLastPlayedLevel(); |
192 | } |
193 | else |
194 | kDebug() << "failed to load level" << l; |
195 | } |
196 | |
197 | void GameWidget::restartLevel() |
198 | { |
199 | switchToLevel(m_level); |
200 | } |
201 | |
202 | void GameWidget::saveGame() |
203 | { |
204 | QString fileName = KFileDialog::getSaveFileName( KUrl(), "*.katomic" , this ); |
205 | if(fileName.isEmpty()) |
206 | return; |
207 | KConfig config(fileName, KConfig::SimpleConfig); |
208 | KConfigGroup gr = config.group("Savegame" ); |
209 | gr.writeEntry( "Level" , m_level ); |
210 | gr.writeEntry( "LevelSet" , m_levelSet.name() ); |
211 | m_playField->saveGame( gr ); |
212 | } |
213 | |
214 | void GameWidget::loadGame() |
215 | { |
216 | QString fileName = KFileDialog::getOpenFileName( KUrl(), "*.katomic" , this ); |
217 | if(fileName.isEmpty()) |
218 | return; |
219 | KConfig config(fileName, KConfig::SimpleConfig); |
220 | KConfigGroup gr = config.group("Savegame" ); |
221 | QString levelSet = gr.readEntry("LevelSet" ); |
222 | if (levelSet.isEmpty()) |
223 | { |
224 | kDebug() << "note: savegame file doesn't contain info about levelset, assuming default one" ; |
225 | levelSet = DEFAULT_LEVELSET_NAME; |
226 | } |
227 | |
228 | bool res = setLevelSet(levelSet); |
229 | if (res) |
230 | { |
231 | int l = gr.readEntry( "Level" , 1 ); |
232 | switchToLevel(l); |
233 | m_playField->loadGame( gr ); |
234 | } |
235 | } |
236 | |
237 | void GameWidget::showHighscores () |
238 | { |
239 | // TODO: Implement me! Please... 8-) |
240 | } |
241 | |
242 | int GameWidget::currentHighScore() const |
243 | { |
244 | return m_levelHighscore; |
245 | } |
246 | |
247 | void GameWidget::prevLevel() |
248 | { |
249 | // if user hits toolbar buttons, stop timer |
250 | if (m_timer->isActive()) |
251 | m_timer->stop(); |
252 | |
253 | if (isPrevLevelAvailable()) |
254 | { |
255 | switchToLevel(m_level-1); |
256 | } |
257 | } |
258 | |
259 | void GameWidget::nextLevel() |
260 | { |
261 | // if user hits toolbar buttons, stop timer |
262 | if (m_timer->isActive()) |
263 | m_timer->stop(); |
264 | |
265 | if (isNextLevelAvailable()) |
266 | { |
267 | switchToLevel(m_level+1); |
268 | } |
269 | } |
270 | |
271 | void GameWidget::resizeEvent( QResizeEvent* ev) |
272 | { |
273 | m_playField->resize( ev->size().width(), ev->size().height() ); |
274 | } |
275 | |
276 | void GameWidget::saveLastPlayedLevel() |
277 | { |
278 | KSharedConfigPtr cfg = KGlobal::config(); |
279 | KConfigGroup grp(cfg, m_levelSet.name()); |
280 | grp.writeEntry("LastPlayedLevel" , m_level); |
281 | |
282 | Preferences::setLastPlayedLevelSet(m_levelSet.name()); |
283 | } |
284 | |
285 | void GameWidget::saveMaxAccessibleLevel(int level) |
286 | { |
287 | KSharedConfigPtr cfg = KGlobal::config(); |
288 | KConfigGroup grp(cfg, m_levelSet.name()); |
289 | grp.writeEntry("MaxAccessibleLevel" , level); |
290 | } |
291 | |
292 | int GameWidget::lastPlayedLevel() const |
293 | { |
294 | KSharedConfigPtr cfg = KGlobal::config(); |
295 | KConfigGroup grp(cfg, m_levelSet.name()); |
296 | int lastPlayed = grp.readEntry("LastPlayedLevel" , 1); |
297 | lastPlayed = qMax(1, lastPlayed); // can't be less than 1 |
298 | kDebug() << "last played level:" << lastPlayed; |
299 | return lastPlayed; |
300 | } |
301 | |
302 | int GameWidget::maxAccessibleLevel() const |
303 | { |
304 | KSharedConfigPtr cfg = KGlobal::config(); |
305 | KConfigGroup grp(cfg, m_levelSet.name()); |
306 | int maxAccessible = grp.readEntry("MaxAccessibleLevel" , 1); |
307 | kDebug() << "max accessible level:" << maxAccessible; |
308 | return maxAccessible; |
309 | } |
310 | |
311 | bool GameWidget::isNextLevelAvailable() const |
312 | { |
313 | bool avail = m_allowAnyLevelSwitch ? true : |
314 | (m_level != maxAccessibleLevel() && m_level <= m_levelSet.levelCount()); |
315 | return avail; |
316 | } |
317 | |
318 | bool GameWidget::isPrevLevelAvailable() const |
319 | { |
320 | return m_level > 1; |
321 | } |
322 | |
323 | #include "gamewidget.moc" |
324 | |