#include #include #include #include #include #include "arkanoid.hpp" #include "bonus.hpp" const double pi = 3.14159265358979323846; void Arkanoid::addRandomBonus(sf::Vector2f position) { if (m_bonuses.size() > kMaxNumBonuses) return; int max_rand = 10000; if ((rand() % max_rand) * 1.0f / max_rand < m_bonusProbability) { int max = 4; int min = 1; int range = max - min + 1; int num = rand() % range + min; switch (num) { case 1: m_bonuses.push_back(new TripleBallBonus(position)); break; case 2: m_bonuses.push_back(new EnlargePaddleBonus(position)); break; case 3: m_bonuses.push_back(new ShrinkPaddleBonus(position)); break; case 4: m_bonuses.push_back(new SlowingBonus(position)); break; } } } // Функция, которая обрабатывает все столкновения шарика void Arkanoid::handleBallCollisions(Ball& ball) { ball.handleWallsCollision(m_border); ball.handlePaddleCollision(m_paddle); auto indexes = ball.handleBrickGridCollision(m_brickGrid); if (indexes.first == -1) return; m_brickGrid.deactivateBrick(indexes); addRandomBonus(ball.position); } Arkanoid::Arkanoid(sf::FloatRect border, sf::Font& font) : m_time{0.0}, m_border{border}, m_paddle{{m_border.left + m_border.width / 2, m_border.top + m_border.height - 100}, {120, 20}}, m_gameState{GameState::stuck}, m_numLives{7} { float gap = border.width / 10; m_brickGrid = BrickGrid({border.left + gap, border.top + gap, border.width - 2 * gap, border.height / 2}, 50, 30); m_bonusProbability = 0.1; m_endText.setFont(font); m_endText.setString("You Win!"); m_endText.setCharacterSize(100); m_endText.setFillColor(sf::Color::White); sf::FloatRect textRect = m_endText.getLocalBounds(); m_endText.setOrigin(textRect.left + textRect.width / 2.0f, textRect.top + textRect.height / 2.0f); m_endText.setPosition({border.left + border.width / 2, border.top + border.height / 2}); } sf::FloatRect Arkanoid::getBorder() const { return m_border; } const Paddle& Arkanoid::getPaddle() const { return m_paddle; } const BrickGrid& Arkanoid::getBrickGrid() const { return m_brickGrid; } void Arkanoid::addBall(const Ball& ball) { if (m_balls.size() < kMaxNumBalls) m_balls.push_back(ball); } bool Arkanoid::isMaxBalls() { return m_balls.size() == kMaxNumBalls - 1; } // Эта функция вызывается каждый кадр void Arkanoid::update(const sf::RenderWindow& window, float dt) { m_time += dt; // Устанавливаем положение ракетки sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window)); m_paddle.position.x = mousePosition.x; // Обрабатываем шарики for (std::list::iterator it = m_balls.begin(); it != m_balls.end();) { (*it).update(dt); handleBallCollisions(*it); if ((*it).position.y > m_border.top + m_border.height) { it = m_balls.erase(it); } else { it++; } } // Если шариков нет, то переходи в режим начала игры и уменьшаем кол-во жизней if (m_gameState == GameState::running && m_balls.size() == 0) { m_effects.clear(); m_gameState = GameState::stuck; m_numLives--; } // Если жизни кончились, то переходим в состояние конца игры (проигрыш) if (m_numLives < 0) { m_gameState = GameState::endLose; } // Если блоки кончились, то переходим в состояние конца игры (победа) if (m_brickGrid.getNumActiveBricks() == 0) { m_gameState = GameState::endWin; } // Обрабатываем бонусы for (auto it = m_bonuses.begin(); it != m_bonuses.end();) { (*it)->update(dt); if ((*it)->isColiding(m_paddle)) { (*it)->activate(*this); delete *it; it = m_bonuses.erase(it); } else if ((*it)->m_position.y > m_border.top + m_border.height) { delete (*it); it = m_bonuses.erase(it); } else { it++; } } /* Обработка эффектов */ for (auto it = m_effects.begin(); it != m_effects.end();) { if ((*it)->isExpired(m_time)) { (*it)->deactivate(*this); delete *it; it = m_effects.erase(it); } else { it++; } } } void Arkanoid::draw(sf::RenderWindow& window) { // Рисуем задний прямоугольник static sf::RectangleShape background {{m_border.width, m_border.height}}; background.setPosition({m_border.left, m_border.top}); background.setFillColor(kBackgroundColor); window.draw(background); // Рисуем блоки m_brickGrid.draw(window); // Рисуем шарики for (Ball& ball : m_balls) { ball.draw(window); } // Рисуем ракетку m_paddle.draw(window); // Если мы в режиме начала игры, то рисуем шарик на ракетке if (m_gameState == GameState::stuck) { m_initialBall.position = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2 - m_initialBall.radius}; m_initialBall.position = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2 - m_initialBall.radius}; m_initialBall.draw(window); } // Рисуем кол-во жизней вверху слева for (int i = 0; i < m_numLives; i++) { m_initialBall.position = {m_initialBall.radius * (3 * i + 2), 2 * m_initialBall.radius}; m_initialBall.draw(window); } // Рисуем бонусы for (Bonus* pbonus : m_bonuses) { pbonus->draw(window); } // При завершении игры рисуем надпись if (m_gameState == GameState::endWin) { m_endText.setString("You Win!"); window.draw(m_endText); } // При завершении игры рисуем надпись if (m_gameState == GameState::endLose) { m_endText.setString("You Lose!"); window.draw(m_endText); } } void Arkanoid::onMousePressed(sf::Event& event) { switch (m_gameState) { case GameState::stuck: if (event.mouseButton.button == sf::Mouse::Left) { m_gameState = GameState::running; float velocityAngle = (rand() % 100 + 40) * pi / 180; float velocityNorm = Ball::initialVelocity; sf::Vector2f newPosition = {m_paddle.position.x, m_paddle.position.y - m_paddle.size.y / 2.0f - m_initialBall.radius}; sf::Vector2f newVelocity = {-velocityNorm * cosf(velocityAngle), -velocityNorm * sinf(velocityAngle)}; addBall({m_initialBall.radius, newPosition, newVelocity}); } break; case GameState::running: break; case GameState::endLose: break; case GameState::endWin: break; } }