From a223b7253c5c4eb75cdb12a3e99468a9e114eebc Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 9 Aug 2021 21:59:40 +0300 Subject: [PATCH] Separate ClassicNote from its logic to ClassicNoteManager --- CMakeLists.txt | 2 +- include/game/note.h | 28 ----- include/game/spritecontainer.h | 2 +- include/tools/mathutils.h | 4 +- src/classicgame/classicgame.cpp | 8 +- src/classicgame/classicgame.h | 8 +- src/classicgame/classicmapcreator.cpp | 2 +- src/classicgame/classicmapcreator.h | 4 +- src/classicgame/classicnote.cpp | 162 ------------------------- src/classicgame/classicnote.h | 105 ++++++---------- src/classicgame/classicnotemanager.cpp | 149 +++++++++++++++++++++++ src/classicgame/classicnotemanager.h | 30 +++++ src/classicgame/classictimeline.cpp | 2 +- src/classicgame/classictimeline.h | 2 + 14 files changed, 236 insertions(+), 272 deletions(-) delete mode 100644 include/game/note.h delete mode 100644 src/classicgame/classicnote.cpp create mode 100644 src/classicgame/classicnotemanager.cpp create mode 100644 src/classicgame/classicnotemanager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 72319ce..9add6c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_THREAD_LIBS_INIT "-lpthread") set(CMAKE_USE_WIN32_THREADS_INIT 0) set(CMAKE_USE_PTHREADS_INIT 1) set(THREADS_PREFER_PTHREAD_FLAG ON) -file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h") +file(GLOB_RECURSE SOURCES "include/*.h" "src/*.cpp" "src/*.h") # STATIC # # You need to build SFML from sources with cmake diff --git a/include/game/note.h b/include/game/note.h deleted file mode 100644 index 8841145..0000000 --- a/include/game/note.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "game/inputtype.h" - -class Note -{ -public: - explicit Note(microsec perfect_offset) : - _perfect_offset(perfect_offset) {} - virtual ~Note() = default; - - virtual bool isActive() const = 0; - virtual void update(const microsec& music_offset) = 0; - virtual void input(PlayerInput&& inputdata) = 0; - virtual void draw() const = 0; - - virtual void putToGame(const microsec &offset) = 0; - virtual bool isInGame() const = 0; - virtual bool shouldRemove() const = 0; - - microsec offset() const - { - return _perfect_offset; - } - -protected: - microsec _perfect_offset; -}; diff --git a/include/game/spritecontainer.h b/include/game/spritecontainer.h index 2e2141d..b09ec4b 100644 --- a/include/game/spritecontainer.h +++ b/include/game/spritecontainer.h @@ -37,7 +37,7 @@ public: _sprite_dispatcher[action].push(sprite); } - ~SpriteContainer(){} + ~SpriteContainer() = default; private: inline void reallocatePoll(Type sprite_type) diff --git a/include/tools/mathutils.h b/include/tools/mathutils.h index 9ea1b43..bcf58a4 100644 --- a/include/tools/mathutils.h +++ b/include/tools/mathutils.h @@ -5,8 +5,8 @@ using microsec = sf::Int64; struct Coordinates { - float x; - float y; + float x = 0.; + float y = 0.; constexpr inline Coordinates operator+(const Coordinates& right) const noexcept { diff --git a/src/classicgame/classicgame.cpp b/src/classicgame/classicgame.cpp index 930a5c3..be6dbdd 100644 --- a/src/classicgame/classicgame.cpp +++ b/src/classicgame/classicgame.cpp @@ -2,12 +2,14 @@ #include "classictimeline.h" #include "classicnote.h" #include "classicmapcreator.h" +#include "classicnotemanager.h" #include "tools/music.h" #include -ClassicGame::ClassicGame(std::unique_ptr&& manager, std::unique_ptr&& music) : +ClassicGame::ClassicGame(std::shared_ptr&& manager, std::unique_ptr&& music) : _timeline(std::make_unique()), _graphics_manager(std::move(manager)), + _note_manager(std::make_unique(_graphics_manager)), _music(std::move(music)), _is_paused(false) { @@ -100,10 +102,10 @@ void ClassicGame::input(PlayerInput&& inputdata) if (!_timeline->isExpired(note_it)) { auto note = (*note_it); - note->input(std::move(inputdata)); + _note_manager->input(note, std::move(inputdata)); _slap.play(); - if (note->isHold() && note->allElementsPressed()) // also check for Type + if (note->hold && _note_manager->allElementsPressed(note)) // also check for Type { _notes_on_hold.emplace_back(note); std::cout << "HOLD initited by " << inputdata.event.key.code << '\n'; diff --git a/src/classicgame/classicgame.h b/src/classicgame/classicgame.h index 12801d1..7dd687e 100644 --- a/src/classicgame/classicgame.h +++ b/src/classicgame/classicgame.h @@ -2,6 +2,7 @@ #define CLASSICGAME_H #include +#include #include "game/game.h" #include "classicactions.h" #include @@ -11,11 +12,12 @@ class Music; class ClassicNote; class ClassicTimeline; class ClassicGraphicsManager; +class ClassicNoteManager; class ClassicGame final : public Game { public: - explicit ClassicGame(std::unique_ptr&& manager, std::unique_ptr&& music); + explicit ClassicGame(std::shared_ptr&& manager, std::unique_ptr&& music); virtual ~ClassicGame() override; virtual void run() override; @@ -32,7 +34,9 @@ private: std::vector _notes_on_hold; std::unique_ptr _timeline; - std::unique_ptr _graphics_manager; + std::shared_ptr _graphics_manager; + std::shared_ptr _note_manager; + sf::SoundBuffer _slap_buffer; sf::Sound _slap; diff --git a/src/classicgame/classicmapcreator.cpp b/src/classicgame/classicmapcreator.cpp index ee6a380..4cf5155 100644 --- a/src/classicgame/classicmapcreator.cpp +++ b/src/classicgame/classicmapcreator.cpp @@ -1,7 +1,7 @@ #include "classicmapcreator.h" #include "classicnote.h" -ClassicMapCreator::ClassicMapCreator(const std::unique_ptr& manager) : +ClassicMapCreator::ClassicMapCreator(const std::shared_ptr& manager) : _graphics_manager(manager) {} diff --git a/src/classicgame/classicmapcreator.h b/src/classicgame/classicmapcreator.h index 5576d91..bc0f41f 100644 --- a/src/classicgame/classicmapcreator.h +++ b/src/classicgame/classicmapcreator.h @@ -15,12 +15,12 @@ struct Beatmap class ClassicMapCreator { public: - explicit ClassicMapCreator(const std::unique_ptr& manager); + explicit ClassicMapCreator(const std::shared_ptr& manager); Beatmap createBeatmap(const std::string& filepath) const; private: - const std::unique_ptr& _graphics_manager; + const std::shared_ptr _graphics_manager; }; #endif // CLASSICMAPCREATOR_H diff --git a/src/classicgame/classicnote.cpp b/src/classicgame/classicnote.cpp deleted file mode 100644 index 9cc616d..0000000 --- a/src/classicgame/classicnote.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "classicnote.h" -#include "classicsprite.h" -#include "classicgraphicsmanager.h" - -// Replace with interface by dependency injection -#include "classicflyinganimationscenario.h" -#include "classicdyinganimationscenario.h" -// - -ClassicNote::ClassicNote(ClassicNoteInitializer &&init, const std::unique_ptr &manager) : - Note(init.perfect_offset), - _evaluator(init.intervals, _perfect_offset), - _graphics_manager(manager), - _state(State::NONE) -{ - _elements.resize(init.elements.size()); - _isHold = init.hold; - - for (std::size_t i = 0; i < _elements.size(); ++i) - { - _elements[i].keys = init.elements[i].keys; - _elements[i].coordinates = init.elements[i].coordinates; - _elements[i].type = init.elements[i].type; - - // Animations will be injected into note. - _elements[i].animations[State::NONE] = nullptr; - _elements[i].animations[State::FLYING] = std::make_shared(); - _elements[i].animations[State::ACTIVE] = _elements[i].animations[State::FLYING]; - _elements[i].animations[State::DYING] = std::make_shared(); - _elements[i].animations[State::DEAD] = nullptr; - } -} - -bool ClassicNote::isActive() const -{ - return _state == State::ACTIVE; -} - -void ClassicNote::putToGame(const microsec &music_offset) -{ - _state = State::FLYING; - - for (auto& element : _elements) - { - element.sprite = _graphics_manager->getSprite(element.type); - element.sprite->setCoordinates(element.coordinates, 0., 9.); - element.animations[_state]->launch(element.sprite, music_offset, offset()); - } -} - -bool ClassicNote::isInGame() const -{ - return _state == State::FLYING - || _state == State::ACTIVE - || _state == State::DYING; -} - -bool ClassicNote::shouldRemove() const -{ - return _state == State::DEAD; -} - -void ClassicNote::update(const microsec& music_offset) -{ - switch (_state) - { - default: return; - break; - - case State::FLYING: - if (_evaluator.isActive(music_offset)) - _state = State::ACTIVE; - break; - - case State::DYING: - if (_elements[0].animations[_state]->isDone()) - _state = State::DEAD; - break; - - case State::ACTIVE: - if (!_evaluator.isActive(music_offset)) - { - _state = State::DYING; - for (auto& element : _elements) - element.animations[_state]->launch(element.sprite, music_offset, offset()); - } - break; - } - - for (auto& element : _elements) - if (element.animations[_state]) - element.animations[_state]->update(music_offset); -} - -void ClassicNote::input(PlayerInput&& inputdata) -{ - auto grade = ClassicNote::Grade::BAD; - - bool input_valid = std::any_of(_elements.begin(), _elements.end(), - [inputdata=inputdata](auto& element) - { - if (element.pressed) - return false; - - auto key_iterator = std::find(element.keys.begin(), element.keys.end(), inputdata.event.key.code); - bool found_key = key_iterator != element.keys.end(); - if (found_key) - { - element.pressed = true; - element.pressed_as = inputdata.event.key.code; - } - - return found_key; - }); - - bool all_pressed = allElementsPressed(); - - if (all_pressed) - grade = _evaluator.calculatePrecision(inputdata.timestamp); - - if (all_pressed || !input_valid) - { - _state = State::DYING; - for (auto& element : _elements) - element.animations[_state]->launch(element.sprite, inputdata.timestamp, offset()); - } - - std::cout << "User input: " << static_cast(grade) << "\n"; -} - -void ClassicNote::draw() const -{ - for (std::size_t i = 0; i < _elements.size(); ++i) - { - if (i >= 1) - _graphics_manager->drawLine(_elements[i-1].sprite->trailCoordinates(), _elements[i].sprite->trailCoordinates()); - _graphics_manager->draw(_elements[i].sprite); - } -} - -bool ClassicNote::isHold() const -{ - return _isHold; -} - -bool ClassicNote::allElementsPressed() const -{ - return std::all_of(_elements.begin(), _elements.end(), - [](const auto& element) - { - return element.pressed; - }); -} - -bool ClassicNote::isPressedAs(sf::Keyboard::Key key) const -{ - return std::any_of(_elements.begin(), _elements.end(), - [key=key](const auto& element) - { - return key == element.pressed_as; - }); -} diff --git a/src/classicgame/classicnote.h b/src/classicgame/classicnote.h index 85ff6ab..3507002 100644 --- a/src/classicgame/classicnote.h +++ b/src/classicgame/classicnote.h @@ -1,89 +1,56 @@ #pragma once -#include "game/note.h" -#include "game/precisionevaluator.h" #include "classicactions.h" +#include "game/precisionevaluator.h" + +#include "SFML/Window/Keyboard.hpp" #include +#include #include +enum class ClassicGrade +{ + PERFECT, + GOOD, + BAD +}; + +enum ClassicNoteState +{ + NONE, + + FLYING, + ACTIVE, + DYING, + DEAD +}; + class ClassicSprite; -class ClassicGraphicsManager; class ClassicAnimationScenario; -class ClassicNote : public Note +struct ClassicNote { -public: + PrecisionEvaluator evaluator; + std::vector intervals; + bool hold = false; - enum class Grade + ClassicNoteState state = ClassicNoteState::NONE; + + struct Element { - PERFECT, - GOOD, - BAD - }; - - enum State - { - NONE, - - FLYING, - ACTIVE, - DYING, - DEAD - }; - - struct ClassicNoteInitializer - { - std::vector intervals; - microsec perfect_offset; - bool hold; - - struct Element - { - Type type; - Coordinates coordinates; - std::vector falling_curve_interpolation; - std::array keys; - }; - - std::vector elements; - }; - - explicit ClassicNote(ClassicNoteInitializer&& init, const std::unique_ptr& manager); - virtual ~ClassicNote() = default; - - virtual bool isActive() const override; - virtual void update(const microsec &music_offset) override; - virtual void input(PlayerInput&& inputdata) override; - virtual void putToGame(const microsec &music_offset) override; - virtual bool isInGame() const override; - virtual bool shouldRemove() const override; - virtual void draw() const override; - - bool isHold() const; - bool allElementsPressed() const; - bool isPressedAs(sf::Keyboard::Key key) const; - -private: - - struct NoteElement - { - std::shared_ptr sprite; - std::array, 5> animations; - - std::array keys; Coordinates coordinates; // Each note may consist of several buttons. - Type type; // For example, ↑ → or ↓ → ← + // For example, ↑ → or ↓ → ← // Note Element represents this idea. bool pressed = false; // Each ending button in such sequence sf::Keyboard::Key pressed_as; // is an element. + + Type type = Type::NONE; + std::vector falling_curve_interpolation; + std::array available_keys; + std::shared_ptr sprite; + std::array, 5> animations; }; - std::vector _elements; - - const PrecisionEvaluator _evaluator; - const std::unique_ptr& _graphics_manager; - - State _state; - bool _isHold; + std::vector elements; }; diff --git a/src/classicgame/classicnotemanager.cpp b/src/classicgame/classicnotemanager.cpp new file mode 100644 index 0000000..028dd8a --- /dev/null +++ b/src/classicgame/classicnotemanager.cpp @@ -0,0 +1,149 @@ +#include "classicnotemanager.h" +#include "classicsprite.h" +#include "classicgraphicsmanager.h" + +// Replace with interface by dependency injection +#include "classicflyinganimationscenario.h" +#include "classicdyinganimationscenario.h" +// + +ClassicNoteManager::ClassicNoteManager(const std::shared_ptr& manager) : + _graphics_manager(manager) +{} + +ClassicNoteManager::~ClassicNoteManager() +{} + +bool ClassicNoteManager::isActive(const ClassicNote* note) const +{ + return note->state == ClassicNoteState::ACTIVE; +} + +void ClassicNoteManager::putToGame(ClassicNote* note, const microsec &music_offset) +{ + note->state = ClassicNoteState::FLYING; + + for (auto& element : note->elements) + { + element.sprite = _graphics_manager->getSprite(element.type); + element.sprite->setCoordinates(element.coordinates, 0., 9.); + element.animations[note->state]->launch(element.sprite, music_offset, note->evaluator.offset()); + } +} + +bool ClassicNoteManager::isInGame(const ClassicNote* note) const +{ + return note->state == ClassicNoteState::FLYING + || note->state == ClassicNoteState::ACTIVE + || note->state == ClassicNoteState::DYING; +} + +bool ClassicNoteManager::shouldRemove(const ClassicNote* note) const +{ + return note->state == ClassicNoteState::DEAD; +} + +void ClassicNoteManager::update(ClassicNote* note, const microsec& music_offset) +{ + switch (note->state) + { + default: return; + break; + + case ClassicNoteState::FLYING: + if (note->evaluator.isActive(music_offset)) + note->state = ClassicNoteState::ACTIVE; + break; + + case ClassicNoteState::DYING: + if (note->elements[0].animations[note->state]->isDone()) + note->state = ClassicNoteState::DEAD; + break; + + case ClassicNoteState::ACTIVE: + if (!note->evaluator.isActive(music_offset)) + { + note->state = ClassicNoteState::DYING; + for (auto& element : note->elements) + element.animations[note->state]->launch(element.sprite, music_offset, note->evaluator.offset()); + } + break; + } + + for (auto& element : note->elements) + if (element.animations[note->state]) + element.animations[note->state]->update(music_offset); +} + +void ClassicNoteManager::input(ClassicNote* note, PlayerInput&& inputdata) +{ + auto grade = ClassicGrade::BAD; + + bool input_valid = std::any_of(note->elements.begin(), note->elements.end(), + [inputdata=inputdata](auto& element) + { + if (element.pressed) + return false; + + auto key_iterator = std::find(element.keys.begin(), element.keys.end(), inputdata.event.key.code); + bool found_key = key_iterator != element.keys.end(); + if (found_key) + { + element.pressed = true; + element.pressed_as = inputdata.event.key.code; + } + + return found_key; + }); + + bool all_pressed = allElementsPressed(note); + + if (all_pressed) + grade = note->evaluator.calculatePrecision(inputdata.timestamp); + + if (all_pressed || !input_valid) + { + note->state = ClassicNoteState::DYING; + for (auto& element : note->elements) + element.animations[note->state]->launch(element.sprite, inputdata.timestamp, note->evaluator.offset()); + } + + std::cout << "User input: " << static_cast(grade) << "\n"; +} + +void ClassicNoteManager::draw(const ClassicNote* note) const +{ + for (std::size_t i = 0; i < note->elements.size(); ++i) + { + if (i >= 1) + _graphics_manager->drawLine(note->elements[i-1].sprite->trailCoordinates(), note->elements[i].sprite->trailCoordinates()); + _graphics_manager->draw(note->elements[i].sprite); + } +} + +bool ClassicNoteManager::allElementsPressed(const ClassicNote* note) const +{ + return std::all_of(note->elements.begin(), note->elements.end(), + [](const auto& element) + { + return element.pressed; + }); +} + +bool ClassicNoteManager::isPressedAs(const ClassicNote* note, sf::Keyboard::Key key) const +{ + return std::any_of(note->elements.begin(), note->elements.end(), + [key=key](const auto& element) + { + return key == element.pressed_as; + }); +} + +void ClassicNoteManager::setDefaultAnimations(std::array, 5>& animations) const +{ + animations[ClassicNoteState::NONE] = nullptr; + animations[ClassicNoteState::FLYING] = std::make_shared(); + animations[ClassicNoteState::ACTIVE] = animations[ClassicNoteState::FLYING]; + animations[ClassicNoteState::DYING] = std::make_shared(); + animations[ClassicNoteState::DEAD] = nullptr; +} diff --git a/src/classicgame/classicnotemanager.h b/src/classicgame/classicnotemanager.h new file mode 100644 index 0000000..f7353bc --- /dev/null +++ b/src/classicgame/classicnotemanager.h @@ -0,0 +1,30 @@ +#pragma once + +#include "game/inputtype.h" +#include "classicnote.h" +#include + +class ClassicGraphicsManager; + +class ClassicNoteManager +{ +public: + explicit ClassicNoteManager(const std::shared_ptr& manager); + ~ClassicNoteManager(); + + bool isActive(const ClassicNote* note) const; + void update(ClassicNote* note, const microsec &music_offset); + void input(ClassicNote* note, PlayerInput&& inputdata); + void putToGame(ClassicNote* note, const microsec &music_offset); + bool isInGame(const ClassicNote* note) const; + bool shouldRemove(const ClassicNote* note) const; + void draw(const ClassicNote* note) const; + + bool allElementsPressed(const ClassicNote* note) const; + bool isPressedAs(const ClassicNote* note, sf::Keyboard::Key key) const; + + void setDefaultAnimations(std::array, 5>& animations) const; + +private: + const std::shared_ptr _graphics_manager; +}; diff --git a/src/classicgame/classictimeline.cpp b/src/classicgame/classictimeline.cpp index 11c3cbc..f250733 100644 --- a/src/classicgame/classictimeline.cpp +++ b/src/classicgame/classictimeline.cpp @@ -94,7 +94,7 @@ void ClassicTimeline::expire(Iterator& iterator) bool ClassicTimeline::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const { - return ((*iterator)->offset() - _visibility_offset) <= music_offset; + return ((*iterator)->evaluator.offset() - _visibility_offset) <= music_offset; } void ClassicTimeline::fetchVisibleNotes() diff --git a/src/classicgame/classictimeline.h b/src/classicgame/classictimeline.h index ff81800..4cc8d1c 100644 --- a/src/classicgame/classictimeline.h +++ b/src/classicgame/classictimeline.h @@ -6,6 +6,8 @@ #include class ClassicNote; +class ClassicNoteManager; +class ClassicGraphicsManager; class ClassicTimeline : public Timeline {