File indexing completed on 2024-04-21 04:01:59

0001 /*
0002     SPDX-FileCopyrightText: 1998 Andreas Wüst <AndreasWuest@gmx.de>
0003     SPDX-FileCopyrightText: 2006-2009 Dmitry Suzdalev <dimsuz@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "gamewidget.h"
0009 
0010 #include "highscores.h"
0011 #include "playfield.h"
0012 #include "prefs.h"
0013 #include "katomic_debug.h"
0014 
0015 #include <KMessageBox>
0016 #include <KLocalizedString>
0017 #include <KConfig>
0018 
0019 #include <QGraphicsView>
0020 #include <QResizeEvent>
0021 #include <QApplication> // for qApp->quit()
0022 #include <QVBoxLayout>
0023 #include <QTimer> // Next Level after N seconds
0024 #include <QFileDialog>
0025 
0026 GameWidget::GameWidget ( const QString& levelSet, QWidget *parent )
0027     : QWidget( parent ), m_allowAnyLevelSwitch( false ), m_moves(0), m_level(0)
0028 {
0029     m_highscore = new KAtomicHighscores();
0030     m_levelHighscore = 0;
0031 
0032     QVBoxLayout *top = new QVBoxLayout(this);
0033     top->setContentsMargins(0, 0, 0, 0);
0034 
0035     // playfield
0036     m_playField = new PlayField(this);
0037 
0038     m_view = new QGraphicsView(m_playField, this);
0039     int defaultFieldSize = FIELD_SIZE*MIN_ELEM_SIZE;
0040     // reserve some room for molecule preview
0041     m_view->setMinimumSize( defaultFieldSize+defaultFieldSize/4, defaultFieldSize );
0042     m_view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
0043     m_view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
0044     m_view->setFrameStyle( QFrame::NoFrame );
0045     m_view->setCacheMode( QGraphicsView::CacheBackground );
0046 
0047     // TODO uncomment DontSavePainterState optimization back after this bug in Qt 4.6 preview will be
0048     // fixed (darktears promised it will get fixed in 4.6.0 release). Bug is about wrong coordinates
0049     // for QPainter passed to QGS::drawForeground() function
0050     m_view->setOptimizationFlags( 
0051             //QGraphicsView::DontSavePainterState |
0052             QGraphicsView::DontAdjustForAntialiasing );
0053 
0054     top->addWidget(m_view, 1);
0055 
0056     connect(m_playField, &PlayField::gameOver, this, &GameWidget::gameOver);
0057     connect(m_playField, &PlayField::updateMoves, this, &GameWidget::updateMoves);
0058 
0059     // Next level after some seconds after ending the game. See gameOver, nextLevel and prevLevel
0060     m_timer = new QTimer(this);
0061     m_timer->setSingleShot(true);
0062     connect(m_timer, &QTimer::timeout, this, &GameWidget::nextLevel);
0063 
0064     setLevelSet(levelSet);
0065 }
0066 
0067 GameWidget::~GameWidget()
0068 {
0069     delete m_highscore;
0070 }
0071 
0072 
0073 bool GameWidget::setLevelSet(const QString& levelSet)
0074 {
0075     if (m_levelSet.name() == levelSet)
0076     {
0077         //qCDebug(KATOMIC_LOG) << "level set named" << levelSet << "is already loaded";
0078         return true;
0079     }
0080 
0081     bool res = m_levelSet.load(levelSet);
0082     if (!res)
0083     {
0084         KMessageBox::error(this, i18n("Failed to load level set \"%1\". Check if it is installed on your computer.", levelSet));
0085         //qCDebug(KATOMIC_LOG) << "failed to load levelset" << levelSet;
0086         return false;
0087     }
0088 
0089     int lastPlayed = lastPlayedLevel();
0090     int maxLevel = maxAccessibleLevel();
0091 
0092     int startingLevel = qMin(lastPlayed, maxLevel);
0093     switchToLevel(startingLevel);
0094 
0095     return true;
0096 }
0097 
0098 const LevelSet& GameWidget::levelSet() const
0099 {
0100     return m_levelSet;
0101 }
0102 
0103 QString GameWidget::currentMolecule() const
0104 {
0105     return m_playField->moleculeName();
0106 }
0107 
0108 void GameWidget::moveUp()
0109 {
0110     if(!m_playField->isLevelFinished())
0111         m_playField->moveSelectedAtom( PlayField::Up );
0112 }
0113 
0114 void GameWidget::moveDown()
0115 {
0116     if(!m_playField->isLevelFinished())
0117         m_playField->moveSelectedAtom( PlayField::Down );
0118 }
0119 
0120 void GameWidget::moveLeft()
0121 {
0122     if(!m_playField->isLevelFinished())
0123         m_playField->moveSelectedAtom( PlayField::Left );
0124 }
0125 
0126 void GameWidget::moveRight()
0127 {
0128     if(!m_playField->isLevelFinished())
0129         m_playField->moveSelectedAtom( PlayField::Right );
0130 }
0131 
0132 void GameWidget::gameOver(int moves) {
0133     // writing this info only in normal mode
0134     if ( !m_allowAnyLevelSwitch &&
0135             maxAccessibleLevel() < m_level+1 )
0136     {
0137         saveMaxAccessibleLevel( m_level+1 );
0138     }
0139 
0140     QString message = i18n( "Level %1 finished. ", m_level );
0141 
0142     if( m_highscore->addScore( moves , m_levelSet.name(), m_level ) ) // new highscore!
0143     {
0144         message += i18n("Congratulations! You have a new highscore!" );
0145     }
0146 
0147     m_playField->showMessage( message );
0148     Q_EMIT statsChanged(m_level, moves, m_highscore->levelHighscore(m_levelSet.name(), m_level));
0149     if (!m_allowAnyLevelSwitch)
0150     {
0151         // reuse this signal to allow switching levels over toolbar
0152         Q_EMIT levelChanged(m_level);
0153         // after 4 seconds, nextLevel
0154         m_timer->start(4000);
0155     }
0156 }
0157 
0158 void GameWidget::updateMoves(int moves)
0159 {
0160     m_moves = moves;
0161     Q_EMIT statsChanged(m_level, moves, m_levelHighscore);
0162 }
0163 
0164 void GameWidget::switchToLevel (int l)
0165 {
0166     const LevelData* levelData = m_levelSet.levelData(l);
0167     if (levelData)
0168     {
0169         m_level=l;
0170 
0171         m_playField->setLevelData(levelData);
0172 
0173         m_levelHighscore = m_highscore->levelHighscore( m_levelSet.name(), m_level );
0174 
0175         Q_EMIT statsChanged(m_level, 0, m_levelHighscore);
0176         Q_EMIT levelChanged(m_level);
0177 
0178         saveLastPlayedLevel();
0179     }
0180     else {
0181         //qCDebug(KATOMIC_LOG) << "failed to load level" << l;
0182     }
0183 }
0184 
0185 void GameWidget::restartLevel()
0186 {
0187     switchToLevel(m_level);
0188 }
0189 
0190 void GameWidget::saveGame()
0191 {
0192     QString fileName = QFileDialog::getSaveFileName( this, QString(), QString(), QStringLiteral("*.katomic") );
0193     if(fileName.isEmpty())
0194         return;
0195     KConfig config(fileName, KConfig::SimpleConfig);
0196     KConfigGroup gr = config.group(QStringLiteral("Savegame"));
0197     gr.writeEntry( "Level", m_level );
0198     gr.writeEntry( "LevelSet", m_levelSet.name() );
0199     m_playField->saveGame( gr );
0200 }
0201 
0202 void GameWidget::loadGame()
0203 {
0204     QString fileName = QFileDialog::getOpenFileName( this, QString(), QString(), QStringLiteral("*.katomic") );
0205     if(fileName.isEmpty())
0206         return;
0207     KConfig config(fileName, KConfig::SimpleConfig);
0208     KConfigGroup gr = config.group(QStringLiteral("Savegame"));
0209     QString levelSet = gr.readEntry("LevelSet");
0210     if (levelSet.isEmpty())
0211     {
0212         //qCDebug(KATOMIC_LOG) << "note: savegame file doesn't contain info about levelset, assuming default one";
0213         levelSet = QStringLiteral(DEFAULT_LEVELSET_NAME);
0214     }
0215 
0216     bool res = setLevelSet(levelSet);
0217     if (res)
0218     {
0219         int l = gr.readEntry( "Level", 1 );
0220         switchToLevel(l);
0221         m_playField->loadGame( gr );
0222     }
0223 }
0224 
0225 void GameWidget::showHighscores ()
0226 {
0227     // TODO: Implement me! Please... 8-)
0228 }
0229 
0230 int GameWidget::currentHighScore() const
0231 {
0232     return m_levelHighscore;
0233 }
0234 
0235 void GameWidget::prevLevel()
0236 {
0237     // if user hits toolbar buttons, stop timer
0238     if (m_timer->isActive())
0239         m_timer->stop();
0240 
0241     if (isPrevLevelAvailable())
0242     {
0243         switchToLevel(m_level-1);
0244     }
0245 }
0246 
0247 void GameWidget::nextLevel()
0248 {
0249     // if user hits toolbar buttons, stop timer
0250     if (m_timer->isActive())
0251         m_timer->stop();
0252 
0253     if (isNextLevelAvailable())
0254     {
0255         switchToLevel(m_level+1);
0256     }
0257 }
0258 
0259 void GameWidget::resizeEvent( QResizeEvent* ev)
0260 {
0261     m_playField->resize( ev->size().width(), ev->size().height() );
0262 }
0263 
0264 void GameWidget::saveLastPlayedLevel()
0265 {
0266     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0267     KConfigGroup grp(cfg, m_levelSet.name());
0268     grp.writeEntry("LastPlayedLevel", m_level);
0269 
0270     Preferences::setLastPlayedLevelSet(m_levelSet.name());
0271 }
0272 
0273 void GameWidget::saveMaxAccessibleLevel(int level)
0274 {
0275     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0276     KConfigGroup grp(cfg, m_levelSet.name());
0277     grp.writeEntry("MaxAccessibleLevel", level);
0278 }
0279 
0280 int GameWidget::lastPlayedLevel() const
0281 {
0282     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0283     KConfigGroup grp(cfg, m_levelSet.name());
0284     int lastPlayed = grp.readEntry("LastPlayedLevel", 1);
0285     lastPlayed = qMax(1, lastPlayed); // can't be less than 1
0286     //qCDebug(KATOMIC_LOG) << "last played level:" << lastPlayed;
0287     return lastPlayed;
0288 }
0289 
0290 int GameWidget::maxAccessibleLevel() const
0291 {
0292     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0293     KConfigGroup grp(cfg, m_levelSet.name());
0294     int maxAccessible = grp.readEntry("MaxAccessibleLevel", 1);
0295     //qCDebug(KATOMIC_LOG) << "max accessible level:" << maxAccessible;
0296     return maxAccessible;
0297 }
0298 
0299 bool GameWidget::isNextLevelAvailable() const
0300 {
0301     bool avail = m_allowAnyLevelSwitch ? true :
0302         (m_level != maxAccessibleLevel() && m_level <= m_levelSet.levelCount());
0303     return avail;
0304 }
0305 
0306 bool GameWidget::isPrevLevelAvailable() const
0307 {
0308     return m_level > 1;
0309 }
0310 
0311 #include "moc_gamewidget.cpp"