File indexing completed on 2024-05-05 04:02:56

0001 /*
0002     SPDX-FileCopyrightText: 2010 Ni Hui <shuizhuyuanluo@126.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "mainwindow.h"
0008 
0009 #include "bgselectorconfig.h"
0010 #include "customgameconfig.h"
0011 #include "gameconfig.h"
0012 #include "gamescene.h"
0013 #include "gameview.h"
0014 #include "settings.h"
0015 
0016 #include <KGameHighScoreDialog>
0017 
0018 #include <QAction>
0019 #include <KActionCollection>
0020 #include <KConfigDialog>
0021 #include <QFileDialog>
0022 #include <KGameClock>
0023 #include <KGameDifficulty>
0024 #include <KGameThemeSelector>
0025 #include <QInputDialog>
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 #include <KStandardAction>
0029 #include <KGameStandardAction>
0030 #include <QStatusBar>
0031 #include <KToggleAction>
0032 #include <QIcon>
0033 #include <QPointer>
0034 #include <QRandomGenerator>
0035 
0036 MainWindow::MainWindow( bool kSameMode, QWidget* parent )
0037 : KXmlGuiWindow(parent),
0038 m_kSameMode(kSameMode),
0039 m_gameClock(nullptr),
0040 m_gameScore(0),
0041 m_lastRemainCount(0),
0042 m_statusBarLabelLeft(new QLabel(this)),
0043 m_statusBarLabelRight(new QLabel(this))
0044 {
0045     m_scene = new GameScene(this);
0046     GameView* view = new GameView( m_scene );
0047     view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
0048     view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
0049     view->setFrameStyle( QFrame::NoFrame );
0050     view->setCacheMode( QGraphicsView::CacheBackground );
0051     setCentralWidget( view );
0052 
0053     statusBar()->addPermanentWidget( m_statusBarLabelLeft, 1 );
0054     statusBar()->addPermanentWidget( m_statusBarLabelRight, 1 );
0055 
0056     if ( m_kSameMode ) {
0057 //         statusBar()->insertItem( i18n( "Colors: XX" ), 1 );
0058 //         statusBar()->insertItem( i18n( "Board: XXXXXX" ), 2 );
0059         m_statusBarLabelLeft->setText( i18n( "Marked: 0" ) );
0060         m_statusBarLabelRight->setText( i18n( "Score: 0" ) );
0061         connect(m_scene, &GameScene::remainCountChanged, this, &MainWindow::changeScore);
0062         connect(m_scene, &GameScene::markedCountChanged, this, &MainWindow::changeMarkedCount);
0063     }
0064     else {
0065         m_gameClock = new KGameClock( this, KGameClock::MinSecOnly );
0066         m_statusBarLabelLeft->setText( i18n( "Pieces: 0" ) );
0067         m_statusBarLabelRight->setText( i18n( "Time: 00:00" ) );
0068         connect(m_scene, &GameScene::remainCountChanged, this, &MainWindow::changeRemainCount);
0069         connect(m_gameClock, &KGameClock::timeChanged, this, &MainWindow::changeTime);
0070     }
0071 
0072     connect(m_scene, &GameScene::gameFinished, this, &MainWindow::onGameOver);
0073 
0074     setupActions();
0075 
0076     loadSettings();
0077 
0078     newGame();
0079 }
0080 
0081 MainWindow::~MainWindow()
0082 {
0083 }
0084 
0085 void MainWindow::configureSettings()
0086 {
0087     if ( KConfigDialog::showDialog( QStringLiteral( "settings" ) ) ) {
0088         return;
0089     }
0090 
0091     KConfigDialog* dialog = new KConfigDialog( this, QStringLiteral( "settings" ), Settings::self() );
0092     dialog->addPage( new GameConfig( dialog ), i18n( "General" ), QStringLiteral( "games-config-options" ) );
0093     dialog->addPage( new KGameThemeSelector( m_scene->themeProvider() ), i18n( "Theme" ), QStringLiteral( "games-config-theme" ) );
0094     dialog->addPage( new BgSelectorConfig( dialog ), i18n( "Background" ), QStringLiteral( "games-config-background" ) );
0095     if ( !m_kSameMode ) {
0096         dialog->addPage( new CustomGameConfig( dialog ), i18n( "Custom Game" ), QStringLiteral( "games-config-custom" ) );
0097     }
0098 
0099     connect(m_scene->themeProvider(), &KGameThemeProvider::currentThemeChanged, this, &MainWindow::loadSettings); //setBackgroundType!
0100     connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::loadSettings);
0101     dialog->show();
0102 }
0103 
0104 void MainWindow::loadSettings()
0105 {
0106     m_scene->setShowBoundLines( Settings::showBoundLines() );
0107     m_scene->setEnableAnimation( Settings::enableAnimation() );
0108     m_scene->setEnableHighlight( Settings::enableHighlight() );
0109 
0110     int bgType = 0; // Theme Radio Button
0111     if ( Settings::radioColor() == true ) {
0112         bgType = 1;
0113     } else if ( Settings::radioImage() == true ) {
0114         bgType = 2;
0115     }
0116     m_scene->setBackgroundType( bgType );
0117 }
0118 
0119 void MainWindow::newGame()
0120 {
0121     newGameWithId(QRandomGenerator::global()->bounded(RAND_MAX));
0122 }
0123 
0124 void MainWindow::newGameWithId( int gameId )
0125 {
0126     if ( !confirmAbort() ) {
0127         return;
0128     }
0129 
0130     m_pauseAction->setChecked( false );
0131     m_pauseAction->setEnabled( true );
0132 
0133     if ( m_kSameMode ) {
0134         m_gameScore = 0;
0135         m_lastRemainCount = 15 * 10;
0136         m_scene->startNewGame( 15, 10, 3, gameId );
0137         return;
0138     }
0139 
0140     m_gameClock->restart();
0141 
0142     switch ( KGameDifficulty::globalLevel() ) {
0143         case KGameDifficultyLevel::VeryEasy:
0144             m_scene->startNewGame( 10, 16, 3, gameId );
0145             break;
0146         case KGameDifficultyLevel::Easy:
0147             m_scene->startNewGame( 10, 16, 4, gameId );
0148             break;
0149         case KGameDifficultyLevel::Medium:
0150             m_scene->startNewGame( 10, 16, 5, gameId );
0151             break;
0152         case KGameDifficultyLevel::Hard:
0153             m_scene->startNewGame( 10, 16, 6, gameId );
0154             break;
0155         case KGameDifficultyLevel::Custom:
0156             m_scene->startNewGame( Settings::customWidth(),
0157                                    Settings::customHeight(),
0158                                    Settings::customColorCount(), gameId );
0159             break;
0160         default:
0161             break;
0162     }
0163 }
0164 
0165 void MainWindow::newNumGame()
0166 {
0167     if ( !confirmAbort() ) {
0168         return;
0169     }
0170 
0171     bool ok = false;
0172     int random = QRandomGenerator::global()->bounded(1, RAND_MAX);
0173     int userGameId = QInputDialog::getInt( this, i18nc("@title:window", "Select Board" ),
0174                                                 i18n( "Select a board number:" ),
0175                                                 random, 1, RAND_MAX, 1,
0176                                                 &ok );
0177     if ( ok ) {
0178         newGameWithId(userGameId);
0179     }
0180 }
0181 
0182 void MainWindow::pauseGame( bool isPaused )
0183 {
0184     m_scene->setPaused( isPaused );
0185     if ( !m_kSameMode ) {
0186         if ( isPaused ) {
0187             m_gameClock->pause();
0188         } else {
0189             m_gameClock->resume();
0190         }
0191     }
0192 }
0193 
0194 void MainWindow::restartGame()
0195 {
0196     if ( confirmAbort() ) {
0197         m_pauseAction->setChecked( false );
0198         m_pauseAction->setEnabled( true );
0199         if ( m_kSameMode ) {
0200             m_gameScore = 0;
0201             m_lastRemainCount = 0;
0202         }
0203         else {
0204             m_gameClock->restart();
0205         }
0206         m_scene->restartGame();
0207     }
0208 }
0209 
0210 void MainWindow::loadGame()
0211 {
0212     QString fileName = QFileDialog::getOpenFileName( this, QString(), QString() , i18n( "Klickety Game Files (*.klickety)" ) );
0213     if ( fileName.isEmpty() || !confirmAbort() ) {
0214         return;
0215     }
0216 
0217     m_pauseAction->setChecked( false );
0218     m_pauseAction->setEnabled( true );
0219     KConfig config( fileName, KConfig::SimpleConfig );
0220     KConfigGroup group = config.group( QStringLiteral("Savegame") );
0221     m_scene->loadGame( group );
0222 }
0223 
0224 void MainWindow::saveGame()
0225 {
0226     QString fileName = QFileDialog::getSaveFileName( this, QString(), QString(), i18n( "Klickety Game Files (*.klickety)" ) );
0227     if ( fileName.isEmpty() ) {
0228         return;
0229     }
0230     KConfig config( fileName, KConfig::SimpleConfig );
0231     KConfigGroup group = config.group( QStringLiteral("Savegame") );
0232     m_scene->saveGame( group );
0233 }
0234 
0235 void MainWindow::changeMarkedCount( int markedCount )
0236 {
0237     int markedScore = ( markedCount < 2 ) ? 0 : ( ( markedCount - 2 ) * ( markedCount - 2 ) );
0238     m_statusBarLabelLeft->setText( i18np( "Marked: %2 (1 Point)", "Marked: %2 (%1 Points)", markedScore, markedCount ) );
0239 }
0240 
0241 void MainWindow::changeScore( int remainCount )
0242 {
0243     if ( m_lastRemainCount == 0 ) {
0244         // new game or restart
0245         m_lastRemainCount = remainCount;
0246         m_statusBarLabelRight->setText( i18n( "Score: 0" ) );
0247         return;
0248     }
0249     int removedCount = m_lastRemainCount - remainCount;
0250     if ( removedCount > 0 ) {
0251         // normal move
0252         int score = ( removedCount < 2 ) ? 0 : ( ( removedCount - 2 ) * ( removedCount - 2 ) );
0253         m_gameScore += score;
0254     }
0255     else {
0256         // undo action, minus the score
0257         int score = ( removedCount > -2 ) ? 0 : ( ( removedCount + 2 ) * ( removedCount + 2 ) );
0258         m_gameScore -= score;
0259     }
0260     m_statusBarLabelRight->setText( i18n( "Score: %1", m_gameScore ) );
0261     m_lastRemainCount = remainCount;
0262 }
0263 
0264 void MainWindow::changeRemainCount( int remainCount )
0265 {
0266     m_statusBarLabelLeft->setText( i18n( "Pieces: %1", remainCount ) );
0267 }
0268 
0269 void MainWindow::changeTime( const QString& newTime )
0270 {
0271     m_statusBarLabelRight->setText( i18n( "Time: %1", newTime ) );
0272 }
0273 
0274 void MainWindow::showHighscores()
0275 {
0276     if ( m_kSameMode ) {
0277         QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name | KGameHighScoreDialog::Score, this );
0278         d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ false);
0279         d->setConfigGroup( qMakePair( QByteArray( "KSame" ), i18n( "High Scores" ) ) );
0280         d->setHiddenConfigGroups( QList<QByteArray>() << "Very Easy" << "Easy" << "Medium" << "Hard" << "Custom" );
0281         d->exec();
0282         delete d;
0283         return;
0284     }
0285 
0286     QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name, this );
0287     d->addField( KGameHighScoreDialog::Custom1, i18n( "Remaining pieces" ), QStringLiteral( "remains" ) );
0288     d->addField( KGameHighScoreDialog::Custom2, i18n( "Time" ), QStringLiteral( "time" ) );
0289     d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ true);
0290     d->setHiddenConfigGroups( QList<QByteArray>() << "KSame" );
0291     d->hideField( KGameHighScoreDialog::Score );
0292     d->exec();
0293     delete d;
0294 }
0295 
0296 void MainWindow::onGameOver( int remainCount )
0297 {
0298     m_pauseAction->setEnabled( false );
0299 
0300     if ( m_kSameMode ) {
0301         if ( remainCount == 0 ) {
0302             // if the board is empty, give a bonus
0303             m_gameScore += 1000;
0304         }
0305 
0306         QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name | KGameHighScoreDialog::Score, this );
0307         d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ false);
0308         d->setConfigGroup( qMakePair( QByteArray( "KSame" ), i18n( "High Scores" ) ) );
0309         d->setHiddenConfigGroups( QList<QByteArray>() << "Very Easy" << "Easy" << "Medium" << "Hard" << "Custom" );
0310 
0311         KGameHighScoreDialog::FieldInfo scoreInfo;
0312         scoreInfo[ KGameHighScoreDialog::Score ].setNum( m_gameScore );
0313 
0314         if ( d->addScore( scoreInfo ) != 0 ) {
0315             d->exec();
0316 }
0317         delete d;
0318         return;
0319     }
0320 
0321     m_gameClock->pause();
0322 
0323     QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name, this );
0324     d->addField( KGameHighScoreDialog::Custom1, i18n( "Remaining pieces" ), QStringLiteral( "remains" ) );
0325     d->addField( KGameHighScoreDialog::Custom2, i18n( "Time" ), QStringLiteral( "time" ) );
0326     d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ true);
0327     d->setHiddenConfigGroups( QList<QByteArray>() << "KSame" );
0328     d->hideField( KGameHighScoreDialog::Score );
0329 
0330     KGameHighScoreDialog::FieldInfo scoreInfo;
0331     scoreInfo[KGameHighScoreDialog::Custom1].setNum( remainCount );
0332     scoreInfo[KGameHighScoreDialog::Custom2] = m_gameClock->timeString();
0333     // remainCount*10000000 is much bigger than a usual time seconds
0334     scoreInfo[KGameHighScoreDialog::Score].setNum( remainCount*10000000 + m_gameClock->seconds() );
0335     if ( d->addScore( scoreInfo, KGameHighScoreDialog::LessIsMore ) != 0 ) {
0336         d->exec();
0337     }
0338     delete d;
0339 }
0340 
0341 bool MainWindow::confirmAbort()
0342 {
0343     return m_scene->isGameFinished() ||
0344         ( KMessageBox::questionTwoActions( this,
0345                                       i18n( "Do you want to resign?" ),
0346                                       i18n( "New Game" ),
0347                                       KGuiItem( i18nc("@action:button", "Resign") ),
0348                                       KStandardGuiItem::cancel() )
0349           == KMessageBox::PrimaryAction );
0350 }
0351 
0352 void MainWindow::setupActions()
0353 {
0354     // game menu
0355     KGameStandardAction::gameNew(this, &MainWindow::newGame, actionCollection());
0356     if ( !m_kSameMode ) {
0357         KGameStandardAction::load(this, &MainWindow::loadGame, actionCollection());
0358         KGameStandardAction::save(this, &MainWindow::saveGame, actionCollection());
0359     }
0360     KGameStandardAction::restart(this, &MainWindow::restartGame, actionCollection());
0361     KGameStandardAction::highscores(this, &MainWindow::showHighscores, actionCollection());
0362     m_pauseAction = KGameStandardAction::pause(this, &MainWindow::pauseGame, actionCollection());
0363     KGameStandardAction::quit( this, &MainWindow::close, actionCollection());
0364     QAction * m_newNumGameAction = new QAction( i18n( "New Numbered Game..." ), actionCollection() );
0365     actionCollection()->addAction( QStringLiteral( "game_new_numeric" ), m_newNumGameAction );
0366     connect(m_newNumGameAction, &QAction::triggered, this, &MainWindow::newNumGame);
0367 
0368     KToggleAction * soundAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("speaker")), i18n("Play Sounds"), this);
0369     soundAction->setChecked(Settings::enableSounds());
0370     actionCollection()->addAction(QStringLiteral("sounds"), soundAction);
0371     connect(soundAction, &KToggleAction::triggered, this, &MainWindow::setSoundsEnabled);
0372 
0373     // move menu
0374     QAction * undoAction = KGameStandardAction::undo(m_scene, &GameScene::undoMove, actionCollection());
0375     undoAction->setEnabled( false );
0376     connect(m_scene, &GameScene::canUndoChanged, undoAction, &QAction::setEnabled);
0377     QAction * redoAction = KGameStandardAction::redo(m_scene, &GameScene::redoMove, actionCollection());
0378     redoAction->setEnabled( false );
0379     connect(m_scene, &GameScene::canRedoChanged, redoAction, &QAction::setEnabled);
0380 
0381     QAction * undoAllAction = actionCollection()->addAction( QStringLiteral( "move_undo_all" ) );
0382     undoAllAction->setIcon( QIcon::fromTheme( QStringLiteral( "media-skip-backward" ) ) );
0383     undoAllAction->setText( i18n( "Undo All" ) );
0384     undoAllAction->setEnabled( false );
0385     connect(m_scene, &GameScene::canUndoChanged, undoAllAction, &QAction::setEnabled);
0386     connect(undoAllAction, &QAction::triggered, m_scene, &GameScene::undoAllMove);
0387     QAction * redoAllAction = actionCollection()->addAction( QStringLiteral( "move_redo_all" ) );
0388     redoAllAction->setIcon( QIcon::fromTheme( QStringLiteral( "media-skip-forward" ) ) );
0389     redoAllAction->setText( i18n( "Redo All" ) );
0390     redoAllAction->setEnabled( false );
0391     connect(m_scene, &GameScene::canRedoChanged, redoAllAction, &QAction::setEnabled);
0392     connect(redoAllAction, &QAction::triggered, m_scene, &GameScene::redoAllMove);
0393 
0394     // settings menu
0395     KStandardAction::preferences(this, &MainWindow::configureSettings, actionCollection());
0396 
0397     if ( m_kSameMode ) {
0398         KGameDifficulty::global()->addLevel(new KGameDifficultyLevel(0,
0399             QByteArray( "KSame" ), i18n( "High Scores" )
0400         ));
0401         setupGUI( QSize( 576, 384 ) );
0402         return;
0403     }
0404 
0405     KGameDifficulty::global()->addStandardLevelRange(
0406         KGameDifficultyLevel::VeryEasy, KGameDifficultyLevel::Hard,
0407         KGameDifficultyLevel::Easy //default
0408     );
0409     KGameDifficulty::global()->addLevel(new KGameDifficultyLevel(1000,
0410         QByteArray( "Custom" ), i18n( "Custom" )
0411     ));
0412     KGameDifficultyGUI::init(this);
0413     connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged,
0414             this, &MainWindow::newGame);
0415 
0416     setupGUI( QSize( 340, 510 ) );
0417 }
0418 
0419 void MainWindow::setSoundsEnabled( bool enabled )
0420 {
0421     Settings::setEnableSounds( enabled );
0422     Settings::self()->save();
0423 }
0424 
0425 #include "moc_mainwindow.cpp"