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"