diff --git a/CMakeLists.txt b/CMakeLists.txt index 8857b57..9348163 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,8 @@ add_executable(LotoStatek main.cpp sources/PowerUp.cpp headers/Debuff.h sources/Debuff.cpp + sources/Boss.cpp + headers/Boss.h ) if(WIN32) diff --git a/assets/img/boss/Boss.png b/assets/img/boss/Boss.png new file mode 100644 index 0000000..025a114 Binary files /dev/null and b/assets/img/boss/Boss.png differ diff --git a/assets/img/wiazka/wiazka.png b/assets/img/wiazka/wiazka.png deleted file mode 100644 index a70f3f0..0000000 Binary files a/assets/img/wiazka/wiazka.png and /dev/null differ diff --git a/headers/Boss.h b/headers/Boss.h new file mode 100644 index 0000000..53bee8e --- /dev/null +++ b/headers/Boss.h @@ -0,0 +1,72 @@ +#ifndef BOSS_H +#define BOSS_H + +#include "Actor.h" +#include "Bullet.h" +#include "Beam.h" +#include +#include + +enum class BossDirection { + Up, + Down, + Left, + Right +}; + +class Boss : public Actor { +public: + Boss(int x, int y, const sf::Texture &bossTexture, const sf::Texture &bulletTexture, sf::Texture BombaTexture, + sf::RenderWindow *window); + + void setPlanszaHeight(int height, int width); + + void moveLeft() override; + void moveRight() override; + void moveUp() override; + void moveDown() override; + + + + void shoot() override; + void dropBomb(); + void shootLaser(); + void move(float deltaX, float deltaY) override; + void update(); + void takeDamage(); + bool isAlive() const; + std::vector& getBullets() { return bullets; } + std::vector& getBombs() { return bombs; } + bool isShooting() const { return laserBeam != nullptr; } + Beam* getBeam() const { return laserBeam; } + +private: + float movementSpeed = 5.5f; + BossDirection direction = BossDirection::Down; + + sf::Clock shootClock; + sf::Clock bombClock; + sf::Clock laserClock; + sf::Clock directionClock; + sf::Clock beamDurationClock; + float beamDuration = 1.0f; + + sf::Texture bulletTexture; + sf::Texture BombaTexture; + sf::Texture beamTexture; + sf::RenderWindow* window; + Beam* laserBeam = nullptr; + + std::vector bullets; + std::vector bombs; + + int planszaHeight = 800; + int planszaWidth = 600; + + bool isStationary = false; + + void setRandomDirection(); + void handleBounds(); +}; + +#endif // BOSS_H diff --git a/headers/Plansza.h b/headers/Plansza.h index aeb0739..7803fb4 100644 --- a/headers/Plansza.h +++ b/headers/Plansza.h @@ -18,6 +18,7 @@ #include "PowerUp.h" #include "Debuff.h" #include "Size.h" +#include "Boss.h" enum ships{ nova, @@ -53,16 +54,17 @@ public: void spawn_enemy(); void spawn_advanced_enemy(); void spawn_wiazkowiec(); + void spawn_boss(); void spawn_bomber(); void spawn_kamikadze(); ~Plansza() { - delete ship; // usuwanie wskaźnika ship + delete ship; + delete boss; } static ships selectedShip; static unsigned int score; sf::Font font; - private: Background background; AudioManager audioManager; @@ -100,6 +102,7 @@ private: sf::Texture advancedEnemyTexture; sf::Texture BomberEnemyTexture; sf::Texture BombaTexture; + sf::Texture BossTexture; sf::Texture KamikadzeTexture; sf::Texture WiazkowiecTexture; sf::Texture WiazkaTexture; @@ -127,7 +130,13 @@ private: std::vector debuffs; // Zmienne prymitywne + Boss* boss = nullptr; // Wskaźnik na bossa + sf::Clock bossSpawnClock; // Zegar do spawnowania bossa + unsigned int nextBossScoreThreshold = 1; // Próg punktowy dla spawnu bossa + bool bossSpawned = false; // Flaga informująca, czy boss został już zespawnowany bool gameOver = false; + + // Używane do powerup i debuff flagi struct { bool movingSpeed = false; bool firerate = false; diff --git a/sources/Boss.cpp b/sources/Boss.cpp new file mode 100644 index 0000000..cf9982b --- /dev/null +++ b/sources/Boss.cpp @@ -0,0 +1,188 @@ +#include "../headers/Boss.h" +#include +#include +#include "../headers/RandomNumberGenerator.h" + +Boss::Boss(int x, int y, const sf::Texture& bossTexture, const sf::Texture& bulletTexture, const sf::Texture BombaTexture, sf::RenderWindow* window) + : Actor(x, y, bossTexture), bulletTexture(bulletTexture), BombaTexture(BombaTexture), window(window) { + try { + beamTexture.loadFromFile("../assets/img/wiazka/laser.png"); + } catch (std::exception& e) { + std::cerr << "Failed to load textures: " << e.what() << std::endl; + exit(-500); + } + hp = 100; + firerate = 2000; + movementSpeed = 2.0f; + +} + +void Boss::setPlanszaHeight(int height, int width) { + planszaHeight = height; + planszaWidth = width; +} + +// TODO: Po mergowaniu dodać obsługę pocisku, przy pomocy nowego konstruktora Bullet +void Boss::shoot() { + if (shootClock.getElapsedTime().asMilliseconds() >= firerate) { + Bullet leftBullet(position.x - 20, position.y, bulletTexture); + leftBullet.setSpeed(5.0f); + Bullet centerBullet(position.x, position.y, bulletTexture); + centerBullet.setSpeed(5.0f); + Bullet rightBullet(position.x + 20, position.y, bulletTexture); + rightBullet.setSpeed(5.0f); + bullets.push_back(std::move(leftBullet)); + bullets.push_back(std::move(centerBullet)); + bullets.push_back(std::move(rightBullet)); + std::cout << "Strzal lezy" << std::endl; + shootClock.restart(); + } +} + +void Boss::dropBomb() { + if (bombClock.getElapsedTime().asMilliseconds() >= 5000) { + Bullet Bomb(position.x, position.y, BombaTexture); + Bomb.setSpeed(0.5f); + bombs.emplace_back(std::move(Bomb)); // Można zmienić na bombę + std::cout << "Bombka lezy" << std::endl; + bombClock.restart(); + } +} + +void Boss::shootLaser() { + if (!laserBeam && laserClock.getElapsedTime().asSeconds() >= 5.0f) { + laserBeam = new Beam(position.x, position.y, beamTexture); + beamDurationClock.restart(); + laserClock.restart(); + isStationary = true; + } + + if (laserBeam) { + window->draw(laserBeam->getSprite()); + std::cout << "Laser beam shooted" << std::endl; + + if (beamDurationClock.getElapsedTime().asSeconds() >= beamDuration) { + delete laserBeam; + laserBeam = nullptr; + isStationary = false; + } + } +} + + +void Boss::move(float deltaX, float deltaY) { + if (deltaX == 0 && deltaY == 0) { + std::cerr << "Boss stopped: deltaX and deltaY are both 0" << std::endl; + } + actorSprite.move(deltaX, deltaY); + position.x += static_cast(deltaX); + position.y += static_cast(deltaY); + handleBounds(); +} + +void Boss::update() { + std::cout << "Current movementSpeed: " << movementSpeed << std::endl; + std::cout << "Boss position: (" << position.x << ", " << position.y << "), Direction: " << static_cast(direction) << std::endl; + + if (!isStationary) { + if (directionClock.getElapsedTime().asSeconds() >= 3.0f) { + setRandomDirection(); + directionClock.restart(); + } + + switch (direction) { + case BossDirection::Up: moveUp(); break; + case BossDirection::Down: moveDown(); break; + case BossDirection::Left: moveLeft(); break; + case BossDirection::Right: moveRight(); break; + } + } + + shoot(); + dropBomb(); + shootLaser(); + + // Aktualizacja pocisków + for (auto it = bullets.begin(); it != bullets.end();) { + it->update(); + window->draw(it->getSprite()); + if (it->isOutOfBounds()) { + it = bullets.erase(it); + } else { + ++it; + } + } + + // Aktualizacja bomb + for (auto it = bombs.begin(); it != bombs.end();) { + it->update(); + window->draw(it->getSprite()); + if (it->isOutOfBounds()) { + it = bombs.erase(it); + } else { + ++it; + } + } +} + +void Boss::moveLeft() { + move(-movementSpeed, 0.0f); +} + +void Boss::moveRight() { + move(movementSpeed, 0.0f); +} + +void Boss::moveUp() { + move(0.0f, -movementSpeed); +} + +void Boss::moveDown() { + move(0.0f, movementSpeed); +} + + + +void Boss::takeDamage() { + if (--hp <= 0) { + hp = 0; + std::cout << "HP bossa" << hp << std::endl; + } +} + +bool Boss::isAlive() const { + return hp > 0; +} + +void Boss::setRandomDirection() { + BossDirection previousDirection = direction; + do { + int randomValue = RandomNumberGenerator::getRandomNumber(0, 3); + direction = static_cast(randomValue); + } while ( + (direction == BossDirection::Left && position.x <= 0) || + (direction == BossDirection::Right && position.x >= 600) || + (direction == BossDirection::Up && position.y <= 0) || + (direction == BossDirection::Down && position.y >= 800) + ); + if (previousDirection == direction) { + std::cerr << "Boss kept the same direction: " << static_cast(direction) << std::endl; + } +} + + + +void Boss::handleBounds() { + if (position.x < 0) { + direction = BossDirection::Right; + } else if (position.x > 600 - actorSprite.getGlobalBounds().width) { + direction = BossDirection::Left; + } + + if (position.y < 0) { + direction = BossDirection::Down; + } else if (position.y > 800 - actorSprite.getGlobalBounds().height) { + direction = BossDirection::Up; + } +} + diff --git a/sources/Plansza.cpp b/sources/Plansza.cpp index a83915d..4bb15c0 100644 --- a/sources/Plansza.cpp +++ b/sources/Plansza.cpp @@ -39,6 +39,8 @@ Plansza::Plansza(unsigned int windowHeight, unsigned int windowWidth, sf::Render KamikadzeTexture.loadFromFile("../assets/img/enemy/kamikadze.png"); BombaTexture.loadFromFile("../assets/img/bullets/bomba.png"); WiazkowiecTexture.loadFromFile("../assets/img/enemy/wiazkowiec.png"); + enemyBulletTexture.loadFromFile("../assets/img/bullets/enemy_bullet.png"); + BossTexture.loadFromFile("../assets/img/boss/boss.png"); } catch (std::exception &e) { std::cerr << "Failed to load textures: " << e.what() << std::endl; exit(-500); @@ -116,10 +118,16 @@ void Plansza::update() { ship->update(); // migotanie statku update_score(); // naliczanie punktów // Sprawnowanie wszystkich rodzajów wrogów - // spawn_meteor(); - // spawn_hearts(); + spawn_meteor(); + spawn_hearts(); spawn_power_up(); spawn_debuff(); + // spawn_enemy(); + // spawn_advanced_enemy(); + // spawn_wiazkowiec(); + // spawn_bomber(); + // spawn_kamikadze(); + spawn_boss(); // spawn_enemy(); // spawn_advanced_enemy(); // spawn_wiazkowiec(); @@ -393,6 +401,107 @@ void Plansza::update() { } } + // Aktualizacja bossa + if (boss && boss->isAlive()) { + boss->update(); + window->draw(boss->getSprite()); + + // Kolizje bossa z pociskami + for (auto bulletIt = ship->getBullets().begin(); bulletIt != ship->getBullets().end();) { + if (boss->getSprite().getGlobalBounds().intersects(bulletIt->getSprite().getGlobalBounds())) { + boss->takeDamage(); + bulletIt = ship->getBullets().erase(bulletIt); + } else { + ++bulletIt; + } + } + + for (auto rocketIt = ship->getRockets().begin(); rocketIt != ship->getRockets().end();) { + if (boss->getSprite().getGlobalBounds().intersects(rocketIt->getSprite().getGlobalBounds())) { + boss->takeDamage(); + rocketIt = ship->getRockets().erase(rocketIt); + std::cout << "Rocket hit boss! Boss HP: " << boss->getHP() << std::endl; + } else { + ++rocketIt; + } + } + + // Kolizja bossa z graczem + // if (ship->getSprite().getGlobalBounds().intersects(boss->getSprite().getGlobalBounds())) { + // ship->takeDamage(); + // } + } else if (bossSpawned && (!boss || !boss->isAlive())) { + std::cout << "Boss defeated!" << std::endl; + delete boss; + boss = nullptr; + bossSpawned = false; // Resetowanie flagi, jeśli boss został pokonany + } + + // Obsługa pocisków bossa + if (boss && boss->isAlive()) { + for (auto it = boss->getBullets().begin(); it != boss->getBullets().end();) { + if (ship->getSprite().getGlobalBounds().intersects(it->getSprite().getGlobalBounds())) { + ship->takeDamage(); + it = boss->getBullets().erase(it); // Usuwanie pocisku po trafieniu + } else { + ++it; + } + } + + // Obsługa bomb bossa + for (auto it = boss->getBombs().begin(); it != boss->getBombs().end();) { + if (ship->getSprite().getGlobalBounds().intersects(it->getSprite().getGlobalBounds())) { + ship->takeDamage(); + it = boss->getBombs().erase(it); // Usuwanie bomby po trafieniu + } else { + ++it; + } + } + + + if (boss->isShooting() && boss->getBeam() != nullptr) { + if (ship->getSprite().getGlobalBounds().intersects(boss->getBeam()->getSprite().getGlobalBounds())) { + ship->takeDamage(); // Gracz otrzymuje obrażenia + } + } + + // Sprawdzenie kolizji pocisków gracza z bombami Bossa + for (auto bombIt = boss->getBombs().begin(); bombIt != boss->getBombs().end();) { + bool bombHit = false; + for (auto bulletIt = ship->getBullets().begin(); bulletIt != ship->getBullets().end();) { + if (bombIt->getSprite().getGlobalBounds().intersects(bulletIt->getSprite().getGlobalBounds())) { + ship->getBullets().erase(bulletIt); + bombIt = boss->getBombs().erase(bombIt); + bombHit = true; + break; + } else { + ++bulletIt; + } + } + if (!bombHit) { + ++bombIt; + } + } + + // Sprawdzenie kolizji rakiet gracza z bombami Bossa + for (auto bombIt = boss->getBombs().begin(); bombIt != boss->getBombs().end();) { + bool bombHit = false; + for (auto rocketIt = ship->getRockets().begin(); rocketIt != ship->getRockets().end();) { + if (bombIt->getSprite().getGlobalBounds().intersects(rocketIt->getSprite().getGlobalBounds())) { + ship->getRockets().erase(rocketIt); + bombIt = boss->getBombs().erase(bombIt); + bombHit = true; + break; + } else { + ++rocketIt; + } + } + if (!bombHit) { + ++bombIt; + } + } + } + for (auto &enemy: enemies) { enemy.shoot(); @@ -847,7 +956,7 @@ void Plansza::spawn_enemy() { } void Plansza::spawn_advanced_enemy() { - if (AenemySpawnClock.getElapsedTime().asSeconds() >= 20) { // Spawn co 10 sekund + if (AenemySpawnClock.getElapsedTime().asSeconds() >= 8) { // Spawn co 10 sekund int spawnX = RandomNumberGenerator::getRandomNumber(50, size.width - 50); AEnemies.emplace_back(spawnX, -50, advancedEnemyTexture, enemyBulletTexture); std::cout << "Spawned Advanced Enemy at X: " << spawnX << std::endl; @@ -856,7 +965,7 @@ void Plansza::spawn_advanced_enemy() { } void Plansza::spawn_bomber() { - if (BomberSpawnClock.getElapsedTime().asSeconds() >= 30) { // Spawn co 10 sekund + if (BomberSpawnClock.getElapsedTime().asSeconds() >= 5) { // Spawn co 10 sekund int spawnX = RandomNumberGenerator::getRandomNumber(50, size.width - 50); Bomber bomber(spawnX, -50, BomberEnemyTexture, BombaTexture); bomber.setPlanszaHeight(size.height, size.width); // Przekazanie wysokości i szerokości okna @@ -876,7 +985,7 @@ void Plansza::spawn_kamikadze() { } void Plansza::spawn_wiazkowiec() { - if (WiazkowiecSpawnClock.getElapsedTime().asSeconds() >= 50) { // Spawn co 10 sekund + if (WiazkowiecSpawnClock.getElapsedTime().asSeconds() >= 3) { // Spawn co 10 sekund if (WEnemies.size() < 1) { int spawnX = RandomNumberGenerator::getRandomNumber(50, size.width - 50); Wiazkowiec wiazkowiec(spawnX, -50, WiazkowiecTexture, window); @@ -888,6 +997,17 @@ void Plansza::spawn_wiazkowiec() { } } +void Plansza::spawn_boss() { + if (!bossSpawned && score >= nextBossScoreThreshold) { // Spawn po 60 sekundach + boss = new Boss(size.width / 2, -100, BossTexture, enemyBulletTexture, BombaTexture, window); + boss->setPlanszaHeight(size.width, size.height); + bossSpawned = true; + nextBossScoreThreshold += 1000; + std::cout << "Boss spawned at score: " << score << std::endl; + } +} + + Size Plansza::getSize() { return size; } @@ -977,6 +1097,5 @@ void Plansza::check_Meteor_collisions() { } } - ships Plansza::selectedShip = none; unsigned int Plansza::score = 0; \ No newline at end of file