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

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 John-Paul Stanford <jp@stanwood.org.uk>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 // own
0008 #include "board.h"
0009 
0010 // Qt
0011 #include <QGraphicsView>
0012 #include <QStandardPaths>
0013 #include <QTimer>
0014 #include <QRandomGenerator>
0015 
0016 
0017 // Bomber
0018 #include "bomb.h"
0019 #include "building.h"
0020 #include "plane.h"
0021 #include "settings.h"
0022 
0023 
0024 /** The value that the plane velocity increases by */
0025 const qreal PLANE_INC_VELOCITY = 0.0005;
0026 /** The value of this controls the speed of the game */
0027 const unsigned int GAME_DELAY = 15;
0028 /** The number of tiles vertical in the playing area */
0029 const unsigned int TILE_NUM_H = 20;
0030 /** The number of builds to display */
0031 const unsigned int NUMBER_BUILDINGS = 10;
0032 /** The number of tiles horizontally in the playing area */
0033 const unsigned int TILE_NUM_W = ((NUMBER_BUILDINGS) + 2);
0034 /** The maximum level number before the game stops getting harder */
0035 const unsigned int MAX_LEVEL = 11;
0036 
0037 /** This time in milliseconds that the plane exploding animation is played for */
0038 const unsigned int PLANE_EXPLODE_TIME = 2000;
0039 
0040 /** This time in milliseconds that the bomb exploding animation is played for */
0041 const unsigned int BOMB_EXPLODE_TIME = 1000;
0042 
0043 BomberBoard::BomberBoard(KGameRenderer * renderer, QGraphicsView * view, QObject * parent)
0044     : QGraphicsScene(parent)
0045     , m_renderer(renderer)
0046     , m_bomb(nullptr)
0047     , m_view(view)
0048 {
0049     m_clock = new QTimer(this);
0050     m_clock->setInterval(GAME_DELAY);
0051     connect(m_clock, &QTimer::timeout, this, &BomberBoard::tick);
0052     m_plane = new Plane(m_renderer, this);
0053     m_plane->resize(m_tileSize);
0054     this->addItem(m_plane);
0055     m_plane->show();
0056     resetPlane();
0057     clear();
0058 }
0059 
0060 BomberBoard::~BomberBoard()
0061 {
0062     delete m_bomb;
0063     delete m_plane;
0064     qDeleteAll(m_buildings);
0065     qDeleteAll(m_explodingBombs);
0066 }
0067 
0068 void BomberBoard::resetPlane()
0069 {
0070     m_plane->setState(Explodable::State::Moving);
0071     m_plane->resetPosition();
0072 }
0073 
0074 void BomberBoard::resize(QSize & size)
0075 {
0076     setBackgroundBrush(m_renderer->spritePixmap(QStringLiteral("background"), size));
0077 
0078     unsigned int minTileSizeWidth = size.width() / TILE_NUM_W;
0079     unsigned int minTileSizeHeight = size.height() / TILE_NUM_H;
0080 
0081     m_tileSize = QSize(minTileSizeWidth, minTileSizeHeight);
0082 
0083     for (Building * building : std::as_const(m_buildings)) {
0084         building->resize(m_tileSize);
0085     }
0086 
0087     m_plane->resize(m_tileSize);
0088     if (m_bomb != nullptr) {
0089         m_bomb->resize(m_tileSize);
0090     }
0091 
0092     redraw();
0093 
0094     size.setWidth(minTileSizeWidth * TILE_NUM_W);
0095     size.setHeight(minTileSizeHeight * TILE_NUM_H);
0096 }
0097 
0098 void BomberBoard::redraw()
0099 {
0100     m_plane->resetPixmaps();
0101     if (m_bomb != nullptr) {
0102         m_bomb->resetPixmaps();
0103     }
0104 }
0105 
0106 void BomberBoard::newLevel(unsigned int level)
0107 {
0108     if (level > MAX_LEVEL) {
0109         level = MAX_LEVEL;
0110     }
0111 
0112     if (level == 1) {
0113         m_plane->setVelocity(Plane::DEFAULT_VELOCITY);
0114     } else if (level % 2 == 0) {
0115         m_plane->setVelocity(m_plane->velocity() + PLANE_INC_VELOCITY);
0116     }
0117 
0118     m_clock->stop();
0119     clear();
0120     m_plane->setState(Explodable::State::Moving);
0121     m_buildingBlocks = 0;
0122     //Create the buildings
0123     for (unsigned int i = 0; i < NUMBER_BUILDINGS; ++i) {
0124         unsigned int min = level;
0125         if (min < 3) {
0126             min = 3;
0127         }
0128         unsigned int max = level + 3;
0129         if (max < 5) {
0130             max = 5;
0131         }
0132         unsigned int height = QRandomGenerator::global()->bounded(max - min) + min;
0133 
0134         m_buildingBlocks += height;
0135         auto building = new Building(m_renderer, this, i + 1, height);
0136 
0137         building->resize(m_tileSize);
0138         building->show();
0139 
0140         m_buildings.append(building);
0141     }
0142 }
0143 
0144 void BomberBoard::setPaused(bool val)
0145 {
0146     if (val) {
0147         m_clock->stop();
0148     } else {
0149         m_clock->start();
0150     }
0151 }
0152 
0153 void BomberBoard::tick()
0154 {
0155     checkCollisions();
0156 
0157     m_plane->advanceItem();
0158 
0159     if (m_bomb != nullptr) {
0160         m_bomb->advanceItem();
0161     }
0162 
0163     for (Bomb * bomb : std::as_const(m_explodingBombs)) {
0164         bomb->advanceItem();
0165     }
0166 
0167     // Draw everything
0168     m_plane->update();
0169 
0170     if (m_bomb != nullptr) {
0171         m_bomb->update();
0172     }
0173 
0174     for (Bomb * bomb : std::as_const(m_explodingBombs)) {
0175         bomb->update();
0176     }
0177 }
0178 
0179 void BomberBoard::dropBomb()
0180 {
0181     if (m_bomb == nullptr && m_plane->state() == Explodable::State::Moving) {
0182         QPointF planePos = m_plane->position();
0183         m_bomb = new Bomb(m_renderer, this, planePos.x(), planePos.y() + 1, m_tileSize);
0184         this->addItem(m_bomb);
0185         m_bomb->show();
0186     }
0187 }
0188 
0189 void BomberBoard::checkCollisions()
0190 {
0191     const auto currentBuildings = m_buildings;
0192     for (Building * building : currentBuildings) {
0193         if (m_plane->nextBoundingRect().intersects(building->boundingRect()) && m_plane->state() == Explodable::State::Moving) {
0194             // Plane crashed into the building
0195             building->destoryTop();
0196             --m_buildingBlocks;
0197             crashed();
0198         }
0199 
0200         if (m_bomb != nullptr) {
0201             if (m_bomb->nextBoundingRect().intersects(building->boundingRect()) && m_bomb->state() == Explodable::State::Moving) {
0202                 // Bomb hit a building
0203                 building->destoryTop();
0204                 --m_buildingBlocks;
0205                 Q_EMIT onBombHit();
0206                 bombHit(m_bomb, building->position().x(), Building::BUILD_BASE_LOCATION - (building->height()));
0207                 m_bomb = nullptr;
0208             } else if (m_bomb->position().y() >= Building::BUILD_BASE_LOCATION + 1) {
0209                 // Bomb hit the ground
0210                 bombHit(m_bomb, (unsigned int)m_bomb->position().x(), Building::BUILD_BASE_LOCATION);
0211                 m_bomb = nullptr;
0212             }
0213         }
0214 
0215         if (m_plane->state() == Explodable::State::Moving && m_buildingBlocks == 0) {
0216             Q_EMIT levelCleared();
0217         }
0218     }
0219 }
0220 
0221 void BomberBoard::bombHit(Bomb * bomb, qreal moveBombToX, qreal moveBombToY)
0222 {
0223     bomb->setPosition(moveBombToX, moveBombToY);
0224     bomb->setState(Bomb::State::Exploding);
0225     m_explodingBombs.enqueue(bomb);
0226     QTimer::singleShot(BOMB_EXPLODE_TIME, this, &BomberBoard::bombExploded);
0227     Q_EMIT playBombSound();
0228 }
0229 
0230 void BomberBoard::bombExploded()
0231 {
0232     Bomb * bomb = m_explodingBombs.dequeue();
0233     bomb->hide();
0234     delete bomb;
0235 }
0236 
0237 void BomberBoard::settingsChanged()
0238 {
0239     setBackgroundBrush(m_renderer->spritePixmap(QStringLiteral("background"), m_view->size()));
0240     redraw();
0241 }
0242 
0243 void BomberBoard::planeExploded()
0244 {
0245     m_plane->setState(Plane::State::Exploded);
0246     Q_EMIT onPlaneCrash();
0247 }
0248 
0249 void BomberBoard::crashed()
0250 {
0251     QPointF pos = m_plane->position();
0252     m_plane->setPosition(pos.x() + 1, pos.y());
0253     m_plane->setState(Plane::State::Exploding);
0254     QTimer::singleShot(PLANE_EXPLODE_TIME, this, &BomberBoard::planeExploded);
0255     Q_EMIT playCrashSound();
0256 }
0257 
0258 void BomberBoard::clear()
0259 {
0260     qDeleteAll(m_buildings);
0261     m_buildings.clear();
0262 
0263     delete m_bomb;
0264     m_bomb = nullptr;
0265 
0266     resetPlane();
0267 }
0268 
0269 QPoint BomberBoard::mapPosition(const QPointF & pos) const
0270 {
0271     return QPoint(static_cast<unsigned int>(m_tileSize.width() * pos.x()), static_cast<int>(m_tileSize.height() * pos.y()));
0272 }
0273 
0274 QPointF BomberBoard::unmapPosition(const QPoint & pos) const
0275 {
0276     return QPointF(1.0 * pos.x() / m_tileSize.width(), 1.0 * pos.y() / m_tileSize.height());
0277 }
0278 
0279 #include "moc_board.cpp"