File indexing completed on 2024-04-21 04:02:09

0001 /******************************************************************************
0002 *   KBlocks, a falling blocks game by KDE                                     *
0003 *   SPDX-FileCopyrightText: 2009-2021 Mauricio Piacentini <mauricio@tabuleiro.com>      *
0004 *                           Zhongjie Cai <squall.leonhart.cai@gmail.com>      *
0005 *                           Julian Helfferich <julian.helfferich@mailbox.org> *
0006 *                                                                             *
0007 *   SPDX-License-Identifier: GPL-2.0-or-later
0008 ******************************************************************************/
0009 #include "KBlocksWin.h"
0010 
0011 #include <limits.h>
0012 // KDEGames
0013 #include <KGameThemeSelector>
0014 #include <KGameThemeProvider>
0015 #include <KGameDifficulty>
0016 #include <KGameHighScoreDialog>
0017 #include <KGameStandardAction>
0018 // KF
0019 #include <KToggleAction>
0020 #include <KStandardAction>
0021 #include <KActionCollection>
0022 #include <KLocalizedString>
0023 // Qt
0024 #include <QStatusBar>
0025 #include <QPixmapCache>
0026 #include <QPointer>
0027 #include <QLabel>
0028 #include <QRandomGenerator>
0029 
0030 #include "GameLogicInterface.h"
0031 #include "KBlocksScene.h"
0032 #include "SceneInterface.h"
0033 #include "settings.h"
0034 
0035 KBlocksWin::KBlocksWin(
0036     GameLogicInterface *p,
0037     GraphicsInterface *graphics,
0038     SoundInterface *sound,
0039     KGameThemeProvider *themeProvider,
0040     KBlocksPlayManager *pM,
0041     int capacity,
0042     int gamecount
0043 ) : KXmlGuiWindow()
0044 {
0045     if (themeProvider) {
0046         m_themeSelector = new KGameThemeSelector(themeProvider, KGameThemeSelector::EnableNewStuffDownload);
0047         connect(themeProvider, &KGameThemeProvider::currentThemeChanged, this, &KBlocksWin::onThemeChanged);
0048     }
0049 
0050     //Use up to 3MB for global application pixmap cache
0051     QPixmapCache::setCacheLimit(3 * 1024);
0052 
0053     mpKBPlayer = new KBlocksKeyboardPlayer(this);
0054     mpAIPlayer = new KBlocksAIPlayer();
0055 
0056     mMaxGameCapacity = capacity;
0057     mGameCount = gamecount;
0058     mpGameLogic = p;
0059     connect(mpGameLogic, &GameLogicInterface::allGamesStopped,
0060             this, &KBlocksWin::onAllGamesStopped);
0061 
0062     mpPlayManager = pM;
0063 
0064     mpGameScene = new KBlocksScene(mpGameLogic, graphics, sound, capacity);
0065 
0066     connect(mpKBPlayer, &KBlocksKeyboardPlayer::blockMoved,
0067             mpGameScene, &SceneInterface::playMoveSound);
0068     connect(mpKBPlayer, &KBlocksKeyboardPlayer::blockDropped,
0069             mpGameScene, &SceneInterface::playDropSound);
0070 
0071     mpGameView = new KBlocksView(mpGameScene, this);
0072     mpGameView->show();
0073     setCentralWidget(mpGameView);
0074     connect(mpGameView, &KBlocksView::focusEvent, this, &KBlocksWin::focusEvent);
0075 
0076     setAutoSaveSettings();
0077 }
0078 
0079 KBlocksWin::~KBlocksWin()
0080 {
0081     delete m_themeSelector;
0082     delete mpGameView;
0083     delete mpGameScene;
0084     delete mpAIPlayer;
0085     delete mpKBPlayer;
0086 }
0087 
0088 void KBlocksWin::setGamesPerLine(int count)
0089 {
0090     mpGameScene->setGamesPerLine(count);
0091 }
0092 
0093 void KBlocksWin::setGameAnimEnabled(bool flag)
0094 {
0095     mpGameScene->setGameAnimEnabled(flag);
0096     mGameAnim = flag;
0097 }
0098 
0099 void KBlocksWin::setWaitForAllUpdate(bool flag)
0100 {
0101     mpGameScene->setWaitForAllUpdate(flag);
0102     mWaitForAll = flag;
0103 }
0104 
0105 void KBlocksWin::setUpdateInterval(int interval)
0106 {
0107     mpGameScene->setUpdateInterval(interval);
0108 }
0109 
0110 void KBlocksWin::addScore(int gameIndex, int lineCount)
0111 {
0112     mpGameScene->addScore(gameIndex, lineCount);
0113 }
0114 
0115 void KBlocksWin::startGame()
0116 {
0117     mpGameLogic->setGameSeed(QRandomGenerator::global()->generate());
0118     if (mpGameLogic->startGame(mGameCount)) {
0119         mpPlayManager->startGame();
0120 
0121         mpGameScene->createGameItemGroups(mGameCount, false);
0122         mpGameScene->startGame();
0123 
0124         int levelUpTime = 0;
0125         switch ((int) KGameDifficulty::globalLevel()) {
0126         case KGameDifficultyLevel::Medium:
0127             levelUpTime = 5;
0128             break;
0129         case KGameDifficultyLevel::Hard:
0130             levelUpTime = 10;
0131             break;
0132         }
0133         mpGameLogic->levelUpGame(levelUpTime);
0134 
0135         KGameDifficulty::global()->setGameRunning(true);
0136     } else {
0137         stopGame();
0138         startGame();
0139     }
0140 
0141     mpGameView->fitInView(mpGameScene->sceneRect(), Qt::KeepAspectRatio);
0142 
0143     mScore->setText(i18n("Points: %1 - Lines: %2 - Level: %3", 0, 0, 0));
0144 
0145     m_pauseAction->setEnabled(true);
0146     m_pauseAction->setChecked(false);
0147 }
0148 
0149 void KBlocksWin::stopGame()
0150 {
0151     if (mpGameLogic->hasSingleGames()) {
0152         mpGameScene->stopGame();
0153         mpGameLogic->deleteSingleGames();
0154         // Clear the game field
0155         mpGameScene->deleteGameItemGroups();
0156     }
0157 }
0158 
0159 void KBlocksWin::pauseGame()
0160 {
0161     mpGameLogic->pauseGame(m_pauseAction->isChecked());
0162     mpPlayManager->pauseGame(m_pauseAction->isChecked());
0163     mpGameScene->pauseGame(m_pauseAction->isChecked());
0164 
0165     mpKBPlayer->pauseGame(m_pauseAction->isChecked());
0166     mpAIPlayer->pauseGame(m_pauseAction->isChecked());
0167 
0168     KGameDifficulty::global()->setGameRunning(!m_pauseAction->isChecked());
0169 }
0170 
0171 void KBlocksWin::onAllGamesStopped()
0172 {
0173     mpPlayManager->stopGame();
0174     m_pauseAction->setEnabled(false);
0175     KGameDifficulty::global()->setGameRunning(false);
0176 }
0177 
0178 void KBlocksWin::singleGame()
0179 {
0180     mpPlayManager->stopGame();
0181     mpPlayManager->clearGamePlayer();
0182     mpPlayManager->addGamePlayer(mpKBPlayer, -1, -1);
0183     mGameCount = 1;
0184 
0185     startGame();
0186 }
0187 
0188 void KBlocksWin::pveStepGame()
0189 {
0190     mpPlayManager->stopGame();
0191     mpPlayManager->clearGamePlayer();
0192     mpPlayManager->addGamePlayer(mpKBPlayer, -1, -1);
0193     mpPlayManager->addGamePlayer(mpAIPlayer, 200, 50);
0194     mGameCount = 2;
0195 
0196     mpGameLogic->setGameStandbyMode(true);
0197     setWaitForAllUpdate(true);
0198 
0199     startGame();
0200 }
0201 
0202 void KBlocksWin::focusEvent(bool flag)
0203 {
0204     if (!flag) {
0205         if (m_pauseAction->isChecked()) {
0206             return;
0207         }
0208     }
0209     mpGameLogic->pauseGame(flag);
0210     mpPlayManager->pauseGame(flag);
0211     mpGameScene->pauseGame(flag, true);
0212 }
0213 
0214 void KBlocksWin::closeEvent(QCloseEvent *event)
0215 {
0216     Settings::self()->save();
0217     KXmlGuiWindow::closeEvent(event);
0218 }
0219 
0220 void KBlocksWin::showHighscore()
0221 {
0222     KGameHighScoreDialog ksdialog(KGameHighScoreDialog::Name | KGameHighScoreDialog::Level | KGameHighScoreDialog::Score, this);
0223     ksdialog.initFromDifficulty(KGameDifficulty::global());
0224     ksdialog.exec();
0225 }
0226 
0227 void KBlocksWin::configureSettings()
0228 {
0229     m_themeSelector->showAsDialog();
0230 }
0231 
0232 void KBlocksWin::onThemeChanged(const KGameTheme *theme)
0233 {
0234     // sync to settings store
0235     Settings::setTheme(QString::fromUtf8(theme->identifier()));
0236     // trigger update of resources, then display
0237     mpGameView->loadTheme(theme);
0238 }
0239 
0240 void KBlocksWin::onScoreChanged(int index, int points, int lines, int level)
0241 {
0242     if (index == 0) { // TODO : game id?? multi game display??
0243         mScore->setText(i18n("Points: %1 - Lines: %2 - Level: %3", points, lines, level));
0244     }
0245 }
0246 
0247 void KBlocksWin::onIsHighscore(int index, int points, int level)
0248 {
0249     if (index == 0) { // TODO : game id?? multi game display??
0250         QPointer<KGameHighScoreDialog> ksdialog = new KGameHighScoreDialog(KGameHighScoreDialog::Name | KGameHighScoreDialog::Level | KGameHighScoreDialog::Score, this);
0251         ksdialog->initFromDifficulty(KGameDifficulty::global());
0252         KGameHighScoreDialog::FieldInfo info;
0253         info[KGameHighScoreDialog::Score].setNum(points);
0254         info[KGameHighScoreDialog::Level].setNum(level);
0255         if (ksdialog->addScore(info)) {
0256             ksdialog->exec();
0257         }
0258         delete ksdialog;
0259     }
0260 }
0261 
0262 void KBlocksWin::levelChanged()
0263 {
0264     //Scene reads the difficulty level for us
0265     if (mGameCount == 1) {
0266         singleGame();
0267     } else {
0268         pveStepGame();
0269     }
0270     mpGameView->setFocus(Qt::MouseFocusReason);
0271 }
0272 
0273 void KBlocksWin::setSoundsEnabled(bool enabled)
0274 {
0275     mpGameScene->setSoundsEnabled(enabled);
0276     Settings::setSounds(enabled);
0277 }
0278 
0279 void KBlocksWin::setupGUILayout()
0280 {
0281     QAction *action;
0282 
0283     action = KGameStandardAction::gameNew(this, &KBlocksWin::singleGame, actionCollection());
0284     action->setText(i18n("Single Game"));
0285     actionCollection()->addAction(QStringLiteral("newGame"), action);
0286 
0287     action = new QAction(this);
0288     action->setText(i18n("Human vs AI"));
0289     actionCollection()->addAction(QStringLiteral("pve_step"), action);
0290     connect(action, &QAction::triggered, this, &KBlocksWin::pveStepGame);
0291 
0292     m_pauseAction = KGameStandardAction::pause(this, &KBlocksWin::pauseGame, actionCollection());
0293     actionCollection()->addAction(QStringLiteral("pauseGame"), m_pauseAction);
0294     m_pauseAction->setEnabled(false);
0295 
0296     action = KGameStandardAction::highscores(this, &KBlocksWin::showHighscore, actionCollection());
0297     actionCollection()->addAction(QStringLiteral("showHighscores"), action);
0298 
0299     action = KGameStandardAction::quit(this, &KBlocksWin::close, actionCollection());
0300     actionCollection()->addAction(QStringLiteral("quit"), action);
0301 
0302     if (m_themeSelector) {
0303         KStandardAction::preferences(this, &KBlocksWin::configureSettings, actionCollection());
0304     }
0305 
0306     KToggleAction *soundAction = new KToggleAction(i18n("&Play Sounds"), this);
0307     soundAction->setChecked(Settings::sounds());
0308     actionCollection()->addAction(QStringLiteral("sounds"), soundAction);
0309     connect(soundAction, &KToggleAction::triggered, this, &KBlocksWin::setSoundsEnabled);
0310     mpGameScene->setSoundsEnabled(Settings::sounds());
0311 
0312     // TODO
0313     mScore = new QLabel(i18n("Points: 0 - Lines: 0 - Level: 0"));
0314     statusBar()->addPermanentWidget(mScore);
0315     connect(mpGameScene, &KBlocksScene::scoreChanged, this, &KBlocksWin::onScoreChanged);
0316     connect(mpGameScene, &KBlocksScene::isHighscore, this, &KBlocksWin::onIsHighscore);
0317 
0318     KGameDifficulty::global()->addStandardLevelRange(
0319         KGameDifficultyLevel::Easy, KGameDifficultyLevel::Hard
0320     );
0321     KGameDifficultyGUI::init(this);
0322     connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, this, &KBlocksWin::levelChanged);
0323 
0324     setupGUI();
0325 }
0326 
0327 #include "moc_KBlocksWin.cpp"