File indexing completed on 2023-12-03 07:52:31

0001 /*
0002     SPDX-FileCopyrightText: 2009 Mathias Kraus <k.hias@gmx.de>
0003     SPDX-FileCopyrightText: 2007-2008 Thomas Gallinari <tg8187@yahoo.fr>
0004     SPDX-FileCopyrightText: 2007-2008 Pierre-Benoit Besse <besse@gmail.com>
0005     SPDX-FileCopyrightText: 2007-2008 Alexandre Galinier <alex.galinier@hotmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "game.h"
0011 #include "mapparser.h"
0012 #include "settings.h"
0013 #include "gamescene.h"
0014 #include "arena.h"
0015 #include "player.h"
0016 #include "bonus.h"
0017 #include "bomb.h"
0018 #include "block.h"
0019 #include "config/playersettings.h"
0020 #include "granatier_random.h"
0021 
0022 #include <QPointF>
0023 #include <QTimer>
0024 #include <QKeyEvent>
0025 #include <QDir>
0026 #include <KConfig>
0027 #include <KgSound>
0028 #include <QStandardPaths>
0029 
0030 
0031 Game::Game(PlayerSettings* playerSettings)
0032 {
0033     m_playerSettings = playerSettings;
0034 
0035     // Initialize the sound state
0036     setSoundsEnabled(Settings::sounds());
0037     m_wilhelmScream = Settings::useWilhelmScream();
0038 
0039     m_soundPutBomb = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/putbomb.wav")));
0040     m_soundExplode = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/explode.wav")));
0041     m_soundBonus = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/wow.wav")));
0042     m_soundFalling = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/deepfall.wav")));
0043     m_soundDie = new KgSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/die.wav")));
0044 
0045     m_arena = nullptr;
0046     m_randomArenaModeArenaList.clear();
0047     m_gameScene = nullptr;
0048     m_winPoints = Settings::self()->pointsToWin();
0049 
0050     QStringList strPlayerIDs = m_playerSettings->playerIDs();
0051     for(int i = 0; i < strPlayerIDs.count(); i++)
0052     {
0053         if(m_playerSettings->enabled(strPlayerIDs[i]))
0054         {
0055             auto* player = new Player(qreal(Granatier::CellSize * (-0.5)),qreal(Granatier::CellSize * 0.5), strPlayerIDs[i], playerSettings, m_arena);
0056             m_players.append(player);
0057             connect(player, &Player::dying, this, &Game::playerDeath);
0058             connect(player, &Player::falling, this, &Game::playerFalling);
0059             connect(player, &Player::resurrectBonusTaken, this, &Game::resurrectBonusTaken);
0060         }
0061     }
0062 
0063     init();
0064 
0065     for (auto & player : m_players)
0066     {
0067         connect(player, &Player::bombDropped, this, &Game::createBomb);
0068     }
0069 
0070     m_gameOver = false;
0071 }
0072 
0073 void Game::init()
0074 {
0075     // Create the Arena instance
0076     m_arena = new Arena();
0077 
0078     m_remainingTime = Settings::roundTime();
0079     m_bombCount = 0;
0080 
0081     // Create the parser that will parse the XML file in order to initialize the Arena instance
0082     // This also creates all the characters
0083     MapParser mapParser(m_arena);
0084     // Set the XML file as input source for the parser
0085     QString filePath;
0086     if(Settings::self()->randomArenaMode())
0087     {
0088         if(m_randomArenaModeArenaList.isEmpty())
0089         {
0090             auto randomArenaModeArenaList = Settings::self()->randomArenaModeArenaList();
0091 
0092             QStringList arenasAvailable;
0093             const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("arenas"), QStandardPaths::LocateDirectory);
0094             for(const auto& dir: dirs) {
0095                 const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop"));
0096                 for(const auto& file: fileNames) {
0097                     arenasAvailable.append(dir + QLatin1Char('/') + file);
0098                 }
0099             }
0100 
0101             // store the random arenas if they are available
0102             for(const auto& randomArena: randomArenaModeArenaList) {
0103                 for(const auto& arena: arenasAvailable) {
0104                     if(arena.endsWith(randomArena)) {
0105                         m_randomArenaModeArenaList.append(arena);
0106                         break;
0107                     }
0108                 }
0109             }
0110 
0111             if(m_randomArenaModeArenaList.isEmpty()){
0112                 m_randomArenaModeArenaList = arenasAvailable;
0113             }
0114         }
0115 
0116         int nIndex = granatier::RNG::fromRange<int>(0, m_randomArenaModeArenaList.count() - 1);
0117         filePath = m_randomArenaModeArenaList.at(nIndex);
0118         m_randomArenaModeArenaList.removeAt(nIndex);
0119     }
0120     else
0121     {
0122         filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, Settings::self()->arena());
0123     }
0124 
0125     if(!QFile::exists(filePath))
0126     {
0127         Settings::self()->useDefaults(true);
0128         filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, Settings::self()->arena());
0129         Settings::self()->useDefaults(false);
0130     }
0131 
0132     KConfig arenaConfig(filePath, KConfig::SimpleConfig);
0133     KConfigGroup group = arenaConfig.group("Arena");
0134     QString arenaFileName = group.readEntry("FileName");
0135 
0136     QFile arenaXmlFile(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("arenas/%1").arg(arenaFileName)));
0137     if (!arenaXmlFile.open(QIODevice::ReadOnly)) {
0138         qWarning() << " impossible to open file " << arenaFileName;
0139     }
0140     mapParser.parse(&arenaXmlFile);
0141 
0142     QString arenaName = group.readEntry("Name");
0143     m_arena->setName(arenaName);
0144 
0145     //create the block items
0146     for (int i = 0; i < m_arena->getNbRows(); ++i)
0147     {
0148         for (int j = 0; j < m_arena->getNbColumns(); ++j)
0149         {
0150             if(m_arena->getCell(i,j).getType() == Granatier::Cell::BLOCK)
0151             {
0152                 auto* block = new Block((j + 0.5) * Granatier::CellSize, (i + 0.5) * Granatier::CellSize, m_arena, QStringLiteral("arena_block"));
0153                 m_blocks.append(block);
0154                 m_arena->setCellElement(i, j, block);
0155             }
0156         }
0157     }
0158 
0159     // Start the Game timer
0160     m_timer = new QTimer(this);
0161     m_timer->setInterval(int(1000 / Granatier::FPS));
0162     connect(m_timer, &QTimer::timeout, this, &Game::update);
0163     m_timer->start();
0164     m_state = RUNNING;
0165 
0166     m_roundTimer  = new QTimer(this);
0167     m_roundTimer->setInterval(1000);
0168     connect(m_roundTimer, &QTimer::timeout, this, &Game::decrementRemainingRoundTime);
0169     m_roundTimer->start();
0170 
0171     m_setRoundFinishedTimer  = new QTimer(this);
0172     m_setRoundFinishedTimer->setSingleShot(true);
0173     connect(m_setRoundFinishedTimer, &QTimer::timeout, this, &Game::setRoundFinished);
0174 
0175     // Init the characters coordinates on the Arena
0176     for (int i = 0; i < m_players.size(); i++)
0177     {
0178         m_players[i]->setArena(m_arena);
0179         QPointF playerPosition = m_arena->getPlayerPosition(i);
0180         m_players[i]->setInitialCoordinates(qreal(Granatier::CellSize * playerPosition.x()), qreal(Granatier::CellSize * playerPosition.y()));
0181     }
0182     initCharactersPosition();
0183 
0184     // Create the hidden Bonuses
0185     createBonus();
0186 }
0187 
0188 Game::~Game()
0189 {
0190     //pause is needed to stop all animations and therefore the access of the *items to their model
0191     pause(true);
0192 
0193     qDeleteAll(m_players);
0194     m_players.clear();
0195 
0196     cleanUp();
0197 
0198     delete m_soundPutBomb;
0199     delete m_soundExplode;
0200     delete m_soundBonus;
0201     delete m_soundFalling;
0202     delete m_soundDie;
0203 }
0204 
0205 void Game::cleanUp()
0206 {
0207     qDeleteAll(m_blocks);
0208     m_blocks.clear();
0209     qDeleteAll(m_bonus);
0210     m_bonus.clear();
0211     qDeleteAll(m_bombs);
0212     m_bombs.clear();
0213     delete m_arena;
0214     m_arena = nullptr;
0215     delete m_timer;
0216     m_timer = nullptr;
0217     delete m_roundTimer;
0218     m_roundTimer = nullptr;
0219     delete m_setRoundFinishedTimer;
0220     m_setRoundFinishedTimer = nullptr;
0221 }
0222 
0223 void Game::setGameScene(GameScene* p_gameScene)
0224 {
0225     m_gameScene = p_gameScene;
0226 }
0227 
0228 void Game::start()
0229 {
0230     // Restart the Game timer
0231     m_timer->start();
0232     m_state = RUNNING;
0233     m_roundTimer->start();
0234     Q_EMIT pauseChanged(false, false);
0235 }
0236 
0237 void Game::pause(bool p_locked)
0238 {
0239     // Stop the Game timer
0240     m_timer->stop();
0241     m_roundTimer->stop();
0242     if (p_locked)
0243     {
0244         m_state = PAUSED_LOCKED;
0245     }
0246     else
0247     {
0248         m_state = PAUSED_UNLOCKED;
0249     }
0250     Q_EMIT pauseChanged(true, false);
0251 }
0252 
0253 void Game::switchPause()
0254 {
0255     // If the Game is not already paused
0256     if (m_state == RUNNING)
0257     {
0258         // Pause the Game
0259         pause();
0260         Q_EMIT pauseChanged(true, true);
0261     }
0262     // If the Game is already paused
0263     else
0264     {
0265         // Resume the Game
0266         start();
0267         Q_EMIT pauseChanged(false, true);
0268     }
0269 }
0270 
0271 QList<Player*> Game::getPlayers() const
0272 {
0273     return m_players;
0274 }
0275 
0276 QTimer* Game::getTimer() const
0277 {
0278     return m_timer;
0279 }
0280 
0281 int Game::getRemainingTime() const
0282 {
0283     return m_remainingTime;
0284 }
0285 
0286 Arena* Game::getArena() const
0287 {
0288     return m_arena;
0289 }
0290 
0291 bool Game::isPaused() const
0292 {
0293     return (m_state != RUNNING);
0294 }
0295 
0296 bool Game::getGameOver() const
0297 {
0298     return m_gameOver;
0299 }
0300 
0301 QString Game::getWinner() const
0302 {
0303     return m_strWinner;
0304 }
0305 
0306 int Game::getWinPoints() const
0307 {
0308     return m_winPoints;
0309 }
0310 
0311 QList<Bonus*> Game::getBonus() const
0312 {
0313     return m_bonus;
0314 }
0315 
0316 void Game::createBonus()
0317 {
0318     int nRemainingNeutralBonuses = 1;
0319     for(int nQuarter = 0; nQuarter < 4; nQuarter++)
0320     {
0321         Bonus* bonus;
0322         int nBonusCount = static_cast<int>(0.3 * m_blocks.size() / 4);
0323         int nBadBonusCount = static_cast<int>(0.1 * m_blocks.size() / 4);
0324         int nNeutralBonusCount = granatier::RNG::fromRange(0, nRemainingNeutralBonuses);
0325         QList<Granatier::Bonus::Type> bonusTypeList;
0326         Granatier::Bonus::Type bonusType;
0327         int nFullSize = m_blocks.size();
0328         int nQuarterSize = (nQuarter < 3 ? nFullSize / 4 : nFullSize - 3 * (nFullSize / 4));
0329 
0330         for (int i = 0; i < nQuarterSize; ++i)
0331         {
0332             if(i < nBonusCount)
0333             {
0334                 constexpr int NumberOfBonuses = 6;
0335                 switch (granatier::RNG::fromRange(0, NumberOfBonuses-1))
0336                 {
0337                     case 0: bonusType = Granatier::Bonus::SPEED;
0338                             break;
0339                     case 1: bonusType = Granatier::Bonus::BOMB;
0340                             break;
0341                     case 2: bonusType = Granatier::Bonus::POWER;
0342                             break;
0343                     case 3: bonusType = Granatier::Bonus::SHIELD;
0344                             break;
0345                     case 4: bonusType = Granatier::Bonus::THROW;
0346                             break;
0347                     case 5: bonusType = Granatier::Bonus::KICK;
0348                             break;
0349                     default: bonusType = Granatier::Bonus::SPEED;
0350                 }
0351             }
0352             else if (i-nBonusCount < nBadBonusCount)
0353             {
0354                 constexpr int NumberOfBadBonuses = 5;
0355                 switch (granatier::RNG::fromRange(0, NumberOfBadBonuses-1))
0356                 {
0357                     case 0: bonusType = Granatier::Bonus::HYPERACTIVE;
0358                             break;
0359                     case 1: bonusType = Granatier::Bonus::SLOW;
0360                             break;
0361                     case 2: bonusType = Granatier::Bonus::MIRROR;
0362                             break;
0363                     case 3: bonusType = Granatier::Bonus::SCATTY;
0364                             break;
0365                     case 4: bonusType = Granatier::Bonus::RESTRAIN;
0366                             break;
0367                     default: bonusType = Granatier::Bonus::HYPERACTIVE;
0368                 }
0369             }
0370             else if(i-nBonusCount-nBadBonusCount < nNeutralBonusCount)
0371             {
0372                 bonusType = Granatier::Bonus::RESURRECT;
0373                 nRemainingNeutralBonuses--;
0374             }
0375             else {
0376                 bonusType = Granatier::Bonus::NONE;
0377             }
0378 
0379             bonusTypeList.append(bonusType);
0380         }
0381 
0382         int nShuffle;
0383         for (int i = 0; i < nQuarterSize; ++i)
0384         {
0385             nShuffle = granatier::RNG::fromRange(0, nQuarterSize-1);
0386 
0387             bonusTypeList.swapItemsAt(i, nShuffle);
0388         }
0389 
0390         for (int i = 0; i < nQuarterSize; ++i)
0391         {
0392             if(bonusTypeList[i] != Granatier::Bonus::NONE)
0393             {
0394                 int nIndex = nQuarter * (nFullSize/4) + i;
0395                 bonus = new Bonus(m_blocks[nIndex]->getX(), m_blocks[nIndex]->getY(), m_arena, bonusTypeList[i]);
0396                 m_bonus.append(bonus);
0397                 m_blocks[nIndex]->setBonus(bonus);
0398             }
0399         }
0400     }
0401 }
0402 
0403 void Game::removeBonus(Bonus* bonus)
0404 {
0405     m_bonus.removeAt(m_bonus.indexOf(bonus));
0406     //do not delete the Bonus, because the ElementItem will delete it
0407     if(m_soundEnabled && !bonus->isDestroyed())
0408     {
0409         m_soundBonus->start();
0410     }
0411 }
0412 
0413 void Game::setSoundsEnabled(bool p_enabled)
0414 {
0415     m_soundEnabled = p_enabled;
0416     Settings::setSounds(p_enabled);
0417     Settings::self()->save();
0418 }
0419 
0420 void Game::initCharactersPosition()
0421 {
0422     // If the timer is stopped, it means that collisions are already being handled
0423     if (m_timer->isActive())
0424     {
0425         // At the beginning, the timer is stopped but the Game isn't paused (to allow keyPressedEvent detection)
0426         m_timer->stop();
0427         m_roundTimer->stop();
0428         m_state = RUNNING;
0429         // Initialize the Player coordinates
0430         for(auto & player : m_players)
0431         {
0432             player->initCoordinate();
0433             player->init();
0434         }
0435     }
0436 }
0437 
0438 void Game::keyPressEvent(QKeyEvent* p_event)
0439 {
0440     if(p_event->isAutoRepeat())
0441     {
0442         return;
0443     }
0444 
0445     // At the beginning or when paused, we start the timer when a key is pressed
0446     if (!m_timer->isActive())
0447     {
0448         if(p_event->key() == Qt::Key_Space)
0449         {
0450             // If paused
0451             if (m_state == PAUSED_UNLOCKED)
0452             {
0453                 switchPause();
0454             }
0455             else if (m_state == RUNNING)      // At the game beginning
0456             {
0457                 // Start the game
0458                 m_timer->start();
0459                 m_roundTimer->start();
0460                 Q_EMIT gameStarted();
0461             }
0462             else if (m_state == PAUSED_LOCKED)
0463             {
0464                 // if the game is over, start a new game
0465                 if (m_gameOver)
0466                 {
0467                     Q_EMIT gameOver();
0468                     return;
0469                 }
0470                 else
0471                 {
0472                     m_gameScene->cleanUp();
0473                     cleanUp();
0474                     init();
0475                     m_gameScene->init();
0476                     for(auto & player : m_players)
0477                     {
0478                         player->resurrect();
0479                     }
0480                 }
0481             }
0482         }
0483         return;
0484     }
0485     // Behaviour when the game has begun
0486     switch (p_event->key())
0487     {
0488         case Qt::Key_P:
0489         case Qt::Key_Escape:
0490             switchPause();
0491             return;
0492         default:
0493             break;
0494     }
0495 
0496     //TODO: make signal
0497     for(auto & player : m_players)
0498     {
0499         player->keyPressed(p_event);
0500     }
0501 }
0502 
0503 void Game::keyReleaseEvent(QKeyEvent* p_event)
0504 {
0505     if(p_event->isAutoRepeat())
0506     {
0507         return;
0508     }
0509     //TODO: make signal
0510     for(auto & player : m_players)
0511     {
0512         player->keyReleased(p_event);
0513     }
0514 }
0515 
0516 void Game::update()
0517 {
0518     //update Bombs
0519     for (auto & bomb : m_bombs)
0520     {
0521         bomb->updateMove();
0522     }
0523 
0524     //update Player
0525     for(auto & player : m_players)
0526     {
0527         player->updateMove();
0528         player->emitGameUpdated();
0529     }
0530 }
0531 
0532 void Game::decrementRemainingRoundTime()
0533 {
0534     m_remainingTime--;
0535     if(m_remainingTime >= 0)
0536     {
0537         Q_EMIT infoChanged(Granatier::Info::TimeInfo);
0538     }
0539     else
0540     {
0541         if(m_remainingTime % 2 == 0)
0542         {
0543             //create bombs at randoms places
0544             int nRow;
0545             int nCol;
0546             Granatier::Cell::Type cellType;
0547             bool bFound = false;
0548             do
0549             {
0550                 nRow = granatier::RNG::fromRange(0, m_arena->getNbRows()-1);
0551                 nCol = granatier::RNG::fromRange(0, m_arena->getNbColumns()-1);
0552                 cellType = m_arena->getCell(nRow, nCol).getType();
0553                 if(cellType != Granatier::Cell::WALL && cellType != Granatier::Cell::HOLE && m_arena->getCell(nRow, nCol).isWalkable(nullptr))
0554                 {
0555                     bFound = true;
0556                 }
0557             }
0558             while (!bFound);
0559 
0560             m_bombCount++;
0561             Bomb* bomb = new Bomb((nCol + 0.5) * Granatier::CellSize, (nRow + 0.5) * Granatier::CellSize, m_arena, m_bombCount, 1000);    // time in ms
0562             bomb->setBombPower(static_cast<int>(granatier::RNG::fromRange(1.0, 2.5)));
0563             Q_EMIT bombCreated(bomb);
0564             connect(bomb, &Bomb::bombDetonated, this, &Game::bombDetonated);
0565             m_bombs.append(bomb);
0566             if(m_remainingTime > -100 && m_roundTimer->interval() > 150)
0567             {
0568                 m_roundTimer->setInterval(m_roundTimer->interval() + m_remainingTime);
0569             }
0570             else if (m_roundTimer->interval() > 30)
0571             {
0572                 m_roundTimer->setInterval(m_roundTimer->interval() - 1);
0573             }
0574         }
0575     }
0576 }
0577 
0578 void Game::playerFalling()
0579 {
0580     if(m_soundEnabled)
0581     {
0582         m_soundFalling->start();
0583     }
0584 }
0585 
0586 void Game::playerDeath()
0587 {
0588     if(m_soundEnabled)
0589     {
0590         m_soundDie->start();
0591     }
0592 
0593     //check if at most one player is alive if not already finished
0594     if(!m_setRoundFinishedTimer->isActive())
0595     {
0596         int nPlayerAlive = 0;
0597         for(auto & player : m_players)
0598         {
0599             if(player->isAlive())
0600             {
0601                 nPlayerAlive++;
0602             }
0603         }
0604         if(nPlayerAlive <= 1)
0605         {
0606             //wait some time until the game stops
0607             m_setRoundFinishedTimer->start(1500);
0608         }
0609    }
0610 }
0611 
0612 void Game::resurrectBonusTaken()
0613 {
0614     for(auto & player : m_players)
0615     {
0616         if(!(player->isAlive()))
0617         {
0618             player->resurrect();
0619         }
0620     }
0621 }
0622 
0623 void Game::setRoundFinished()
0624 {
0625     int nPlayerAlive = 0;
0626     int nIndex = 0;;
0627     if(m_gameOver)
0628     {
0629         return;
0630     }
0631     for(int i = 0; i < m_players.length(); ++i)
0632     {
0633         if(m_players[i]->isAlive())
0634         {
0635             nPlayerAlive++;
0636             nIndex = i;
0637         }
0638     }
0639     //this check is needed, if in the meantime the resurrect bonus was taken
0640     if (nPlayerAlive > 1)
0641     {
0642         return;
0643     }
0644 
0645     if (nPlayerAlive == 1)
0646     {
0647         m_players[nIndex]->addPoint();
0648     }
0649 
0650     pause(true);
0651 
0652     for(auto & player : m_players)
0653     {
0654         // check if a player reaches the win points
0655         if (player->points() >= m_winPoints)
0656         {
0657             m_gameOver = true;
0658             m_strWinner = player->getPlayerName();
0659             break;
0660         }
0661     }
0662     m_gameScene->showScore();
0663 }
0664 
0665 void Game::createBomb(Player* player, qreal x, qreal y, bool newBomb, int throwDistance)
0666 {
0667     int col = m_arena->getColFromX(x);
0668     int row = m_arena->getRowFromY(y);
0669     if(col >= 0 && col < m_arena->getNbColumns() && row >= 0 && row < m_arena->getNbRows())
0670     {
0671         QList<Element*> bombElements =  m_arena->getCell(row, col).getElements(Granatier::Element::BOMB);
0672         if (!bombElements.isEmpty())
0673         {
0674             if(player->hasThrowBomb() && throwDistance > 0)
0675             {
0676                 for(auto& element: bombElements)
0677                 {
0678                     dynamic_cast <Bomb*> (element)->setThrown(player->direction());
0679                 }
0680             }
0681             return;
0682         }
0683     }
0684 
0685     if(!newBomb)
0686     {
0687         return;
0688     }
0689 
0690     m_bombCount++;
0691     Bomb* bomb = new Bomb((col + 0.5) * Granatier::CellSize, (row + 0.5) * Granatier::CellSize, m_arena, m_bombCount, 2500);    // time in ms
0692     bomb->setBombPower(player->getBombPower());
0693     Q_EMIT bombCreated(bomb);
0694     connect(bomb, &Bomb::bombDetonated, this, &Game::bombDetonated);
0695     connect(bomb, &Bomb::releaseBombArmory, player, &Player::slot_refillBombArmory);
0696     m_bombs.append(bomb);
0697     player->decrementBombArmory();
0698 
0699     if(m_soundEnabled)
0700     {
0701         m_soundPutBomb->start();
0702     }
0703 }
0704 
0705 void Game::removeBomb(Bomb* bomb)
0706 {
0707     // Find the Bomb
0708     int index = m_bombs.indexOf(bomb);
0709     //remove the bomb
0710     if(index != -1)
0711     {
0712         //do not delete the bomb because it will be deleted through the destructor of elementitem
0713         m_bombs.removeAt(index);
0714     }
0715 }
0716 
0717 void Game::bombDetonated()
0718 {
0719     if(m_soundEnabled)
0720     {
0721         m_soundExplode->start();
0722     }
0723 }
0724 
0725 void Game::blockDestroyed(const int row, const int col, Block* block)
0726 {
0727     // find the Block
0728     int index = m_blocks.indexOf(block);
0729     // remove the Block
0730     if(index != -1)
0731     {
0732         //do not delete the block because it will be deleted through the destructor of elementitem
0733         m_arena->removeCellElement(row, col, block);
0734     }
0735 }
0736 
0737 #include "moc_game.cpp"