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
39GameWidget::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
80GameWidget::~GameWidget()
81{
82 delete m_highscore;
83}
84
85
86bool 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
111const LevelSet& GameWidget::levelSet() const
112{
113 return m_levelSet;
114}
115
116QString GameWidget::currentMolecule() const
117{
118 return m_playField->moleculeName();
119}
120
121void GameWidget::moveUp()
122{
123 if(!m_playField->isLevelFinished())
124 m_playField->moveSelectedAtom( PlayField::Up );
125}
126
127void GameWidget::moveDown()
128{
129 if(!m_playField->isLevelFinished())
130 m_playField->moveSelectedAtom( PlayField::Down );
131}
132
133void GameWidget::moveLeft()
134{
135 if(!m_playField->isLevelFinished())
136 m_playField->moveSelectedAtom( PlayField::Left );
137}
138
139void GameWidget::moveRight()
140{
141 if(!m_playField->isLevelFinished())
142 m_playField->moveSelectedAtom( PlayField::Right );
143}
144
145void 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
171void GameWidget::updateMoves(int moves)
172{
173 m_moves = moves;
174 emit statsChanged(m_level, moves, m_levelHighscore);
175}
176
177void 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
197void GameWidget::restartLevel()
198{
199 switchToLevel(m_level);
200}
201
202void 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
214void 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
237void GameWidget::showHighscores ()
238{
239 // TODO: Implement me! Please... 8-)
240}
241
242int GameWidget::currentHighScore() const
243{
244 return m_levelHighscore;
245}
246
247void 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
259void 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
271void GameWidget::resizeEvent( QResizeEvent* ev)
272{
273 m_playField->resize( ev->size().width(), ev->size().height() );
274}
275
276void 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
285void GameWidget::saveMaxAccessibleLevel(int level)
286{
287 KSharedConfigPtr cfg = KGlobal::config();
288 KConfigGroup grp(cfg, m_levelSet.name());
289 grp.writeEntry("MaxAccessibleLevel", level);
290}
291
292int 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
302int 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
311bool GameWidget::isNextLevelAvailable() const
312{
313 bool avail = m_allowAnyLevelSwitch ? true :
314 (m_level != maxAccessibleLevel() && m_level <= m_levelSet.levelCount());
315 return avail;
316}
317
318bool GameWidget::isPrevLevelAvailable() const
319{
320 return m_level > 1;
321}
322
323#include "gamewidget.moc"
324