forked from NaiJi/project-kyoku
Separate ClassicNote from its logic to ClassicNoteManager
parent
cf1119c742
commit
a223b7253c
@ -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;
|
|
||||||
};
|
|
@ -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<ClassicGraphicsManager> &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<ClassicFlyingAnimationScenario>();
|
|
||||||
_elements[i].animations[State::ACTIVE] = _elements[i].animations[State::FLYING];
|
|
||||||
_elements[i].animations[State::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
|
|
||||||
_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<int>(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;
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,89 +1,56 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "game/note.h"
|
|
||||||
#include "game/precisionevaluator.h"
|
|
||||||
#include "classicactions.h"
|
#include "classicactions.h"
|
||||||
|
#include "game/precisionevaluator.h"
|
||||||
|
|
||||||
|
#include "SFML/Window/Keyboard.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
class ClassicSprite;
|
enum class ClassicGrade
|
||||||
class ClassicGraphicsManager;
|
|
||||||
class ClassicAnimationScenario;
|
|
||||||
|
|
||||||
class ClassicNote : public Note
|
|
||||||
{
|
{
|
||||||
public:
|
PERFECT,
|
||||||
|
GOOD,
|
||||||
enum class Grade
|
BAD
|
||||||
{
|
};
|
||||||
PERFECT,
|
|
||||||
GOOD,
|
|
||||||
BAD
|
|
||||||
};
|
|
||||||
|
|
||||||
enum State
|
|
||||||
{
|
|
||||||
NONE,
|
|
||||||
|
|
||||||
FLYING,
|
|
||||||
ACTIVE,
|
|
||||||
DYING,
|
|
||||||
DEAD
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ClassicNoteInitializer
|
|
||||||
{
|
|
||||||
std::vector<microsec> intervals;
|
|
||||||
microsec perfect_offset;
|
|
||||||
bool hold;
|
|
||||||
|
|
||||||
struct Element
|
|
||||||
{
|
|
||||||
Type type;
|
|
||||||
Coordinates coordinates;
|
|
||||||
std::vector<Coordinates> falling_curve_interpolation;
|
|
||||||
std::array<sf::Keyboard::Key, 2> keys;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Element> elements;
|
enum ClassicNoteState
|
||||||
};
|
{
|
||||||
|
NONE,
|
||||||
|
|
||||||
explicit ClassicNote(ClassicNoteInitializer&& init, const std::unique_ptr<ClassicGraphicsManager>& manager);
|
FLYING,
|
||||||
virtual ~ClassicNote() = default;
|
ACTIVE,
|
||||||
|
DYING,
|
||||||
|
DEAD
|
||||||
|
};
|
||||||
|
|
||||||
virtual bool isActive() const override;
|
class ClassicSprite;
|
||||||
virtual void update(const microsec &music_offset) override;
|
class ClassicAnimationScenario;
|
||||||
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;
|
struct ClassicNote
|
||||||
bool allElementsPressed() const;
|
{
|
||||||
bool isPressedAs(sf::Keyboard::Key key) const;
|
PrecisionEvaluator<ClassicGrade> evaluator;
|
||||||
|
std::vector<microsec> intervals;
|
||||||
|
bool hold = false;
|
||||||
|
|
||||||
private:
|
ClassicNoteState state = ClassicNoteState::NONE;
|
||||||
|
|
||||||
struct NoteElement
|
struct Element
|
||||||
{
|
{
|
||||||
std::shared_ptr<ClassicSprite> sprite;
|
|
||||||
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> animations;
|
|
||||||
|
|
||||||
std::array<sf::Keyboard::Key, 2> keys;
|
|
||||||
Coordinates coordinates; // Each note may consist of several buttons.
|
Coordinates coordinates; // Each note may consist of several buttons.
|
||||||
Type type; // For example, ↑ → or ↓ → ←
|
// For example, ↑ → or ↓ → ←
|
||||||
// Note Element represents this idea.
|
// Note Element represents this idea.
|
||||||
bool pressed = false; // Each ending button in such sequence
|
bool pressed = false; // Each ending button in such sequence
|
||||||
sf::Keyboard::Key pressed_as; // is an element.
|
sf::Keyboard::Key pressed_as; // is an element.
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<NoteElement> _elements;
|
|
||||||
|
|
||||||
const PrecisionEvaluator<Grade> _evaluator;
|
Type type = Type::NONE;
|
||||||
const std::unique_ptr<ClassicGraphicsManager>& _graphics_manager;
|
std::vector<Coordinates> falling_curve_interpolation;
|
||||||
|
std::array<sf::Keyboard::Key, 2> available_keys;
|
||||||
|
std::shared_ptr<ClassicSprite> sprite;
|
||||||
|
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> animations;
|
||||||
|
};
|
||||||
|
|
||||||
State _state;
|
std::vector<Element> elements;
|
||||||
bool _isHold;
|
|
||||||
};
|
};
|
||||||
|
@ -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<ClassicGraphicsManager>& 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<int>(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<std::shared_ptr<ClassicAnimationScenario>, 5>& animations) const
|
||||||
|
{
|
||||||
|
animations[ClassicNoteState::NONE] = nullptr;
|
||||||
|
animations[ClassicNoteState::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
|
||||||
|
animations[ClassicNoteState::ACTIVE] = animations[ClassicNoteState::FLYING];
|
||||||
|
animations[ClassicNoteState::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
|
||||||
|
animations[ClassicNoteState::DEAD] = nullptr;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "game/inputtype.h"
|
||||||
|
#include "classicnote.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class ClassicGraphicsManager;
|
||||||
|
|
||||||
|
class ClassicNoteManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ClassicNoteManager(const std::shared_ptr<ClassicGraphicsManager>& 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<std::shared_ptr<ClassicAnimationScenario>, 5>& animations) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::shared_ptr<ClassicGraphicsManager> _graphics_manager;
|
||||||
|
};
|
Loading…
Reference in New Issue