diff --git a/Assets/enemy_killer.png b/Assets/enemy_killer.png new file mode 100644 index 0000000000000000000000000000000000000000..036e508d805ebb411173f17acd0bfea0f60bf054 Binary files /dev/null and b/Assets/enemy_killer.png differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 00afc1ec03207b49f2678b603929e1604f965eec..8e1d893d33f07f172b3d6e600d0cd08d080b8e4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,4 +10,5 @@ FetchContent_MakeAvailable(game_pacman) file(GLOB_RECURSE SOURCES config.h sources/*.cpp) add_executable(${PROJECT_NAME} main.cpp ${SOURCES}) -target_link_libraries(game_pacman PUBLIC sfml-window sfml-graphics sfml-system) \ No newline at end of file +target_link_libraries(game_pacman PUBLIC sfml-window sfml-graphics sfml-system) +target_compile_definitions(game_pacman PUBLIC ASSETS_PATH="${CMAKE_INCLUDE_CURRENT_DIR}/Assets/") \ No newline at end of file diff --git a/Sources/Entities/Entities.cpp b/Sources/Entities/Entities.cpp index 95c13062b77cadb1f75f2d13da1c1270baf51f5b..b58f2dd69df0c62aa552f0028aae74b2f01385c8 100644 --- a/Sources/Entities/Entities.cpp +++ b/Sources/Entities/Entities.cpp @@ -13,6 +13,10 @@ Room* IEntity::get_location() { return m_ptr_room; } +sf::Vector2f IEntity::get_position() { + return m_ptr_room->get_position() + sf::Vector2f{ config::ROOM_SIZE / 2 ,config::ROOM_SIZE / 2 }; +} + //ENEMY Enemy::Enemy() { size_t ind = rand() % 4; diff --git a/Sources/Entities/Entities.h b/Sources/Entities/Entities.h index 45ae486adcd118c5f3007c8797a39306ceded7db..85a9af272eafbf85b99594856c4365e0c3e291a0 100644 --- a/Sources/Entities/Entities.h +++ b/Sources/Entities/Entities.h @@ -14,6 +14,7 @@ class IEntity: public IPreparable { //РјР± entity отсюда надо СѓР±СЂР° public: void set_location(Room* ptr_room); Room* get_location(); + sf::Vector2f get_position(); protected: Room* m_ptr_room; //РЅРµ unique тк room без него живет diff --git a/Sources/Entities/Pacman.cpp b/Sources/Entities/Pacman.cpp index 39719677fef68d5b480f95f5cbf04b260f67d3dc..08f11b68953062cdcaf76879f1f0ecc8ba6fdc7c 100644 --- a/Sources/Entities/Pacman.cpp +++ b/Sources/Entities/Pacman.cpp @@ -80,7 +80,7 @@ std::unique_ptr<IGameEvent> Pacman::visit(InvisPotion* ptr_potion) { } void Pacman::become_invisible(GameContext& context) { - if (context.m_inventory.use_potion()) { //если можно применить зелье + if (context.m_inventory.try_use_potion()) { //если можно применить зелье m_ticks = 0; m_visible = 0; m_circle.setFillColor(config::GAME_COLOR_PACMAN_INVISIBLE); @@ -106,4 +106,24 @@ void Pacman::update_color() noexcept { return; } ++m_ticks; +} + +void Pacman::kill_enemy(GameContext& context) { + if (context.dynamic_objects.empty() || !context.m_inventory.try_use_EK()) return; + + sf::Vector2f pacman_pos = context.pacman.get_position(); + float cur_distance, distance = config::GAME_VIDEO_MODE.width; + size_t ind_of_min = 0; + + for (size_t i = 0; i < context.dynamic_objects.size(); ++i) { + auto enemy_pos = context.dynamic_objects.at(i)->get_position(); + cur_distance = std::pow(enemy_pos.x - pacman_pos.x, 2) + std::pow(enemy_pos.y - pacman_pos.y, 2); + if (cur_distance < std::pow(distance, 2)) { + distance = std::sqrt(cur_distance); + ind_of_min = i; + } + } + + auto iter = context.dynamic_objects.begin() + ind_of_min; + context.dynamic_objects.erase(iter); } \ No newline at end of file diff --git a/Sources/Entities/Pacman.h b/Sources/Entities/Pacman.h index 5bd8b537066d11f01678f2078ffe08117f7cad89..562f6e414a0372a75d73591e5d34696d4267fdab 100644 --- a/Sources/Entities/Pacman.h +++ b/Sources/Entities/Pacman.h @@ -1,8 +1,7 @@ #pragma once #include "../Room/Room.h" -#include "Inventory.h" - +#include "../Inventory/Inventory.h" #include "../Events/IGameEvent.h" class GameContext; @@ -40,10 +39,9 @@ public: }; - class Pacman: public IEntity, public IVisitor { public: - Pacman(); + Pacman(); void move(Room::Direction direction); void draw_into(sf::RenderWindow& window) const override; @@ -52,10 +50,11 @@ public: std::unique_ptr<IGameEvent> visit(Enemy* ptr_enemy) override; std::unique_ptr<IGameEvent> visit(InvisPotion* ptr_potion) override; void become_invisible(GameContext& context); - void update_color() noexcept; + void update_color() noexcept; + void kill_enemy(GameContext& context); private: sf::CircleShape m_circle; int m_ticks; bool m_visible = 1; -}; +}; \ No newline at end of file diff --git a/Sources/Font/MyFont.cpp b/Sources/Font/MyFont.cpp index 8df8bea32857fd315a535ae3a3787b9e0944689f..b507e9dfe1f8124273ffeb7303019d039a2426a3 100644 --- a/Sources/Font/MyFont.cpp +++ b/Sources/Font/MyFont.cpp @@ -1,7 +1,9 @@ #include "MyFont.h" +#include <iostream> + MyFont::MyFont() { - std::string path = std::string("../../../assets/") + std::string(config::FONT_FILE); + std::string path = std::string("../../../Assets/") + std::string(config::FONT_FILE); if (!m_font.loadFromFile(path)) throw std::runtime_error("Font installation error"); } @@ -11,6 +13,6 @@ MyFont& MyFont::Instance() { return instance; } -MyFont::operator const sf::Font& () const { +MyFont::operator const sf::Font& () const { return m_font; } \ No newline at end of file diff --git a/Sources/Entities/Inventory.cpp b/Sources/Inventory/Inventory.cpp similarity index 72% rename from Sources/Entities/Inventory.cpp rename to Sources/Inventory/Inventory.cpp index 0a815989bd571ab23f1ece7104d7b687b92ef6f4..d1998367c4567f40a8b7a84ee90ee6bc38c16ed7 100644 --- a/Sources/Entities/Inventory.cpp +++ b/Sources/Inventory/Inventory.cpp @@ -61,6 +61,14 @@ Inventory::Inventory() { m_texts.emplace_back(potion_count); + sf::Vector2f pos_cell3 = m_rects.at(3).getPosition(); + sf::Text EK_count = sf::Text(std::to_string(m_count_of_EK), MyFont::Instance(), FS); + EK_count.setPosition({ pos_cell3.x + CS / 2 - FS + 0.2f * FS, pos_cell3.y + CS / 2 - FS - 0.2f * FS }); + EK_count.setFillColor(config::INVENTORY_COLOR_TEXT); + EK_count.setStyle(sf::Text::Bold); + + m_texts.emplace_back(EK_count); + //food auto food_circle = sf::CircleShape(config::GAME_FOOD_SIZE / 1.5f); food_circle.setOrigin({ config::GAME_FOOD_SIZE / 1.5f, config::GAME_FOOD_SIZE / 1.5f }); @@ -70,11 +78,22 @@ Inventory::Inventory() { m_circles.emplace_back(food_circle); //bottle - m_bottle_texture = &PotionTexture::Instance(); - m_sprite.setOrigin({ m_bottle_texture->get_texture().getSize().x / 2.0f, m_bottle_texture->get_texture().getSize().y / 2.0f }); - m_sprite.setScale({ 0.13f,0.13f }); - m_sprite.setPosition({ pos_cell2.x ,pos_cell2.y }); - m_sprite.setTexture(m_bottle_texture->get_texture()); + m_textures.push_back(&PotionTexture::Instance()); + sf::Sprite sprite; + sprite.setOrigin({ m_textures.at(0)->get_texture().getSize().x / 2.0f, m_textures.at(0)->get_texture().getSize().y / 2.0f }); + sprite.setScale({ 0.13f,0.13f }); + sprite.setPosition({ pos_cell2.x ,pos_cell2.y }); + sprite.setTexture(m_textures.at(0)->get_texture()); + m_sprites.push_back(sprite); + + //enemy killer + sf::Sprite sprite2; + m_textures.push_back(&EnemyKillerTexture::Instance()); + sprite2.setOrigin({ m_textures.at(1)->get_texture().getSize().x / 2.0f, m_textures.at(1)->get_texture().getSize().y / 2.0f }); + sprite2.setScale({ 2.2f,2.2f }); + sprite2.setPosition({ pos_cell3.x ,pos_cell3.y }); + sprite2.setTexture(m_textures.at(1)->get_texture()); + m_sprites.push_back(sprite2); } void Inventory::prepare_for_drawing() { //меняем числа РІ инветаре @@ -99,6 +118,17 @@ void Inventory::prepare_for_drawing() { //меняем числа РІ инвет sf::Vector2f pos = pois_text.getPosition(); pois_text.setPosition({ pos.x - gap * 0.6f * config::INVENTORY_FONT_SIZE, pos.y }); } + + sf::Text& enemy_killer_text = m_texts.at(4); + old_s = enemy_killer_text.getString(); + new_s = std::to_string(m_count_of_EK); + enemy_killer_text.setString(new_s); + + gap = new_s.length() - old_s.length(); + if (gap != 0) { + sf::Vector2f pos = enemy_killer_text.getPosition(); + enemy_killer_text.setPosition({ pos.x - gap * 0.6f * config::INVENTORY_FONT_SIZE, pos.y }); + } } void Inventory::draw_into(sf::RenderWindow& window) const { @@ -110,7 +140,10 @@ void Inventory::draw_into(sf::RenderWindow& window) const { for (const auto& circle : m_circles) { window.draw(circle); } - window.draw(m_sprite); + + for (const auto& sprite : m_sprites) { + window.draw(sprite); + } for (const auto& text : m_texts) { window.draw(text); @@ -121,7 +154,7 @@ void Inventory::get_item(IStaticEntity* item) { item->catched(*this); } -bool Inventory::use_potion() { +bool Inventory::try_use_potion() { if (m_count_of_IP > 0) { --m_count_of_IP; return true; @@ -129,6 +162,14 @@ bool Inventory::use_potion() { return false; } +bool Inventory::try_use_EK() { + if (m_count_of_EK > 0) { + --m_count_of_EK; + return true; + } + return false; +} + void Inventory::increase_IP() { ++m_count_of_IP; } @@ -137,6 +178,12 @@ void Inventory::increase_food() { ++m_count_of_food; } +void Inventory::add_enemy_killer() { + if (m_count_of_food >= config::ENEMY_KILL_COUNT_FOOD) { + m_count_of_food -= config::ENEMY_KILL_COUNT_FOOD; + ++m_count_of_EK; + } +} void Inventory::change_visibility() noexcept { m_visible = !m_visible; diff --git a/Sources/Entities/Inventory.h b/Sources/Inventory/Inventory.h similarity index 68% rename from Sources/Entities/Inventory.h rename to Sources/Inventory/Inventory.h index 1bf09fe4bb4a1b7277a799fddf54c90a3ffa13cb..d872707f43d0ecee65fce031bf35a8688a105fa6 100644 --- a/Sources/Entities/Inventory.h +++ b/Sources/Inventory/Inventory.h @@ -13,13 +13,15 @@ public: void prepare_for_drawing() override; void draw_into(sf::RenderWindow& window) const override; - void set(float count_of_food, float count_of_IP); - Inventory clone(); + void set(float count_of_food, float count_of_IP); //УДАЛРРўР¬ + Inventory clone(); //УДАЛРРўР¬ void get_item(IStaticEntity* item); - bool use_potion(); + bool try_use_potion(); + bool try_use_EK(); void increase_IP(); void increase_food(); + void add_enemy_killer(); void change_visibility() noexcept; bool is_visible() const noexcept; @@ -27,10 +29,14 @@ public: private: size_t m_count_of_food = 0; size_t m_count_of_IP = 0; + size_t m_count_of_EK = 0; //enemy killers static inline bool m_visible = 1; + +private: std::vector<sf::RectangleShape> m_rects; - std::vector<sf::CircleShape> m_circles; - ITexture* m_bottle_texture; - sf::Sprite m_sprite; std::vector<sf::Text> m_texts; + std::vector<sf::CircleShape> m_circles; + + std::vector<sf::Sprite> m_sprites; + std::vector<ITexture*> m_textures; }; \ No newline at end of file diff --git a/Sources/Room/Maze_generator.cpp b/Sources/Room/Maze_generator.cpp index 4b1d5052d108b13b58156f660a3cd10454203911..c604ad09a48b4134519d0b09e2dd23d4424b1c33 100644 --- a/Sources/Room/Maze_generator.cpp +++ b/Sources/Room/Maze_generator.cpp @@ -1,19 +1,6 @@ #include <vector> #include <iostream> -static void print(std::vector<std::vector<bool>> vector) { - for (size_t i = 0; i < vector.size(); ++i) { - for (size_t j = 0; j < vector.at(i).size(); ++j) { - if (vector.at(i).at(j)) { - std::cout << "в–€в–€"; - } - else { - std::cout << " "; - } - } - std::cout << '\n'; - } -} static void pop(std::vector<std::vector<size_t>>& coords, size_t y, size_t x) { //удаляет РёР· вектора точку СЃ координатами x, y for (auto it = coords.begin(); it != coords.end(); ++it) { diff --git a/Sources/States/States.cpp b/Sources/States/States.cpp index e0acc764fdb0d5238c97fbd95382984163271e76..43dc0f519f26139d976bb5718e615377aba14654 100644 --- a/Sources/States/States.cpp +++ b/Sources/States/States.cpp @@ -57,9 +57,14 @@ static void process_key_pressed(sf::Event& event, ContextManager& manager) { manager.get_current_context().pacman.become_invisible(manager.get_current_context()); break; - case sf::Keyboard::Q: + case config::KEY_CONVERT: manager.save_current_context(); + manager.get_current_context().m_inventory.add_enemy_killer(); + break; + case config::KEY_KILL_ENEMY: + manager.save_current_context(); + manager.get_current_context().pacman.kill_enemy(manager.get_current_context()); break; } } diff --git a/Sources/Textures/Texture.cpp b/Sources/Textures/Texture.cpp index 6ee54b4c1de7c8c14a64c5c78a31c4cb392df748..7e1c0526a3976aa55d27b043a8814ca6c1501bb4 100644 --- a/Sources/Textures/Texture.cpp +++ b/Sources/Textures/Texture.cpp @@ -63,4 +63,16 @@ PotionTexture::PotionTexture() { PotionTexture& PotionTexture::Instance() { static PotionTexture instance; return instance; +} + +//EnemyKillerTexture +EnemyKillerTexture::EnemyKillerTexture() { + if (!m_texture.loadFromFile("../../../assets/enemy_killer.png")) { + throw std::runtime_error("Textures loading error\n"); + } +} + +EnemyKillerTexture& EnemyKillerTexture::Instance() { + static EnemyKillerTexture instance; + return instance; } \ No newline at end of file diff --git a/Sources/Textures/Texture.h b/Sources/Textures/Texture.h index 9a1d934de5f47c852daa24fccea1fc9be752ff5b..61d3416133f46022efb366f32c996f793f6bc33a 100644 --- a/Sources/Textures/Texture.h +++ b/Sources/Textures/Texture.h @@ -54,4 +54,12 @@ public: protected: PotionTexture(); +}; + +class EnemyKillerTexture : public ITexture { +public: + static EnemyKillerTexture& Instance(); + +protected: + EnemyKillerTexture(); }; \ No newline at end of file diff --git a/config.h b/config.h index f65546c97da2479cec1265eb031f1840e8e5b3ca..3311a50ad0a7c645a4d05e3daeec971483036931 100644 --- a/config.h +++ b/config.h @@ -2,7 +2,7 @@ #include <SFML/Graphics.hpp> -#define IS_LIGHT 0 +#define IS_LIGHT 1 namespace config { @@ -25,8 +25,8 @@ namespace config { const char MEDIUM_GAME_TITLE[] = "Level: Medium"; const char HARD_GAME_TITLE[] = "Level: Hard"; const float EASY_GAME_ENEMY_RATIO = 0.0f; - const float MEDIUM_GAME_ENEMY_RATIO = 0.03f; - const float HARD_GAME_ENEMY_RATIO = 0.06f; + const float MEDIUM_GAME_ENEMY_RATIO = 0.04f; + const float HARD_GAME_ENEMY_RATIO = 0.07f; const float ROOM_SIZE = 50; const float GAME_ENEMY_SIZE = ROOM_SIZE * 0.7; const float GAME_FOOD_SIZE = ROOM_SIZE * 0.2; @@ -38,6 +38,8 @@ namespace config { const sf::Keyboard::Key KEY_DOWN = sf::Keyboard::S; const sf::Keyboard::Key KEY_INVENTORY = sf::Keyboard::E; const sf::Keyboard::Key KEY_POTION = sf::Keyboard::V; + const sf::Keyboard::Key KEY_CONVERT = sf::Keyboard::C; + const sf::Keyboard::Key KEY_KILL_ENEMY = sf::Keyboard::Q; //Дополнительно: const float GAME_INVIS_POTION_SIZE = ROOM_SIZE * 0.35; @@ -57,6 +59,8 @@ namespace config { const float INVETORY_FRAME_THICKNESS = 1; const size_t INVENTORY_FONT_SIZE = static_cast<size_t>(INVENTORY_HEIGHT / 4.5f); + const size_t ENEMY_KILL_COUNT_FOOD = 50; + // Цвета: #if IS_LIGHT == 1: const sf::Color BUTTON_COLOR_TEXT { 0, 0, 0 };