diff --git a/src/modes/classicmode/classicfactory.cpp b/src/modes/classicmode/classicfactory.cpp index f276f62..e5240a6 100644 --- a/src/modes/classicmode/classicfactory.cpp +++ b/src/modes/classicmode/classicfactory.cpp @@ -6,6 +6,7 @@ #include "core/timeline.h" #include "game/classicgame.h" #include "editor/classiceditor.h" +#include "editor/selectionmanager.h" std::unique_ptr classic::getGame(const std::shared_ptr& core_factory) @@ -15,6 +16,7 @@ std::unique_ptr classic::getGame(const std::shared_ptr(core_factory); const auto timeline = std::make_shared>(); + const auto selection_manager = std::make_shared(); const auto graphics_manager = std::make_shared(timeline, factory, visibility_offset); return std::make_unique(timeline, graphics_manager); @@ -27,7 +29,8 @@ std::unique_ptr classic::getEditor(const std::shared_ptr(core_factory); const auto timeline = std::make_shared>(); + const auto selection_manager = std::make_shared(); const auto graphics_manager = std::make_shared(timeline, factory, visibility_offset); - return std::make_unique(timeline, graphics_manager); + return std::make_unique(timeline, graphics_manager, selection_manager); } diff --git a/src/modes/classicmode/game/classicnote.cpp b/src/modes/classicmode/classicnote.cpp similarity index 76% rename from src/modes/classicmode/game/classicnote.cpp rename to src/modes/classicmode/classicnote.cpp index ea4868f..bc6708a 100644 --- a/src/modes/classicmode/game/classicnote.cpp +++ b/src/modes/classicmode/classicnote.cpp @@ -1,9 +1,8 @@ -#include "classicnote.h" +#include "classicmode/classicnote.h" ClassicNote::ClassicNote(NoteInitializer &&init) : Note(init.perfect_offset), - _state(State::NONE), - _context(init.context) + _state(State::NONE) {} void ClassicNote::setState(ClassicNote::State state) noexcept diff --git a/src/modes/classicmode/editor/classiceditor.cpp b/src/modes/classicmode/editor/classiceditor.cpp index dc81292..99c7cd5 100644 --- a/src/modes/classicmode/editor/classiceditor.cpp +++ b/src/modes/classicmode/editor/classiceditor.cpp @@ -1,8 +1,8 @@ #include "classiceditor.h" -#include "game/classicmocknote.h" +#include "classicmocknote.h" #include "graphics/classicgraphicsmanager.h" -#include "game/classicmapcreator.h" +#include "editor/selectionmanager.h" // Replace with interface by dependency injection #include "graphics/animations/classicflyinganimationscenario.h" @@ -10,9 +10,11 @@ // ClassicEditor::ClassicEditor(const std::shared_ptr>& timeline, - const std::shared_ptr& graphics_manager) : + const std::shared_ptr& graphics_manager, + const std::shared_ptr& selection_manager) : _timeline(timeline), _graphics_manager(graphics_manager), + _selection_manager(selection_manager), _selected_type(Type::UP), _current_time(0), _scroll_step(500000) @@ -66,7 +68,7 @@ ClassicEditor::ClassicEditor(const std::shared_ptr>& init.elements = {element}; - notes.insert(new ClassicMockNote(std::move(init))); + notes.insert(new ClassicMockNote(std::move(init), _selection_manager)); bpm_iterator += tempo_interval; x += 70; @@ -97,6 +99,26 @@ void ClassicEditor::input(kku::GameEvent&& input) default: break; + case kku::SystemEvent::Type::KeyPress: + { + const auto key_data = std::get(input.event.data); + if (key_data.view == kku::SystemEvent::Key::Code::LControl) + { + _selection_manager->enableMultiselection(true); + } + break; + } + + case kku::SystemEvent::Type::KeyRelease: + { + const auto key_data = std::get(input.event.data); + if (key_data.view == kku::SystemEvent::Key::Code::LControl) + { + _selection_manager->enableMultiselection(false); + } + break; + } + case kku::SystemEvent::Type::MousePress: { const auto note = _timeline->getNoteBy(_current_time); @@ -107,7 +129,6 @@ void ClassicEditor::input(kku::GameEvent&& input) init.initializer.intervals = {}; init.initializer.perfect_offset = input.timestamp; init.hold = false; - init.initializer.context = nullptr; element.element.position = std::get(event.data).position; element.element.falling_curve_interpolation = {}; @@ -119,10 +140,14 @@ void ClassicEditor::input(kku::GameEvent&& input) init.elements = { element }; - _timeline->insertNote(new ClassicMockNote(std::move(init))); + _timeline->insertNote(new ClassicMockNote(std::move(init), _selection_manager)); } - else - _graphics_manager->input(std::move(input)); + + if (!_selection_manager->isMultiselectionEnabled()) + _selection_manager->discard(); + + _graphics_manager->input(std::move(input)); + break; } diff --git a/src/modes/classicmode/editor/classiceditor.h b/src/modes/classicmode/editor/classiceditor.h index 21ab6c8..897b8ba 100644 --- a/src/modes/classicmode/editor/classiceditor.h +++ b/src/modes/classicmode/editor/classiceditor.h @@ -5,16 +5,18 @@ #include "core/editor.h" #include "core/timeline.h" -#include "game/classicnote.h" +#include "classicmode/classicnote.h" #include "classicmode/classicactions.h" class ClassicGraphicsManager; +class SelectionManager; class ClassicEditor : public kku::Editor { public: explicit ClassicEditor(const std::shared_ptr>& timeline, - const std::shared_ptr& graphics_manager); + const std::shared_ptr& graphics_manager, + const std::shared_ptr& selection_manager); virtual void input(kku::GameEvent&& input) override; virtual void update(kku::UpdateData&& updatedata) override; @@ -26,10 +28,9 @@ public: private: inline kku::microsec adjustOffset(kku::microsec offset) const noexcept; - Context _context; - const std::shared_ptr> _timeline; const std::shared_ptr _graphics_manager; + const std::shared_ptr _selection_manager; Type _selected_type; kku::microsec _current_time; diff --git a/src/modes/classicmode/game/classicmocknote.cpp b/src/modes/classicmode/editor/classicmocknote.cpp similarity index 73% rename from src/modes/classicmode/game/classicmocknote.cpp rename to src/modes/classicmode/editor/classicmocknote.cpp index 3f6dc77..d1e7c48 100644 --- a/src/modes/classicmode/game/classicmocknote.cpp +++ b/src/modes/classicmode/editor/classicmocknote.cpp @@ -1,10 +1,11 @@ #include "classicmocknote.h" #include "graphics/classicscenegraphicsmanager.h" #include "graphics/animations/classicanimationscenario.h" -#include "holdmanager.h" +#include "editor/selectionmanager.h" -ClassicMockNote::ClassicMockNote(ArrowNoteInitializer&& init) : - ClassicNote(std::move(init.initializer)) +ClassicMockNote::ClassicMockNote(ArrowNoteInitializer&& init, const std::shared_ptr& selection_manager) : + ClassicNote(std::move(init.initializer)), + _selection_manager(selection_manager) { _elements.resize(init.elements.size()); @@ -32,15 +33,29 @@ void ClassicMockNote::input(kku::GameEvent&& input) case kku::SystemEvent::Type::MousePress: { + bool selection_changed = false; + std::size_t amount_selected = 0; + const auto position = std::get(input.event.data).position; for (auto& element : _elements) { if (element.sprite->getRectangle()->contains(position)) { element.selected = !element.selected; - element.selection->toggle(element.selected); + selection_changed = true; + if (element.selected) + ++amount_selected; } } + + if (selection_changed) + { + if (amount_selected > 0) + _selection_manager->fetch(this); + else + _selection_manager->remove(this); + } + break; } @@ -83,3 +98,8 @@ void ClassicMockNote::setGraphics(ClassicGraphicsManager * const manager, kku::T { manager->setGraphics(_elements, std::move(range)); } + +std::vector& ClassicMockNote::getElements() +{ + return _elements; +} diff --git a/src/modes/classicmode/game/classicmocknote.h b/src/modes/classicmode/editor/classicmocknote.h similarity index 65% rename from src/modes/classicmode/game/classicmocknote.h rename to src/modes/classicmode/editor/classicmocknote.h index c9b4f08..620183b 100644 --- a/src/modes/classicmode/game/classicmocknote.h +++ b/src/modes/classicmode/editor/classicmocknote.h @@ -1,15 +1,16 @@ #pragma once -#include "arrowelement.h" -#include "classicnote.h" +#include "classicmode/classicnote.h" #include "mockelement.h" -#include "initializers/arrownoteinitializer.h" +#include "classicmode/arrownoteinitializer.h" + +class SelectionManager; class ClassicMockNote : public ClassicNote { public: - explicit ClassicMockNote(ArrowNoteInitializer&& init); + explicit ClassicMockNote(ArrowNoteInitializer&& init, const std::shared_ptr& selection_manager); virtual ~ClassicMockNote() = default; virtual bool isActive(const kku::microsec& offset) const override; @@ -19,9 +20,11 @@ public: virtual void display(const ClassicGraphicsManager * const manager) const override; virtual void setGraphics(ClassicGraphicsManager * const manager, kku::TimeRange&& range) override; + std::vector& getElements(); + private: + const std::shared_ptr _selection_manager; std::vector _elements; - bool _is_selected; }; using MockElements = std::vector; diff --git a/src/modes/classicmode/game/mockelement.h b/src/modes/classicmode/editor/mockelement.h similarity index 100% rename from src/modes/classicmode/game/mockelement.h rename to src/modes/classicmode/editor/mockelement.h diff --git a/src/modes/classicmode/editor/selectionmanager.cpp b/src/modes/classicmode/editor/selectionmanager.cpp new file mode 100644 index 0000000..835a165 --- /dev/null +++ b/src/modes/classicmode/editor/selectionmanager.cpp @@ -0,0 +1,53 @@ +#include "selectionmanager.h" +#include "classicmocknote.h" +#include "mockelement.h" +#include "graphics/classicselection.h" +#include + +SelectionManager::SelectionManager() : + _multiselection_enabled(false) +{} + +void SelectionManager::discard() +{ + for (const auto& note : _selected_notes) + for (auto& element : note->getElements()) + element.selected = false; + + _selected_notes.clear(); +} + +void SelectionManager::fetch(ClassicMockNote * const note) +{ + bool already_there = std::any_of(_selected_notes.begin(), _selected_notes.end(), + [¬e](const auto& selected_note) + { + return note == selected_note; + }); + + if (!already_there) + _selected_notes.emplace_back(note); +} + +void SelectionManager::remove(ClassicMockNote * const note) +{ + for (std::size_t i = 0; i < _selected_notes.size(); ++i) + { + if (note == _selected_notes.at(i)) + { + _selected_notes[i] = std::move(_selected_notes.back()); + _selected_notes.pop_back(); + break; + } + } +} + +void SelectionManager::enableMultiselection(bool enable) +{ + _multiselection_enabled = enable; +} + +bool SelectionManager::isMultiselectionEnabled() const +{ + return _multiselection_enabled; +} diff --git a/src/modes/classicmode/editor/selectionmanager.h b/src/modes/classicmode/editor/selectionmanager.h new file mode 100644 index 0000000..8238fca --- /dev/null +++ b/src/modes/classicmode/editor/selectionmanager.h @@ -0,0 +1,25 @@ +#pragma once + +#include "classicmocknote.h" + +#include +#include + +class SelectionManager +{ +public: + explicit SelectionManager(); + + // Remove whole selection completely + void discard(); + + void fetch(ClassicMockNote * const note); + void remove(ClassicMockNote * const note); + + void enableMultiselection(bool enable = true); + bool isMultiselectionEnabled() const; + +private: + std::vector _selected_notes; + bool _multiselection_enabled; +}; diff --git a/src/modes/classicmode/game/classicarrownote.cpp b/src/modes/classicmode/game/classicarrownote.cpp index 95accde..fa945e6 100644 --- a/src/modes/classicmode/game/classicarrownote.cpp +++ b/src/modes/classicmode/game/classicarrownote.cpp @@ -3,9 +3,10 @@ #include "graphics/animations/classicanimationscenario.h" #include "holdmanager.h" -ClassicArrowNote::ClassicArrowNote(ArrowNoteInitializer&& init) : +ClassicArrowNote::ClassicArrowNote(ArrowNoteInitializer&& init, const std::shared_ptr& hold_manager) : ClassicNote(std::move(init.initializer)), _evaluator(init.initializer.intervals, _perfect_offset), + _hold_manager(hold_manager), _is_hold(init.hold) { _elements.resize(init.elements.size()); @@ -53,7 +54,7 @@ void ClassicArrowNote::input(kku::GameEvent&& input) { grade = _evaluator.calculatePrecision(input.timestamp); if (isHold()) - _context->hold_manager->emplace(this); + _hold_manager->emplace(this); } if (all_pressed || !input_valid) diff --git a/src/modes/classicmode/game/classicarrownote.h b/src/modes/classicmode/game/classicarrownote.h index 67c4294..614c6ba 100644 --- a/src/modes/classicmode/game/classicarrownote.h +++ b/src/modes/classicmode/game/classicarrownote.h @@ -1,9 +1,11 @@ #pragma once #include "arrowelement.h" -#include "classicnote.h" +#include "classicmode/classicnote.h" -#include "initializers/arrownoteinitializer.h" +#include "classicmode/arrownoteinitializer.h" + +class HoldManager; class ClassicArrowNote : public ClassicNote { @@ -15,7 +17,7 @@ public: BAD }; - explicit ClassicArrowNote(ArrowNoteInitializer&& init); + explicit ClassicArrowNote(ArrowNoteInitializer&& init, const std::shared_ptr& hold_manager); virtual ~ClassicArrowNote() = default; virtual bool isActive(const kku::microsec& offset) const override; @@ -31,6 +33,7 @@ public: private: const kku::PrecisionEvaluator _evaluator; + const std::shared_ptr _hold_manager; std::vector _elements; bool _is_hold; diff --git a/src/modes/classicmode/game/classicgame.cpp b/src/modes/classicmode/game/classicgame.cpp index c8acec6..626dffc 100644 --- a/src/modes/classicmode/game/classicgame.cpp +++ b/src/modes/classicmode/game/classicgame.cpp @@ -1,5 +1,5 @@ #include "classicgame.h" -#include "classicnote.h" +#include "classicmode/classicnote.h" #include "classicmapcreator.h" #include "graphics/classicscenegraphicsmanager.h" #include "holdmanager.h" @@ -16,9 +16,7 @@ ClassicGame::~ClassicGame() void ClassicGame::run() { - _context.hold_manager = _hold_manager; - - auto beatmap = classic::createBeatmap("aa", _context); + auto beatmap = classic::createBeatmap("aa", _hold_manager); _timeline->setNotes(beatmap.notes); } diff --git a/src/modes/classicmode/game/classicgame.h b/src/modes/classicmode/game/classicgame.h index 4717aeb..ed5e8c2 100644 --- a/src/modes/classicmode/game/classicgame.h +++ b/src/modes/classicmode/game/classicgame.h @@ -5,10 +5,7 @@ #include "core/game.h" #include "core/timeline.h" - -#include "classicmode/context.h" -#include "classicnote.h" - +#include "classicmode/classicnote.h" #include "classicmode/classicactions.h" class ClassicGraphicsManager; @@ -30,7 +27,5 @@ public: private: const std::shared_ptr> _timeline; const std::shared_ptr _graphics_manager; - std::shared_ptr _hold_manager; - - Context _context; + const std::shared_ptr _hold_manager; }; diff --git a/src/modes/classicmode/game/classicmapcreator.cpp b/src/modes/classicmode/game/classicmapcreator.cpp index eff9fb3..60c41c1 100644 --- a/src/modes/classicmode/game/classicmapcreator.cpp +++ b/src/modes/classicmode/game/classicmapcreator.cpp @@ -6,7 +6,7 @@ #include "graphics/animations/classicdyinganimationscenario.h" // -auto classic::createBeatmap(const std::string& filepath, const Context &context) -> Beatmap +auto classic::createBeatmap(const std::string& filepath, const std::shared_ptr& hold_manager) -> Beatmap { (void) filepath; @@ -36,7 +36,6 @@ auto classic::createBeatmap(const std::string& filepath, const Context &context) init.initializer.intervals = input_intervals; init.initializer.perfect_offset = bpm_iterator; init.hold = false; - init.initializer.context = &context; element.element.position = kku::Point(x, 390.f); element.element.falling_curve_interpolation = {}; @@ -60,7 +59,7 @@ auto classic::createBeatmap(const std::string& filepath, const Context &context) init.elements = {element}; - notes.insert(new ClassicArrowNote(std::move(init))); + notes.insert(new ClassicArrowNote(std::move(init), hold_manager)); bpm_iterator += tempo_interval; x += 70; diff --git a/src/modes/classicmode/game/classicmapcreator.h b/src/modes/classicmode/game/classicmapcreator.h index c621750..4c8c64a 100644 --- a/src/modes/classicmode/game/classicmapcreator.h +++ b/src/modes/classicmode/game/classicmapcreator.h @@ -3,7 +3,9 @@ #include #include "core/time.h" -#include "classicnote.h" +#include "classicmode/classicnote.h" + +class HoldManager; namespace classic { @@ -14,7 +16,7 @@ struct Beatmap kku::microsec visibility_offset; }; -Beatmap createBeatmap(const std::string& filepath, const Context& context); +Beatmap createBeatmap(const std::string& filepath, const std::shared_ptr& hold_manager); } diff --git a/src/modes/classicmode/graphics/classicgraphicsfactory.cpp b/src/modes/classicmode/graphics/classicgraphicsfactory.cpp index e11e562..efaf9e0 100644 --- a/src/modes/classicmode/graphics/classicgraphicsfactory.cpp +++ b/src/modes/classicmode/graphics/classicgraphicsfactory.cpp @@ -52,5 +52,5 @@ std::shared_ptr ClassicGraphicsFactory::createSprite(Type type) c std::shared_ptr ClassicGraphicsFactory::createSelection() const { const auto shape = _core_factory->getRectangle(); - return std::make_shared(ClassicSelection::Init{shape, kku::Color{51, 153, 255, 0}}); + return std::make_shared(ClassicSelection::Init{shape, kku::Color{51, 153, 255, 120}}); } diff --git a/src/modes/classicmode/graphics/classicscenegraphicsmanager.cpp b/src/modes/classicmode/graphics/classicscenegraphicsmanager.cpp index d7b344a..bc0883b 100644 --- a/src/modes/classicmode/graphics/classicscenegraphicsmanager.cpp +++ b/src/modes/classicmode/graphics/classicscenegraphicsmanager.cpp @@ -1,6 +1,6 @@ #include "classicscenegraphicsmanager.h" -#include "game/mockelement.h" +#include "editor/mockelement.h" #include "game/arrowelement.h" #include "graphics/animations/classicflyinganimationscenario.h" @@ -92,7 +92,6 @@ void ClassicSceneGraphicsManager::display(const std::vector& elemen for (std::size_t i = 0; i < elements.size(); ++i) { const auto& sprite = elements[i].sprite; - const auto& selection = elements[i].selection; if (i >= 1) { @@ -105,7 +104,8 @@ void ClassicSceneGraphicsManager::display(const std::vector& elemen } sprite->display(); - selection->display(); + if (elements[i].selected) + elements[i].selection->display(); } } diff --git a/src/modes/classicmode/graphics/classicscenegraphicsmanager.h b/src/modes/classicmode/graphics/classicscenegraphicsmanager.h index a9473a4..3b9ede7 100644 --- a/src/modes/classicmode/graphics/classicscenegraphicsmanager.h +++ b/src/modes/classicmode/graphics/classicscenegraphicsmanager.h @@ -1,6 +1,6 @@ #pragma once -#include "game/classicnote.h" +#include "classicmode/classicnote.h" #include "graphics/classicgraphicsmanager.h" #include "graphics/classicgraphicsfactory.h" #include "core/timeline.h" diff --git a/src/modes/classicmode/graphics/classicselection.cpp b/src/modes/classicmode/graphics/classicselection.cpp index dd44672..bae13b9 100644 --- a/src/modes/classicmode/graphics/classicselection.cpp +++ b/src/modes/classicmode/graphics/classicselection.cpp @@ -11,7 +11,7 @@ ClassicSelection::ClassicSelection(ClassicSelection::Init&& init) : void ClassicSelection::reset() { _shape->setPosition(kku::Point{0, 0}); - _shape->setColor(kku::Color{0, 0, 0, 0}); + _shape->setColor(kku::Color{51, 153, 255, 120}); } void ClassicSelection::display() const @@ -23,12 +23,3 @@ void ClassicSelection::adjustTo(const std::shared_ptr& sprite) { _shape->setRect(sprite->getRectangle()->getRect()); } - -void ClassicSelection::toggle(bool selected) -{ - _fill_color.alpha = selected - ? 120 - : 0; - - _shape->setColor(_fill_color); -} diff --git a/src/modes/classicmode/graphics/classicselection.h b/src/modes/classicmode/graphics/classicselection.h index b105258..e3808bc 100644 --- a/src/modes/classicmode/graphics/classicselection.h +++ b/src/modes/classicmode/graphics/classicselection.h @@ -23,7 +23,6 @@ public: virtual void display() const override; void adjustTo(const std::shared_ptr& sprite); - void toggle(bool selected); protected: kku::Color _fill_color; diff --git a/src/modes/classicmode/game/initializers/arrowelementinitializer.h b/src/modes/classicmode/include/classicmode/arrowelementinitializer.h similarity index 100% rename from src/modes/classicmode/game/initializers/arrowelementinitializer.h rename to src/modes/classicmode/include/classicmode/arrowelementinitializer.h diff --git a/src/modes/classicmode/game/initializers/arrownoteinitializer.h b/src/modes/classicmode/include/classicmode/arrownoteinitializer.h similarity index 100% rename from src/modes/classicmode/game/initializers/arrownoteinitializer.h rename to src/modes/classicmode/include/classicmode/arrownoteinitializer.h diff --git a/src/modes/classicmode/game/classicnote.h b/src/modes/classicmode/include/classicmode/classicnote.h similarity index 93% rename from src/modes/classicmode/game/classicnote.h rename to src/modes/classicmode/include/classicmode/classicnote.h index 658600c..2168512 100644 --- a/src/modes/classicmode/game/classicnote.h +++ b/src/modes/classicmode/include/classicmode/classicnote.h @@ -3,7 +3,6 @@ #include "core/note.h" #include "core/precisionevaluator.h" #include "classicmode/noteinitializer.h" -#include "classicmode/context.h" class ClassicGraphicsManager; @@ -36,5 +35,4 @@ public: protected: State _state; - const Context *_context; }; diff --git a/src/modes/classicmode/include/classicmode/context.h b/src/modes/classicmode/include/classicmode/context.h deleted file mode 100644 index 9cf2dea..0000000 --- a/src/modes/classicmode/include/classicmode/context.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -class HoldManager; - -struct Context -{ - std::shared_ptr hold_manager; -}; diff --git a/src/modes/classicmode/include/classicmode/noteinitializer.h b/src/modes/classicmode/include/classicmode/noteinitializer.h index 0e46ac1..f806061 100644 --- a/src/modes/classicmode/include/classicmode/noteinitializer.h +++ b/src/modes/classicmode/include/classicmode/noteinitializer.h @@ -3,12 +3,10 @@ #include #include -#include "context.h" #include "core/time.h" struct NoteInitializer { - const Context *context; std::vector intervals; kku::microsec perfect_offset = 0; };