diff --git a/CMakeLists.txt b/CMakeLists.txt index 522c937..a5a910f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,8 @@ add_executable(LotoStatek main.cpp sources/Wiazkowiec.cpp headers/Beam.h sources/Beam.cpp + headers/Heart.hpp + sources/Heart.cpp ) if(WIN32) diff --git a/assets/img/hearts/heart_gray.png b/assets/img/hearts/heart_gray.png new file mode 100644 index 0000000..851ca81 Binary files /dev/null and b/assets/img/hearts/heart_gray.png differ diff --git a/headers/Actor.h b/headers/Actor.h index 7b26426..4f6a82e 100644 --- a/headers/Actor.h +++ b/headers/Actor.h @@ -5,12 +5,20 @@ #include "SFML/Graphics/Texture.hpp" #include "Bullet.h" #include "Position.h" +#include "SFML/System/Clock.hpp" class Actor { public: Actor(int x, int y, const sf::Texture& texture); + + + sf::Sprite& getSprite(); + unsigned int getHP(); + Position getPosition(); + std::vector& getBullets(); + virtual void move(float deltaX, float deltaY) = 0; virtual void moveLeft() = 0; virtual void moveRight() = 0; @@ -21,8 +29,10 @@ public: std::vector& getBullets(); sf::Sprite& getSprite(); Position getPosition(); - + void updateBullets(); void setMovingSpeed(float speed); + void dealDamage(); + void healUP(); void updateBullets(); @@ -33,7 +43,9 @@ protected: sf::Texture bulletTexture; sf::Texture rocketTexture; std::vector bullets; - unsigned int hp; + sf::Clock damageDealClock; + Position position; + int hp; unsigned int damage; unsigned int firerate; float moving_speed; diff --git a/headers/Heart.hpp b/headers/Heart.hpp new file mode 100644 index 0000000..ecaf8b6 --- /dev/null +++ b/headers/Heart.hpp @@ -0,0 +1,14 @@ +#ifndef LOTOSTATEK_HEART_HPP +#define LOTOSTATEK_HEART_HPP + +#include "SFML/Graphics/Texture.hpp" +#include "SFML/Graphics/Sprite.hpp" +#include "ObjectItem.hpp" + +class Heart : public ObjectItem { +public: + Heart(float x, float y, sf::Texture &texture); + void update(); +}; + +#endif //LOTOSTATEK_HEART_HPP diff --git a/headers/Plansza.h b/headers/Plansza.h index 7379d8a..d67a800 100644 --- a/headers/Plansza.h +++ b/headers/Plansza.h @@ -14,6 +14,7 @@ #include "Background.h" #include "AudioManager.h" #include "Plansza.h" +#include "Heart.hpp" #include "Size.h" class Plansza { @@ -22,7 +23,9 @@ public: Size getSize(); std::vector &getMeteors(); void spawn_meteor(); + void spawn_hearts(); void update_meteors(); + void update_hearts(); void update(); void update_score(); void setOutOfBounds(bool status); @@ -36,14 +39,19 @@ public: delete ship; // usuwanie wskaźnika ship } private: - Size size; + sf::RenderWindow *window; Background background; AudioManager audioManager; Player *ship; sf::RenderWindow *window; sf::Font font; + Size size; sf::Texture meteorTexture1; sf::Texture meteorTexture2; + sf::Texture heartTexture; + sf::Texture heartTextureGray; + sf::Clock meteorSpawnClock; + sf::Clock heartSpawnClock; sf::Texture enemyBulletTexture; sf::Texture WiazkaTexture; sf::Texture BombaTexture; @@ -69,6 +77,9 @@ private: std::vector KEnemies; std::vector WEnemies; std::vector meteors; + std::vector hearts; + std::vector heartStats; + bool gameOver = false; unsigned int score = 0; bool isPlayerSpawned = false; }; diff --git a/headers/Player.h b/headers/Player.h index 6698580..f0a566d 100644 --- a/headers/Player.h +++ b/headers/Player.h @@ -1,6 +1,7 @@ #ifndef LOTOSTATEK_PLAYER_H #define LOTOSTATEK_PLAYER_H + #include #include @@ -30,6 +31,8 @@ public: void takeDamage(); bool isAlive() const; void update(); + void onHit(); + void update(); std::vector& getRockets(); void loadTexture(); @@ -39,6 +42,9 @@ private: std::vector rockets; sf::Texture rocketTexture; sf::Texture bulletTexture; + bool isBlinking = false; + sf::Color originalColor; + sf::Clock immortalityClock; // Zegar kontrolujący czas nieśmiertelności int health = 3; // Liczba punktów życia gracza float immortalityDuration = 1.5f; // Czas trwania nieśmiertelności w sec diff --git a/sources/Actor.cpp b/sources/Actor.cpp index 74535fb..d8dbec4 100644 --- a/sources/Actor.cpp +++ b/sources/Actor.cpp @@ -21,6 +21,25 @@ Position Actor::getPosition() { return {position.x, position.y}; } +unsigned int Actor::getHP() { + return hp; +} + +void Actor::dealDamage() { + if(damageDealClock.getElapsedTime().asSeconds() > 1.5) { + if(hp > 0) { + hp--; + } + damageDealClock.restart(); + } +} + +void Actor::healUP() { + if(hp < 3){ + hp++; + } +} + std::vector &Actor::getBullets() { return bullets; } @@ -39,4 +58,4 @@ void Actor::updateBullets() { void Actor::setMovingSpeed(float speed) { moving_speed = speed; -} +} \ No newline at end of file diff --git a/sources/Heart.cpp b/sources/Heart.cpp new file mode 100644 index 0000000..4a61998 --- /dev/null +++ b/sources/Heart.cpp @@ -0,0 +1,23 @@ +#include "../headers/Heart.hpp" + +Heart::Heart(float x, float y, sf::Texture &texture) : ObjectItem(x,y,texture) { + outOfBounds = false; + texture = texture; + sprite.setTexture(texture); + sprite.setOrigin(sprite.getLocalBounds().width / 2, sprite.getLocalBounds().height / 2); // wycentrowanie sprite + sprite.setPosition(x, y); + movingSpeed = 3.0f; +// sprite.scale(0.05f, 0.05f); + position.x = x; + position.y = y; + rotationSpeed = 0; +} + +void Heart::update() { + sprite.move(0.0f, movingSpeed); // przesunięcie sprajta + position.y += int(movingSpeed); // przesunięcie pozycji +// sprite.rotate(rotationSpeed); // obracanie tym meteorkiem pięknym + if(position.y > 900) { + outOfBounds = true; // jeżeli wyszedł poza granice ekranu ustaw tą zmienną + } +} \ No newline at end of file diff --git a/sources/Plansza.cpp b/sources/Plansza.cpp index 1dba665..a1d4477 100644 --- a/sources/Plansza.cpp +++ b/sources/Plansza.cpp @@ -40,6 +40,20 @@ Plansza::Plansza(unsigned int windowHeight, unsigned int windowWidth, sf::Render audioManager.loadSoundEffect("shoot_alt", "../assets/sounds/shoot_alt.ogg"); audioManager.loadSoundEffect("fail", "../assets/sounds/fail.mp3"); + meteorTexture1.loadFromFile("../assets/img/meteors/meteor-1.png"); + meteorTexture2.loadFromFile("../assets/img/meteors/meteor-2.png"); + + heartTexture.loadFromFile("../assets/img/hearts/heart.png"); + heartTextureGray.loadFromFile("../assets/img/hearts/heart_gray.png"); + + heartStats.emplace_back(sf::Sprite(heartTexture)); + heartStats.emplace_back(sf::Sprite(heartTexture)); + heartStats.emplace_back(sf::Sprite(heartTexture)); + heartStats[0].setPosition(565, 10); + heartStats[1].setPosition(530, 10); + heartStats[2].setPosition(495, 10); + + meteorSpawnClock.restart(); spawn_player(); spawnClock.restart(); @@ -88,19 +102,26 @@ void Plansza::update() { update_score(); // naliczanie punktów // Sprawnowanie wszystkich rodzajów wrogów spawn_meteor(); + spawn_hearts(); spawn_enemy(); spawn_advanced_enemy(); spawn_wiazkowiec(); spawn_bomber(); spawn_kamikadze(); + + + // utrzymanie meteorów i pocisków w ruchu - for (auto &meteor: getMeteors()) { + for (auto &meteor: meteors) { meteor.update(); window->draw(meteor.getSprite()); } - for (auto &bullet: ship->getBullets()) { + for (auto &heart: hearts) { + heart.update(); + window->draw(heart.getSprite()); + }for (auto &bullet: ship->getBullets()) { bullet.update(); window->draw(bullet.getSprite()); } @@ -112,12 +133,64 @@ void Plansza::update() { // Sprawdzenie czy meteory i pociski są poza granicami ekranu update_meteors(); + update_hearts(); ship->updateBullets(); + ship.update(); window->draw(ship->getSprite()); - for (auto &meteor: getMeteors()) { + for (auto &meteor: meteors) { if (ship->getSprite().getGlobalBounds().intersects(meteor.getSprite().getGlobalBounds())) { + ship.onHit(); + } + } + + for (auto heartIt = hearts.begin(); heartIt != hearts.end();) { + if (ship.getSprite().getGlobalBounds().intersects(heartIt->getSprite().getGlobalBounds())) { + ship.healUP(); + heartIt = hearts.erase(heartIt); + } else { + ++heartIt; + } + } + + for (auto meteorIt = getMeteors().begin(); meteorIt != getMeteors().end();) { + bool meteorHit = false; + for (auto rocketIt = ship.getBullets().begin(); rocketIt != ship.getBullets().end();) { + if (meteorIt->getSprite().getGlobalBounds().intersects(rocketIt->getSprite().getGlobalBounds())) { + ship.getBullets().erase(rocketIt); + meteorIt = getMeteors().erase(meteorIt); + meteorHit = true; + break; + } else { + ++rocketIt; + } + } + if (!meteorHit) { + ++meteorIt; + } + } + + for (auto meteorIt = getMeteors().begin(); meteorIt != getMeteors().end();) { + bool meteorHit = false; + for (auto rocketIt = ship.getRockets().begin(); rocketIt != ship.getRockets().end();) { + if (meteorIt->getSprite().getGlobalBounds().intersects(rocketIt->getSprite().getGlobalBounds())) { + ship.getRockets().erase(rocketIt); + meteorIt = getMeteors().erase(meteorIt); + meteorHit = true; + break; + } else { + ++rocketIt; + } + } + if (!meteorHit) { + ++meteorIt; + } + } + + + // Warunek przeganej + if (gameOver) { ship->takeDamage(); } } @@ -588,8 +661,36 @@ void Plansza::update() { } -} + // Statystyka życia gracza (wyświetlanie) + switch (ship.getHP()) { + case 0: + heartStats[0].setTexture(heartTextureGray); + heartStats[1].setTexture(heartTextureGray); + heartStats[2].setTexture(heartTextureGray); + gameOver = true; + break; + case 1: + heartStats[0].setTexture(heartTexture); + heartStats[1].setTexture(heartTextureGray); + heartStats[2].setTexture(heartTextureGray); + break; + case 2: + heartStats[0].setTexture(heartTexture); + heartStats[1].setTexture(heartTexture); + heartStats[2].setTexture(heartTextureGray); + break; + case 3: + heartStats[0].setTexture(heartTexture); + heartStats[1].setTexture(heartTexture); + heartStats[2].setTexture(heartTexture); + break; + } + + for (const auto& heart: heartStats) { + window->draw(heart); + } +} // Meteor-related niżej @@ -603,7 +704,7 @@ void Plansza::update_meteors() { } void Plansza::spawn_meteor() { - if (spawnClock.getElapsedTime().asSeconds() > rand() % 10 + 1) { // randomowy spawn meteorytów od 10 do 1 sekundy + if (meteorSpawnClock.getElapsedTime().asSeconds() > rand() % 10 + 1) { // randomowy spawn meteorytów od 10 do 1 sekundy if (meteors.size() < 5) { // jeśli jest mniej niż 5 meteorów na planszy if (rand() % 2 == 1) { meteors.emplace_back(RandomNumberGenerator::getRandomNumber(50, 499), -100, meteorTexture2); @@ -611,7 +712,16 @@ void Plansza::spawn_meteor() { meteors.emplace_back(RandomNumberGenerator::getRandomNumber(50, 499), -100, meteorTexture1); } } - spawnClock.restart(); + meteorSpawnClock.restart(); + } +} + +void Plansza::spawn_hearts() { + if (heartSpawnClock.getElapsedTime().asSeconds() > rand() % 10 + 1) { // randomowy spawn meteorytów od 10 do 1 sekundy + if (hearts.size() < 5) { // jeśli jest mniej niż 5 meteorów na planszy + hearts.emplace_back(RandomNumberGenerator::getRandomNumber(50, 499), -100, heartTexture); + } + heartSpawnClock.restart(); } } @@ -673,6 +783,15 @@ void Plansza::spawn_wiazkowiec() { } +void Plansza::update_hearts() { + // usuwanie serduszek które wyleciały poza ekran + for (auto& heart : hearts) { + if(heart.getStatus()) { + hearts.erase(hearts.begin()); + } + } +} + Size Plansza::getSize() { return size; } diff --git a/sources/Player.cpp b/sources/Player.cpp index a5149e5..6cb593c 100644 --- a/sources/Player.cpp +++ b/sources/Player.cpp @@ -1,3 +1,5 @@ +#include + #include "../headers/Player.h" #include @@ -19,6 +21,9 @@ Player* Player::getInstance(int x, int y, const sf::Texture& texture) { void Player::loadTexture() { bulletTexture.loadFromFile("../assets/img/bullets/bullet_pink.png"); rocketTexture.loadFromFile("../assets/img/rockets/Rocket_111.png"); + hp = 3; + damageDealClock.restart(); + originalColor = actorSprite.getColor(); } void Player::shoot() { @@ -79,6 +84,27 @@ bool Player::isAlive() const { return health > 0; } +void Player::onHit() { + dealDamage(); + isBlinking = true; +} + +void Player::update() { + if (isBlinking) { + auto elapsed = damageDealClock.getElapsedTime().asMilliseconds(); + if (elapsed < 1000) { // miganie przez 1 sekundę + if ((elapsed / 100) % 2 == 0) { + actorSprite.setColor(sf::Color(255, 255, 255, 128)); // półprzeźroczysty + } else { + actorSprite.setColor(originalColor); // oryginalny kolor + } + } else { + isBlinking = false; + actorSprite.setColor(originalColor); // przywróć oryginalny kolor + } + } +} + void Player::setFirerate(unsigned int firerate) { this->firerate = firerate; }