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

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 <KGameSound>
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 KGameSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/putbomb.wav")));
0040     m_soundExplode = new KGameSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/explode.wav")));
0041     m_soundBonus = new KGameSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/wow.wav")));
0042     m_soundFalling = new KGameSound(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("sounds/deepfall.wav")));
0043     m_soundDie = new KGameSound(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({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(QStringLiteral("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     if (!mapParser.parse(&arenaXmlFile)) {
0141         qWarning() << " failed to parse file " << arenaFileName;
0142     }
0143 
0144     QString arenaName = group.readEntry("Name");
0145     m_arena->setName(arenaName);
0146 
0147     //create the block items
0148     for (int i = 0; i < m_arena->getNbRows(); ++i)
0149     {
0150         for (int j = 0; j < m_arena->getNbColumns(); ++j)
0151         {
0152             if(m_arena->getCell(i,j).getType() == Granatier::Cell::BLOCK)
0153             {
0154                 auto* block = new Block((j + 0.5) * Granatier::CellSize, (i + 0.5) * Granatier::CellSize, m_arena, QStringLiteral("arena_block"));
0155                 m_blocks.append(block);
0156                 m_arena->setCellElement(i, j, block);
0157             }
0158         }
0159     }
0160 
0161     // Start the Game timer
0162     m_timer = new QTimer(this);
0163     m_timer->setInterval(int(1000 / Granatier::FPS));
0164     connect(m_timer, &QTimer::timeout, this, &Game::update);
0165     m_timer->start();
0166     m_state = RUNNING;
0167 
0168     m_roundTimer  = new QTimer(this);
0169     m_roundTimer->setInterval(1000);
0170     connect(m_roundTimer, &QTimer::timeout, this, &Game::decrementRemainingRoundTime);
0171     m_roundTimer->start();
0172 
0173     m_setRoundFinishedTimer  = new QTimer(this);
0174     m_setRoundFinishedTimer->setSingleShot(true);
0175     connect(m_setRoundFinishedTimer, &QTimer::timeout, this, &Game::setRoundFinished);
0176 
0177     // Init the characters coordinates on the Arena
0178     for (int i = 0; i < m_players.size(); i++)
0179     {
0180         m_players[i]->setArena(m_arena);
0181         QPointF playerPosition = m_arena->getPlayerPosition(i);
0182         m_players[i]->setInitialCoordinates(qreal(Granatier::CellSize * playerPosition.x()), qreal(Granatier::CellSize * playerPosition.y()));
0183     }
0184     initCharactersPosition();
0185 
0186     // Create the hidden Bonuses
0187     createBonus();
0188 }
0189 
0190 Game::~Game()
0191 {
0192     //pause is needed to stop all animations and therefore the access of the *items to their model
0193     pause(true);
0194 
0195     qDeleteAll(m_players);
0196     m_players.clear();
0197 
0198     cleanUp();
0199 
0200     delete m_soundPutBomb;
0201     delete m_soundExplode;
0202     delete m_soundBonus;
0203     delete m_soundFalling;
0204     delete m_soundDie;
0205 }
0206 
0207 void Game::cleanUp()
0208 {
0209     qDeleteAll(m_blocks);
0210     m_blocks.clear();
0211     qDeleteAll(m_bonus);
0212     m_bonus.clear();
0213     qDeleteAll(m_bombs);
0214     m_bombs.clear();
0215     delete m_arena;
0216     m_arena = nullptr;
0217     delete m_timer;
0218     m_timer = nullptr;
0219     delete m_roundTimer;
0220     m_roundTimer = nullptr;
0221     delete m_setRoundFinishedTimer;
0222     m_setRoundFinishedTimer = nullptr;
0223 }
0224 
0225 void Game::setGameScene(GameScene* p_gameScene)
0226 {
0227     m_gameScene = p_gameScene;
0228 }
0229 
0230 void Game::start()
0231 {
0232     // Restart the Game timer
0233     m_timer->start();
0234     m_state = RUNNING;
0235     m_roundTimer->start();
0236     Q_EMIT pauseChanged(false, false);
0237 }
0238 
0239 void Game::pause(bool p_locked)
0240 {
0241     // Stop the Game timer
0242     m_timer->stop();
0243     m_roundTimer->stop();
0244     if (p_locked)
0245     {
0246         m_state = PAUSED_LOCKED;
0247     }
0248     else
0249     {
0250         m_state = PAUSED_UNLOCKED;
0251     }
0252     Q_EMIT pauseChanged(true, false);
0253 }
0254 
0255 void Game::switchPause()
0256 {
0257     // If the Game is not already paused
0258     if (m_state == RUNNING)
0259     {
0260         // Pause the Game
0261         pause();
0262         Q_EMIT pauseChanged(true, true);
0263     }
0264     // If the Game is already paused
0265     else
0266     {
0267         // Resume the Game
0268         start();
0269         Q_EMIT pauseChanged(false, true);
0270     }
0271 }
0272 
0273 QList<Player*> Game::getPlayers() const
0274 {
0275     return m_players;
0276 }
0277 
0278 QTimer* Game::getTimer() const
0279 {
0280     return m_timer;
0281 }
0282 
0283 int Game::getRemainingTime() const
0284 {
0285     return m_remainingTime;
0286 }
0287 
0288 Arena* Game::getArena() const
0289 {
0290     return m_arena;
0291 }
0292 
0293 bool Game::isPaused() const
0294 {
0295     return (m_state != RUNNING);
0296 }
0297 
0298 bool Game::getGameOver() const
0299 {
0300     return m_gameOver;
0301 }
0302 
0303 QString Game::getWinner() const
0304 {
0305     return m_strWinner;
0306 }
0307 
0308 int Game::getWinPoints() const
0309 {
0310     return m_winPoints;
0311 }
0312 
0313 QList<Bonus*> Game::getBonus() const
0314 {
0315     return m_bonus;
0316 }
0317 
0318 void Game::createBonus()
0319 {
0320     int nRemainingNeutralBonuses = 1;
0321     for(int nQuarter = 0; nQuarter < 4; nQuarter++)
0322     {
0323         Bonus* bonus;
0324         int nBonusCount = static_cast<int>(0.3 * m_blocks.size() / 4);
0325         int nBadBonusCount = static_cast<int>(0.1 * m_blocks.size() / 4);
0326         int nNeutralBonusCount = granatier::RNG::fromRange(0, nRemainingNeutralBonuses);
0327         QList<Granatier::Bonus::Type> bonusTypeList;
0328         Granatier::Bonus::Type bonusType;
0329         int nFullSize = m_blocks.size();
0330         int nQuarterSize = (nQuarter < 3 ? nFullSize / 4 : nFullSize - 3 * (nFullSize / 4));
0331 
0332         for (int i = 0; i < nQuarterSize; ++i)
0333         {
0334             if(i < nBonusCount)
0335             {
0336                 constexpr int NumberOfBonuses = 6;
0337                 switch (granatier::RNG::fromRange(0, NumberOfBonuses-1))
0338                 {
0339                     case 0: bonusType = Granatier::Bonus::SPEED;
0340                             break;
0341                     case 1: bonusType = Granatier::Bonus::BOMB;
0342                             break;
0343                     case 2: bonusType = Granatier::Bonus::POWER;
0344                             break;
0345                     case 3: bonusType = Granatier::Bonus::SHIELD;
0346                             break;
0347                     case 4: bonusType = Granatier::Bonus::THROW;
0348                             break;
0349                     case 5: bonusType = Granatier::Bonus::KICK;
0350                             break;
0351                     default: bonusType = Granatier::Bonus::SPEED;
0352                 }
0353             }
0354             else if (i-nBonusCount < nBadBonusCount)
0355             {
0356                 constexpr int NumberOfBadBonuses = 5;
0357                 switch (granatier::RNG::fromRange(0, NumberOfBadBonuses-1))
0358                 {
0359                     case 0: bonusType = Granatier::Bonus::HYPERACTIVE;
0360                             break;
0361                     case 1: bonusType = Granatier::Bonus::SLOW;
0362                             break;
0363                     case 2: bonusType = Granatier::Bonus::MIRROR;
0364                             break;
0365                     case 3: bonusType = Granatier::Bonus::SCATTY;
0366                             break;
0367                     case 4: bonusType = Granatier::Bonus::RESTRAIN;
0368                             break;
0369                     default: bonusType = Granatier::Bonus::HYPERACTIVE;
0370                 }
0371             }
0372             else if(i-nBonusCount-nBadBonusCount < nNeutralBonusCount)
0373             {
0374                 bonusType = Granatier::Bonus::RESURRECT;
0375                 nRemainingNeutralBonuses--;
0376             }
0377             else {
0378                 bonusType = Granatier::Bonus::NONE;
0379             }
0380 
0381             bonusTypeList.append(bonusType);
0382         }
0383 
0384         int nShuffle;
0385         for (int i = 0; i < nQuarterSize; ++i)
0386         {
0387             nShuffle = granatier::RNG::fromRange(0, nQuarterSize-1);
0388 
0389             bonusTypeList.swapItemsAt(i, nShuffle);
0390         }
0391 
0392         for (int i = 0; i < nQuarterSize; ++i)
0393         {
0394             if(bonusTypeList[i] != Granatier::Bonus::NONE)
0395             {
0396                 int nIndex = nQuarter * (nFullSize/4) + i;
0397                 bonus = new Bonus(m_blocks[nIndex]->getX(), m_blocks[nIndex]->getY(), m_arena, bonusTypeList[i]);
0398                 m_bonus.append(bonus);
0399                 m_blocks[nIndex]->setBonus(bonus);
0400             }
0401         }
0402     }
0403 }
0404 
0405 void Game::removeBonus(Bonus* bonus)
0406 {
0407     m_bonus.removeAt(m_bonus.indexOf(bonus));
0408     //do not delete the Bonus, because the ElementItem will delete it
0409     if(m_soundEnabled && !bonus->isDestroyed())
0410     {
0411         m_soundBonus->start();
0412     }
0413 }
0414 
0415 void Game::setSoundsEnabled(bool p_enabled)
0416 {
0417     m_soundEnabled = p_enabled;
0418     Settings::setSounds(p_enabled);
0419     Settings::self()->save();
0420 }
0421 
0422 void Game::initCharactersPosition()
0423 {
0424     // If the timer is stopped, it means that collisions are already being handled
0425     if (m_timer->isActive())
0426     {
0427         // At the beginning, the timer is stopped but the Game isn't paused (to allow keyPressedEvent detection)
0428         m_timer->stop();
0429         m_roundTimer->stop();
0430         m_state = RUNNING;
0431         // Initialize the Player coordinates
0432         for(auto & player : m_players)
0433         {
0434             player->initCoordinate();
0435             player->init();
0436         }
0437     }
0438 }
0439 
0440 void Game::keyPressEvent(QKeyEvent* p_event)
0441 {
0442     if(p_event->isAutoRepeat())
0443     {
0444         return;
0445     }
0446 
0447     // At the beginning or when paused, we start the timer when a key is pressed
0448     if (!m_timer->isActive())
0449     {
0450         if(p_event->key() == Qt::Key_Space)
0451         {
0452             // If paused
0453             if (m_state == PAUSED_UNLOCKED)
0454             {
0455                 switchPause();
0456             }
0457             else if (m_state == RUNNING)      // At the game beginning
0458             {
0459                 // Start the game
0460                 m_timer->start();
0461                 m_roundTimer->start();
0462                 Q_EMIT gameStarted();
0463             }
0464             else if (m_state == PAUSED_LOCKED)
0465             {
0466                 // if the game is over, start a new game
0467                 if (m_gameOver)
0468                 {
0469                     Q_EMIT gameOver();
0470                     return;
0471                 }
0472                 else
0473                 {
0474                     m_gameScene->cleanUp();
0475                     cleanUp();
0476                     init();
0477                     m_gameScene->init();
0478                     for(auto & player : m_players)
0479                     {
0480                         player->resurrect();
0481                     }
0482                 }
0483             }
0484         }
0485         return;
0486     }
0487     // Behaviour when the game has begun
0488     switch (p_event->key())
0489     {
0490         case Qt::Key_P:
0491         case Qt::Key_Escape:
0492             switchPause();
0493             return;
0494         default:
0495             break;
0496     }
0497 
0498     //TODO: make signal
0499     for(auto & player : m_players)
0500     {
0501         player->keyPressed(p_event);
0502     }
0503 }
0504 
0505 void Game::keyReleaseEvent(QKeyEvent* p_event)
0506 {
0507     if(p_event->isAutoRepeat())
0508     {
0509         return;
0510     }
0511     //TODO: make signal
0512     for(auto & player : m_players)
0513     {
0514         player->keyReleased(p_event);
0515     }
0516 }
0517 
0518 void Game::update()
0519 {
0520     //update Bombs
0521     for (auto & bomb : m_bombs)
0522     {
0523         bomb->updateMove();
0524     }
0525 
0526     //update Player
0527     for(auto & player : m_players)
0528     {
0529         player->updateMove();
0530         player->emitGameUpdated();
0531     }
0532 }
0533 
0534 void Game::decrementRemainingRoundTime()
0535 {
0536     m_remainingTime--;
0537     if(m_remainingTime >= 0)
0538     {
0539         Q_EMIT infoChanged(Granatier::Info::TimeInfo);
0540     }
0541     else
0542     {
0543         if(m_remainingTime % 2 == 0)
0544         {
0545             //create bombs at randoms places
0546             int nRow;
0547             int nCol;
0548             Granatier::Cell::Type cellType;
0549             bool bFound = false;
0550             do
0551             {
0552                 nRow = granatier::RNG::fromRange(0, m_arena->getNbRows()-1);
0553                 nCol = granatier::RNG::fromRange(0, m_arena->getNbColumns()-1);
0554                 cellType = m_arena->getCell(nRow, nCol).getType();
0555                 if(cellType != Granatier::Cell::WALL && cellType != Granatier::Cell::HOLE && m_arena->getCell(nRow, nCol).isWalkable(nullptr))
0556                 {
0557                     bFound = true;
0558                 }
0559             }
0560             while (!bFound);
0561 
0562             m_bombCount++;
0563             Bomb* bomb = new Bomb((nCol + 0.5) * Granatier::CellSize, (nRow + 0.5) * Granatier::CellSize, m_arena, m_bombCount, 1000);    // time in ms
0564             bomb->setBombPower(static_cast<int>(granatier::RNG::fromRange(1.0, 2.5)));
0565             Q_EMIT bombCreated(bomb);
0566             connect(bomb, &Bomb::bombDetonated, this, &Game::bombDetonated);
0567             m_bombs.append(bomb);
0568             if(m_remainingTime > -100 && m_roundTimer->interval() > 150)
0569             {
0570                 m_roundTimer->setInterval(m_roundTimer->interval() + m_remainingTime);
0571             }
0572             else if (m_roundTimer->interval() > 30)
0573             {
0574                 m_roundTimer->setInterval(m_roundTimer->interval() - 1);
0575             }
0576         }
0577     }
0578 }
0579 
0580 void Game::playerFalling()
0581 {
0582     if(m_soundEnabled)
0583     {
0584         m_soundFalling->start();
0585     }
0586 }
0587 
0588 void Game::playerDeath()
0589 {
0590     if(m_soundEnabled)
0591     {
0592         m_soundDie->start();
0593     }
0594 
0595     //check if at most one player is alive if not already finished
0596     if(!m_setRoundFinishedTimer->isActive())
0597     {
0598         int nPlayerAlive = 0;
0599         for(auto & player : m_players)
0600         {
0601             if(player->isAlive())
0602             {
0603                 nPlayerAlive++;
0604             }
0605         }
0606         if(nPlayerAlive <= 1)
0607         {
0608             //wait some time until the game stops
0609             m_setRoundFinishedTimer->start(1500);
0610         }
0611    }
0612 }
0613 
0614 void Game::resurrectBonusTaken()
0615 {
0616     for(auto & player : m_players)
0617     {
0618         if(!(player->isAlive()))
0619         {
0620             player->resurrect();
0621         }
0622     }
0623 }
0624 
0625 void Game::setRoundFinished()
0626 {
0627     int nPlayerAlive = 0;
0628     int nIndex = 0;;
0629     if(m_gameOver)
0630     {
0631         return;
0632     }
0633     for(int i = 0; i < m_players.length(); ++i)
0634     {
0635         if(m_players[i]->isAlive())
0636         {
0637             nPlayerAlive++;
0638             nIndex = i;
0639         }
0640     }
0641     //this check is needed, if in the meantime the resurrect bonus was taken
0642     if (nPlayerAlive > 1)
0643     {
0644         return;
0645     }
0646 
0647     if (nPlayerAlive == 1)
0648     {
0649         m_players[nIndex]->addPoint();
0650     }
0651 
0652     pause(true);
0653 
0654     for(auto & player : m_players)
0655     {
0656         // check if a player reaches the win points
0657         if (player->points() >= m_winPoints)
0658         {
0659             m_gameOver = true;
0660             m_strWinner = player->getPlayerName();
0661             break;
0662         }
0663     }
0664     m_gameScene->showScore();
0665 }
0666 
0667 void Game::createBomb(Player* player, qreal x, qreal y, bool newBomb, int throwDistance)
0668 {
0669     int col = m_arena->getColFromX(x);
0670     int row = m_arena->getRowFromY(y);
0671     if(col >= 0 && col < m_arena->getNbColumns() && row >= 0 && row < m_arena->getNbRows())
0672     {
0673         QList<Element*> bombElements =  m_arena->getCell(row, col).getElements(Granatier::Element::BOMB);
0674         if (!bombElements.isEmpty())
0675         {
0676             if(player->hasThrowBomb() && throwDistance > 0)
0677             {
0678                 for(auto& element: bombElements)
0679                 {
0680                     dynamic_cast <Bomb*> (element)->setThrown(player->direction());
0681                 }
0682             }
0683             return;
0684         }
0685     }
0686 
0687     if(!newBomb)
0688     {
0689         return;
0690     }
0691 
0692     m_bombCount++;
0693     Bomb* bomb = new Bomb((col + 0.5) * Granatier::CellSize, (row + 0.5) * Granatier::CellSize, m_arena, m_bombCount, 2500);    // time in ms
0694     bomb->setBombPower(player->getBombPower());
0695     Q_EMIT bombCreated(bomb);
0696     connect(bomb, &Bomb::bombDetonated, this, &Game::bombDetonated);
0697     connect(bomb, &Bomb::releaseBombArmory, player, &Player::slot_refillBombArmory);
0698     m_bombs.append(bomb);
0699     player->decrementBombArmory();
0700 
0701     if(m_soundEnabled)
0702     {
0703         m_soundPutBomb->start();
0704     }
0705 }
0706 
0707 void Game::removeBomb(Bomb* bomb)
0708 {
0709     // Find the Bomb
0710     int index = m_bombs.indexOf(bomb);
0711     //remove the bomb
0712     if(index != -1)
0713     {
0714         //do not delete the bomb because it will be deleted through the destructor of elementitem
0715         m_bombs.removeAt(index);
0716     }
0717 }
0718 
0719 void Game::bombDetonated()
0720 {
0721     if(m_soundEnabled)
0722     {
0723         m_soundExplode->start();
0724     }
0725 }
0726 
0727 void Game::blockDestroyed(const int row, const int col, Block* block)
0728 {
0729     // find the Block
0730     int index = m_blocks.indexOf(block);
0731     // remove the Block
0732     if(index != -1)
0733     {
0734         //do not delete the block because it will be deleted through the destructor of elementitem
0735         m_arena->removeCellElement(row, col, block);
0736     }
0737 }
0738 
0739 #include "moc_game.cpp"