From 0789859a97b474febcea3ed777d557500c4c72ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=BE=D0=B6=D0=BA=D0=B8=D0=BD=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=9C=D0=B8=D1=85=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=BE=D0=B2=D0=B8=D1=87?= <dmdorozhkin@edu.hse.ru> Date: Tue, 25 Mar 2025 15:46:23 +0300 Subject: [PATCH] added some more templates --- CMakeLists.txt | 6 +++- source/builders.cpp | 1 + source/builders.hpp | 59 ++++++++++++++++++++++++++++++ source/buttonmenu.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++ source/buttonmenu.hpp | 31 ++++++++++++++++ source/commands.cpp | 1 - source/commands.hpp | 6 +++- source/drawable.cpp | 80 ----------------------------------------- source/drawable.hpp | 30 +--------------- source/entities.hpp | 14 ++++++-- source/events.cpp | 1 + source/events.hpp | 23 ++++++++++++ source/gamecontext.hpp | 12 +++++++ source/gamestate.hpp | 19 ++++++++++ source/selectstate.hpp | 3 +- source/states.hpp | 6 ---- source/visitor.cpp | 1 + source/visitor.hpp | 19 ++++++++++ 18 files changed, 272 insertions(+), 122 deletions(-) create mode 100644 source/builders.cpp create mode 100644 source/builders.hpp create mode 100644 source/buttonmenu.cpp create mode 100644 source/buttonmenu.hpp create mode 100644 source/events.cpp create mode 100644 source/events.hpp create mode 100644 source/visitor.cpp create mode 100644 source/visitor.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e93724..1f4b565 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,11 @@ add_executable(application source/main.cpp source/application.cpp source/preparable.cpp source/room.cpp source/gamecontext.cpp - source/gamestate.cpp) + source/gamestate.cpp + source/builders.cpp + source/events.cpp + source/visitor.cpp + source/buttonmenu.cpp) include(FetchContent) set(BUILD_SHARED_LIBS OFF) # Отключаем динамическую линковку (будет статическая) diff --git a/source/builders.cpp b/source/builders.cpp new file mode 100644 index 0000000..1172d5f --- /dev/null +++ b/source/builders.cpp @@ -0,0 +1 @@ +#include "builders.hpp" diff --git a/source/builders.hpp b/source/builders.hpp new file mode 100644 index 0000000..aba5bb0 --- /dev/null +++ b/source/builders.hpp @@ -0,0 +1,59 @@ +#pragma once +#include "states.hpp" +#include "gamestate.hpp" + +class IGameBuilder { +public: + virtual ~IGameBuilder() = default; + virtual void create_rooms() = 0; + virtual void set_room_sides() = 0; + virtual void create_context(float dynamic_object_ratio) = 0; + virtual void create_state(IStateManager& state_manager, sf::VideoMode mode, + std::string window_title) = 0; + virtual void set_all_to_state() = 0; + virtual std::unique_ptr<GameState> get_game() = 0; +}; + +class GameBuilderDirector { +public: + ~GameBuilderDirector() = default; + GameBuilderDirector(std::shared_ptr<IGameBuilder> ptr_builder, sf::VideoMode mode, + std::string title, float dynamic_object_ratio); + std::unique_ptr<GameState> build(); +private: + std::string m_video_title; + sf::VideoMode m_mode; + float m_dynamic_object_ratio; + std::unique_ptr<IGameBuilder> m_ptr_builder; +}; + +class CommonBuilder : public IGameBuilder { +public: + ~CommonBuilder() override = default; + void create_context(float dynamic_object_ratio) override; + void create_state(IStateManager& state_manager, + sf::VideoMode mode, std::string window_title) override; + void set_all_to_state() override; + std::unique_ptr<GameState> get_game() override; +private: + float m_width; + float m_height; + float m_room_size; + std::vector<std::vector<std::unique_ptr<Room>>> m_rooms; + GameContext m_context; + std::unique_ptr<GameState> m_state; +}; + +class SimpleBuilder : public CommonBuilder { +public: + ~SimpleBuilder() override = default; + void create_rooms() override; + void set_room_sides() override; +}; + +class ComplexBuilder : public CommonBuilder { +public: + ~ComplexBuilder() override = default; + void create_rooms() override; + void set_room_sides() override; +}; \ No newline at end of file diff --git a/source/buttonmenu.cpp b/source/buttonmenu.cpp new file mode 100644 index 0000000..617f095 --- /dev/null +++ b/source/buttonmenu.cpp @@ -0,0 +1,82 @@ +#include "buttonmenu.hpp" +#include "config.hpp" + +void Button::draw_into(sf::RenderWindow& window) const { + window.draw(m_rectangle); + window.draw(m_text); +} + +void Button::set(sf::Vector2f pos, sf::Vector2f button_size, std::string text, + size_t font_size, std::unique_ptr<ISelectCommand> ptr_command) { + m_rectangle.setSize(button_size); + m_rectangle.setOrigin(button_size / 2.0f); + m_rectangle.setPosition(pos); + m_rectangle.setOutlineThickness(config::BUTTON_FRAME_THICKNESS); + m_rectangle.setOutlineColor(config::BUTTON_COLOR_FRAME); + m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); //TODO: + + m_text.setString(text); + m_text.setFont(MyFont::Instance()); + m_text.setFillColor(config::BUTTON_COLOR_TEXT); + m_text.setCharacterSize(font_size); + auto bounds = m_text.getLocalBounds(); + m_text.setOrigin(bounds.getPosition() + bounds.getSize() / 2.0f); + m_text.setPosition(pos); + + m_ptr_command = std::move(ptr_command); +} + +void Button::select() { + m_rectangle.setFillColor(config::BUTTON_COLOR_SELECTION); +} + +void Button::unselect() { + m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); +} + +bool Button::is_position_in(sf::Vector2f pos) { + sf::Vector2f diff = pos - m_rectangle.getPosition(); + return abs(diff.x) < m_rectangle.getSize().x / 2.0f && abs(diff.y) < m_rectangle.getSize().y / 2.0f; +} + +void Button::push() { + m_ptr_command->execute(); +} + +Menu::Menu(IStateManager& stateManager): m_state_manager(stateManager) { + float vertical_padding = config::BUTTON_SIZE.x / 10.0f; + float first_pos = (config::SELECT_LEVEL_VIDEO_MODE.height - (m_buttons.size() - 1) * vertical_padding - m_buttons.size() * config::BUTTON_SIZE.y) / 2 + config::BUTTON_SIZE.y / 2; + float hor_pos = config::SELECT_LEVEL_VIDEO_MODE.width / 2.0f; + sf::Vector2f pos = sf::Vector2f(hor_pos, first_pos); + m_buttons[0].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EASY, + config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); + pos.y += vertical_padding + config::BUTTON_SIZE.y; + m_buttons[1].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_MEDIUM, + config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); + pos.y += vertical_padding + config::BUTTON_SIZE.y; + m_buttons[2].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_HARD, + config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); + pos.y += vertical_padding + config::BUTTON_SIZE.y; + m_buttons[3].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EXIT, + config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); +} + + +void Menu::draw_into(sf::RenderWindow& window) const { + for (auto& button: m_buttons) { + button.draw_into(window); + } +} + +void Menu::process_mouse(sf::Vector2f pos, bool is_pressed) { + for (auto& button: m_buttons) { + if (button.is_position_in(pos)) { + button.select(); + if (is_pressed) { + button.push(); + } + } else { + button.unselect(); + } + } +} \ No newline at end of file diff --git a/source/buttonmenu.hpp b/source/buttonmenu.hpp new file mode 100644 index 0000000..314fa97 --- /dev/null +++ b/source/buttonmenu.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "drawable.hpp" +#include "commands.hpp" + +class Button: IDrawable { +public: + ~Button() override = default; + void draw_into(sf::RenderWindow& window) const override; + Button() = default; + void set(sf::Vector2f pos, sf::Vector2f button_size, std::string text, + size_t font_size, std::unique_ptr<ISelectCommand> ptr_command); + void select(); + void unselect(); + bool is_position_in(sf::Vector2f pos); + void push(); +private: + sf::RectangleShape m_rectangle {}; + sf::Text m_text {}; + std::unique_ptr<ISelectCommand> m_ptr_command {}; +}; + +class Menu: public IDrawable { +public: + ~Menu() override = default; + Menu(IStateManager& stateManager); + void draw_into(sf::RenderWindow& window) const override; + void process_mouse(sf::Vector2f pos, bool is_pressed); +private: + std::array<Button, 4> m_buttons {}; + IStateManager& m_state_manager; +}; \ No newline at end of file diff --git a/source/commands.cpp b/source/commands.cpp index 6c022c7..32f8f57 100644 --- a/source/commands.cpp +++ b/source/commands.cpp @@ -9,4 +9,3 @@ void ExitCommand::execute() { ExitCommand::ExitCommand(IStateManager& state_manager): ChangeStateCommand(state_manager) {} void GameCommand::execute() {} -GameCommand::GameCommand(IStateManager& state_manager): ChangeStateCommand(state_manager) {} diff --git a/source/commands.hpp b/source/commands.hpp index 8d2fb26..27bb8a0 100644 --- a/source/commands.hpp +++ b/source/commands.hpp @@ -1,4 +1,5 @@ #pragma once +#include "builders.hpp" #include "states.hpp" class ISelectCommand { @@ -26,5 +27,8 @@ class GameCommand : public ChangeStateCommand { public: ~GameCommand() = default; void execute() override; - GameCommand(IStateManager& state_manager); + GameCommand(IStateManager& state_manager, + std::unique_ptr<GameBuilderDirector> ptr_director); +private: + std::unique_ptr<GameBuilderDirector> m_ptr_director; }; \ No newline at end of file diff --git a/source/drawable.cpp b/source/drawable.cpp index 68432b3..2987116 100644 --- a/source/drawable.cpp +++ b/source/drawable.cpp @@ -12,83 +12,3 @@ MyFont::MyFont() { throw std::runtime_error("Failed to load font"); } - -void Button::draw_into(sf::RenderWindow& window) const { - window.draw(m_rectangle); - window.draw(m_text); -} - -void Button::set(sf::Vector2f pos, sf::Vector2f button_size, std::string text, - size_t font_size, std::unique_ptr<ISelectCommand> ptr_command) { - m_rectangle.setSize(button_size); - m_rectangle.setOrigin(button_size / 2.0f); - m_rectangle.setPosition(pos); - m_rectangle.setOutlineThickness(config::BUTTON_FRAME_THICKNESS); - m_rectangle.setOutlineColor(config::BUTTON_COLOR_FRAME); - m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); //TODO: - - m_text.setString(text); - m_text.setFont(MyFont::Instance()); - m_text.setFillColor(config::BUTTON_COLOR_TEXT); - m_text.setCharacterSize(font_size); - auto bounds = m_text.getLocalBounds(); - m_text.setOrigin(bounds.getPosition() + bounds.getSize() / 2.0f); - m_text.setPosition(pos); - - m_ptr_command = std::move(ptr_command); -} - -void Button::select() { - m_rectangle.setFillColor(config::BUTTON_COLOR_SELECTION); -} - -void Button::unselect() { - m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); -} - -bool Button::is_position_in(sf::Vector2f pos) { - sf::Vector2f diff = pos - m_rectangle.getPosition(); - return abs(diff.x) < m_rectangle.getSize().x / 2.0f && abs(diff.y) < m_rectangle.getSize().y / 2.0f; -} - -void Button::push() { - m_ptr_command->execute(); -} - -Menu::Menu(IStateManager& stateManager): m_state_manager(stateManager) { - float vertical_padding = config::BUTTON_SIZE.x / 10.0f; - float first_pos = (config::SELECT_LEVEL_VIDEO_MODE.height - (m_buttons.size() - 1) * vertical_padding - m_buttons.size() * config::BUTTON_SIZE.y) / 2 + config::BUTTON_SIZE.y / 2; - float hor_pos = config::SELECT_LEVEL_VIDEO_MODE.width / 2.0f; - sf::Vector2f pos = sf::Vector2f(hor_pos, first_pos); - m_buttons[0].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EASY, - config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); - pos.y += vertical_padding + config::BUTTON_SIZE.y; - m_buttons[1].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_MEDIUM, - config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); - pos.y += vertical_padding + config::BUTTON_SIZE.y; - m_buttons[2].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_HARD, - config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); - pos.y += vertical_padding + config::BUTTON_SIZE.y; - m_buttons[3].set(pos, config::BUTTON_SIZE, config::BUTTON_TEXT_EXIT, - config::BUTTON_FONT_SIZE, std::make_unique<ExitCommand>(stateManager)); -} - - -void Menu::draw_into(sf::RenderWindow& window) const { - for (auto& button: m_buttons) { - button.draw_into(window); - } -} - -void Menu::process_mouse(sf::Vector2f pos, bool is_pressed) { - for (auto& button: m_buttons) { - if (button.is_position_in(pos)) { - button.select(); - if (is_pressed) { - button.push(); - } - } else { - button.unselect(); - } - } -} diff --git a/source/drawable.hpp b/source/drawable.hpp index fb4cdc8..e3d333c 100644 --- a/source/drawable.hpp +++ b/source/drawable.hpp @@ -1,7 +1,7 @@ #pragma once #include <SFML/Graphics.hpp> #include <array> -#include "commands.hpp" + class MyFont { public: @@ -14,32 +14,4 @@ private: struct IDrawable { virtual void draw_into(sf::RenderWindow& window) const = 0; virtual ~IDrawable() = default; -}; - -class Button: IDrawable { -public: - ~Button() override = default; - void draw_into(sf::RenderWindow& window) const override; - Button() = default; - void set(sf::Vector2f pos, sf::Vector2f button_size, std::string text, - size_t font_size, std::unique_ptr<ISelectCommand> ptr_command); - void select(); - void unselect(); - bool is_position_in(sf::Vector2f pos); - void push(); -private: - sf::RectangleShape m_rectangle {}; - sf::Text m_text {}; - std::unique_ptr<ISelectCommand> m_ptr_command {}; -}; - -class Menu: public IDrawable { -public: - ~Menu() = default; - Menu(IStateManager& stateManager); - void draw_into(sf::RenderWindow& window) const override; - void process_mouse(sf::Vector2f pos, bool is_pressed); -private: - std::array<Button, 4> m_buttons {}; - IStateManager& m_state_manager; }; \ No newline at end of file diff --git a/source/entities.hpp b/source/entities.hpp index fcf6d0b..5b73db4 100644 --- a/source/entities.hpp +++ b/source/entities.hpp @@ -1,6 +1,7 @@ #pragma once #include "preparable.hpp" #include "room.hpp" +#include "visitor.hpp" class IEntity: public IPreparable { public: @@ -11,37 +12,44 @@ protected: std::shared_ptr<Room> m_ptr_room; }; -class Pacman: public IEntity { +class Pacman: public IEntity, public IVisitor { public: ~Pacman() override = default; void prepare_for_drawing() override; void draw_into(sf::RenderWindow& window) const override; void move(Room::Direction direction); + std::unique_ptr<IGameEvent> visit(std::unique_ptr<Food> ptr_food) override; + std::unique_ptr<IGameEvent> visit(std::unique_ptr<Enemy> ptr_enemy) override; }; -class IStaticEntity: public IEntity { +class IStaticEntity: public IEntity, public IVisitable { public: ~IStaticEntity() override = default; virtual std::unique_ptr<IStaticEntity> clone() = 0; }; -class IDynamicEntity: public IEntity { +class IDynamicEntity: public IEntity, public IVisitable { +public: ~IDynamicEntity() override = default; virtual std::unique_ptr<IDynamicEntity> clone() = 0; virtual void action() = 0; }; class Food: public IStaticEntity { +public: ~Food() override = default; void prepare_for_drawing() override; void draw_into(sf::RenderWindow& window) const override; std::unique_ptr<IStaticEntity> clone() override; + std::unique_ptr<IGameEvent> accept(std::unique_ptr<IVisitor> ptr_visitor) override; }; class Enemy: public IDynamicEntity { +public: ~Enemy() override = default; void prepare_for_drawing() override; void draw_into(sf::RenderWindow& window) const override; std::unique_ptr<IDynamicEntity> clone() override; void action() override; + std::unique_ptr<IGameEvent> accept(std::unique_ptr<IVisitor> ptr_visitor) override; }; \ No newline at end of file diff --git a/source/events.cpp b/source/events.cpp new file mode 100644 index 0000000..8997233 --- /dev/null +++ b/source/events.cpp @@ -0,0 +1 @@ +#include "events.hpp" diff --git a/source/events.hpp b/source/events.hpp new file mode 100644 index 0000000..e2f34b3 --- /dev/null +++ b/source/events.hpp @@ -0,0 +1,23 @@ +#pragma once +#include "gamecontext.hpp" + +class IGameEvent { +public: + virtual ~IGameEvent() = default; + virtual void handle(std::unique_ptr<GameContext> context) = 0; +}; + +class DeleteStaticEntity: public IGameEvent { +public: + ~DeleteStaticEntity() override = default; + void handle(std::unique_ptr<GameContext> context) override; + DeleteStaticEntity(std::shared_ptr<IStaticEntity> ptr_entity); +private: + std::shared_ptr<IStaticEntity> ptr_entity; +}; + +class LostGame: public IGameEvent { + public: + ~LostGame() override = default; + void handle(std::unique_ptr<GameContext> context) override; +}; \ No newline at end of file diff --git a/source/gamecontext.hpp b/source/gamecontext.hpp index 914ef88..5870ee3 100644 --- a/source/gamecontext.hpp +++ b/source/gamecontext.hpp @@ -1,5 +1,6 @@ #pragma once #include "entities.hpp" +#include <stack> class GameContext { public: @@ -9,4 +10,15 @@ public: std::vector<std::unique_ptr<IStaticEntity>> static_objects; std::vector<std::unique_ptr<IDynamicEntity>> dynamic_objects; enum State { INGAME, WIN, LOST } state = INGAME; +}; + +class ContextManager { +public: + void reset(GameContext&& context); + GameContext& get_current_context(); + void save_current_context(); + void restore_previous_context(); +private: + GameContext m_initial_context; + std::stack<GameContext> m_context_stack; }; \ No newline at end of file diff --git a/source/gamestate.hpp b/source/gamestate.hpp index 6f70f09..0516548 100644 --- a/source/gamestate.hpp +++ b/source/gamestate.hpp @@ -1 +1,20 @@ #pragma once +#include "gamecontext.hpp" +#include "maze.hpp" +#include "states.hpp" + +class GameState : public IState, public IWindowKeeper { +public: + ~GameState() override = default; + GameState(IStateManager& state_manager, + sf::VideoMode video_mode, std::string window_title); + bool do_step() override; + void set_maze(Maze&& maze); + void set_context(GameContext&& context); +private: + void event_handling() override; + void update() override; + void render() override; + Maze m_maze; + ContextManager m_context_manager; +}; diff --git a/source/selectstate.hpp b/source/selectstate.hpp index 90c1f43..7ec3f01 100644 --- a/source/selectstate.hpp +++ b/source/selectstate.hpp @@ -1,6 +1,7 @@ #pragma once #include "states.hpp" -#include "drawable.hpp" +// #include "drawable.hpp" +#include "buttonmenu.hpp" class SelectState: public IState, public IWindowKeeper { public: diff --git a/source/states.hpp b/source/states.hpp index 9cbb1d4..ed4f0cd 100644 --- a/source/states.hpp +++ b/source/states.hpp @@ -18,12 +18,6 @@ protected: IStateManager& m_state_manager; }; -class GameState : public IState, public IWindowKeeper { - public: - GameState(IStateManager& state_manager, - sf::VideoMode video_mode, std::string window_title); -}; - class ExitState: public IState { public: ~ExitState() override = default; diff --git a/source/visitor.cpp b/source/visitor.cpp new file mode 100644 index 0000000..0bcb666 --- /dev/null +++ b/source/visitor.cpp @@ -0,0 +1 @@ +#include "visitor.hpp" diff --git a/source/visitor.hpp b/source/visitor.hpp new file mode 100644 index 0000000..67b50c1 --- /dev/null +++ b/source/visitor.hpp @@ -0,0 +1,19 @@ +#pragma once +#include <memory> + +class IGameEvent; +class Food; +class Enemy; + +class IVisitor { +public: + virtual ~IVisitor() = default; + virtual std::unique_ptr<IGameEvent> visit(std::unique_ptr<Food> ptr_food) = 0; + virtual std::unique_ptr<IGameEvent> visit(std::unique_ptr<Enemy> ptr_enemy) = 0; +}; + +class IVisitable { + public: + virtual ~IVisitable() = default; + virtual std::unique_ptr<IGameEvent> accept(std::unique_ptr<IVisitor> ptr_visitor) = 0; +}; \ No newline at end of file -- GitLab